diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index bea94e089..677f0e5a5 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -2,9 +2,9 @@ name: Build and upload jar on: push: - branches: [ master ] + branches: [ dev ] pull_request: - branches: [ master ] + branches: [ dev ] jobs: build: diff --git a/AnarchyExploitFixesFolia/build.gradle.kts b/AnarchyExploitFixesFolia/build.gradle.kts index 271e81d8e..389677fa0 100755 --- a/AnarchyExploitFixesFolia/build.gradle.kts +++ b/AnarchyExploitFixesFolia/build.gradle.kts @@ -36,7 +36,6 @@ tasks.shadowJar { relocate("org.reflections", "me.xginko.aef.libs.reflections") relocate("org.apache.commons.math3", "me.xginko.aef.libs.fastmath") relocate("com.github.benmanes.caffeine", "me.xginko.aef.libs.caffeine") - relocate("io.papermc.lib", "me.xginko.aef.libs.paperlib") relocate("de.tr7zw.changeme.nbtapi", "me.xginko.aef.libs.nbtapi") relocate("org.bstats", "me.xginko.aef.libs.bstats") relocate("com.cryptomorin.xseries", "me.xginko.aef.libs.xseries") diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/AnarchyExploitFixes.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/AnarchyExploitFixes.java index 4158a0432..c3da0514c 100755 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/AnarchyExploitFixes.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/AnarchyExploitFixes.java @@ -2,14 +2,15 @@ import com.github.retrooper.packetevents.PacketEvents; import io.github.retrooper.packetevents.factory.spigot.SpigotPacketEventsBuilder; -import io.papermc.lib.PaperLib; import me.xginko.aef.commands.AEFCommand; import me.xginko.aef.config.Config; import me.xginko.aef.config.LanguageCache; import me.xginko.aef.enums.AEFPermission; +import me.xginko.aef.listeners.AEFListener; import me.xginko.aef.modules.AEFModule; import me.xginko.aef.utils.CachingPermTool; import me.xginko.aef.utils.KyoriUtil; +import me.xginko.aef.utils.PlatformUtil; import me.xginko.aef.utils.tickdata.TickReporter; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.logger.slf4j.ComponentLogger; @@ -48,10 +49,11 @@ public final class AnarchyExploitFixes extends JavaPlugin { private static CachingPermTool cachingPermTool; private static ComponentLogger prefixedLogger, unPrefixedLogger; private static Metrics metrics; - private static boolean isServerFolia, isPacketEventsInstalled; + private static boolean isPacketEventsInstalled; @Override public void onLoad() { + PlatformUtil.load(); prefixedLogger = ComponentLogger.logger(getLogger().getName()); unPrefixedLogger = ComponentLogger.logger(""); // Disable logging for some shaded libraries as those can get very verbose @@ -89,6 +91,7 @@ public void onEnable() { instance = this; cachingPermTool = CachingPermTool.enable(this); + metrics = new Metrics(this, 8700); Stream.of(" ", " ", @@ -101,28 +104,23 @@ public void onEnable() { " " ).map(str -> Component.text(str).color(KyoriUtil.AEF_WHITE)).forEach(prefixedLogger::info); - if (!PaperLib.isPaper()) { + if (!PlatformUtil.isPaper()) { prefixedLogger.error("This plugin depends on Paper's API, which is not present on your server."); - PaperLib.suggestPaper(this, java.util.logging.Level.SEVERE); getServer().getPluginManager().disablePlugin(this); return; } - prefixedLogger.info("Detected Version 1.{}.{}", PaperLib.getMinecraftVersion(), PaperLib.getMinecraftPatchVersion()); + prefixedLogger.info("Detected Version 1.{}.{}", PlatformUtil.getMinecraftVersion(), PlatformUtil.getMinecraftPatchVersion()); - if (PaperLib.getMinecraftVersion() < 19) { + if (PlatformUtil.getMinecraftVersion() < 19) { prefixedLogger.error("The Folia jar is intended for Paper and Folia servers running 1.19 and above."); prefixedLogger.error("Please replace it with the Legacy jar."); getServer().getPluginManager().disablePlugin(this); return; } - try { - Class.forName("io.papermc.paper.threadedregions.RegionizedServer"); - prefixedLogger.info("Detected Folia server"); - isServerFolia = true; - } catch (ClassNotFoundException e) { - isServerFolia = false; + if (PlatformUtil.isFolia()) { + prefixedLogger.info("Detected Folia server."); } try { @@ -153,14 +151,16 @@ public void onEnable() { @Override public void onDisable() { - AEFModule.ENABLED_MODULES.forEach(AEFModule::disable); - AEFModule.ENABLED_MODULES.clear(); if (isPacketEventsInstalled) { + AEFModule.ENABLED_MODULES.forEach(AEFModule::disable); + AEFModule.ENABLED_MODULES.clear(); + AEFListener.LISTENERS.forEach(AEFListener::disable); + AEFListener.LISTENERS.clear(); PacketEvents.getAPI().terminate(); } - if (metrics != null) { - metrics.shutdown(); - metrics = null; + if (languageCacheMap != null) { + languageCacheMap.clear(); + languageCacheMap = null; } if (cachingPermTool != null) { cachingPermTool.disable(); @@ -170,41 +170,48 @@ public void onDisable() { tickReporter.disable(); tickReporter = null; } + if (metrics != null) { + metrics.shutdown(); + metrics = null; + } + unPrefixedLogger = null; + prefixedLogger = null; instance = null; config = null; - languageCacheMap = null; - prefixedLogger = null; - unPrefixedLogger = null; } public static AnarchyExploitFixes getInstance() { return instance; } + public static TickReporter getTickReporter() { return tickReporter; } + public static Config config() { return config; } + public static ComponentLogger getPrefixedLogger() { return prefixedLogger; } + public static ComponentLogger getUnprefixedLogger() { return unPrefixedLogger; } + public static LanguageCache getLang(Locale locale) { return getLang(locale.toString().toLowerCase()); } + public static LanguageCache getLang(CommandSender commandSender) { return commandSender instanceof Player player ? getLang(player.locale()) : getLang(config.default_lang); } + public static LanguageCache getLang(String lang) { if (!config.auto_lang) return languageCacheMap.get(config.default_lang.toString().toLowerCase()); return languageCacheMap.getOrDefault(lang.replace("-", "_"), languageCacheMap.get(config.default_lang.toString().toLowerCase())); } - public static boolean isServerFolia() { - return isServerFolia; - } public void createDirectory(File dir) throws IOException { try { @@ -225,9 +232,8 @@ private void reloadConfiguration() { config = new Config(); if (tickReporter != null) tickReporter.disable(); tickReporter = TickReporter.create(this, config.tickData_cache_duration); + AEFListener.reloadListeners(); AEFModule.reloadModules(); - if (metrics != null) metrics.shutdown(); - metrics = new Metrics(this, 8700); config.saveConfig(); } catch (Throwable t) { prefixedLogger.error("Failed while loading config!", t); diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/commands/aef/AEFCmd.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/commands/aef/AEFCmd.java index 41c7612dc..6e3b87525 100644 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/commands/aef/AEFCmd.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/commands/aef/AEFCmd.java @@ -84,6 +84,8 @@ public void enable() { return subCommand.tabComplete(sender, alias, args); } } + + return tabCompletes.stream().filter(cmd -> cmd.startsWith(args[0])).toList(); } return Collections.emptyList(); diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/config/Config.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/config/Config.java index d523023a7..20d2716ae 100755 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/config/Config.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/config/Config.java @@ -20,7 +20,7 @@ public class Config { public final Sound elytra_too_fast_sound; public final Component cmd_say_format; public final Duration tickData_cache_duration; - public final long elytra_speed_calc_period; + public final long elytra_speed_calc_period, elytra_old_chunk_limit; public final int nether_ceiling_max_y, elytra_spawn_radius; public final boolean auto_lang, packets_disabled, connectionMsgsAreOnByDefault, cmd_say_enabled, cmd_help_enabled, cmd_toggleConMsgs_enabled, @@ -29,9 +29,11 @@ public class Config { elytra_teleport_back, elytra_calculate_3D; public Config() throws Exception { + AnarchyExploitFixes plugin = AnarchyExploitFixes.getInstance(); // Load config.yml with ConfigMaster - this.config = ConfigFile.loadConfig(new File(AnarchyExploitFixes.getInstance().getDataFolder(), "config.yml")); - config.set("config-version", 1.1); + this.config = ConfigFile.loadConfig(new File(plugin.getDataFolder(), "config.yml")); + config.set("plugin-version", plugin.getPluginMeta().getVersion()); + config.set("server-version", plugin.getServer().getVersion()); // Pre-structure to force order structureConfig(); @@ -64,8 +66,13 @@ The time in ticks (1 sec = 20 ticks) a checked tps will be cached\s A server restart is required when changing a command's enable status!"""); // Elytra Speed - this.elytra_speed_calc_period = getInt("elytra.elytra-speed.check-period-ticks", 10, - "The period in ticks players will be checked to determine their speed."); + this.elytra_old_chunk_limit = getLong("elytra.elytra-speed.old-chunk-inhabited-ticks", 200L, """ + Time in ticks that a chunk has to have been inhabited to count as old chunk.\s + Note that the time is incremented once per tick per player within mob spawning\s + distance of a chunk."""); + this.elytra_speed_calc_period = getInt("elytra.elytra-speed.check-period-millis", 500, """ + The period in millis players will be checked to determine their speed.\s + If you have lagging players with consistent high ping, you can increase this number."""); this.elytra_calculate_3D = getBoolean("elytra.elytra-speed.calculate-3D-speed", false, """ If set to false, will only calculate 2-Dimensional speed without taking height\s changes into consideration."""); @@ -170,6 +177,16 @@ public int getInt(String path, int def) { return config.getInteger(path, def); } + public long getLong(String path, long def, String comment) { + config.addDefault(path, def, comment); + return config.getLong(path, def); + } + + public long getLong(String path, long def) { + config.addDefault(path, def); + return config.getLong(path, def); + } + public List getList(String path, List def, String comment) { config.addDefault(path, def, comment); return config.getStringList(path); diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/events/PacketPlayerRespawnEvent.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/events/PacketPlayerRespawnEvent.java new file mode 100644 index 000000000..4a20147e1 --- /dev/null +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/events/PacketPlayerRespawnEvent.java @@ -0,0 +1,37 @@ +package me.xginko.aef.events; + +import org.bukkit.entity.Player; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; +import org.jetbrains.annotations.NotNull; + +public class PacketPlayerRespawnEvent extends Event { + + private static final @NotNull HandlerList handlers = new HandlerList(); + + private final @NotNull Player player; + + public PacketPlayerRespawnEvent(@NotNull Player player) { + super(false); + this.player = player; + } + + public @NotNull Player getPlayer() { + return player; + } + + public boolean isPotentialBedSpawn() { + if (player.getPotentialBedLocation() == null) + return false; + return player.getPotentialBedLocation().distanceSquared(player.getLocation()) <= 16; + } + + @Override + public @NotNull HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/listeners/AEFListener.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/listeners/AEFListener.java new file mode 100644 index 000000000..689af3099 --- /dev/null +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/listeners/AEFListener.java @@ -0,0 +1,45 @@ +package me.xginko.aef.listeners; + +import com.github.retrooper.packetevents.event.PacketListenerAbstract; +import me.xginko.aef.AnarchyExploitFixes; +import me.xginko.aef.utils.models.ConditionalEnableable; +import me.xginko.aef.utils.models.Disableable; +import org.reflections.Reflections; +import org.reflections.scanners.Scanners; + +import java.lang.reflect.Modifier; +import java.util.HashSet; +import java.util.Set; + +public interface AEFListener extends ConditionalEnableable, Disableable { + + Reflections LISTENERS_PACKAGE = new Reflections(AEFListener.class.getPackage().getName()); + + Set LISTENERS = new HashSet<>(); + + static void reloadListeners() { + LISTENERS.forEach(AEFListener::disable); + LISTENERS.clear(); + + for (Class clazz : LISTENERS_PACKAGE.get(Scanners.SubTypes.of(AEFListener.class).asClass())) { + if (clazz.isInterface() || Modifier.isAbstract(clazz.getModifiers())) continue; + + try { + AEFListener listener = (AEFListener) clazz.getDeclaredConstructor().newInstance(); + if (listener.shouldEnable()) { + if (listener instanceof PacketListenerAbstract && AnarchyExploitFixes.config().packets_disabled) { + AnarchyExploitFixes.getPrefixedLogger() + .warn("Cannot enable listener {} because you disabled packets in config!", clazz.getSimpleName()); + continue; + } + + listener.enable(); + LISTENERS.add(listener); + } + } catch (Throwable t) { + AnarchyExploitFixes.getPrefixedLogger().error("Failed to load listener {}", clazz.getSimpleName(), t); + } + } + } + +} diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/listeners/PacketPlayerRespawnListener.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/listeners/PacketPlayerRespawnListener.java new file mode 100644 index 000000000..26cf0856f --- /dev/null +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/listeners/PacketPlayerRespawnListener.java @@ -0,0 +1,73 @@ +package me.xginko.aef.listeners; + +import com.github.retrooper.packetevents.PacketEvents; +import com.github.retrooper.packetevents.event.PacketListenerAbstract; +import com.github.retrooper.packetevents.event.PacketListenerPriority; +import com.github.retrooper.packetevents.event.PacketReceiveEvent; +import com.github.retrooper.packetevents.protocol.packettype.PacketType; +import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientClientStatus; +import me.xginko.aef.AnarchyExploitFixes; +import me.xginko.aef.events.PacketPlayerRespawnEvent; +import me.xginko.aef.utils.PlatformUtil; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.PlayerDeathEvent; + +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.CopyOnWriteArraySet; + +public class PacketPlayerRespawnListener extends PacketListenerAbstract implements AEFListener, Listener { + + private static final Set DEAD_PLAYERS = new CopyOnWriteArraySet<>(); + private final AnarchyExploitFixes plugin; + + public PacketPlayerRespawnListener() { + super(PacketListenerPriority.HIGHEST); + this.plugin = AnarchyExploitFixes.getInstance(); + } + + @Override + public boolean shouldEnable() { + return PlatformUtil.isFolia(); + } + + @Override + public void enable() { + plugin.getServer().getPluginManager().registerEvents(this, plugin); + PacketEvents.getAPI().getEventManager().unregisterListener(this); + } + + @Override + public void disable() { + HandlerList.unregisterAll(this); + PacketEvents.getAPI().getEventManager().registerListener(this); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + private void onPlayerDeath(PlayerDeathEvent event) { + DEAD_PLAYERS.add(event.getPlayer().getUniqueId()); + } + + @Override + public void onPacketReceive(PacketReceiveEvent event) { + if (event.isCancelled()) return; + if (event.getPacketType() != PacketType.Play.Client.CLIENT_STATUS) return; + WrapperPlayClientClientStatus packet = new WrapperPlayClientClientStatus(event); + if (packet.getAction() != WrapperPlayClientClientStatus.Action.PERFORM_RESPAWN) return; + + UUID uuid = event.getUser().getUUID(); + Player bukkitPlayer = plugin.getServer().getPlayer(uuid); + if (bukkitPlayer == null) return; + + if (DEAD_PLAYERS.contains(uuid) || bukkitPlayer.isDead()) { + bukkitPlayer.getScheduler().execute(plugin, () -> { + new PacketPlayerRespawnEvent(bukkitPlayer).callEvent(); + DEAD_PLAYERS.remove(bukkitPlayer.getUniqueId()); + }, null, 20L); + } + } +} diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/AEFModule.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/AEFModule.java index e3d991415..dd77b19dd 100755 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/AEFModule.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/AEFModule.java @@ -3,8 +3,8 @@ import me.xginko.aef.AnarchyExploitFixes; import me.xginko.aef.config.Config; import me.xginko.aef.modules.packets.PacketModule; +import me.xginko.aef.utils.models.ConditionalEnableable; import me.xginko.aef.utils.models.Disableable; -import me.xginko.aef.utils.models.Enableable; import org.reflections.Reflections; import org.reflections.scanners.Scanners; @@ -12,7 +12,7 @@ import java.util.HashSet; import java.util.Set; -public abstract class AEFModule implements Enableable, Disableable { +public abstract class AEFModule implements ConditionalEnableable, Disableable { private static final Reflections MODULES_PACKAGE = new Reflections(AEFModule.class.getPackage().getName()); public static final Set ENABLED_MODULES = new HashSet<>(); @@ -22,8 +22,6 @@ public abstract class AEFModule implements Enableable, Disableable { public final String configPath; private final String logFormat; - public abstract boolean shouldEnable(); - public AEFModule(String configPath) { this.plugin = AnarchyExploitFixes.getInstance(); this.config = AnarchyExploitFixes.config(); diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/bedrock/FillNetherCeilingOnChunkload.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/bedrock/FillNetherCeilingOnChunkload.java index b5a936d02..f94bf6b17 100755 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/bedrock/FillNetherCeilingOnChunkload.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/bedrock/FillNetherCeilingOnChunkload.java @@ -50,11 +50,12 @@ public void disable() { @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onChunkLoad(ChunkLoadEvent event) { if (!alsoCheckNewChunks && event.isNewChunk()) return; - final World world = event.getWorld(); - if (exemptedWorlds.contains(world.getName())) return; + World world = event.getWorld(); if (world.getEnvironment() != World.Environment.NETHER) return; - if (checkShouldPauseOnLowTPS && (AnarchyExploitFixes.getTickReporter().getTPS() <= pauseTPS)) return; + if (exemptedWorlds.contains(world.getName())) return; - ChunkUtil.createBedrockLayer(event.getChunk(), config.nether_ceiling_max_y); + if (!checkShouldPauseOnLowTPS || AnarchyExploitFixes.getTickReporter().getTPS() >= pauseTPS) { + ChunkUtil.createBedrockLayer(event.getChunk(), config.nether_ceiling_max_y); + } } } diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/bedrock/FillNetherFloorOnChunkload.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/bedrock/FillNetherFloorOnChunkload.java index 2fbd722bd..211ed9860 100755 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/bedrock/FillNetherFloorOnChunkload.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/bedrock/FillNetherFloorOnChunkload.java @@ -50,11 +50,12 @@ public void disable() { @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onChunkLoad(ChunkLoadEvent event) { if (!alsoCheckNewChunks && event.isNewChunk()) return; - final World world = event.getWorld(); - if (exemptedWorlds.contains(world.getName())) return; + World world = event.getWorld(); if (world.getEnvironment() != World.Environment.NETHER) return; - if (checkShouldPauseOnLowTPS && (AnarchyExploitFixes.getTickReporter().getTPS() <= pauseTPS)) return; + if (exemptedWorlds.contains(world.getName())) return; - ChunkUtil.createBedrockLayer(event.getChunk(), world.getMinHeight()); + if (!checkShouldPauseOnLowTPS || AnarchyExploitFixes.getTickReporter().getTPS() >= pauseTPS) { + ChunkUtil.createBedrockLayer(event.getChunk(), world.getMinHeight()); + } } } diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/bedrock/FillOverworldFloorOnChunkload.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/bedrock/FillOverworldFloorOnChunkload.java index 33c281656..02379ee27 100755 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/bedrock/FillOverworldFloorOnChunkload.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/bedrock/FillOverworldFloorOnChunkload.java @@ -50,11 +50,12 @@ public void disable() { @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onChunkLoad(ChunkLoadEvent event) { if (!alsoCheckNewChunks && event.isNewChunk()) return; - final World world = event.getWorld(); - if (exemptedWorlds.contains(world.getName())) return; + World world = event.getWorld(); if (world.getEnvironment() != World.Environment.NETHER) return; - if (checkShouldPauseOnLowTPS && (AnarchyExploitFixes.getTickReporter().getTPS() <= pauseTPS)) return; + if (exemptedWorlds.contains(world.getName())) return; - ChunkUtil.createBedrockLayer(event.getChunk(), world.getMinHeight()); + if (!checkShouldPauseOnLowTPS || AnarchyExploitFixes.getTickReporter().getTPS() >= pauseTPS) { + ChunkUtil.createBedrockLayer(event.getChunk(), world.getMinHeight()); + } } } diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/bedrock/PeriodicallyFillNetherCeiling.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/bedrock/PeriodicallyFillNetherCeiling.java index 84272bc5a..5a4af33aa 100755 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/bedrock/PeriodicallyFillNetherCeiling.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/bedrock/PeriodicallyFillNetherCeiling.java @@ -50,7 +50,7 @@ public void disable() { @Override public void accept(ScheduledTask task) { - if (pauseOnLowTPS && (AnarchyExploitFixes.getTickReporter().getGlobalTPS() <= pauseTPS)) return; + if (pauseOnLowTPS && AnarchyExploitFixes.getTickReporter().getGlobalTPS() <= pauseTPS) return; for (World world : plugin.getServer().getWorlds()) { if (world.getEnvironment() != World.Environment.NETHER) continue; @@ -59,7 +59,8 @@ public void accept(ScheduledTask task) { for (Chunk chunk : world.getLoadedChunks()) { plugin.getServer().getRegionScheduler().execute(plugin, world, chunk.getX(), chunk.getZ(), () -> { if (!chunk.isEntitiesLoaded()) return; - if (pauseOnLowTPS && (AnarchyExploitFixes.getTickReporter().getTPS() <= pauseTPS)) return; + if (pauseOnLowTPS && AnarchyExploitFixes.getTickReporter().getTPS() <= pauseTPS) return; + ChunkUtil.createBedrockLayer(chunk, config.nether_ceiling_max_y); }); } diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/bedrock/PeriodicallyFillNetherFloor.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/bedrock/PeriodicallyFillNetherFloor.java index 638dbab0d..f292b3f35 100755 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/bedrock/PeriodicallyFillNetherFloor.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/bedrock/PeriodicallyFillNetherFloor.java @@ -50,7 +50,7 @@ public void disable() { @Override public void accept(ScheduledTask task) { - if (pauseOnLowTPS && (AnarchyExploitFixes.getTickReporter().getGlobalTPS() <= pauseTPS)) return; + if (pauseOnLowTPS && AnarchyExploitFixes.getTickReporter().getGlobalTPS() <= pauseTPS) return; for (World world : plugin.getServer().getWorlds()) { if (world.getEnvironment() != World.Environment.NETHER) continue; @@ -59,7 +59,8 @@ public void accept(ScheduledTask task) { for (Chunk chunk : world.getLoadedChunks()) { plugin.getServer().getRegionScheduler().execute(plugin, world, chunk.getX(), chunk.getZ(), () -> { if (!chunk.isEntitiesLoaded()) return; - if (pauseOnLowTPS && (AnarchyExploitFixes.getTickReporter().getTPS() <= pauseTPS)) return; + if (pauseOnLowTPS && AnarchyExploitFixes.getTickReporter().getTPS() <= pauseTPS) return; + ChunkUtil.createBedrockLayer(chunk, world.getMinHeight()); }); } diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/bedrock/PeriodicallyFillOverworldFloor.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/bedrock/PeriodicallyFillOverworldFloor.java index e4db9fd83..09e9665a8 100755 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/bedrock/PeriodicallyFillOverworldFloor.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/bedrock/PeriodicallyFillOverworldFloor.java @@ -51,7 +51,7 @@ public void disable() { @Override public void accept(ScheduledTask task) { - if (pauseOnLowTPS && (AnarchyExploitFixes.getTickReporter().getGlobalTPS() <= pauseTPS)) return; + if (pauseOnLowTPS && AnarchyExploitFixes.getTickReporter().getGlobalTPS() <= pauseTPS) return; for (World world : plugin.getServer().getWorlds()) { if (world.getEnvironment() != World.Environment.NORMAL) continue; @@ -60,7 +60,8 @@ public void accept(ScheduledTask task) { for (Chunk chunk : world.getLoadedChunks()) { plugin.getServer().getRegionScheduler().execute(plugin, world, chunk.getX(), chunk.getZ(), () -> { if (!chunk.isEntitiesLoaded()) return; - if (pauseOnLowTPS && (AnarchyExploitFixes.getTickReporter().getTPS() <= pauseTPS)) return; + if (pauseOnLowTPS && AnarchyExploitFixes.getTickReporter().getTPS() <= pauseTPS) return; + ChunkUtil.createBedrockLayer(chunk, world.getMinHeight()); }); } diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/bedrock/PreventGoingBelowBedrockFloor.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/bedrock/PreventGoingBelowBedrockFloor.java index 4f6681adc..d406581f8 100755 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/bedrock/PreventGoingBelowBedrockFloor.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/bedrock/PreventGoingBelowBedrockFloor.java @@ -19,16 +19,21 @@ public class PreventGoingBelowBedrockFloor extends AEFModule implements Listener private final Set exemptedWorlds; private final Material fillMaterial; - private final boolean filling_enabled, eject_enabled, stop_elytra_enabled; + private final boolean fillHole, vehicleEject, closeElytra, teleport; + private final double damageOnMove; public PreventGoingBelowBedrockFloor() { super("bedrock.prevent-going-below-bedrock-floor"); config.addComment(configPath + ".enable", "Prevents players from going below the bedrock floor."); - this.eject_enabled = config.getBoolean(configPath + ".leave-vehicle", true, + this.vehicleEject = config.getBoolean(configPath + ".leave-vehicle", true, "Whether to make player leave their vehicle."); - this.stop_elytra_enabled = config.getBoolean(configPath + ".stop-elytra", true, + this.closeElytra = config.getBoolean(configPath + ".stop-elytra", true, "Whether to close the player's elytra if they were flying."); - this.filling_enabled = config.getBoolean(configPath + ".fill-bedrock-hole", true, + this.teleport = config.getBoolean(configPath + ".teleport", true, + "Teleport player on top of that bedrock"); + this.damageOnMove = config.getDouble(configPath + ".damage-when-moving",8.0, + "1.0 = Half a heart of damage every time you move. Set 0 to disable"); + this.fillHole = config.getBoolean(configPath + ".fill-bedrock-hole", true, "Whether the bedrock hole should be filled or not."); this.exemptedWorlds = new HashSet<>(config.getList(configPath + ".exempted-worlds", List.of("world_the_end", "skyblock_world"))); @@ -65,18 +70,32 @@ private void onPlayerMove(PlayerMoveEvent event) { final Location playerLoc = player.getLocation(); if (playerLoc.getY() >= world.getMinHeight()) return; - if (eject_enabled && player.isInsideVehicle()) - player.leaveVehicle(); - if (stop_elytra_enabled && player.isGliding()) + if (vehicleEject && player.getVehicle() != null) { + player.getVehicle().eject(); + } + + if (closeElytra && player.isGliding()) { player.setGliding(false); + } - // place bedrock at the min world height - if (filling_enabled) - world.getBlockAt(playerLoc.getBlockX(), world.getMinHeight(), playerLoc.getBlockZ()).setType(fillMaterial); + if (fillHole) { + // Place bedrock at the lowest possible valid location in the world + plugin.getServer().getRegionScheduler().execute(plugin, playerLoc, () -> + world.getBlockAt(playerLoc.getBlockX(), world.getMinHeight(), playerLoc.getBlockZ()) + .setType(fillMaterial, false)); + } - // teleport player on top of that bedrock - Location tploc = event.getFrom().clone().add(0, 2, 0); - event.setTo(tploc); - player.teleportAsync(tploc); + if (teleport) { + // Teleport player up, so he lands on top of that bedrock + Location threeBlocksUp = event.getFrom().clone().add(0, 3, 0); + event.setTo(threeBlocksUp); + player.teleportAsync(threeBlocksUp); + } + + if (damageOnMove > 0) { + // Deal damage when moving below bedrock. Just in case everything else fails + // to prevent players from moving below bedrock. + player.damage(damageOnMove); + } } } diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/chat/PreventPluginScanning.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/chat/PreventPluginScanning.java index ea993eff2..3fec1232c 100755 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/chat/PreventPluginScanning.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/chat/PreventPluginScanning.java @@ -44,16 +44,18 @@ private boolean isSuspectedScanPacket(String buffer) { @EventHandler(priority = EventPriority.HIGHEST) private void onAsyncCommandTabComplete(AsyncTabCompleteEvent event) { - if (event.getSender().hasPermission(AEFPermission.BYPASS_CHAT.string())) return; - if (this.isSuspectedScanPacket(event.getBuffer())) { + if (event.getSender().hasPermission(AEFPermission.BYPASS_CHAT.bukkit())) return; + + if (isSuspectedScanPacket(event.getBuffer())) { event.setCancelled(true); } } @EventHandler(priority = EventPriority.HIGHEST) private void onCommandTabComplete(TabCompleteEvent event) { - if (event.getSender().hasPermission(AEFPermission.BYPASS_CHAT.string())) return; - if (this.isSuspectedScanPacket(event.getBuffer())) { + if (event.getSender().hasPermission(AEFPermission.BYPASS_CHAT.bukkit())) return; + + if (isSuspectedScanPacket(event.getBuffer())) { event.setCancelled(true); } } diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/chat/commandwhitelist/CommandWhitelist.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/chat/commandwhitelist/CommandWhitelist.java index 1d72f0963..4faf7833c 100755 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/chat/commandwhitelist/CommandWhitelist.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/chat/commandwhitelist/CommandWhitelist.java @@ -94,6 +94,8 @@ public void disable() { @Override public void onPacketReceive(PacketReceiveEvent event) { + if (event.isCancelled()) return; + String message; if (event.getPacketType() == PacketType.Play.Client.CHAT_MESSAGE) { message = new WrapperPlayClientChatMessage(event).getMessage(); @@ -105,7 +107,7 @@ public void onPacketReceive(PacketReceiveEvent event) { } final Player player = (Player) event.getPlayer(); - if (player != null && player.hasPermission(AEFPermission.BYPASS_CMD_WHITELIST.string())) return; + if (player != null && player.hasPermission(AEFPermission.BYPASS_CMD_WHITELIST.bukkit())) return; if (!allowedCommands.contains(CommandUtil.getCommandLabel(message).toLowerCase())) { event.setCancelled(true); @@ -127,7 +129,7 @@ public void onPacketReceive(PacketReceiveEvent event) { @EventHandler(priority = EventPriority.HIGHEST) private void onCommandPreProcess(PlayerCommandPreprocessEvent event) { final Player player = event.getPlayer(); - if (player.hasPermission(AEFPermission.BYPASS_CMD_WHITELIST.string())) return; + if (player.hasPermission(AEFPermission.BYPASS_CMD_WHITELIST.bukkit())) return; String message = event.getMessage(); String commandLabel = CommandUtil.getCommandLabel(message).toLowerCase(); @@ -156,16 +158,16 @@ private void onCommandPreProcess(PlayerCommandPreprocessEvent event) { private void onAsyncCommandTabComplete(AsyncTabCompleteEvent event) { if (event.getCompletions().isEmpty()) return; if (!(event.getSender() instanceof Player)) return; - if (event.getSender().hasPermission(AEFPermission.BYPASS_CMD_WHITELIST.string())) return; + if (event.getSender().hasPermission(AEFPermission.BYPASS_CMD_WHITELIST.bukkit())) return; event.setCompletions(getFilteredTabCompletions(event.getBuffer(), event.getCompletions())); } - @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onCommandTabComplete(TabCompleteEvent event) { if (event.getCompletions().isEmpty()) return; if (!(event.getSender() instanceof Player)) return; - if (event.getSender().hasPermission(AEFPermission.BYPASS_CMD_WHITELIST.string())) return; + if (event.getSender().hasPermission(AEFPermission.BYPASS_CMD_WHITELIST.bukkit())) return; event.setCompletions(getFilteredTabCompletions(event.getBuffer(), event.getCompletions())); } diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/chunklimits/BlockLimit.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/chunklimits/BlockLimit.java index 8df8ed028..16ffd2913 100755 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/chunklimits/BlockLimit.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/chunklimits/BlockLimit.java @@ -131,7 +131,7 @@ public BlockLimit() { for (String configuredMaterial : section.getKeys(false)) { try { Material blockMaterial = Material.valueOf(configuredMaterial); - Integer maxAmountPerChunk = Integer.valueOf(section.getString(configuredMaterial)); + Integer maxAmountPerChunk = Integer.parseInt(section.getString(configuredMaterial)); this.blockLimits.put(blockMaterial, maxAmountPerChunk); } catch (NumberFormatException e) { notRecognized(Integer.class, configuredMaterial); diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/chunklimits/CustomEntityLimit.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/chunklimits/CustomEntityLimit.java index e2e4fa6a5..a1778ca5c 100755 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/chunklimits/CustomEntityLimit.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/chunklimits/CustomEntityLimit.java @@ -135,7 +135,7 @@ public CustomEntityLimit() { Make sure your minecraft version is matching as well."""); for (String configuredEntity : section.getKeys(false)) { try { - Integer maxAmountPerChunk = Integer.valueOf(section.getString(configuredEntity)); + Integer maxAmountPerChunk = Integer.parseInt(section.getString(configuredEntity)); EntityType limitedEntity = EntityType.valueOf(configuredEntity); entityLimits.put(limitedEntity, maxAmountPerChunk); } catch (NumberFormatException e) { diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/chunklimits/FallingBlockLimit.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/chunklimits/FallingBlockLimit.java old mode 100755 new mode 100644 diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/chunklimits/VillagerLimit.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/chunklimits/VillagerLimit.java index 9047f7a45..3b6bbf1de 100755 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/chunklimits/VillagerLimit.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/chunklimits/VillagerLimit.java @@ -7,7 +7,6 @@ import org.bukkit.Chunk; import org.bukkit.World; import org.bukkit.entity.Entity; -import org.bukkit.entity.EntityType; import org.bukkit.entity.Villager; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; @@ -17,17 +16,22 @@ import java.util.ArrayList; import java.util.Comparator; +import java.util.HashSet; import java.util.List; import java.util.Objects; +import java.util.Set; import java.util.function.Consumer; +import java.util.stream.Collectors; +import java.util.stream.Stream; public class VillagerLimit extends AEFModule implements Consumer, Listener { private ScheduledTask scheduledTask; private final List removalPriority; + private final Set professionWhitelist; private final long checkPeriod; private final int maxVillagersPerChunk; - private final boolean logIsEnabled; + private final boolean logIsEnabled, whitelistEnabled; public VillagerLimit() { super("chunk-limits.entity-limits.villager-limit"); @@ -35,12 +39,20 @@ public VillagerLimit() { this.logIsEnabled = config.getBoolean(configPath + ".log-removals", false); this.checkPeriod = Math.max(config.getInt(configPath + ".check-period-in-ticks", 600, "Check all chunks every x ticks."), 1); - this.removalPriority = config.getList(configPath + ".removal-priority", - List.of("NONE", "NITWIT", "SHEPHERD", "FISHERMAN", "BUTCHER", "CARTOGRAPHER", - "LEATHERWORKER", "FLETCHER", "MASON", "FARMER", "ARMORER", "TOOLSMITH", "WEAPONSMITH", - "CLERIC", "LIBRARIAN"), """ - Professions that are in the top of the list are going to be scheduled\s - for removal first.""") + final List defPriority = Stream.of("NONE", "NITWIT", "SHEPHERD", "FISHERMAN", "BUTCHER", "CARTOGRAPHER", + "LEATHERWORKER", "FLETCHER", "MASON", "FARMER", "ARMORER", "TOOLSMITH", "WEAPONSMITH", "CLERIC", "LIBRARIAN") + .filter(prof -> { + try { + Villager.Profession.valueOf(prof); + return true; + } catch (IllegalArgumentException e) { + return false; + } + }) + .collect(Collectors.toList()); + this.removalPriority = config.getList(configPath + ".removal-priority", defPriority, """ + Professions that are in the top of the list are going to be scheduled for\s + removal first.""") .stream() .map(configuredProfession -> { try { @@ -52,6 +64,30 @@ public VillagerLimit() { }) .filter(Objects::nonNull) .toList(); + final List defWhitelist = Stream.of("NONE", "NITWIT", "SHEPHERD", "FISHERMAN", "BUTCHER", "CARTOGRAPHER", + "LEATHERWORKER", "FLETCHER", "MASON", "FARMER", "ARMORER", "TOOLSMITH", "WEAPONSMITH", "CLERIC", "LIBRARIAN") + .filter(prof -> { + try { + Villager.Profession.valueOf(prof); + return true; + } catch (IllegalArgumentException e) { + return false; + } + }) + .collect(Collectors.toList()); + this.whitelistEnabled = config.getBoolean(configPath + ".whitelist.enable", false); + this.professionWhitelist = config.getList(configPath + ".whitelist.professions", defWhitelist) + .stream() + .map(configuredProfession -> { + try { + return Villager.Profession.valueOf(configuredProfession); + } catch (IllegalArgumentException e) { + notRecognized(Villager.Profession.class, configuredProfession); + return null; + } + }) + .filter(Objects::nonNull) + .collect(Collectors.toCollection(HashSet::new)); } @Override @@ -99,8 +135,11 @@ private void checkVillagersInChunk(Chunk chunk) { // Create a list with all villagers in that chunk final List villagers_in_chunk = new ArrayList<>(); for (Entity entity : entities) { - if (entity.getType() == EntityType.VILLAGER) { - villagers_in_chunk.add((Villager) entity); + if (entity.getType() == XEntityType.VILLAGER.get()) { + Villager villager = (Villager) entity; + if (whitelistEnabled && !professionWhitelist.contains(villager.getProfession())) { + villagers_in_chunk.add(villager); + } } } diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/combat/AnchorAuraDelay.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/combat/AnchorAuraDelay.java deleted file mode 100755 index fee262e1d..000000000 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/combat/AnchorAuraDelay.java +++ /dev/null @@ -1,81 +0,0 @@ -package me.xginko.aef.modules.combat; - -import me.xginko.aef.modules.AEFModule; -import me.xginko.aef.utils.models.ExpiringSet; -import org.bukkit.Material; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.HandlerList; -import org.bukkit.event.Listener; -import org.bukkit.event.block.Action; -import org.bukkit.event.block.BlockPlaceEvent; -import org.bukkit.event.player.PlayerInteractEvent; -import org.bukkit.inventory.ItemStack; - -import java.time.Duration; -import java.util.UUID; - -public class AnchorAuraDelay extends AEFModule implements Listener { - - private final ExpiringSet placeCooldowns, breakCooldowns; - private final long placeDelayMillis, breakDelayMillis; - private final boolean updateInv; - - public AnchorAuraDelay() { - super("combat.anchor-aura-delay"); - this.updateInv = config.getBoolean(configPath + ".update-inventory-on-cancel", false, - "Can help with desync but recommended to leave off unless needed."); - this.placeDelayMillis = config.getInt(configPath + ".place-delay-in-ticks", 8) * 50L; - this.placeCooldowns = placeDelayMillis <= 0 ? null : new ExpiringSet<>(Duration.ofMillis(placeDelayMillis)); - this.breakDelayMillis = config.getInt(configPath + ".break-delay-in-ticks", -1) * 50L; - this.breakCooldowns = breakDelayMillis <= 0 ? null : new ExpiringSet<>(Duration.ofMillis(breakDelayMillis)); - } - - @Override - public void enable() { - plugin.getServer().getPluginManager().registerEvents(this, plugin); - } - - @Override - public boolean shouldEnable() { - return config.getBoolean(configPath + ".enable", false); - } - - @Override - public void disable() { - HandlerList.unregisterAll(this); - } - - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - private void onAnchorBreak(PlayerInteractEvent event) { - if (breakDelayMillis <= 0) return; - if (event.getAction() != Action.RIGHT_CLICK_BLOCK) return; - final ItemStack interactItem = event.getItem(); - if (interactItem == null || interactItem.getType() != Material.GLOWSTONE) return; - final Player player = event.getPlayer(); - if (player.getWorld().isRespawnAnchorWorks()) return; - - if (breakCooldowns.contains(player.getUniqueId())) { - event.setCancelled(true); - if (updateInv) event.getPlayer().updateInventory(); - } else { - breakCooldowns.add(player.getUniqueId()); - } - } - - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - private void onAnchorPlace(BlockPlaceEvent event) { - if (placeDelayMillis <= 0) return; - if (event.getBlock().getType() != Material.RESPAWN_ANCHOR) return; - final Player player = event.getPlayer(); - if (player.getWorld().isRespawnAnchorWorks()) return; - - if (placeCooldowns.contains(player.getUniqueId())) { - event.setCancelled(true); - if (updateInv) event.getPlayer().updateInventory(); - } else { - placeCooldowns.add(player.getUniqueId()); - } - } -} \ No newline at end of file diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/combat/AutoBedOrSpigot5988.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/combat/AutoBedOrSpigot5988.java deleted file mode 100644 index 139eb871d..000000000 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/combat/AutoBedOrSpigot5988.java +++ /dev/null @@ -1,44 +0,0 @@ -package me.xginko.aef.modules.combat; - -import com.destroystokyo.paper.event.player.PlayerSetSpawnEvent; -import me.xginko.aef.modules.AEFModule; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.HandlerList; -import org.bukkit.event.Listener; - -public class AutoBedOrSpigot5988 extends AEFModule implements Listener { - - public AutoBedOrSpigot5988() { - super("combat.auto-bed"); - } - - @Override - public void enable() { - plugin.getServer().getPluginManager().registerEvents(this, plugin); - } - - @Override - public void disable() { - HandlerList.unregisterAll(this); - } - - @Override - public boolean shouldEnable() { - return config.getBoolean(configPath + ".enable", false, "As it happens, players on a server with a huge game world somehow need to travel long distances.\n" + - "From Minecraft version 1.16 (≈June 2020) to version 1.17.1(≈October 2021) in the game there was a bug SPIGOT-5988 which did not reset the respawn point of the player after death, if his bed was blocked.\n" + - "After some manipulations, it turned out to be a simple mechanism that allowed the player to get to the spavn by blocking his bed with a shalker before death, and after the second death to be near his bed again.\n" + - "This bug persisted from the Spigot server to Paper and all its forks until October 2021, after which it was fixed by the Spigot development team.\n" + - "Attempts by players to reach out to Spigot to allow them to disable the patch that fixes the SPIGOT-5988 bug have failed.\n" + - "\n" + - "Demonstration of how the patch works:\n" + - "https://www.youtube.com/watch?v=3y5SbQXzMss&feature=youtu.be"); - } - - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - private void onPlayerSetSpawn(PlayerSetSpawnEvent event) { - if (event.getLocation() == null) { - event.setCancelled(true); - } - } -} \ No newline at end of file diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/combat/BedAuraDelay.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/combat/BedAuraDelay.java deleted file mode 100755 index fe3cde970..000000000 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/combat/BedAuraDelay.java +++ /dev/null @@ -1,69 +0,0 @@ -package me.xginko.aef.modules.combat; - -import com.destroystokyo.paper.MaterialTags; -import io.papermc.paper.event.player.PlayerBedFailEnterEvent; -import me.xginko.aef.modules.AEFModule; -import me.xginko.aef.utils.models.ExpiringSet; -import org.bukkit.block.Block; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.HandlerList; -import org.bukkit.event.Listener; -import org.bukkit.event.block.BlockPlaceEvent; - -import java.time.Duration; -import java.util.UUID; - -public class BedAuraDelay extends AEFModule implements Listener { - - private final ExpiringSet breakCooldowns, placeCooldowns; - private final long breakDelayMillis, placeDelayMillis; - - public BedAuraDelay() { - super("combat.bed-aura-delay"); - this.breakDelayMillis = config.getInt(configPath + ".break-delay-in-ticks", 5, - "Set to -1 to disable.") * 50L; - this.breakCooldowns = breakDelayMillis <= 0 ? null : new ExpiringSet<>(Duration.ofMillis(breakDelayMillis)); - this.placeDelayMillis = config.getInt(configPath + ".place-delay-in-ticks", -1) * 50L; - this.placeCooldowns = placeDelayMillis <= 0 ? null : new ExpiringSet<>(Duration.ofMillis(placeDelayMillis)); - } - - @Override - public void enable() { - plugin.getServer().getPluginManager().registerEvents(this, plugin); - } - - @Override - public boolean shouldEnable() { - return config.getBoolean(configPath + ".enable", false); - } - - @Override - public void disable() { - HandlerList.unregisterAll(this); - } - - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - private void onBedBreak(PlayerBedFailEnterEvent event) { - if (breakDelayMillis <= 0 || !event.getWillExplode()) return; - - if (breakCooldowns.contains(event.getPlayer().getUniqueId())) { - event.setCancelled(true); - } else { - breakCooldowns.add(event.getPlayer().getUniqueId()); - } - } - - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - private void onBedPlace(BlockPlaceEvent event) { - if (placeDelayMillis <= 0) return; - final Block placed = event.getBlockPlaced(); - if (!MaterialTags.BEDS.isTagged(placed) || placed.getWorld().isBedWorks()) return; - - if (placeCooldowns.contains(event.getPlayer().getUniqueId())) { - event.setCancelled(true); - } else { - placeCooldowns.add(event.getPlayer().getUniqueId()); - } - } -} \ No newline at end of file diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/combat/BowBomb.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/combat/BowBomb.java index 8664811b9..eb6c83ef5 100755 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/combat/BowBomb.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/combat/BowBomb.java @@ -1,7 +1,7 @@ package me.xginko.aef.modules.combat; +import com.cryptomorin.xseries.XEntityType; import me.xginko.aef.modules.AEFModule; -import org.bukkit.entity.EntityType; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.HandlerList; @@ -35,10 +35,9 @@ public void disable() { @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onProjectileLaunch(EntityShootBowEvent event) { - if ( - event.getEntityType() == EntityType.PLAYER - && event.getProjectile().getVelocity().lengthSquared() > maxBowSquaredVelocity - ) { + if (event.getEntityType() != XEntityType.PLAYER.get()) return; // Skeletons also shoot bows + + if (event.getProjectile().getVelocity().lengthSquared() > maxBowSquaredVelocity) { event.setCancelled(true); } } diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/combat/Burrow.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/combat/Burrow.java index 9113b1ee4..cf43023b3 100755 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/combat/Burrow.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/combat/Burrow.java @@ -1,6 +1,7 @@ package me.xginko.aef.modules.combat; -import com.destroystokyo.paper.MaterialTags; +import com.cryptomorin.xseries.XMaterial; +import com.cryptomorin.xseries.XTag; import me.xginko.aef.modules.AEFModule; import me.xginko.aef.utils.MaterialUtil; import org.bukkit.GameMode; @@ -16,8 +17,16 @@ import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.event.player.PlayerMoveEvent; +import java.util.EnumSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + public class Burrow extends AEFModule implements Listener { + private final Set ignoredMaterial; private final double damageWhenMovingInBurrow; private final boolean shouldTeleportUp, preventIfBlockAboveBurrow, breakAnvilInsteadOfTP, allowSlabs; @@ -35,6 +44,24 @@ public Burrow() { this.allowSlabs = config.getBoolean(configPath + ".allow-slabs-in-burrow", true, """ Needs to be enabled to prevent a bug where players are teleported\s above a slab when the slab is underwater."""); + List defaults = Stream.concat(XTag.SHULKER_BOXES.getValues().stream(), + Stream.of(XMaterial.AIR, XMaterial.DIRT, XMaterial.DIRT_PATH, XMaterial.SAND, XMaterial.GRAVEL)) + .filter(XMaterial::isSupported) + .map(XMaterial::parseMaterial) + .map(Enum::name) + .toList(); + this.ignoredMaterial = config.getList(configPath + ".ignored-materials", defaults) + .stream() + .map(ignored -> { + try { + return Material.valueOf(ignored); + } catch (IllegalArgumentException e) { + notRecognized(Material.class, ignored); + return null; + } + }) + .filter(Objects::nonNull) + .collect(Collectors.toCollection(() -> EnumSet.noneOf(Material.class))); } @Override @@ -68,63 +95,56 @@ private void onSelfPlace(BlockPlaceEvent event) { @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onPlayerMove(PlayerMoveEvent event) { Player player = event.getPlayer(); - if (!player.getGameMode().equals(GameMode.SURVIVAL)) return; - if (player.isInsideVehicle() || player.isGliding()) return; + if (player.getGameMode() != GameMode.SURVIVAL) return; + if (player.isInsideVehicle() || player.isGliding() || player.isSwimming()) return; final Location playerLocation = player.getLocation(); final Block burrowBlock = playerLocation.getBlock(); - final Material burrowMaterial = burrowBlock.getType(); - - if ( - burrowMaterial.equals(Material.AIR) - || burrowMaterial.equals(Material.DIRT) // Fixes false positives when trampling farmland - || burrowMaterial.equals(Material.SAND) - || burrowMaterial.equals(Material.GRAVEL) - || MaterialTags.SHULKER_BOXES.isTagged(burrowMaterial) - ) return; - - if (preventIfBlockAboveBurrow || burrowBlock.getRelative(BlockFace.UP).getType().equals(Material.AIR)) { - - // Occluding Blocks - if (burrowMaterial.isOccluding() && !MaterialUtil.SINK_IN_BLOCKS.contains(burrowMaterial)) { - if (!allowSlabs || !MaterialUtil.SLAB_LIKE.contains(burrowMaterial)) { - player.damage(damageWhenMovingInBurrow); - if (shouldTeleportUp) teleportUpAndCenter(player, burrowBlock.getLocation()); - } - return; - } + if (ignoredMaterial.contains(burrowBlock.getType())) return; + + if (!preventIfBlockAboveBurrow && burrowBlock.getRelative(BlockFace.UP).getType() != XMaterial.AIR.parseMaterial()) { + return; + } - // Ender chest & Blocks that are slightly lower in height - if (burrowMaterial.equals(Material.ENDER_CHEST) || MaterialUtil.SINK_IN_BLOCKS.contains(burrowMaterial)) { - if (playerLocation.getY() - playerLocation.getBlockY() < 0.875) { - player.damage(damageWhenMovingInBurrow); - if (shouldTeleportUp) teleportUpAndCenter(player, burrowBlock.getLocation()); - } - return; + // Beacon and Indestructibles + if (MaterialUtil.SOLID_INDESTRUCTIBLES.contains(burrowBlock.getType()) || burrowBlock.getType() == XMaterial.BEACON.parseMaterial()) { + player.damage(damageWhenMovingInBurrow); + if (shouldTeleportUp) teleportUpAndCenter(player, burrowBlock.getLocation()); + return; + } + + // Occluding blocks that do not lower the player into themselves + if (burrowBlock.getType().isOccluding() && !MaterialUtil.SINK_IN_BLOCKS.contains(burrowBlock.getType())) { + if (!allowSlabs || !MaterialUtil.SLAB_LIKE.contains(burrowBlock.getType())) { + player.damage(damageWhenMovingInBurrow); + if (shouldTeleportUp) teleportUpAndCenter(player, burrowBlock.getLocation()); } + return; + } - // Enchantment Table - if (burrowMaterial.equals(Material.ENCHANTING_TABLE)) { - if (playerLocation.getY() - playerLocation.getBlockY() < 0.75) { - player.damage(damageWhenMovingInBurrow); - if (shouldTeleportUp) teleportUpAndCenter(player, burrowBlock.getLocation()); - } - return; + // Anvil + if (MaterialUtil.ANVILS.contains(burrowBlock.getType())) { + player.damage(damageWhenMovingInBurrow); + if (breakAnvilInsteadOfTP) { + burrowBlock.setType(XMaterial.AIR.parseMaterial()); + } else { + if (shouldTeleportUp) teleportUpAndCenter(player, burrowBlock.getLocation()); } + return; + } - // Anvil - if (MaterialUtil.ANVILS.contains(burrowMaterial)) { + // Ender chest & Blocks that are slightly lower in height + if (burrowBlock.getType() == XMaterial.ENDER_CHEST.parseMaterial() || MaterialUtil.SINK_IN_BLOCKS.contains(burrowBlock.getType())) { + if (playerLocation.getY() - playerLocation.getBlockY() < 0.875) { player.damage(damageWhenMovingInBurrow); - if (breakAnvilInsteadOfTP) { - burrowBlock.breakNaturally(); - } else { - if (shouldTeleportUp) teleportUpAndCenter(player, burrowBlock.getLocation()); - } - return; + if (shouldTeleportUp) teleportUpAndCenter(player, burrowBlock.getLocation()); } + return; + } - // Beacon and Indestructibles - if (burrowMaterial.equals(Material.BEACON) || MaterialUtil.SOLID_INDESTRUCTIBLES.contains(burrowMaterial)) { + // Enchantment Table + if (burrowBlock.getType() == XMaterial.ENCHANTING_TABLE.parseMaterial()) { + if (playerLocation.getY() - playerLocation.getBlockY() < 0.75) { player.damage(damageWhenMovingInBurrow); if (shouldTeleportUp) teleportUpAndCenter(player, burrowBlock.getLocation()); } diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/combat/CrystalAuraDelay.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/combat/CrystalAuraDelay.java deleted file mode 100755 index 7a330f8cb..000000000 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/combat/CrystalAuraDelay.java +++ /dev/null @@ -1,79 +0,0 @@ -package me.xginko.aef.modules.combat; - -import io.papermc.paper.event.player.PrePlayerAttackEntityEvent; -import me.xginko.aef.modules.AEFModule; -import me.xginko.aef.utils.models.ExpiringSet; -import org.bukkit.Material; -import org.bukkit.entity.EntityType; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.HandlerList; -import org.bukkit.event.Listener; -import org.bukkit.event.block.Action; -import org.bukkit.event.player.PlayerInteractEvent; -import org.bukkit.inventory.ItemStack; - -import java.time.Duration; -import java.util.UUID; - -public class CrystalAuraDelay extends AEFModule implements Listener { - - private final ExpiringSet breakCooldowns, placeCooldowns; - private final long breakDelayMillis, placeDelayMillis; - private final boolean updateInv; - - public CrystalAuraDelay() { - super("combat.crystal-aura.regular-delay"); - this.updateInv = config.getBoolean(configPath + ".update-inventory-on-cancel", false, - "Can help with desync but recommended to leave off unless needed."); - this.breakDelayMillis = config.getInt(configPath + ".break-delay-in-ticks", 4, - "Set to -1 to disable.") * 50L; - this.breakCooldowns = breakDelayMillis <= 0 ? null : new ExpiringSet<>(Duration.ofMillis(breakDelayMillis)); - this.placeDelayMillis = config.getInt(configPath + ".place-delay-in-ticks", -1) * 50L; - this.placeCooldowns = placeDelayMillis <= 0 ? null : new ExpiringSet<>(Duration.ofMillis(placeDelayMillis)); - } - - @Override - public void enable() { - plugin.getServer().getPluginManager().registerEvents(this, plugin); - } - - @Override - public boolean shouldEnable() { - return config.getBoolean(configPath + ".enable", false); - } - - @Override - public void disable() { - HandlerList.unregisterAll(this); - } - - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - private void onCrystalBreak(PrePlayerAttackEntityEvent event) { - if (breakDelayMillis <= 0 || !event.willAttack()) return; - - if (event.getAttacked().getType() == EntityType.ENDER_CRYSTAL) { - if (breakCooldowns.contains(event.getPlayer().getUniqueId())) { - event.setCancelled(true); - if (updateInv) event.getPlayer().updateInventory(); - } else { - breakCooldowns.add(event.getPlayer().getUniqueId()); - } - } - } - - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - private void onCrystalPlace(PlayerInteractEvent event) { - if (placeDelayMillis <= 0) return; - if (event.getAction() != Action.RIGHT_CLICK_BLOCK) return; - final ItemStack interactItem = event.getItem(); - if (interactItem == null || interactItem.getType() != Material.END_CRYSTAL) return; - - if (placeCooldowns.contains(event.getPlayer().getUniqueId())) { - event.setCancelled(true); - if (updateInv) event.getPlayer().updateInventory(); - } else { - placeCooldowns.add(event.getPlayer().getUniqueId()); - } - } -} \ No newline at end of file diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/combat/CrystalAuraHotbarSwitchDelay.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/combat/CrystalAuraHotbarSwitchDelay.java deleted file mode 100755 index 1469adeff..000000000 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/combat/CrystalAuraHotbarSwitchDelay.java +++ /dev/null @@ -1,93 +0,0 @@ -package me.xginko.aef.modules.combat; - -import me.xginko.aef.modules.AEFModule; -import me.xginko.aef.utils.models.ExpiringSet; -import org.bukkit.Material; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.HandlerList; -import org.bukkit.event.Listener; -import org.bukkit.event.player.PlayerItemHeldEvent; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.PlayerInventory; - -import java.time.Duration; -import java.util.EnumSet; -import java.util.List; -import java.util.Objects; -import java.util.Set; -import java.util.UUID; -import java.util.stream.Collectors; - -public class CrystalAuraHotbarSwitchDelay extends AEFModule implements Listener { - - private final ExpiringSet hotbarItemSwitchCooldowns; - private final Set blacklistedSwitchMaterials; - private final boolean onlyForSpecificMaterials; - - public CrystalAuraHotbarSwitchDelay() { - super("combat.crystal-aura.hotbar-switch-delay"); - final long switchAwayFromCrystalsDelayInMillis = Math.max(config.getInt(configPath + ".delay-in-ticks", 2, - "Delay between switching from an end crystal to other items in hotbar"), 1) * 50L; - this.hotbarItemSwitchCooldowns = new ExpiringSet<>(Duration.ofMillis(switchAwayFromCrystalsDelayInMillis)); - this.onlyForSpecificMaterials = config.getBoolean(configPath + ".only-delay-specific-materials", true, - "Only delay when switched to specific materials"); - List defaults = List.of( - "BOW", "DIAMOND_SWORD", "DIAMOND_AXE", "TRIDENT", - "GOLDEN_SWORD", "GOLDEN_AXE", "IRON_SWORD", "IRON_AXE", - "STONE_SWORD", "STONE_AXE", "WOODEN_SWORD", "WOODEN_AXE", - "BLACK_BED", "BLUE_BED", "BROWN_BED", "CYAN_BED", - "GRAY_BED", "GREEN_BED", "LIGHT_BLUE_BED", "LIGHT_GRAY_BED", - "LIME_BED", "MAGENTA_BED", "ORANGE_BED", "PINK_BED", - "PURPLE_BED", "RED_BED", "WHITE_BED", "YELLOW_BED", "CROSSBOW", - "NETHERITE_SWORD", "NETHERITE_AXE" - ); - this.blacklistedSwitchMaterials = config.getList(configPath + ".delayed-specific-materials", defaults) - .stream() - .map(configuredMaterial -> { - try { - return Material.valueOf(configuredMaterial); - } catch (IllegalArgumentException e) { - notRecognized(Material.class, configuredMaterial); - return null; - } - }) - .filter(Objects::nonNull) - .collect(Collectors.toCollection(() -> EnumSet.noneOf(Material.class))); - } - - @Override - public void enable() { - plugin.getServer().getPluginManager().registerEvents(this, plugin); - } - - @Override - public boolean shouldEnable() { - return config.getBoolean(configPath + ".enable", false); - } - - @Override - public void disable() { - HandlerList.unregisterAll(this); - } - - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - private void onHotbarSwitch(PlayerItemHeldEvent event) { - if (!hotbarItemSwitchCooldowns.contains(event.getPlayer().getUniqueId())) { - hotbarItemSwitchCooldowns.add(event.getPlayer().getUniqueId()); - return; - } - - final PlayerInventory playerInventory = event.getPlayer().getInventory(); - - final ItemStack previouslyHeldItem = playerInventory.getItem(event.getPreviousSlot()); - if (previouslyHeldItem == null || previouslyHeldItem.getType() != Material.END_CRYSTAL) return; - - final ItemStack newHeldItem = playerInventory.getItem(event.getNewSlot()); - if (newHeldItem == null || newHeldItem.getType() == Material.END_CRYSTAL) return; - - if (onlyForSpecificMaterials && !blacklistedSwitchMaterials.contains(newHeldItem.getType())) return; - - event.setCancelled(true); - } -} \ No newline at end of file diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/combat/MultiTask.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/combat/MultiTask.java new file mode 100755 index 000000000..c3ee3098b --- /dev/null +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/combat/MultiTask.java @@ -0,0 +1,65 @@ +package me.xginko.aef.modules.combat; + +import com.cryptomorin.xseries.XEntityType; +import io.papermc.paper.event.player.PrePlayerAttackEntityEvent; +import me.xginko.aef.modules.AEFModule; +import org.bukkit.entity.LivingEntity; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.player.PlayerInteractEvent; + +public class MultiTask extends AEFModule implements Listener { + + public MultiTask() { + super("combat.multi-task-patch"); + } + + @Override + public void enable() { + plugin.getServer().getPluginManager().registerEvents(this, plugin); + } + + @Override + public boolean shouldEnable() { + return config.getBoolean(configPath + ".enable", false); + } + + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onPrePlayerAttackEntity(PrePlayerAttackEntityEvent event) { + if (event.getPlayer().hasActiveItem()) { + event.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onEntityDamageByEntity(EntityDamageByEntityEvent event) { + if (event.getDamager().getType() == XEntityType.PLAYER.get()) { + if (((LivingEntity) event.getDamager()).hasActiveItem()) { + event.setCancelled(true); + } + } + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onPlayerInteract(PlayerInteractEvent event) { + if (event.getPlayer().hasActiveItem()) { + event.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onBlockPlace(BlockPlaceEvent event) { + if (event.getPlayer().hasActiveItem()) { + event.setCancelled(true); + } + } +} \ No newline at end of file diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/combat/PistonAuraDelay.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/combat/PistonAuraDelay.java deleted file mode 100755 index 4071f8379..000000000 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/combat/PistonAuraDelay.java +++ /dev/null @@ -1,52 +0,0 @@ -package me.xginko.aef.modules.combat; - -import me.xginko.aef.modules.AEFModule; -import me.xginko.aef.utils.models.ExpiringSet; -import org.bukkit.Location; -import org.bukkit.entity.EnderCrystal; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.HandlerList; -import org.bukkit.event.Listener; -import org.bukkit.event.block.BlockPistonExtendEvent; - -import java.time.Duration; - -public class PistonAuraDelay extends AEFModule implements Listener { - - private final ExpiringSet pistonsPushingCrystals; - - public PistonAuraDelay() { - super("crystal-aura.piston-aura-delay"); - config.addComment("combat.crystal-aura.piston-aura-delay.enable", - "Rate-limits pistons that extend into crystals."); - this.pistonsPushingCrystals = new ExpiringSet<>(Duration.ofMillis(Math.max(1, - config.getInt("combat.crystal-aura.piston-aura-delay.piston-extend-delay-in-ticks", 40)) * 50L)); - } - - @Override - public void enable() { - plugin.getServer().getPluginManager().registerEvents(this, plugin); - } - - @Override - public boolean shouldEnable() { - return config.getBoolean("combat.crystal-aura.piston-aura-delay.enable", false); - } - - @Override - public void disable() { - HandlerList.unregisterAll(this); - } - - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - private void onPistonExtend(BlockPistonExtendEvent event) { - if (!event.getBlock().getRelative(event.getDirection()).getLocation().getNearbyEntitiesByType(EnderCrystal.class, 1).isEmpty()) { - if (pistonsPushingCrystals.contains(event.getBlock().getLocation())) { - event.setCancelled(true); - } else { - pistonsPushingCrystals.add(event.getBlock().getLocation()); - } - } - } -} diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/combat/PistonCrystalDelay.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/combat/PistonCrystalDelay.java new file mode 100755 index 000000000..5484c096d --- /dev/null +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/combat/PistonCrystalDelay.java @@ -0,0 +1,55 @@ +package me.xginko.aef.modules.combat; + +import com.cryptomorin.xseries.XEntityType; +import me.xginko.aef.modules.AEFModule; +import me.xginko.aef.utils.models.ExpiringSet; +import org.bukkit.Location; +import org.bukkit.entity.Entity; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockPistonExtendEvent; + +import java.time.Duration; + +public class PistonCrystalDelay extends AEFModule implements Listener { + + private final ExpiringSet pistonsPushingCrystals; + + public PistonCrystalDelay() { + super("combat.crystal-aura.piston-aura-delay"); + config.addComment(configPath+".enable", "Rate-limits pistons that extend into crystals."); + this.pistonsPushingCrystals = new ExpiringSet<>(Duration.ofMillis( + Math.max(1, config.getInt(configPath + ".piston-extend-delay-in-ticks", 40)) * 50L)); + } + + @Override + public void enable() { + plugin.getServer().getPluginManager().registerEvents(this, plugin); + } + + @Override + public boolean shouldEnable() { + return config.getBoolean(configPath+".enable", false); + } + + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onPistonExtend(BlockPistonExtendEvent event) { + for (Entity entity : event.getBlock().getRelative(event.getDirection()).getLocation().getNearbyEntities(1, 1, 1)) { + if (entity.getType() == XEntityType.END_CRYSTAL.get()) { + if (pistonsPushingCrystals.contains(event.getBlock().getLocation())) { + event.setCancelled(true); + } else { + pistonsPushingCrystals.add(event.getBlock().getLocation()); + } + return; + } + } + } +} diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/combat/PistonPush.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/combat/PistonPush.java new file mode 100755 index 000000000..b03c60595 --- /dev/null +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/combat/PistonPush.java @@ -0,0 +1,65 @@ +package me.xginko.aef.modules.combat; + +import me.xginko.aef.modules.AEFModule; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockPistonExtendEvent; + +import java.util.Collections; +import java.util.EnumSet; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +public class PistonPush extends AEFModule implements Listener { + + private final Set pushDisabledTypes; + + public PistonPush() { + super("combat.piston-push"); + config.addComment(configPath+".enable", + "Disables pistons from extending if it would push certain configured entities.\n" + + "This can be used to prevent players from pushing other players out of burrows, by\n" + + "configuring PLAYER, or to disable piston-crystal by adding ENDER_CRYSTAL to the list."); + this.pushDisabledTypes = config.getList(configPath+".piston-push-blocked-entities", Collections.singletonList("PLAYER")) + .stream() + .map(configuredType -> { + try { + return EntityType.valueOf(configuredType); + } catch (IllegalArgumentException e) { + return null; + } + }) + .filter(Objects::nonNull) + .collect(Collectors.toCollection(() -> EnumSet.noneOf(EntityType.class))); + } + + @Override + public void enable() { + plugin.getServer().getPluginManager().registerEvents(this, plugin); + } + + @Override + public boolean shouldEnable() { + return config.getBoolean(configPath+".enable", false); + } + + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onPistonExtend(BlockPistonExtendEvent event) { + for (Entity entity : event.getBlock().getRelative(event.getDirection()).getLocation().getNearbyEntities(1,1,1)) { + if (pushDisabledTypes.contains(entity.getType())) { + event.setCancelled(true); + return; + } + } + } +} diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/combat/PortalGodMode.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/combat/PortalGodMode.java new file mode 100755 index 000000000..4863b307f --- /dev/null +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/combat/PortalGodMode.java @@ -0,0 +1,93 @@ +package me.xginko.aef.modules.combat; + +import com.cryptomorin.xseries.XEntityType; +import com.cryptomorin.xseries.XMaterial; +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; +import io.papermc.paper.threadedregions.scheduler.ScheduledTask; +import me.xginko.aef.modules.AEFModule; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityPortalEnterEvent; +import org.bukkit.event.player.PlayerMoveEvent; +import org.bukkit.event.player.PlayerTeleportEvent; +import org.checkerframework.checker.nullness.qual.Nullable; + +import java.time.Duration; +import java.util.UUID; + +public class PortalGodMode extends AEFModule implements Listener { + + private final Cache playersWaitingForPortalTeleport; + private final long delayTicks; + + public PortalGodMode() { + super("combat.portal-god-mode-patch"); + config.addComment(configPath + ".enable", """ + Prevents an exploit that allows players to stand in nether portals and not\s + take damage indefinitely by just never sending a TeleportConfirm packet to\s + the server.\s + A similar method is used for the chorus tp exploit, which is not covered\s + by this module."""); + this.delayTicks = config.getInt(configPath + ".break-portal-delay-ticks", 100, """ + If the player stays inside the nether portal for this time without teleporting,\s + the portal will be broken, making the player inside vulnerable again.\s + Nether portal teleports normally happen within ~3s after enter, so 5s (100ticks)\s + should be a safe value."""); + this.playersWaitingForPortalTeleport = Caffeine.newBuilder() + .expireAfterWrite(Duration.ofMillis((delayTicks * 50L) + 1000L)).build(); // Keep cached content for a second longer just in case + } + + @Override + public void enable() { + plugin.getServer().getPluginManager().registerEvents(this, plugin); + } + + @Override + public boolean shouldEnable() { + return config.getBoolean(configPath + ".enable", false); + } + + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + private void onEntityPortalEnter(EntityPortalEnterEvent event) { + if (event.getEntityType() != XEntityType.PLAYER.get()) return; + + if (playersWaitingForPortalTeleport.getIfPresent(event.getEntity().getUniqueId()) != null) return; + + playersWaitingForPortalTeleport.put(event.getEntity().getUniqueId(), + plugin.getServer().getRegionScheduler().runDelayed(plugin, event.getLocation(), breakPortal -> { + event.getLocation().getBlock().setType(XMaterial.AIR.parseMaterial(), true); + playersWaitingForPortalTeleport.invalidate(event.getEntity().getUniqueId()); + }, delayTicks)); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + private void onPlayerTeleport(PlayerTeleportEvent event) { + if (event.getCause() != PlayerTeleportEvent.TeleportCause.NETHER_PORTAL) return; + + @Nullable ScheduledTask breakPortalTask = playersWaitingForPortalTeleport.getIfPresent(event.getPlayer().getUniqueId()); + + if (breakPortalTask != null) { + breakPortalTask.cancel(); + playersWaitingForPortalTeleport.invalidate(event.getPlayer().getUniqueId()); + } + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + private void onPlayerMove(PlayerMoveEvent event) { + @Nullable ScheduledTask breakPortalTask = playersWaitingForPortalTeleport.getIfPresent(event.getPlayer().getUniqueId()); + if (breakPortalTask == null) return; + + if (event.getTo().getBlock().getType() != XMaterial.NETHER_PORTAL.parseMaterial()) { + breakPortalTask.cancel(); + playersWaitingForPortalTeleport.invalidate(event.getPlayer().getUniqueId()); + } + } +} diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/combat/SilentSwapDelay.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/combat/SilentSwapDelay.java new file mode 100755 index 000000000..fe33809c9 --- /dev/null +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/combat/SilentSwapDelay.java @@ -0,0 +1,86 @@ +package me.xginko.aef.modules.combat; + +import com.cryptomorin.xseries.XEntityType; +import me.xginko.aef.modules.AEFModule; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.event.player.PlayerItemHeldEvent; +import org.bukkit.event.player.PlayerKickEvent; +import org.bukkit.event.player.PlayerQuitEvent; + +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; + +public class SilentSwapDelay extends AEFModule implements Listener { + + private final Map swapItemCooldowns; + private final long cooldownNanos; + + public SilentSwapDelay() { + super("combat.silent-swap-delay"); + this.swapItemCooldowns = new ConcurrentHashMap<>(); + this.cooldownNanos = TimeUnit.MILLISECONDS.toNanos( + config.getLong(configPath + ".min-swap-delay-millis", 40L,""" + The delay in millis a player cant swap hotbar items after placing + a block, clicking a block (for example to place a crystal) or + damaging an entity. (50 ms = 1 tick)""")); + } + + @Override + public void enable() { + plugin.getServer().getPluginManager().registerEvents(this, plugin); + } + + @Override + public boolean shouldEnable() { + return config.getBoolean(configPath + ".enable", false); + } + + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onPlayerItemHeld(PlayerItemHeldEvent event) { // Fired when a hot bar item selection changes + if (!swapItemCooldowns.containsKey(event.getPlayer().getUniqueId())) return; + + if (swapItemCooldowns.get(event.getPlayer().getUniqueId()) > System.nanoTime()) { + event.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + private void onEntityDamageByEntity(EntityDamageByEntityEvent event) { + if (event.getDamager().getType() == XEntityType.PLAYER.get()) { + swapItemCooldowns.put(event.getDamager().getUniqueId(), System.nanoTime() + cooldownNanos); + } + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + private void onPlayerInteract(PlayerInteractEvent event) { + swapItemCooldowns.put(event.getPlayer().getUniqueId(), System.nanoTime() + cooldownNanos); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + private void onBlockPlace(BlockPlaceEvent event) { + swapItemCooldowns.put(event.getPlayer().getUniqueId(), System.nanoTime() + cooldownNanos); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + private void onPlayerQuit(PlayerQuitEvent event) { + swapItemCooldowns.remove(event.getPlayer().getUniqueId()); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + private void onPlayerKick(PlayerKickEvent event) { + swapItemCooldowns.remove(event.getPlayer().getUniqueId()); + } +} \ No newline at end of file diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/combat/auras/AnchorAuraDelay.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/combat/auras/AnchorAuraDelay.java new file mode 100755 index 000000000..10222c847 --- /dev/null +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/combat/auras/AnchorAuraDelay.java @@ -0,0 +1,51 @@ +package me.xginko.aef.modules.combat.auras; + +import com.cryptomorin.xseries.XMaterial; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.Action; +import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.event.player.PlayerInteractEvent; + +import java.util.Map; + +public class AnchorAuraDelay extends AuraDelayModule { + + public AnchorAuraDelay() { + super("combat.anchor-aura-delay", 400, 0); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onPlayerInteract(PlayerInteractEvent event) { + if (event.getAction() != Action.RIGHT_CLICK_BLOCK) return; + if (event.getClickedBlock().getType() != XMaterial.RESPAWN_ANCHOR.parseMaterial()) return; + if (event.getItem() == null || event.getItem().getType() != XMaterial.GLOWSTONE.parseMaterial()) return; + if (event.getPlayer().getWorld().isRespawnAnchorWorks()) return; + + for (Map.Entry entry : cooldownSettings.entrySet()) { + if (entry.getKey() != SettingType.GLOBAL && entry.getKey().slot != event.getHand()) continue; + + if (isOnCooldown(event.getPlayer().getUniqueId(), entry.getValue().breakCooldowns, entry.getValue().breakDelayNanos)) { + event.setCancelled(true); + if (updateInventory) event.getPlayer().updateInventory(); + return; + } + } + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onBlockPlace(BlockPlaceEvent event) { + if (event.getBlock().getType() != XMaterial.RESPAWN_ANCHOR.parseMaterial()) return; + if (event.getPlayer().getWorld().isRespawnAnchorWorks()) return; + + for (Map.Entry entry : cooldownSettings.entrySet()) { + if (entry.getKey() != SettingType.GLOBAL && entry.getKey().slot != event.getHand()) continue; + + if (isOnCooldown(event.getPlayer().getUniqueId(), entry.getValue().placeCooldowns, entry.getValue().placeDelayNanos)) { + event.setCancelled(true); + if (updateInventory) event.getPlayer().updateInventory(); + return; + } + } + } +} \ No newline at end of file diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/combat/auras/AuraDelayModule.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/combat/auras/AuraDelayModule.java new file mode 100644 index 000000000..47543cc3e --- /dev/null +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/combat/auras/AuraDelayModule.java @@ -0,0 +1,112 @@ +package me.xginko.aef.modules.combat.auras; + +import me.xginko.aef.modules.AEFModule; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerKickEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.inventory.EquipmentSlot; + +import java.util.EnumMap; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; + +public abstract class AuraDelayModule extends AEFModule implements Listener { + + protected static class Cooldowns { + + public final Map placeCooldowns, breakCooldowns; + public final long placeDelayNanos, breakDelayNanos; + + public Cooldowns(long placeDelayMillis, long breakDelayMillis) { + this.placeCooldowns = new ConcurrentHashMap<>(); + this.breakCooldowns = new ConcurrentHashMap<>(); + this.placeDelayNanos = TimeUnit.MILLISECONDS.toNanos(placeDelayMillis); + this.breakDelayNanos = TimeUnit.MILLISECONDS.toNanos(breakDelayMillis); + } + } + + protected final Map cooldownSettings = new EnumMap<>(SettingType.class); + protected final boolean updateInventory; + + public AuraDelayModule(String configPath, long defPlaceDelayMillis, long defBreakDelayMillis) { + super(configPath); + this.updateInventory = config.getBoolean(configPath + ".update-inventory-on-cancel", false, + "Can help with desync but recommended to leave off unless you have issues."); + for (SettingType settingType : SettingType.values()) { + String typePath = configPath + "." + settingType.name().toLowerCase().replace("_", "-"); + boolean enabled = config.getBoolean(typePath + ".enable", false); + Cooldowns cooldowns = new Cooldowns( + config.getLong(typePath + ".place-delay-millis", defPlaceDelayMillis, "1 tick = 50 ms"), + config.getLong(typePath + ".break-delay-millis", defBreakDelayMillis)); + if (enabled) { + cooldownSettings.put(settingType, cooldowns); + } + } + } + + @Override + public void enable() { + plugin.getServer().getPluginManager().registerEvents(this, plugin); + } + + @Override + public boolean shouldEnable() { + return config.getBoolean(configPath + ".enable", false); + } + + @Override + public void disable() { + HandlerList.unregisterAll(this); + cooldownSettings.forEach((settingType, cooldowns) -> { + cooldowns.placeCooldowns.clear(); + cooldowns.breakCooldowns.clear(); + }); + } + + protected boolean isOnCooldown(UUID uuid, Map cooldownMap, long delayNanos) { + if (delayNanos <= 0) { + return false; + } + + if (cooldownMap.containsKey(uuid) && cooldownMap.get(uuid) > System.nanoTime()) { + return true; + } + + cooldownMap.put(uuid, System.nanoTime() + delayNanos); + return false; + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onPlayerQuit(PlayerQuitEvent event) { + cooldownSettings.forEach((settingType, cooldowns) -> { + cooldowns.placeCooldowns.remove(event.getPlayer().getUniqueId()); + cooldowns.breakCooldowns.remove(event.getPlayer().getUniqueId()); + }); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onPlayerKick(PlayerKickEvent event) { + cooldownSettings.forEach((settingType, cooldowns) -> { + cooldowns.placeCooldowns.remove(event.getPlayer().getUniqueId()); + cooldowns.breakCooldowns.remove(event.getPlayer().getUniqueId()); + }); + } + + protected enum SettingType { + + GLOBAL(EquipmentSlot.HEAD), + MAIN_HAND(EquipmentSlot.HAND), + OFF_HAND(EquipmentSlot.OFF_HAND); + + public final EquipmentSlot slot; + + SettingType(EquipmentSlot slot) { + this.slot = slot; + } + } +} diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/combat/auras/BedAuraDelay.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/combat/auras/BedAuraDelay.java new file mode 100755 index 000000000..8d2a93539 --- /dev/null +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/combat/auras/BedAuraDelay.java @@ -0,0 +1,46 @@ +package me.xginko.aef.modules.combat.auras; + +import com.destroystokyo.paper.MaterialTags; +import io.papermc.paper.event.player.PlayerBedFailEnterEvent; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.BlockPlaceEvent; + +import java.util.Map; + +public class BedAuraDelay extends AuraDelayModule { + + public BedAuraDelay() { + super("combat.bed-aura-delay", 250, 0); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onPlayerBedFailEnter(PlayerBedFailEnterEvent event) { + if (!event.getWillExplode()) return; + + for (Map.Entry entry : cooldownSettings.entrySet()) { + if (entry.getKey() != SettingType.GLOBAL && entry.getKey().slot != event.getPlayer().getActiveItemHand()) continue; + + if (isOnCooldown(event.getPlayer().getUniqueId(), entry.getValue().breakCooldowns, entry.getValue().breakDelayNanos)) { + event.setCancelled(true); + if (updateInventory) event.getPlayer().updateInventory(); + return; + } + } + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onBlockPlace(BlockPlaceEvent event) { + if (!MaterialTags.BEDS.isTagged(event.getBlockPlaced()) || event.getBlockPlaced().getWorld().isBedWorks()) return; + + for (Map.Entry entry : cooldownSettings.entrySet()) { + if (entry.getKey() != SettingType.GLOBAL && entry.getKey().slot != event.getHand()) continue; + + if (isOnCooldown(event.getPlayer().getUniqueId(), entry.getValue().placeCooldowns, entry.getValue().placeDelayNanos)) { + event.setCancelled(true); + if (updateInventory) event.getPlayer().updateInventory(); + return; + } + } + } +} \ No newline at end of file diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/combat/auras/CrystalAuraDelay.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/combat/auras/CrystalAuraDelay.java new file mode 100755 index 000000000..e80f37b50 --- /dev/null +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/combat/auras/CrystalAuraDelay.java @@ -0,0 +1,49 @@ +package me.xginko.aef.modules.combat.auras; + +import com.cryptomorin.xseries.XEntityType; +import com.cryptomorin.xseries.XMaterial; +import io.papermc.paper.event.player.PrePlayerAttackEntityEvent; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.Action; +import org.bukkit.event.player.PlayerInteractEvent; + +import java.util.Map; + +public class CrystalAuraDelay extends AuraDelayModule { + + public CrystalAuraDelay() { + super("combat.crystal-aura.regular-delay", 0, 200); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onPrePlayerAttackEntity(PrePlayerAttackEntityEvent event) { + if (event.getAttacked().getType() != XEntityType.END_CRYSTAL.get()) return; + + for (Map.Entry entry : cooldownSettings.entrySet()) { + if (entry.getKey() != SettingType.GLOBAL && entry.getKey().slot != event.getPlayer().getActiveItemHand()) continue; + + if (isOnCooldown(event.getPlayer().getUniqueId(), entry.getValue().breakCooldowns, entry.getValue().breakDelayNanos)) { + event.setCancelled(true); + if (updateInventory) event.getPlayer().updateInventory(); + return; + } + } + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onPlayerInteract(PlayerInteractEvent event) { + if (event.getAction() != Action.RIGHT_CLICK_BLOCK) return; // Need to right-click a block to place a crystal + if (event.getItem() == null || event.getItem().getType() != XMaterial.END_CRYSTAL.parseMaterial()) return; + + for (Map.Entry entry : cooldownSettings.entrySet()) { + if (entry.getKey() != SettingType.GLOBAL && entry.getKey().slot != event.getHand()) continue; + + if (isOnCooldown(event.getPlayer().getUniqueId(), entry.getValue().placeCooldowns, entry.getValue().placeDelayNanos)) { + event.setCancelled(true); + if (updateInventory) event.getPlayer().updateInventory(); + return; + } + } + } +} \ No newline at end of file diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/dupepreventions/ChestedEntitiesInPortals.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/dupepreventions/ChestedEntitiesInPortals.java index 112ff069c..6f66a62b1 100755 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/dupepreventions/ChestedEntitiesInPortals.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/dupepreventions/ChestedEntitiesInPortals.java @@ -1,8 +1,8 @@ package me.xginko.aef.modules.dupepreventions; -import me.xginko.aef.AnarchyExploitFixes; import me.xginko.aef.modules.AEFModule; import me.xginko.aef.utils.EntityUtil; +import me.xginko.aef.utils.PlatformUtil; import org.bukkit.entity.ChestedHorse; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; @@ -48,7 +48,7 @@ private void onEntityPortalEvent(EntityPortalEvent event) { // Does not fire on @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onPortalEnter(EntityPortalEnterEvent event) { // Only portal event that can be listened to on folia - if (!AnarchyExploitFixes.isServerFolia()) return; // Avoid fallback logic on non-folia + if (!PlatformUtil.isFolia()) return; // Avoid fallback logic on non-folia if (!EntityUtil.isChestableHorse(event.getEntity())) return; ChestedHorse chestedHorse = (ChestedHorse) event.getEntity(); diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/elytra/ElytraHelper.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/elytra/ElytraHelper.java index 0dc2ee49a..cc8474676 100755 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/elytra/ElytraHelper.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/elytra/ElytraHelper.java @@ -5,7 +5,6 @@ import com.github.retrooper.packetevents.event.PacketListenerAbstract; import com.github.retrooper.packetevents.event.PacketListenerPriority; import com.github.retrooper.packetevents.event.PacketReceiveEvent; -import com.github.retrooper.packetevents.protocol.PacketSide; import com.github.retrooper.packetevents.protocol.packettype.PacketType; import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerFlying; import me.xginko.aef.modules.AEFModule; @@ -37,6 +36,7 @@ public class ElytraHelper extends AEFModule implements Runnable, PacketListener, private final PacketListenerAbstract packetListener; private ScheduledExecutorService executorService; private ScheduledFuture scheduledTask; + private final long speed_as_ticks = config.elytra_speed_calc_period / 50L; public ElytraHelper() { super("elytra.elytra-speed"); @@ -55,7 +55,7 @@ public void enable() { PacketEvents.getAPI().getEventManager().registerListener(packetListener); executorService = Executors.newScheduledThreadPool(1); scheduledTask = executorService.scheduleAtFixedRate( - this, 50L, config.elytra_speed_calc_period * 50L, TimeUnit.MILLISECONDS); + this, 50L, config.elytra_speed_calc_period, TimeUnit.MILLISECONDS); } @Override @@ -74,23 +74,22 @@ public void disable() { @Override public void run() { for (Map.Entry entry : playerDataMap.entrySet()) { - entry.getValue().calcSpeedAvg(config.elytra_calculate_3D, config.elytra_speed_calc_period); + entry.getValue().calcSpeedAvg(config.elytra_calculate_3D, speed_as_ticks); } } @Override public void onPacketReceive(PacketReceiveEvent event) { - if (event.getPacketType().getSide() != PacketSide.CLIENT) return; - if (event.getUser() == null) return; - UUID player = event.getUser().getUUID(); - if (player == null || !playerDataMap.containsKey(event.getUser().getUUID())) return; + if (event.isCancelled() || event.getUser() == null) return; + if (event.getUser().getUUID() == null || !playerDataMap.containsKey(event.getUser().getUUID())) return; if ( event.getPacketType() == PacketType.Play.Client.PLAYER_FLYING || event.getPacketType() == PacketType.Play.Client.PLAYER_POSITION || event.getPacketType() == PacketType.Play.Client.PLAYER_POSITION_AND_ROTATION ) { - playerDataMap.get(player).updateLatestPosition(new WrapperPlayClientPlayerFlying(event).getLocation()); + playerDataMap.get(event.getUser().getUUID()) + .updateLatestPosition(new WrapperPlayClientPlayerFlying(event).getLocation()); } } @@ -126,7 +125,7 @@ public double getBlocksPerTick(PlayerMoveEvent event) { } public boolean isInNewChunks(Player player) { - return player.getChunk().getInhabitedTime() <= 200L; + return player.getChunk().getInhabitedTime() <= config.elytra_old_chunk_limit; } public Location getSetbackLocation(PlayerMoveEvent event) { @@ -160,8 +159,9 @@ public void updateLatestPosition(Location location) { latest.setZ((float) location.getZ()); } - public void calcSpeedAvg(boolean using3D, long period) { - speedAvg = Math.abs(using3D ? LocationUtil.getRelDistance3D(previous, latest) : LocationUtil.getRelDistance2D(previous, latest)) / period; + public void calcSpeedAvg(boolean using3D, long tickPeriod) { + // Blockdistance per tick + speedAvg = Math.abs(using3D ? LocationUtil.getRelDistance3D(previous, latest) : LocationUtil.getRelDistance2D(previous, latest)) / tickPeriod; previous = latest.clone(); } }; diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/items/BannedItemNames.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/items/BannedItemNames.java index 62d134742..085b417f0 100755 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/items/BannedItemNames.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/items/BannedItemNames.java @@ -12,6 +12,8 @@ import org.bukkit.event.Listener; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.jetbrains.annotations.NotNull; import java.util.Collections; import java.util.EnumSet; @@ -78,7 +80,7 @@ public boolean shouldEnable() { } @Override - public ItemLegality legalityOf(ItemStack itemStack) { + public @NotNull ItemLegality legalityOf(@Nullable ItemStack itemStack) { if ( itemStack == null || whitelistedTypes.contains(itemStack.getType()) diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/items/BannedMaterials.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/items/BannedMaterials.java index df1627637..838dfd9d0 100755 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/items/BannedMaterials.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/items/BannedMaterials.java @@ -7,6 +7,8 @@ import me.xginko.aef.utils.ItemUtil; import org.bukkit.Material; import org.bukkit.inventory.ItemStack; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.jetbrains.annotations.NotNull; import java.util.EnumSet; import java.util.List; @@ -65,7 +67,7 @@ public boolean shouldEnable() { } @Override - public ItemLegality legalityOf(ItemStack itemStack) { + public @NotNull ItemLegality legalityOf(@Nullable ItemStack itemStack) { if (itemStack == null) { return ItemLegality.LEGAL; } diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/items/IllegalItemModule.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/items/IllegalItemModule.java index 86a925ffb..b0edc56e1 100644 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/items/IllegalItemModule.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/items/IllegalItemModule.java @@ -17,6 +17,7 @@ import org.bukkit.block.Block; import org.bukkit.block.BlockState; import org.bukkit.entity.EntityType; +import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; import org.bukkit.event.Event; import org.bukkit.event.EventHandler; @@ -37,7 +38,9 @@ import org.bukkit.event.world.ChunkLoadEvent; import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.ItemStack; +import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.PolyNull; +import org.jetbrains.annotations.NotNull; import java.time.Duration; import java.util.Arrays; @@ -48,13 +51,9 @@ public abstract class IllegalItemModule extends AEFModule implements Listener { - public abstract ItemLegality legalityOf(ItemStack itemStack); - public abstract void handleItem(ItemStack itemStack, ItemLegality legality); - protected final AEFPermission bypassPermission; protected final IllegalHandling handling; protected final Set optionalListeners; - private final boolean guiPluginsSupported; private final Cache, ExpiringSet> listenerCooldowns; private final Function, @PolyNull ExpiringSet> createIfAbsent; @@ -62,11 +61,7 @@ public IllegalItemModule(String configPath, AEFPermission bypassPermission) { super(configPath); this.bypassPermission = bypassPermission; this.optionalListeners = new HashSet<>(); - this.guiPluginsSupported = config.getBoolean(configPath + ".gui-plugins-supported", false, """ - Enable this if you have problems with the plugin removing items from chest guis. - Check if the inventory is connected to a location in the game. - If it is not, its very likely created by custom gui plugin. - """); + String configuredHandling = config.getString(configPath + ".handling", IllegalHandling.PREVENT_USE_ONLY.name(), "Available options:\n" + Arrays.stream(IllegalHandling.values()) .map(option -> option.name() + " - " + option.description()) @@ -81,16 +76,14 @@ public IllegalItemModule(String configPath, AEFPermission bypassPermission) { this.handling = handling; final boolean guiPluginsSupported = config.getBoolean(configPath + ".gui-plugins-supported", false, """ - Enable this if you have problems with the plugin removing items from chest guis. - Check if the inventory is connected to a location in the game. - If it is not, its very likely created by custom gui plugin."""); + Enable this if you have problems with the plugin removing items from chest guis."""); if (this.handling == IllegalHandling.STRICT) { optionalListeners.add(new Listener() { @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onInventoryOpen(InventoryOpenEvent event) { if (CachingPermTool.hasPermission(bypassPermission, event.getPlayer())) return; // Check if the inventory is connected to a location in the game. If it is not, - // its very likely created by custom gui plugin + // it was very likely created by a plugin if (!guiPluginsSupported || event.getInventory().getLocation() != null) { for (ItemStack invItem : event.getInventory()) { handleItem(invItem, legalityOf(invItem)); @@ -101,11 +94,11 @@ private void onInventoryOpen(InventoryOpenEvent event) { } if (config.getBoolean(configPath + ".prevent-hopper32k-mechanic", false, """ - Prevents Hopper32k mechanic of placing a shulker containing illegals\s - on top of a hopper, then using the illegal out of the hoppers inventory.\s + Prevents Hopper32k mechanic of placing a shulker containing illegals on top\s + of a hopper, then using the illegal out of the hopper's inventory.\s WARNING: Hooks into InventoryMoveItemEvent, which can become VERY resource\s - intense as the event fires for every single item getting moved by the\s - hopper. Enable only if you need to.""")) { + intense as the event fires in high frequencies as soon as players start using\s + farms or item sorters. Recommended to leave off if not necessary.""")) { optionalListeners.add(new Listener() { @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onItemGoesThroughHopper(InventoryMoveItemEvent event) { @@ -120,7 +113,7 @@ private void onItemGoesThroughHopper(InventoryMoveItemEvent event) { WARNING: CHECKING ON CHUNKLOAD IS NOT RECOMMENDED AS IT IS VERY RESOURCE INTENSE.\s BE VERY SURE YOU ACTUALLY NEED THIS.\s Iterates over all blocks in a chunk when it is loaded and checks any inventories\s - for illegals. If a container with illegals is found, it will be REMOVED."""); + for illegals. If a container with illegals is found, the container will be REMOVED."""); final boolean removeContainers = config.getBoolean(configPath + ".check-on-chunkload.remove-container", false, """ If set to true, immediately replaces the container with air. Otherwise, will try\s to handle items separately."""); @@ -138,7 +131,7 @@ private void onChunkLoad(ChunkLoadEvent event) { for (int z = 0; z < 16; z++) { for (int y = minY; y < maxY; y++) { Block block = chunk.getBlock(x, y, z); - if (!MaterialUtil.INVENTORY_HOLDER_ITEMS.contains(block.getType())) continue; + if (!MaterialUtil.INVENTORY_HOLDERS.contains(block.getType())) continue; if (removeContainers) { if (legalityOf(((InventoryHolder) block.getState()).getInventory()) != ItemLegality.LEGAL) @@ -157,14 +150,17 @@ private void onChunkLoad(ChunkLoadEvent event) { } this.listenerCooldowns = Caffeine.newBuilder().expireAfterWrite(Duration.ofMinutes(5)).build(); - this.createIfAbsent = k -> new ExpiringSet<>(Duration.ofMillis( - config.getInt(configPath+".check-rate-limit-millis", 3000, """ - The time in milliseconds to wait before performing another check,\s - if a check was positive. Helps with lag resulting from repeatedly\s - checking illegals.""") - )); + final Duration delay = Duration.ofMillis(config.getInt(configPath+".check-rate-limit-millis", 3000, """ + The time in milliseconds to wait before performing another check,\s + if a check was positive. Helps with lag resulting from repeatedly\s + checking illegals.""")); + this.createIfAbsent = eventClass -> new ExpiringSet<>(delay); } + public abstract @NotNull ItemLegality legalityOf(@Nullable ItemStack itemStack); + + public abstract void handleItem(ItemStack itemStack, ItemLegality legality); + @Override public void enable() { plugin.getServer().getPluginManager().registerEvents(this, plugin); @@ -178,7 +174,7 @@ public void disable() { optionalListeners.clear(); } - public ItemLegality legalityOf(Iterable itemStacks) { + public ItemLegality legalityOf(@Nullable Iterable itemStacks) { if (itemStacks == null) { return ItemLegality.LEGAL; } @@ -220,6 +216,8 @@ public void onBlockDispense(BlockDispenseEvent event) { @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void onPlayerArmorChange(PlayerArmorChangeEvent event) { + if (handling == IllegalHandling.PREVENT_USE_ONLY) return; // Cant cancel this event + if (!CachingPermTool.hasPermission(bypassPermission, event.getPlayer())) { handleItem(event.getNewItem(), legalityOf(event.getNewItem())); handleItem(event.getOldItem(), legalityOf(event.getOldItem())); @@ -321,10 +319,20 @@ public void onEntityDamageByEntity(EntityDamageByEntityEvent event) { return; } + if (EntityUtil.isLivingEntity(event.getDamager())) { + if (legalityOf(((LivingEntity) event.getDamager()).getActiveItem()) != ItemLegality.LEGAL) { + event.setCancelled(true); + if (handling != IllegalHandling.PREVENT_USE_ONLY) + event.getDamager().getScheduler().execute(plugin, event.getDamager()::remove, null, 1L); + return; + } + } + if (EntityUtil.isInventoryHolder(event.getDamager())) { if (legalityOf(((InventoryHolder) event.getDamager()).getInventory()) != ItemLegality.LEGAL) { event.setCancelled(true); - event.getDamager().getScheduler().execute(plugin, event.getDamager()::remove, null, 1L); + if (handling != IllegalHandling.PREVENT_USE_ONLY) + event.getDamager().getScheduler().execute(plugin, event.getDamager()::remove, null, 1L); } } } diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/items/IllegalPotions.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/items/IllegalPotions.java new file mode 100755 index 000000000..2383479cf --- /dev/null +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/items/IllegalPotions.java @@ -0,0 +1,63 @@ +package me.xginko.aef.modules.illegals.items; + +import me.xginko.aef.enums.AEFPermission; +import me.xginko.aef.enums.IllegalHandling; +import me.xginko.aef.enums.ItemLegality; +import me.xginko.aef.utils.ItemUtil; +import me.xginko.aef.utils.MaterialUtil; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.PotionMeta; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.jetbrains.annotations.NotNull; + +public class IllegalPotions extends IllegalItemModule { + + private final boolean checkStored; + + public IllegalPotions() { + super("illegals.potions", AEFPermission.BYPASS_ILLEGAL_POTIONS); + config.addComment(configPath + ".enable", + "Bypass permission: " + bypassPermission.string() + "\n" + + "Prevents usage of or reverts items with any attribute modifiers\n" + + "or item flags."); + this.checkStored = config.getBoolean(configPath + ".check-stored-items", false); + } + + @Override + public boolean shouldEnable() { + return config.getBoolean(configPath + ".enable", false); + } + + @Override + public @NotNull ItemLegality legalityOf(@Nullable ItemStack itemStack) { + if (itemStack == null || !MaterialUtil.POTIONS.contains(itemStack.getType()) || !itemStack.hasItemMeta()) { + return ItemLegality.LEGAL; + } + + PotionMeta potionMeta = (PotionMeta) itemStack.getItemMeta(); + if (potionMeta.hasCustomEffects() || potionMeta.hasColor()) { + return ItemLegality.ILLEGAL; + } + + if (checkStored) { + return legalityOf(ItemUtil.getStoredItems(itemStack)); + } + + return ItemLegality.LEGAL; + } + + @Override + public void handleItem(ItemStack itemStack, ItemLegality legality) { + if (handling == IllegalHandling.PREVENT_USE_ONLY) return; // We are cancelling the action in the super class + + switch (legality) { + case CONTAINS_ILLEGAL -> itemStack.setAmount(0); + case ILLEGAL -> { + PotionMeta potionMeta = (PotionMeta) itemStack.getItemMeta(); + potionMeta.setColor(null); + potionMeta.clearCustomEffects(); + itemStack.setItemMeta(potionMeta); + } + } + } +} diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/items/InvalidStackSize.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/items/InvalidStackSize.java index 4ca5f52ad..9e8694eb0 100755 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/items/InvalidStackSize.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/items/InvalidStackSize.java @@ -6,6 +6,8 @@ import me.xginko.aef.utils.ItemUtil; import org.bukkit.Material; import org.bukkit.inventory.ItemStack; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.jetbrains.annotations.NotNull; import java.util.EnumSet; import java.util.List; @@ -47,7 +49,7 @@ public boolean shouldEnable() { } @Override - public ItemLegality legalityOf(ItemStack itemStack) { + public @NotNull ItemLegality legalityOf(@Nullable ItemStack itemStack) { if (itemStack == null || itemStack.getType().isAir()) { return ItemLegality.LEGAL; } @@ -70,7 +72,16 @@ public void handleItem(ItemStack itemStack, ItemLegality legality) { if (handling == IllegalHandling.PREVENT_USE_ONLY) return; // We are cancelling the action in the super class switch (legality) { - case ILLEGAL -> itemStack.setAmount(itemStack.getMaxStackSize()); + case ILLEGAL -> { + if (itemStack.getAmount() < 1) { + itemStack.setAmount(1); + return; + } + + if (itemStack.getAmount() > itemStack.getMaxStackSize()) { + itemStack.setAmount(itemStack.getMaxStackSize()); + } + } case CONTAINS_ILLEGAL -> itemStack.setAmount(0); } } diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/items/ItemAttributes.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/items/ItemAttributes.java new file mode 100755 index 000000000..df8637b82 --- /dev/null +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/items/ItemAttributes.java @@ -0,0 +1,87 @@ +package me.xginko.aef.modules.illegals.items; + +import me.xginko.aef.enums.AEFPermission; +import me.xginko.aef.enums.IllegalHandling; +import me.xginko.aef.enums.ItemLegality; +import me.xginko.aef.utils.ItemUtil; +import org.bukkit.Material; +import org.bukkit.inventory.ItemFlag; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.jetbrains.annotations.NotNull; + +import java.util.EnumSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +public class ItemAttributes extends IllegalItemModule { + + private final Set whitelistedTypes; + private final boolean useWhitelist, blacklistMode, checkStored; + + public ItemAttributes() { + super("illegals.attribute-modifiers", AEFPermission.BYPASS_ILLEGAL_ATTRIBUTES); + config.addComment(configPath + ".enable", + "Bypass permission: " + bypassPermission.string() + "\n" + + "Prevents usage of or reverts items with any attribute modifiers\n" + + "or item flags."); + this.useWhitelist = config.getBoolean(configPath + ".item-whitelist-enabled", false); + this.blacklistMode = config.getBoolean(configPath + ".use-as-blacklist-instead", true); + this.checkStored = config.getBoolean(configPath + ".check-stored-items", false); + this.whitelistedTypes = config.getList(configPath + ".whitelisted-items", List.of("TOTEM_OF_UNDYING")) + .stream() + .map(configuredType -> { + try { + return Material.valueOf(configuredType); + } catch (IllegalArgumentException e) { + notRecognized(Material.class, configuredType); + return null; + } + }) + .filter(Objects::nonNull) + .collect(Collectors.toCollection(() -> EnumSet.noneOf(Material.class))); + } + + @Override + public boolean shouldEnable() { + return config.getBoolean(configPath + ".enable", false); + } + + @Override + public @NotNull ItemLegality legalityOf(@Nullable ItemStack itemStack) { + if (itemStack == null || itemStack.getType().isAir() || !itemStack.hasItemMeta()) { + return ItemLegality.LEGAL; + } + + if (!useWhitelist || blacklistMode == whitelistedTypes.contains(itemStack.getType())) { + ItemMeta itemMeta = itemStack.getItemMeta(); + if (itemMeta.hasAttributeModifiers() || !itemMeta.getItemFlags().isEmpty()) { + return ItemLegality.ILLEGAL; + } + } + + if (checkStored) { + return legalityOf(ItemUtil.getStoredItems(itemStack)); + } + + return ItemLegality.LEGAL; + } + + @Override + public void handleItem(ItemStack itemStack, ItemLegality legality) { + if (handling == IllegalHandling.PREVENT_USE_ONLY) return; // We are cancelling the action in the super class + + switch (legality) { + case CONTAINS_ILLEGAL -> itemStack.setAmount(0); + case ILLEGAL -> { + ItemMeta itemMeta = itemStack.getItemMeta(); + itemMeta.setAttributeModifiers(null); + itemMeta.removeItemFlags(itemMeta.getItemFlags().toArray(new ItemFlag[0])); + itemStack.setItemMeta(itemMeta); + } + } + } +} diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/items/PlayerHeads.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/items/PlayerHeads.java index 86e85d57d..4fe7f19e7 100755 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/items/PlayerHeads.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/items/PlayerHeads.java @@ -6,6 +6,8 @@ import me.xginko.aef.utils.ItemUtil; import me.xginko.aef.utils.MaterialUtil; import org.bukkit.inventory.ItemStack; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.jetbrains.annotations.NotNull; public class PlayerHeads extends IllegalItemModule { @@ -26,7 +28,7 @@ public boolean shouldEnable() { } @Override - public ItemLegality legalityOf(ItemStack itemStack) { + public @NotNull ItemLegality legalityOf(@Nullable ItemStack itemStack) { if (itemStack == null) { return ItemLegality.LEGAL; } diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/items/SpawnEggs.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/items/SpawnEggs.java index 9ca06e555..2ffeb7c01 100755 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/items/SpawnEggs.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/items/SpawnEggs.java @@ -8,6 +8,8 @@ import me.xginko.aef.utils.ItemUtil; import org.bukkit.Material; import org.bukkit.inventory.ItemStack; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.jetbrains.annotations.NotNull; import java.util.Collections; import java.util.EnumSet; @@ -49,7 +51,7 @@ public boolean shouldEnable() { } @Override - public ItemLegality legalityOf(ItemStack itemStack) { + public @NotNull ItemLegality legalityOf(@Nullable ItemStack itemStack) { if (itemStack == null || whitelistedTypes.contains(itemStack.getType())) { return ItemLegality.LEGAL; } diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/items/Unbreakables.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/items/Unbreakables.java index f4cfade09..895d40224 100755 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/items/Unbreakables.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/items/Unbreakables.java @@ -1,12 +1,15 @@ package me.xginko.aef.modules.illegals.items; import me.xginko.aef.enums.AEFPermission; +import me.xginko.aef.enums.IllegalHandling; import me.xginko.aef.enums.ItemLegality; import me.xginko.aef.utils.ItemUtil; import org.bukkit.Material; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.Damageable; import org.bukkit.inventory.meta.ItemMeta; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.jetbrains.annotations.NotNull; import java.util.EnumSet; import java.util.List; @@ -52,13 +55,27 @@ public boolean shouldEnable() { } @Override - public ItemLegality legalityOf(ItemStack itemStack) { - if (itemStack == null || itemStack.getType().isAir()) { + public @NotNull ItemLegality legalityOf(@Nullable ItemStack itemStack) { + if (itemStack == null || itemStack.getType().isAir() || !itemStack.hasItemMeta()) { return ItemLegality.LEGAL; } if (!useWhitelist || blacklistMode == whitelistedTypes.contains(itemStack.getType())) { - if (isUnbreakable(itemStack)) { + if (itemStack.getItemMeta().isUnbreakable()) { + return ItemLegality.ILLEGAL; + } + + if (itemStack.getType().getMaxDurability() == 0) { + return ItemLegality.LEGAL; + } + + Damageable damageable = (Damageable) itemStack.getItemMeta(); + + if (!damageable.hasDamage()) { + return ItemLegality.LEGAL; + } + + if (damageable.getDamage() > itemStack.getType().getMaxDurability() || damageable.getDamage() < 0) { return ItemLegality.ILLEGAL; } } @@ -72,25 +89,33 @@ public ItemLegality legalityOf(ItemStack itemStack) { @Override public void handleItem(ItemStack itemStack, ItemLegality legality) { - // We need to always take action here as we cant reliably prevent usage otherwise - if (legality != ItemLegality.LEGAL) { - itemStack.setAmount(0); - } - } + if (handling == IllegalHandling.PREVENT_USE_ONLY) return; - private boolean isUnbreakable(ItemStack itemStack) { - if (!itemStack.hasItemMeta()) return false; + switch (legality) { + case CONTAINS_ILLEGAL -> itemStack.setAmount(0); + case ILLEGAL -> { + ItemMeta itemMeta = itemStack.getItemMeta(); - ItemMeta meta = itemStack.getItemMeta(); - if (meta.isUnbreakable()) return true; + if (itemMeta.isUnbreakable()) { + itemMeta.setUnbreakable(false); + itemStack.setItemMeta(itemMeta); + } - Damageable damageMeta = (Damageable) meta; - if (!damageMeta.hasDamage()) return false; + if (itemStack.getType().getMaxDurability() == 0) { + return; + } - final short itemMaxDurability = itemStack.getType().getMaxDurability(); - if (itemMaxDurability == 0) return false; + Damageable damageable = (Damageable) itemStack.getItemMeta(); - final int remainingDurability = itemMaxDurability - damageMeta.getDamage(); - return remainingDurability > itemMaxDurability || remainingDurability < 0; + if (!damageable.hasDamage()) { + return; + } + + if (damageable.getDamage() > itemStack.getType().getMaxDurability() || damageable.getDamage() < 0) { + damageable.setDamage(itemStack.getType().getMaxDurability()); + itemStack.setItemMeta(damageable); + } + } + } } } diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/items/enchantments/HigherEnchants.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/items/enchantments/HigherEnchants.java index 204f436cf..d1bab08d6 100755 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/items/enchantments/HigherEnchants.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/items/enchantments/HigherEnchants.java @@ -8,6 +8,8 @@ import org.bukkit.Material; import org.bukkit.enchantments.Enchantment; import org.bukkit.inventory.ItemStack; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.jetbrains.annotations.NotNull; import java.util.EnumSet; import java.util.HashSet; @@ -63,7 +65,7 @@ public boolean shouldEnable() { } @Override - public ItemLegality legalityOf(ItemStack itemStack) { + public @NotNull ItemLegality legalityOf(@Nullable ItemStack itemStack) { if (itemStack == null || itemStack.getType().isAir()) { return ItemLegality.LEGAL; } diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/items/enchantments/InapplicableEnchants.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/items/enchantments/InapplicableEnchants.java index e298cde1d..dde4d37d1 100755 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/items/enchantments/InapplicableEnchants.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/items/enchantments/InapplicableEnchants.java @@ -8,6 +8,8 @@ import org.bukkit.Material; import org.bukkit.enchantments.Enchantment; import org.bukkit.inventory.ItemStack; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.jetbrains.annotations.NotNull; import java.util.EnumSet; import java.util.List; @@ -51,7 +53,7 @@ public boolean shouldEnable() { } @Override - public ItemLegality legalityOf(ItemStack itemStack) { + public @NotNull ItemLegality legalityOf(@Nullable ItemStack itemStack) { if (itemStack == null || itemStack.getType().isAir()) { return ItemLegality.LEGAL; } diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/items/enchantments/IncompatibleEnchants.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/items/enchantments/IncompatibleEnchants.java index dc6a6d58d..4a2f981e6 100755 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/items/enchantments/IncompatibleEnchants.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/items/enchantments/IncompatibleEnchants.java @@ -1,6 +1,5 @@ package me.xginko.aef.modules.illegals.items.enchantments; -import com.cryptomorin.xseries.XEnchantment; import me.xginko.aef.enums.AEFPermission; import me.xginko.aef.enums.IllegalHandling; import me.xginko.aef.enums.ItemLegality; @@ -9,6 +8,8 @@ import org.bukkit.Material; import org.bukkit.enchantments.Enchantment; import org.bukkit.inventory.ItemStack; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.jetbrains.annotations.NotNull; import java.util.Collections; import java.util.EnumSet; @@ -16,42 +17,38 @@ import java.util.Set; import java.util.stream.Collectors; -public class IncompatibleEnchants extends IllegalItemModule { +import static com.cryptomorin.xseries.XEnchantment.BANE_OF_ARTHROPODS; +import static com.cryptomorin.xseries.XEnchantment.BINDING_CURSE; +import static com.cryptomorin.xseries.XEnchantment.BLAST_PROTECTION; +import static com.cryptomorin.xseries.XEnchantment.CHANNELING; +import static com.cryptomorin.xseries.XEnchantment.DEPTH_STRIDER; +import static com.cryptomorin.xseries.XEnchantment.FIRE_PROTECTION; +import static com.cryptomorin.xseries.XEnchantment.FORTUNE; +import static com.cryptomorin.xseries.XEnchantment.FROST_WALKER; +import static com.cryptomorin.xseries.XEnchantment.INFINITY; +import static com.cryptomorin.xseries.XEnchantment.LOYALTY; +import static com.cryptomorin.xseries.XEnchantment.MENDING; +import static com.cryptomorin.xseries.XEnchantment.MULTISHOT; +import static com.cryptomorin.xseries.XEnchantment.PIERCING; +import static com.cryptomorin.xseries.XEnchantment.PROJECTILE_PROTECTION; +import static com.cryptomorin.xseries.XEnchantment.PROTECTION; +import static com.cryptomorin.xseries.XEnchantment.RIPTIDE; +import static com.cryptomorin.xseries.XEnchantment.SHARPNESS; +import static com.cryptomorin.xseries.XEnchantment.SILK_TOUCH; +import static com.cryptomorin.xseries.XEnchantment.SMITE; +import static com.cryptomorin.xseries.XEnchantment.VANISHING_CURSE; - private static final Enchantment BINDING_CURSE = XEnchantment.BINDING_CURSE.getEnchant(); - private static final Enchantment VANISHING_CURSE = XEnchantment.VANISHING_CURSE.getEnchant(); - private static final Enchantment MULTISHOT = XEnchantment.MULTISHOT.getEnchant(); - private static final Enchantment PIERCING = XEnchantment.PIERCING.getEnchant(); - private static final Enchantment RIPTIDE = XEnchantment.RIPTIDE.getEnchant(); - private static final Enchantment LOYALTY = XEnchantment.LOYALTY.getEnchant(); - private static final Enchantment CHANNELING = XEnchantment.CHANNELING.getEnchant(); - private static final Enchantment MENDING = XEnchantment.MENDING.getEnchant(); - private static final Enchantment INFINITY = XEnchantment.INFINITY.getEnchant(); - private static final Enchantment SILK_TOUCH = XEnchantment.SILK_TOUCH.getEnchant(); - private static final Enchantment FORTUNE = XEnchantment.FORTUNE.getEnchant(); - private static final Enchantment DEPTH_STRIDER = XEnchantment.DEPTH_STRIDER.getEnchant(); - private static final Enchantment FROST_WALKER = XEnchantment.FROST_WALKER.getEnchant(); - private static final Enchantment SHARPNESS = XEnchantment.SHARPNESS.getEnchant(); - private static final Enchantment BANE_OF_ARTHROPODS = XEnchantment.BANE_OF_ARTHROPODS.getEnchant(); - private static final Enchantment SMITE = XEnchantment.SMITE.getEnchant(); - private static final Enchantment PROTECTION = XEnchantment.PROTECTION.getEnchant(); - private static final Enchantment BLAST_PROTECTION = XEnchantment.BLAST_PROTECTION.getEnchant(); - private static final Enchantment FIRE_PROTECTION = XEnchantment.FIRE_PROTECTION.getEnchant(); - private static final Enchantment PROJECTILE_PROTECTION = XEnchantment.PROJECTILE_PROTECTION.getEnchant(); - - private static final Enchantment[] PROTECT_ENCHANTS = { - PROTECTION, BLAST_PROTECTION, FIRE_PROTECTION, PROJECTILE_PROTECTION - }; - - private static final Enchantment[] DAMAGE_ENCHANTS = { - SHARPNESS, SMITE, BANE_OF_ARTHROPODS - }; +public class IncompatibleEnchants extends IllegalItemModule { private final Set whitelistedTypes; + private final Enchantment[] damageEnchants, protectionEnchants; private final boolean useWhitelist, blacklistMode, checkStored; public IncompatibleEnchants() { super("illegals.enchantments.incompatible-enchants", AEFPermission.BYPASS_ILLEGAL_ENCHANT_INCOMPATIBLE); + this.damageEnchants = new Enchantment[]{SHARPNESS.getEnchant(), SMITE.getEnchant(), BANE_OF_ARTHROPODS.getEnchant()}; + this.protectionEnchants = new Enchantment[]{PROTECTION.getEnchant(), BLAST_PROTECTION.getEnchant(), + FIRE_PROTECTION.getEnchant(), PROJECTILE_PROTECTION.getEnchant()}; config.addComment(configPath + ".enable", "Bypass permission: " + bypassPermission.string() + "\n" + "Reverts or prevents usage of ItemStacks with Enchantments that\n" + @@ -81,7 +78,7 @@ public boolean shouldEnable() { } @Override - public ItemLegality legalityOf(ItemStack itemStack) { + public @NotNull ItemLegality legalityOf(@Nullable ItemStack itemStack) { if (itemStack == null || itemStack.getType() == Material.AIR) { return ItemLegality.LEGAL; } @@ -90,21 +87,22 @@ public ItemLegality legalityOf(ItemStack itemStack) { final Set enchantments = itemStack.getEnchantments().keySet(); if (!enchantments.isEmpty()) { - if (enchantments.contains(BINDING_CURSE) && enchantments.contains(VANISHING_CURSE)) + if (enchantments.contains(SILK_TOUCH.getEnchant()) && enchantments.contains(FORTUNE.getEnchant())) return ItemLegality.ILLEGAL; - if (enchantments.contains(INFINITY) && enchantments.contains(MENDING)) + if (enchantments.contains(DEPTH_STRIDER.getEnchant()) && enchantments.contains(FROST_WALKER.getEnchant())) return ItemLegality.ILLEGAL; - if (enchantments.contains(SILK_TOUCH) && enchantments.contains(FORTUNE)) + if (enchantments.contains(INFINITY.getEnchant()) && enchantments.contains(MENDING.getEnchant())) return ItemLegality.ILLEGAL; - if (enchantments.contains(DEPTH_STRIDER) && enchantments.contains(FROST_WALKER)) + if (enchantments.contains(BINDING_CURSE.getEnchant()) && enchantments.contains(VANISHING_CURSE.getEnchant())) return ItemLegality.ILLEGAL; - if (enchantments.contains(MULTISHOT) && enchantments.contains(PIERCING)) + if (enchantments.contains(RIPTIDE.getEnchant()) + && (enchantments.contains(LOYALTY.getEnchant()) || enchantments.contains(CHANNELING.getEnchant()))) return ItemLegality.ILLEGAL; - if (enchantments.contains(RIPTIDE) && (enchantments.contains(LOYALTY) || enchantments.contains(CHANNELING))) + if (enchantments.contains(MULTISHOT.getEnchant()) && enchantments.contains(PIERCING.getEnchant())) return ItemLegality.ILLEGAL; int dmgEnchCount = 0; - for (Enchantment damageEnchant : DAMAGE_ENCHANTS) { + for (Enchantment damageEnchant : damageEnchants) { if (enchantments.contains(damageEnchant)) { dmgEnchCount++; if (dmgEnchCount > 1) { @@ -114,7 +112,7 @@ public ItemLegality legalityOf(ItemStack itemStack) { } int protEnchCount = 0; - for (Enchantment protectEnchant : PROTECT_ENCHANTS) { + for (Enchantment protectEnchant : protectionEnchants) { if (enchantments.contains(protectEnchant)) { protEnchCount++; if (protEnchCount > 1) { @@ -144,43 +142,43 @@ public void handleItem(ItemStack itemStack, ItemLegality legality) { final Set enchantments = itemStack.getEnchantments().keySet(); - if (enchantments.contains(BINDING_CURSE) && enchantments.contains(VANISHING_CURSE)) - itemStack.removeEnchantment(BINDING_CURSE); - if (enchantments.contains(MULTISHOT) && enchantments.contains(PIERCING)) - itemStack.removeEnchantment(MULTISHOT); - if (enchantments.contains(RIPTIDE) && (enchantments.contains(LOYALTY) || enchantments.contains(CHANNELING))) - itemStack.removeEnchantment(RIPTIDE); - if (enchantments.contains(INFINITY) && enchantments.contains(MENDING)) - itemStack.removeEnchantment(INFINITY); - if (enchantments.contains(SILK_TOUCH) && enchantments.contains(FORTUNE)) - itemStack.removeEnchantment(FORTUNE); - if (enchantments.contains(DEPTH_STRIDER) && enchantments.contains(FROST_WALKER)) - itemStack.removeEnchantment(FROST_WALKER); - - if (enchantments.contains(SHARPNESS)) { // Prefer keeping Sharpness enchantment if present - for (Enchantment dmgEnchant : DAMAGE_ENCHANTS) { - if (dmgEnchant != SHARPNESS) { + if (enchantments.contains(SILK_TOUCH.getEnchant()) && enchantments.contains(FORTUNE.getEnchant())) + itemStack.removeEnchantment(FORTUNE.getEnchant()); + if (enchantments.contains(DEPTH_STRIDER.getEnchant()) && enchantments.contains(FROST_WALKER.getEnchant())) + itemStack.removeEnchantment(FROST_WALKER.getEnchant()); + if (enchantments.contains(INFINITY.getEnchant()) && enchantments.contains(MENDING.getEnchant())) + itemStack.removeEnchantment(INFINITY.getEnchant()); + if (enchantments.contains(BINDING_CURSE.getEnchant()) && enchantments.contains(VANISHING_CURSE.getEnchant())) + itemStack.removeEnchantment(BINDING_CURSE.getEnchant()); + if (enchantments.contains(MULTISHOT.getEnchant()) && enchantments.contains(PIERCING.getEnchant())) + itemStack.removeEnchantment(MULTISHOT.getEnchant()); + if (enchantments.contains(RIPTIDE.getEnchant()) && (enchantments.contains(LOYALTY.getEnchant()) || enchantments.contains(CHANNELING.getEnchant()))) + itemStack.removeEnchantment(RIPTIDE.getEnchant()); + + if (enchantments.contains(SHARPNESS.getEnchant())) { // Prefer keeping sharpness enchantment if present + for (Enchantment dmgEnchant : damageEnchants) { + if (dmgEnchant != SHARPNESS.getEnchant()) { itemStack.removeEnchantment(dmgEnchant); } } - } else if (enchantments.contains(BANE_OF_ARTHROPODS) && enchantments.contains(SMITE)) { - itemStack.removeEnchantment(BANE_OF_ARTHROPODS); + } else if (enchantments.contains(BANE_OF_ARTHROPODS.getEnchant()) && enchantments.contains(SMITE.getEnchant())) { + itemStack.removeEnchantment(BANE_OF_ARTHROPODS.getEnchant()); } - if (enchantments.contains(PROTECTION)) { // Prefer keeping Protection enchantment if present - for (Enchantment protEnchant : PROTECT_ENCHANTS) { - if (protEnchant != PROTECTION) { + if (enchantments.contains(PROTECTION.getEnchant())) { // Prefer keeping protection enchantment if present + for (Enchantment protEnchant : protectionEnchants) { + if (protEnchant != PROTECTION.getEnchant()) { itemStack.removeEnchantment(protEnchant); } } - } else if (enchantments.contains(BLAST_PROTECTION)) { // If protection is present, prefer blast protection - for (Enchantment protEnchant : PROTECT_ENCHANTS) { - if (protEnchant != BLAST_PROTECTION) { + } else if (enchantments.contains(BLAST_PROTECTION.getEnchant())) { // If protection isn't present, prefer blast protection + for (Enchantment protEnchant : protectionEnchants) { + if (protEnchant != BLAST_PROTECTION.getEnchant()) { itemStack.removeEnchantment(protEnchant); } } - } else if (enchantments.contains(PROJECTILE_PROTECTION) && enchantments.contains(FIRE_PROTECTION)) { - itemStack.removeEnchantment(FIRE_PROTECTION); // If protection and blast protection is not present, prefer projectile protection + } else if (enchantments.contains(PROJECTILE_PROTECTION.getEnchant()) && enchantments.contains(FIRE_PROTECTION.getEnchant())) { + itemStack.removeEnchantment(FIRE_PROTECTION.getEnchant()); // If protection and blast protection is not present, prefer projectile protection } } } \ No newline at end of file diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/items/nbt/CommandItems.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/items/nbt/CommandItems.java index 16fd258f5..c99c3d724 100755 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/items/nbt/CommandItems.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/items/nbt/CommandItems.java @@ -1,11 +1,13 @@ package me.xginko.aef.modules.illegals.items.nbt; -import de.tr7zw.changeme.nbtapi.NBTItem; import me.xginko.aef.enums.AEFPermission; import me.xginko.aef.enums.ItemLegality; import me.xginko.aef.modules.illegals.items.IllegalItemModule; +import me.xginko.aef.utils.ItemUtil; import me.xginko.aef.utils.MaterialUtil; import org.bukkit.inventory.ItemStack; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.jetbrains.annotations.NotNull; public class CommandItems extends IllegalItemModule { @@ -27,16 +29,16 @@ public boolean shouldEnable() { } @Override - public ItemLegality legalityOf(ItemStack itemStack) { + public @NotNull ItemLegality legalityOf(@Nullable ItemStack itemStack) { if (itemStack == null || itemStack.getType().isAir()) { return ItemLegality.LEGAL; } - if (!checkStored && MaterialUtil.INVENTORY_HOLDER_ITEMS.contains(itemStack.getType())) { + if (!checkStored && MaterialUtil.INVENTORY_HOLDERS.contains(itemStack.getType())) { return ItemLegality.LEGAL; } - if (new NBTItem(itemStack).toString().contains("run_command")) { + if (ItemUtil.getNBTString(itemStack).contains("run_command")) { return ItemLegality.ILLEGAL; } diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/items/nbt/CustomNBTFilter.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/items/nbt/CustomNBTFilter.java index 2966e8cb4..ae0369127 100755 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/items/nbt/CustomNBTFilter.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/items/nbt/CustomNBTFilter.java @@ -1,6 +1,5 @@ package me.xginko.aef.modules.illegals.items.nbt; -import de.tr7zw.changeme.nbtapi.NBTItem; import me.xginko.aef.enums.AEFPermission; import me.xginko.aef.enums.IllegalHandling; import me.xginko.aef.enums.ItemLegality; @@ -8,6 +7,8 @@ import me.xginko.aef.utils.ItemUtil; import org.bukkit.Material; import org.bukkit.inventory.ItemStack; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.jetbrains.annotations.NotNull; import java.util.EnumSet; import java.util.HashSet; @@ -52,13 +53,13 @@ public boolean shouldEnable() { } @Override - public ItemLegality legalityOf(ItemStack itemStack) { + public @NotNull ItemLegality legalityOf(@Nullable ItemStack itemStack) { if (itemStack == null || itemStack.getType().isAir()) { return ItemLegality.LEGAL; } if (!useWhitelist || blacklistMode == whitelistedTypes.contains(itemStack.getType())) { - String nbtItemToString = new NBTItem(itemStack).toString(); + String nbtItemToString = ItemUtil.getNBTString(itemStack); for (String tag : illegalTags) { if (nbtItemToString.contains(tag)) { return ItemLegality.ILLEGAL; diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/items/nbt/NBTFilledStorageItem.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/items/nbt/NBTFilledStorageItem.java index 57a1b5de3..2f8fc47f6 100755 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/items/nbt/NBTFilledStorageItem.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/items/nbt/NBTFilledStorageItem.java @@ -1,7 +1,7 @@ package me.xginko.aef.modules.illegals.items.nbt; import com.destroystokyo.paper.MaterialTags; -import de.tr7zw.changeme.nbtapi.NBTItem; +import de.tr7zw.changeme.nbtapi.NBT; import me.xginko.aef.enums.AEFPermission; import me.xginko.aef.enums.IllegalHandling; import me.xginko.aef.enums.ItemLegality; @@ -10,6 +10,8 @@ import me.xginko.aef.utils.MaterialUtil; import org.bukkit.Material; import org.bukkit.inventory.ItemStack; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.jetbrains.annotations.NotNull; import java.util.EnumSet; import java.util.Objects; @@ -34,7 +36,7 @@ public NBTFilledStorageItem() { this.stored_items_tag = config.getString(configPath + ".tag", "BlockEntityTag", "The exact name of the nbt tag that signals items are stored inside."); this.checkStored = config.getBoolean(configPath + ".check-stored-items", false); - this.storageTypes = config.getList(configPath + ".storage-types", MaterialUtil.INVENTORY_HOLDER_ITEMS.stream() + this.storageTypes = config.getList(configPath + ".storage-types", MaterialUtil.INVENTORY_HOLDERS.stream() .filter(material -> !MaterialTags.SHULKER_BOXES.isTagged(material)).map(Enum::name).sorted().toList()) .stream() .map(configuredType -> { @@ -55,12 +57,12 @@ public boolean shouldEnable() { } @Override - public ItemLegality legalityOf(ItemStack itemStack) { + public @NotNull ItemLegality legalityOf(@Nullable ItemStack itemStack) { if (itemStack == null || itemStack.getType().isAir()) { return ItemLegality.LEGAL; } - if (storageTypes.contains(itemStack.getType()) && new NBTItem(itemStack).getKeys().contains(stored_items_tag)) { + if (storageTypes.contains(itemStack.getType()) && NBT.readNbt(itemStack).getKeys().contains(stored_items_tag)) { return ItemLegality.ILLEGAL; } diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/placedblocks/PeriodicallyRemoveIllegalBlocks.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/placedblocks/PeriodicallyRemoveIllegalBlocks.java index 5c688d9eb..9555a7b25 100755 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/placedblocks/PeriodicallyRemoveIllegalBlocks.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/placedblocks/PeriodicallyRemoveIllegalBlocks.java @@ -1,5 +1,6 @@ package me.xginko.aef.modules.illegals.placedblocks; +import com.cryptomorin.xseries.XMaterial; import io.papermc.paper.threadedregions.scheduler.ScheduledTask; import me.xginko.aef.AnarchyExploitFixes; import me.xginko.aef.modules.AEFModule; @@ -23,7 +24,6 @@ public class PeriodicallyRemoveIllegalBlocks extends AEFModule implements Consum private final Set exemptedWorlds; private final long checkPeriod; private final double pauseTPS; - private final int netherCeilY; private final boolean checkShouldPauseOnLowTPS; public PeriodicallyRemoveIllegalBlocks() { @@ -48,7 +48,6 @@ public PeriodicallyRemoveIllegalBlocks() { this.checkPeriod = config.getInt(configPath + ".check-period-in-seconds", 10) * 20L; this.checkShouldPauseOnLowTPS = config.getBoolean(configPath + ".pause-on-low-TPS", false); this.pauseTPS = config.getDouble(configPath + ".pause-TPS", 14.0); - this.netherCeilY = config.nether_ceiling_max_y; } @Override @@ -88,19 +87,20 @@ public void accept(ScheduledTask task) { if (!blocksToRemove.contains(block.getType())) continue; // If is bedrock, make sure not to delete naturally generated - if (block.getType() == Material.BEDROCK) { + if (block.getType() == XMaterial.BEDROCK.parseMaterial()) { if (y > minY + 4) { // offset to not delete natural bedrock floor if (inNether) { // offset to not delete bedrock ceiling - if (y < netherCeilY - 5) block.setType(Material.AIR, false); + if (y < config.nether_ceiling_max_y - 5) + block.setType(XMaterial.AIR.parseMaterial(), false); } else { - block.setType(Material.AIR, false); + block.setType(XMaterial.AIR.parseMaterial(), false); } } continue; } - block.setType(Material.AIR, false); + block.setType(XMaterial.AIR.parseMaterial(), false); } } } diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/placedblocks/RemoveIllegalBlocksOnChunkload.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/placedblocks/RemoveIllegalBlocksOnChunkload.java index 37c4528e6..5fcbee944 100755 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/placedblocks/RemoveIllegalBlocksOnChunkload.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/placedblocks/RemoveIllegalBlocksOnChunkload.java @@ -1,5 +1,6 @@ package me.xginko.aef.modules.illegals.placedblocks; +import com.cryptomorin.xseries.XMaterial; import me.xginko.aef.AnarchyExploitFixes; import me.xginko.aef.modules.AEFModule; import org.bukkit.Chunk; @@ -24,7 +25,6 @@ public class RemoveIllegalBlocksOnChunkload extends AEFModule implements Listene private final Set blocksToRemove; private final Set exemptedWorlds; private final boolean checkShouldPauseOnLowTPS; - private final int netherCeilY; private final double pauseTPS; public RemoveIllegalBlocksOnChunkload() { @@ -50,7 +50,6 @@ public RemoveIllegalBlocksOnChunkload() { List.of("exampleworld1", "exampleworld2"))); this.checkShouldPauseOnLowTPS = config.getBoolean(configPath + ".pause-on-low-TPS", false); this.pauseTPS = config.getDouble(configPath + ".pause-TPS", 14.0); - this.netherCeilY = config.nether_ceiling_max_y; } @Override @@ -88,19 +87,20 @@ private void onChunkLoad(ChunkLoadEvent event) { if (!blocksToRemove.contains(block.getType())) continue; // If is bedrock, make sure not to delete naturally generated - if (block.getType() == Material.BEDROCK) { + if (block.getType() == XMaterial.BEDROCK.parseMaterial()) { if (y > minY + 4) { // offset to not delete natural bedrock floor if (inNether) { // offset to not delete bedrock ceiling - if (y < netherCeilY - 5) block.setType(Material.AIR, false); + if (y < config.nether_ceiling_max_y - 5) + block.setType(XMaterial.AIR.parseMaterial(), false); } else { - block.setType(Material.AIR, false); + block.setType(XMaterial.AIR.parseMaterial(), false); } } continue; } - block.setType(Material.AIR, false); + block.setType(XMaterial.AIR.parseMaterial(), false); } } } diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/placedblocks/RemoveUnnaturalSpawners.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/placedblocks/RemoveUnnaturalSpawners.java index c96d21fc3..7add22e96 100755 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/placedblocks/RemoveUnnaturalSpawners.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/illegals/placedblocks/RemoveUnnaturalSpawners.java @@ -6,7 +6,6 @@ import me.xginko.aef.AnarchyExploitFixes; import me.xginko.aef.modules.AEFModule; import org.bukkit.Chunk; -import org.bukkit.Material; import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.block.CreatureSpawner; @@ -77,7 +76,7 @@ public void enable() { @Override public boolean shouldEnable() { - return config.getBoolean(configPath + ".enable", false) && !naturalSpawners.isEmpty(); + return config.getBoolean(configPath + ".enable", false); } @Override @@ -87,8 +86,7 @@ public void disable() { @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) private void onChunkLoad(ChunkLoadEvent event) { - if (event.isNewChunk()) return; - if (checkShouldPauseOnLowTPS && (AnarchyExploitFixes.getTickReporter().getTPS() <= pauseTPS)) return; + if (event.isNewChunk() || checkShouldPauseOnLowTPS && AnarchyExploitFixes.getTickReporter().getTPS() <= pauseTPS) return; Chunk chunk = event.getChunk(); World world = chunk.getWorld(); @@ -104,7 +102,7 @@ private void onChunkLoad(ChunkLoadEvent event) { if (block.getType() == XMaterial.SPAWNER.parseMaterial() && !naturalSpawners.get(world.getName()).contains(((CreatureSpawner) block.getState()).getSpawnedType()) ) { - block.setType(Material.AIR, false); + block.setType(XMaterial.AIR.parseMaterial(), false); } } } diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/InventoryActionLag.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/InventoryActionLag.java deleted file mode 100755 index 02c343009..000000000 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/InventoryActionLag.java +++ /dev/null @@ -1,120 +0,0 @@ -package me.xginko.aef.modules.lagpreventions; - -import com.github.benmanes.caffeine.cache.Cache; -import com.github.benmanes.caffeine.cache.Caffeine; -import io.github.thatsmusic99.configurationmaster.api.ConfigSection; -import me.xginko.aef.modules.AEFModule; -import me.xginko.aef.utils.LocationUtil; -import org.bukkit.Location; -import org.bukkit.block.Block; -import org.bukkit.entity.Entity; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.HandlerList; -import org.bukkit.event.Listener; -import org.bukkit.event.inventory.InventoryAction; -import org.bukkit.event.inventory.InventoryClickEvent; - -import java.time.Duration; -import java.util.EnumMap; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; - -public class InventoryActionLag extends AEFModule implements Listener { - - private final Map clickActionLimits = new EnumMap<>(InventoryAction.class); - private final Cache> entityInventoryClicks; - private final Cache> blockInventoryClicks; - private final boolean logIsEnabled; - - public InventoryActionLag() { - super("lag-preventions.prevent-inventory-action-lag"); - config.addComment(configPath + ".enable", """ - WARNING: VERY EXPERIMENTAL!\s - Prevent lag generated by players quickly moving big items back and\s - forth between inventories. Uses cached counters that auto-reset after\s - the configurable time in milliseconds."""); - this.logIsEnabled = config.getBoolean(configPath + ".log", true); - Duration cacheTime = Duration.ofMillis(Math.max(config.getInt(configPath + ".cache-time-millis", 2000, - "The amount of time in milliseconds an entry is kept after writing."), 1)); - this.blockInventoryClicks = Caffeine.newBuilder().expireAfterWrite(cacheTime).build(); - this.entityInventoryClicks = Caffeine.newBuilder().expireAfterWrite(cacheTime).build(); - Map defaults = new HashMap<>(); - defaults.put("COLLECT_TO_CURSOR", 15); - defaults.put("MOVE_TO_OTHER_INVENTORY", 8); - defaults.put("HOTBAR_SWAP", 30); - ConfigSection section = config.getConfigSection(configPath + ".click-action-limits", defaults, """ - Use correct enums from:\s - https://jd.papermc.io/paper/1.20/org/bukkit/event/inventory/InventoryAction.html.\s - Format is: InventoryClickAction: AllowedClicksPerTime"""); - for (String configuredAction : section.getKeys(false)) { - try { - InventoryAction action = InventoryAction.valueOf(configuredAction); - Integer maxClicksPerTime = Integer.valueOf(section.getString(configuredAction)); - clickActionLimits.put(action, maxClicksPerTime); - } catch (NumberFormatException e) { - notRecognized(Integer.class, configuredAction); - } catch (IllegalArgumentException e) { - notRecognized(InventoryAction.class, configuredAction); - } - } - } - - @Override - public void enable() { - plugin.getServer().getPluginManager().registerEvents(this, plugin); - } - - @Override - public boolean shouldEnable() { - return config.getBoolean(configPath + ".enable", false); - } - - @Override - public void disable() { - HandlerList.unregisterAll(this); - } - - @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) - private void onInventoryClick(InventoryClickEvent event) { - if (!clickActionLimits.containsKey(event.getAction())) return; - if (event.getInventory().getHolder() == null) return; - - if (event.getInventory().getHolder() instanceof Block block) { - Map recordedClicks = blockInventoryClicks.get(block.getLocation(), - k -> new EnumMap<>(InventoryAction.class)); - Integer clickActionCounter = recordedClicks.getOrDefault(event.getAction(), 0); - - clickActionCounter++; - - if (clickActionCounter > clickActionLimits.get(event.getAction())) { - event.setCancelled(true); - if (logIsEnabled) info("Cancelled spammy inventory click of type " + event.getAction().name() + " at " + - LocationUtil.toString(block.getLocation())); - } - - recordedClicks.put(event.getAction(), clickActionCounter); - blockInventoryClicks.put(block.getLocation(), recordedClicks); - - return; - } - - if (event.getInventory().getHolder() instanceof Entity entity) { - Map recordedClicks = entityInventoryClicks.get(entity.getUniqueId(), - k -> new EnumMap<>(InventoryAction.class)); - Integer clickActionCounter = recordedClicks.getOrDefault(event.getAction(), 0); - - clickActionCounter++; - - if (clickActionCounter > clickActionLimits.get(event.getAction())) { - event.setCancelled(true); - if (logIsEnabled) info("Cancelled spammy inventory click of type " + event.getAction().name() - + " at " + LocationUtil.toString(entity.getLocation())); - } - - recordedClicks.put(event.getAction(), clickActionCounter); - entityInventoryClicks.put(entity.getUniqueId(), recordedClicks); - } - } -} \ No newline at end of file diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/InventoryOpenSpam.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/InventoryOpenSpam.java deleted file mode 100755 index d03f53f7b..000000000 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/InventoryOpenSpam.java +++ /dev/null @@ -1,85 +0,0 @@ -package me.xginko.aef.modules.lagpreventions; - -import com.github.benmanes.caffeine.cache.Cache; -import com.github.benmanes.caffeine.cache.Caffeine; -import me.xginko.aef.modules.AEFModule; -import me.xginko.aef.utils.EntityUtil; -import me.xginko.aef.utils.MaterialUtil; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.HandlerList; -import org.bukkit.event.Listener; -import org.bukkit.event.block.Action; -import org.bukkit.event.player.PlayerInteractEntityEvent; -import org.bukkit.event.player.PlayerInteractEvent; - -import java.time.Duration; -import java.util.UUID; - -public class InventoryOpenSpam extends AEFModule implements Listener { - - /** - * This is pretty much an ugly bandage to the actual problem and will need more work. - * For now this is just barely enough to make a meaningful difference on some servers but - * this needs a better implementation - */ - - private final Cache playerInvOpenCooldowns; - private final int interactLimit; - - public InventoryOpenSpam() { - super("lag-preventions.prevent-inventory-open-spam"); - config.addComment(configPath + ".enable", - "Rate-limit interactions with inventory holders to prevent a lag exploit."); - this.interactLimit = config.getInt(configPath + ".max-interacts-per-time", 2); - this.playerInvOpenCooldowns = Caffeine.newBuilder().expireAfterWrite(Duration.ofMillis( - config.getInt(configPath + ".time-in-ticks", 20) * 50L - )).build(); - } - - @Override - public void enable() { - plugin.getServer().getPluginManager().registerEvents(this, plugin); - } - - @Override - public boolean shouldEnable() { - return config.getBoolean(configPath + ".enable", false); - } - - @Override - public void disable() { - HandlerList.unregisterAll(this); - } - - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - private void onPlayerInteractEntity(PlayerInteractEntityEvent event) { - if (!EntityUtil.isInventoryHolder(event.getRightClicked())) return; - - Player player = event.getPlayer(); - int interactCount = playerInvOpenCooldowns.get(player.getUniqueId(), k -> 0); - interactCount++; - playerInvOpenCooldowns.put(player.getUniqueId(), interactCount); - - if (interactCount > interactLimit) { - event.setCancelled(true); - } - } - - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - private void onPlayerInteract(PlayerInteractEvent event) { - if (event.getAction() != Action.RIGHT_CLICK_BLOCK) return; - if (!MaterialUtil.INVENTORY_HOLDER_ITEMS.contains(event.getClickedBlock().getType())) return; - Player player = event.getPlayer(); - if (player.isSneaking()) return; // Try not to interfere with building - - int interactCount = playerInvOpenCooldowns.get(player.getUniqueId(), k -> 0); - interactCount++; - playerInvOpenCooldowns.put(player.getUniqueId(), interactCount); - - if (interactCount > interactLimit) { - event.setCancelled(true); - } - } -} \ No newline at end of file diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/KeepStashLoaded.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/KeepStashLoaded.java index ce71e0408..8c2a7ad5b 100755 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/KeepStashLoaded.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/KeepStashLoaded.java @@ -25,6 +25,10 @@ import java.util.function.Consumer; import java.util.stream.Collectors; +/** + * Credits to the initial idea of just keeping big chunks loaded to reduce lag + * created from loading them go to kumori (Soft1k) of 3b3t.org. + */ public class KeepStashLoaded extends AEFModule implements Consumer, Listener { private final Map forceLoadedChunks; @@ -38,7 +42,7 @@ public KeepStashLoaded() { super("lag-preventions.keep-stash-chunks-loaded"); this.forceLoadedChunks = new ConcurrentHashMap<>(); config.addComment(configPath + ".enable", """ - Idea by 6g6s admin kumori:\s + Idea by 3b3t admin kumori (Soft1k)\s Improves lag generated by large stash chunks constantly loading and\s unloading by setting them force loaded. This might cause increased ram\s usage, so keep an eye out for that."""); @@ -48,13 +52,13 @@ public KeepStashLoaded() { as a stash chunk to keep force loaded."""); this.minInhabitedTime = config.getInt(configPath + ".min-chunk-inhabited-time-ticks", 1000, """ The minimum time in ticks a chunk has to have been inhabited to be checked."""); - this.keepLoadedMillis = TimeUnit.MINUTES.toMillis(config.getInt(configPath + ".keep-loaded-minutes", 60, """ + this.keepLoadedMillis = TimeUnit.MINUTES.toMillis(config.getInt(configPath + ".keep-loaded-minutes", 120, """ The time in minutes a stash chunks will be kept force loaded before\s setting it back to normal.""")); this.onlyTileEntities = config.getBoolean(configPath + ".only-check-tile-entities", true, """ Set to false if you want to check more blocks than just tile entities.\s Makes the overall speed of the module faster if set to true."""); - this.storageTypes = config.getList(configPath + ".container-types", MaterialUtil.INVENTORY_HOLDER_ITEMS + this.storageTypes = config.getList(configPath + ".container-types", MaterialUtil.INVENTORY_HOLDERS .stream() .map(Enum::name) .collect(Collectors.toList())) @@ -96,10 +100,11 @@ public void enable() { public void disable() { HandlerList.unregisterAll(this); for (Map.Entry entry : forceLoadedChunks.entrySet()) { - Chunk chunk = entry.getKey().getChunk(); - if (chunk != null) - plugin.getServer().getGlobalRegionScheduler().execute(plugin, () -> chunk.setForceLoaded(false)); - forceLoadedChunks.remove(entry.getKey()); + entry.getKey().getChunkAsync(false).thenAccept(chunk -> { + if (chunk != null) + plugin.getServer().getGlobalRegionScheduler().execute(plugin, () -> chunk.setForceLoaded(false)); + forceLoadedChunks.remove(entry.getKey()); + }); } } @@ -115,19 +120,20 @@ public void accept(ScheduledTask task) { continue; } - Chunk chunk = entry.getKey().getChunk(); - if (chunk == null) { - forceLoadedChunks.remove(entry.getKey()); - if (logIsEnabled) - info("Removing key that returns a null chunk: "+entry.getKey()+"."); - continue; - } + entry.getKey().getChunkAsync(false).thenAccept(chunk -> { + if (chunk == null) { + forceLoadedChunks.remove(entry.getKey()); + if (logIsEnabled) + info("Removing key that returned a null chunk: "+entry.getKey()+"."); + return; + } - plugin.getServer().getGlobalRegionScheduler().execute(plugin, () -> { - chunk.setForceLoaded(false); - forceLoadedChunks.remove(entry.getKey()); - if (logIsEnabled) - info("Set chunk "+entry.getKey()+" to no longer force loaded."); + plugin.getServer().getGlobalRegionScheduler().execute(plugin, () -> { + chunk.setForceLoaded(false); + forceLoadedChunks.remove(entry.getKey()); + if (logIsEnabled) + info("Set chunk "+entry.getKey()+" to no longer force loaded."); + }); }); } } diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/LiquidUpdateLag.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/LiquidUpdateLag.java deleted file mode 100755 index a757b1437..000000000 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/LiquidUpdateLag.java +++ /dev/null @@ -1,78 +0,0 @@ -package me.xginko.aef.modules.lagpreventions; - -import com.cryptomorin.xseries.XMaterial; -import com.github.benmanes.caffeine.cache.Cache; -import com.github.benmanes.caffeine.cache.Caffeine; -import me.xginko.aef.modules.AEFModule; -import me.xginko.aef.utils.models.ChunkUID; -import org.bukkit.Chunk; -import org.bukkit.block.Block; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.HandlerList; -import org.bukkit.event.Listener; -import org.bukkit.event.block.BlockFromToEvent; - -import java.time.Duration; - -public class LiquidUpdateLag extends AEFModule implements Listener { - - private final Cache liquidSpreadEventCountCache; - private final int maxLiquidSpreadEventsPerChunk; - private final boolean logIsEnabled; - - public LiquidUpdateLag() { - super("lag-preventions.prevent-liquid-update-lag"); - this.maxLiquidSpreadEventsPerChunk = config.getInt(configPath + ".max-liquid-events-in-same-chunk-per-time", 1200, """ - WARNING: DEFAULTS ARE VERY ROUGH, DEFINITELY TWEAK THIS!\s - Number is the result of:\s - Amount of liquid source blocks\s - multiplied by sides it can spread to\s - multiplied by block spread length."""); - this.liquidSpreadEventCountCache = Caffeine.newBuilder().expireAfterWrite(Duration.ofMillis( - Math.max(config.getInt(configPath + ".time-in-ticks", 100, """ - Record time after first liquid spread.\s - When this time runs out, the spread counter resets"""), 1) * 50L - )).build(); - this.logIsEnabled = config.getBoolean(configPath + ".log", false, - "Very spammy, use for testing/debugging only"); - } - - @Override - public void enable() { - plugin.getServer().getPluginManager().registerEvents(this, plugin); - } - - @Override - public boolean shouldEnable() { - return config.getBoolean(configPath + ".enable", false); - } - - @Override - public void disable() { - HandlerList.unregisterAll(this); - } - - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - private void onLiquidSpread(BlockFromToEvent event) { - final Block sourceBlock = event.getBlock(); - if (sourceBlock.getType() == XMaterial.DRAGON_EGG.parseMaterial()) return; // Event fires only for liquids and the dragon egg - - final Chunk chunk = sourceBlock.getChunk(); - final ChunkUID chunkUID = ChunkUID.of(chunk); - - Integer liquidSpreadCount = liquidSpreadEventCountCache.get(chunkUID, k -> 0); - liquidSpreadCount++; - liquidSpreadEventCountCache.put(chunkUID, liquidSpreadCount); - - if (liquidSpreadCount > maxLiquidSpreadEventsPerChunk) { - event.setCancelled(true); - if (logIsEnabled) warn("Cancelled liquid events for chunk x=" + chunk.getX() + ", z=" + chunk.getZ() + - " in world: " + chunk.getWorld().getName()); - return; - } - - if (logIsEnabled) info("Recorded " + liquidSpreadCount + " liquid updates in chunk x=" + chunk.getX() + - ", z=" + chunk.getZ() + " in world: " + chunk.getWorld().getName()); - } -} \ No newline at end of file diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/StashExplosions.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/StashExplosions.java index 88f78da7f..1144b62b6 100755 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/StashExplosions.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/StashExplosions.java @@ -1,5 +1,6 @@ package me.xginko.aef.modules.lagpreventions; +import com.cryptomorin.xseries.XEntityType; import com.destroystokyo.paper.MaterialTags; import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; @@ -44,7 +45,7 @@ public StashExplosions() { The time in seconds to wait after an explosion for another one to happen.\s If no explosion happens within x seconds after the first one, the count resets to 0.""")) )).build(); - this.storageTypes = config.getList(configPath + ".container-types", MaterialUtil.INVENTORY_HOLDER_ITEMS + this.storageTypes = config.getList(configPath + ".container-types", MaterialUtil.INVENTORY_HOLDERS .stream() .filter(material -> !MaterialTags.SHULKER_BOXES.isTagged(material)) .map(Enum::name) @@ -114,6 +115,7 @@ private void onBlockExplode(BlockExplodeEvent event) { @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onEntityExplode(EntityExplodeEvent event) { + if (event.getEntityType().equals(XEntityType.WIND_CHARGE.get())) return; handleExplosion(event.getEntity().getChunk(), event.blockList()); } } diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/agelimits/CustomAgeLimits.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/agelimits/CustomAgeLimits.java index 33e24bb07..3ed74dd8d 100755 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/agelimits/CustomAgeLimits.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/agelimits/CustomAgeLimits.java @@ -60,7 +60,7 @@ public CustomAgeLimits() { for (String configuredEntity : section.getKeys(false)) { try { EntityType limitedEntity = EntityType.valueOf(configuredEntity); - Integer maxAmountPerChunk = Integer.valueOf(section.getString(configuredEntity)); + Integer maxAmountPerChunk = Integer.parseInt(section.getString(configuredEntity)); entityLimits.put(limitedEntity, maxAmountPerChunk); } catch (NumberFormatException e) { notRecognized(Integer.class, configuredEntity); @@ -72,6 +72,7 @@ public CustomAgeLimits() { @Override public void enable() { + plugin.getServer().getPluginManager().registerEvents(this, plugin); scheduledTask = plugin.getServer().getGlobalRegionScheduler() .runAtFixedRate(plugin, this, 20L, checkPeriod); } diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/lowtpsphysics/BlockMelting.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/lowtpsphysics/BlockMelting.java deleted file mode 100755 index e1988438e..000000000 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/lowtpsphysics/BlockMelting.java +++ /dev/null @@ -1,44 +0,0 @@ -package me.xginko.aef.modules.lagpreventions.lowtpsphysics; - -import me.xginko.aef.AnarchyExploitFixes; -import me.xginko.aef.modules.AEFModule; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.HandlerList; -import org.bukkit.event.Listener; -import org.bukkit.event.block.BlockFadeEvent; - -public class BlockMelting extends AEFModule implements Listener { - - private final double disableMeltingTPS; - private final boolean logIsEnabled; - - public BlockMelting() { - super("lag-preventions.disable-physics-during-low-tps.melting-blocks"); - this.disableMeltingTPS = config.getDouble(configPath + ".disable-TPS", 16.0); - this.logIsEnabled = config.getBoolean(configPath + ".log", false); - } - - @Override - public void enable() { - plugin.getServer().getPluginManager().registerEvents(this, plugin); - } - - @Override - public boolean shouldEnable() { - return config.getBoolean(configPath + ".enable", false); - } - - @Override - public void disable() { - HandlerList.unregisterAll(this); - } - - @EventHandler(priority = EventPriority.LOWEST) - private void onBlockFade(BlockFadeEvent event) { - if (AnarchyExploitFixes.getTickReporter().getTPS() <= disableMeltingTPS) { - event.setCancelled(true); - if (logIsEnabled) info("Stopped block melting because tps is lower than " + disableMeltingTPS); - } - } -} diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/lowtpsphysics/BlockPhysics.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/lowtpsphysics/BlockPhysics.java deleted file mode 100755 index 5eb0db246..000000000 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/lowtpsphysics/BlockPhysics.java +++ /dev/null @@ -1,46 +0,0 @@ -package me.xginko.aef.modules.lagpreventions.lowtpsphysics; - -import me.xginko.aef.AnarchyExploitFixes; -import me.xginko.aef.modules.AEFModule; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.HandlerList; -import org.bukkit.event.Listener; -import org.bukkit.event.block.BlockPhysicsEvent; - -public class BlockPhysics extends AEFModule implements Listener { - - private final double disablePhysicsTPS; - private final boolean logIsEnabled; - - public BlockPhysics() { - super("lag-preventions.disable-physics-during-low-tps.block-physics"); - config.addComment(configPath + ".enable", - "Stop block physics (like falling blocks) when the TPS gets below a certain value."); - this.disablePhysicsTPS = config.getDouble(configPath + ".disable-TPS", 16.0); - this.logIsEnabled = config.getBoolean(configPath + ".log", false); - } - - @Override - public void enable() { - plugin.getServer().getPluginManager().registerEvents(this, plugin); - } - - @Override - public boolean shouldEnable() { - return config.getBoolean(configPath + ".enable", false); - } - - @Override - public void disable() { - HandlerList.unregisterAll(this); - } - - @EventHandler(priority = EventPriority.LOWEST) - private void onEntityChange(BlockPhysicsEvent event) { - if (AnarchyExploitFixes.getTickReporter().getTPS() <= disablePhysicsTPS) { - event.setCancelled(true); - if (logIsEnabled) info("Cancelled block physics because TPS is lower than " + disablePhysicsTPS); - } - } -} diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/lowtpsphysics/BlockSpread.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/lowtpsphysics/BlockSpread.java deleted file mode 100755 index db68ab69e..000000000 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/lowtpsphysics/BlockSpread.java +++ /dev/null @@ -1,53 +0,0 @@ -package me.xginko.aef.modules.lagpreventions.lowtpsphysics; - -import me.xginko.aef.AnarchyExploitFixes; -import me.xginko.aef.modules.AEFModule; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.HandlerList; -import org.bukkit.event.Listener; -import org.bukkit.event.block.BlockFormEvent; -import org.bukkit.event.block.BlockSpreadEvent; - -public class BlockSpread extends AEFModule implements Listener { - - private final double disableGrassTPS; - private final boolean logIsEnabled; - - public BlockSpread() { - super("lag-preventions.disable-physics-during-low-tps.block-spread"); - this.disableGrassTPS = config.getDouble(configPath + ".disable-TPS", 14.0); - this.logIsEnabled = config.getBoolean(configPath + ".log", false); - } - - @Override - public void enable() { - plugin.getServer().getPluginManager().registerEvents(this, plugin); - } - - @Override - public boolean shouldEnable() { - return config.getBoolean(configPath + ".enable", false); - } - - @Override - public void disable() { - HandlerList.unregisterAll(this); - } - - @EventHandler(priority = EventPriority.LOWEST) - private void onBlockForm(BlockFormEvent event) { - if (AnarchyExploitFixes.getTickReporter().getTPS() <= disableGrassTPS) { - event.setCancelled(true); - if (logIsEnabled) info("Stopped block spread because tps is lower than " + disableGrassTPS); - } - } - - @EventHandler(priority = EventPriority.LOWEST) - private void onBlockForm(BlockSpreadEvent event) { - if (AnarchyExploitFixes.getTickReporter().getTPS() <= disableGrassTPS) { - event.setCancelled(true); - if (logIsEnabled) info("Stopped block spread because tps is lower than " + disableGrassTPS); - } - } -} diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/lowtpsphysics/Explosions.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/lowtpsphysics/Explosions.java deleted file mode 100755 index 2a659b08d..000000000 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/lowtpsphysics/Explosions.java +++ /dev/null @@ -1,64 +0,0 @@ -package me.xginko.aef.modules.lagpreventions.lowtpsphysics; - -import me.xginko.aef.AnarchyExploitFixes; -import me.xginko.aef.modules.AEFModule; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.HandlerList; -import org.bukkit.event.Listener; -import org.bukkit.event.block.BlockExplodeEvent; -import org.bukkit.event.entity.EntityExplodeEvent; -import org.bukkit.event.entity.ExplosionPrimeEvent; - -public class Explosions extends AEFModule implements Listener { - - private final double disableExplosionsTPS; - private final boolean logIsEnabled; - - public Explosions() { - super("lag-preventions.disable-physics-during-low-tps.explosions"); - config.addComment(configPath + ".enable", - "Disable explosions during low tps to combat lag."); - this.disableExplosionsTPS = config.getDouble(configPath + ".disable-TPS", 14.0); - this.logIsEnabled = config.getBoolean(configPath + ".log", false); - } - - @Override - public void enable() { - plugin.getServer().getPluginManager().registerEvents(this, plugin); - } - - @Override - public boolean shouldEnable() { - return config.getBoolean(configPath + ".enable", false); - } - - @Override - public void disable() { - HandlerList.unregisterAll(this); - } - - @EventHandler(priority = EventPriority.LOWEST) - private void onEntityExplode(EntityExplodeEvent event) { - if (AnarchyExploitFixes.getTickReporter().getTPS() <= disableExplosionsTPS) { - event.setCancelled(true); - if (logIsEnabled) info("Cancelled explosion because tps is lower than " + disableExplosionsTPS); - } - } - - @EventHandler(priority = EventPriority.LOWEST) - private void onExplodePrime(ExplosionPrimeEvent event) { - if (AnarchyExploitFixes.getTickReporter().getTPS() <= disableExplosionsTPS) { - event.setCancelled(true); - if (logIsEnabled) info("Cancelled explosion because tps is lower than " + disableExplosionsTPS); - } - } - - @EventHandler(priority = EventPriority.LOWEST) - private void onBlockExplode(BlockExplodeEvent event) { - if (AnarchyExploitFixes.getTickReporter().getTPS() <= disableExplosionsTPS) { - event.setCancelled(true); - if (logIsEnabled) info("Cancelled explosion because tps is lower than " + disableExplosionsTPS); - } - } -} diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/lowtpsphysics/FireSpread.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/lowtpsphysics/FireSpread.java deleted file mode 100755 index 95aee9d50..000000000 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/lowtpsphysics/FireSpread.java +++ /dev/null @@ -1,44 +0,0 @@ -package me.xginko.aef.modules.lagpreventions.lowtpsphysics; - -import me.xginko.aef.AnarchyExploitFixes; -import me.xginko.aef.modules.AEFModule; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.HandlerList; -import org.bukkit.event.Listener; -import org.bukkit.event.block.BlockIgniteEvent; - -public class FireSpread extends AEFModule implements Listener { - - private final double disableFireTPS; - private final boolean logIsEnabled; - - public FireSpread() { - super("lag-preventions.disable-physics-during-low-tps.fire-spread"); - this.disableFireTPS = config.getDouble(configPath + ".disable-TPS", 14.0); - this.logIsEnabled = config.getBoolean(configPath + ".log", false); - } - - @Override - public void enable() { - plugin.getServer().getPluginManager().registerEvents(this, plugin); - } - - @Override - public boolean shouldEnable() { - return config.getBoolean(configPath + ".enable", false); - } - - @Override - public void disable() { - HandlerList.unregisterAll(this); - } - - @EventHandler(priority = EventPriority.LOWEST) - private void onLiquidSpread(BlockIgniteEvent event) { - if (AnarchyExploitFixes.getTickReporter().getTPS() <= disableFireTPS) { - event.setCancelled(true); - if (logIsEnabled) info("Stopped fire spread because tps is lower than " + disableFireTPS); - } - } -} diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/lowtpsphysics/LeaveDecay.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/lowtpsphysics/LeaveDecay.java deleted file mode 100755 index b57d99cde..000000000 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/lowtpsphysics/LeaveDecay.java +++ /dev/null @@ -1,44 +0,0 @@ -package me.xginko.aef.modules.lagpreventions.lowtpsphysics; - -import me.xginko.aef.AnarchyExploitFixes; -import me.xginko.aef.modules.AEFModule; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.HandlerList; -import org.bukkit.event.Listener; -import org.bukkit.event.block.LeavesDecayEvent; - -public class LeaveDecay extends AEFModule implements Listener { - - private final double disableLeaveDecayTPS; - private final boolean logIsEnabled; - - public LeaveDecay() { - super("lag-preventions.disable-physics-during-low-tps.leave-decay"); - this.disableLeaveDecayTPS = config.getDouble(configPath + ".disable-TPS", 14.0); - this.logIsEnabled = config.getBoolean(configPath + ".log", false); - } - - @Override - public void enable() { - plugin.getServer().getPluginManager().registerEvents(this, plugin); - } - - @Override - public boolean shouldEnable() { - return config.getBoolean(configPath + ".enable", false); - } - - @Override - public void disable() { - HandlerList.unregisterAll(this); - } - - @EventHandler(priority = EventPriority.LOWEST) - private void onLeaveDecay(LeavesDecayEvent event) { - if (AnarchyExploitFixes.getTickReporter().getTPS() <= disableLeaveDecayTPS) { - event.setCancelled(true); - if (logIsEnabled) info("Cancelled leave decay because tps is lower than " + disableLeaveDecayTPS); - } - } -} diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/lowtpsphysics/LiquidSpread.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/lowtpsphysics/LiquidSpread.java deleted file mode 100755 index a2627afe3..000000000 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/lowtpsphysics/LiquidSpread.java +++ /dev/null @@ -1,44 +0,0 @@ -package me.xginko.aef.modules.lagpreventions.lowtpsphysics; - -import me.xginko.aef.AnarchyExploitFixes; -import me.xginko.aef.modules.AEFModule; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.HandlerList; -import org.bukkit.event.Listener; -import org.bukkit.event.block.BlockFromToEvent; - -public class LiquidSpread extends AEFModule implements Listener { - - private final double disableLiquidsTPS; - private final boolean logIsEnabled; - - public LiquidSpread() { - super("lag-preventions.disable-physics-during-low-tps.liquid-spread"); - this.disableLiquidsTPS = config.getDouble(configPath + ".disable-TPS", 16.0); - this.logIsEnabled = config.getBoolean(configPath + ".log", false); - } - - @Override - public void enable() { - plugin.getServer().getPluginManager().registerEvents(this, plugin); - } - - @Override - public boolean shouldEnable() { - return config.getBoolean(configPath + ".enable", false); - } - - @Override - public void disable() { - HandlerList.unregisterAll(this); - } - - @EventHandler(priority = EventPriority.LOWEST) - private void onLiquidSpread(BlockFromToEvent event) { - if (AnarchyExploitFixes.getTickReporter().getTPS() <= disableLiquidsTPS) { - event.setCancelled(true); - if (logIsEnabled) info("Stopped liquid spread because tps is lower than " + disableLiquidsTPS); - } - } -} diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/lowtpsphysics/Noteblocks.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/lowtpsphysics/Noteblocks.java deleted file mode 100755 index 16452d5f3..000000000 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/lowtpsphysics/Noteblocks.java +++ /dev/null @@ -1,46 +0,0 @@ -package me.xginko.aef.modules.lagpreventions.lowtpsphysics; - -import me.xginko.aef.AnarchyExploitFixes; -import me.xginko.aef.modules.AEFModule; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.HandlerList; -import org.bukkit.event.Listener; -import org.bukkit.event.block.NotePlayEvent; - -public class Noteblocks extends AEFModule implements Listener { - - private final double disableNoteblockTPS; - private final boolean logIsEnabled; - - public Noteblocks() { - super("lag-preventions.disable-physics-during-low-tps.noteblocks"); - config.addComment(configPath + ".enable", - "Some lag machines use noteblocks to work around redstone limitations."); - this.disableNoteblockTPS = config.getDouble(configPath + ".disable-TPS", 16.0); - this.logIsEnabled = config.getBoolean(configPath + ".log", false); - } - - @Override - public void enable() { - plugin.getServer().getPluginManager().registerEvents(this, plugin); - } - - @Override - public boolean shouldEnable() { - return config.getBoolean(configPath + ".enable", false); - } - - @Override - public void disable() { - HandlerList.unregisterAll(this); - } - - @EventHandler(priority = EventPriority.LOWEST) - private void onNoteblockGetsPlayed(NotePlayEvent event) { - if (AnarchyExploitFixes.getTickReporter().getTPS() <= disableNoteblockTPS) { - event.setCancelled(true); - if (logIsEnabled) info("Cancelled noteblocks playing because tps is lower than " + disableNoteblockTPS); - } - } -} diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/lowtpsphysics/Redstone.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/lowtpsphysics/Redstone.java deleted file mode 100755 index a7612d64e..000000000 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/lowtpsphysics/Redstone.java +++ /dev/null @@ -1,63 +0,0 @@ -package me.xginko.aef.modules.lagpreventions.lowtpsphysics; - -import me.xginko.aef.AnarchyExploitFixes; -import me.xginko.aef.modules.AEFModule; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.HandlerList; -import org.bukkit.event.Listener; -import org.bukkit.event.block.BlockPistonExtendEvent; -import org.bukkit.event.block.BlockPistonRetractEvent; -import org.bukkit.event.block.BlockRedstoneEvent; - -public class Redstone extends AEFModule implements Listener { - - private final double disableRedstoneTPS; - private final boolean logIsEnabled; - - public Redstone() { - super("lag-preventions.disable-physics-during-low-tps.redstone"); - config.addComment(configPath + ".enable", "Disable redstone during low TPS to prevent some lag machines."); - this.disableRedstoneTPS = config.getDouble(configPath + ".disable-TPS", 16.0); - this.logIsEnabled = config.getBoolean(configPath + ".log", false); - } - - @Override - public void enable() { - plugin.getServer().getPluginManager().registerEvents(this, plugin); - } - - @Override - public boolean shouldEnable() { - return config.getBoolean(configPath + ".enable", false); - } - - @Override - public void disable() { - HandlerList.unregisterAll(this); - } - - @EventHandler(priority = EventPriority.LOWEST) - private void onRedstoneEvent(BlockRedstoneEvent event) { - if (AnarchyExploitFixes.getTickReporter().getTPS() <= disableRedstoneTPS) { - event.setNewCurrent(0); - if (logIsEnabled) info("Disabled redstone because tps is lower than " + disableRedstoneTPS); - } - } - - @EventHandler(priority = EventPriority.LOWEST) - private void onPistonExtendEvent(BlockPistonExtendEvent event) { - if (AnarchyExploitFixes.getTickReporter().getTPS() <= disableRedstoneTPS) { - event.setCancelled(true); - if (logIsEnabled) info("Cancelled piston event because tps is lower than " + disableRedstoneTPS); - } - } - - @EventHandler(priority = EventPriority.LOWEST) - private void onPistonRetractEvent(BlockPistonRetractEvent event) { - if (AnarchyExploitFixes.getTickReporter().getTPS() <= disableRedstoneTPS) { - event.setCancelled(true); - if (logIsEnabled) info("Cancelled piston event because tps is lower than " + disableRedstoneTPS); - } - } -} diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/lowtpsphysics/SculkBloom.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/lowtpsphysics/SculkBloom.java deleted file mode 100755 index e4ce006b0..000000000 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/lowtpsphysics/SculkBloom.java +++ /dev/null @@ -1,44 +0,0 @@ -package me.xginko.aef.modules.lagpreventions.lowtpsphysics; - -import me.xginko.aef.AnarchyExploitFixes; -import me.xginko.aef.modules.AEFModule; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.HandlerList; -import org.bukkit.event.Listener; -import org.bukkit.event.block.SculkBloomEvent; - -public class SculkBloom extends AEFModule implements Listener { - - private final double disableSculkTPS; - private final boolean logIsEnabled; - - public SculkBloom() { - super("lag-preventions.disable-physics-during-low-tps.sculk-bloom"); - this.disableSculkTPS = config.getDouble(configPath + ".disable-TPS", 14.0); - this.logIsEnabled = config.getBoolean(configPath + ".log", false); - } - - @Override - public void enable() { - plugin.getServer().getPluginManager().registerEvents(this, plugin); - } - - @Override - public boolean shouldEnable() { - return config.getBoolean(configPath + ".enable", false); - } - - @Override - public void disable() { - HandlerList.unregisterAll(this); - } - - @EventHandler(priority = EventPriority.LOWEST) - private void onSkulkBloom(SculkBloomEvent event) { - if (AnarchyExploitFixes.getTickReporter().getTPS() <= disableSculkTPS) { - event.setCancelled(true); - if (logIsEnabled) info("Stopped sculk bloom because tps is lower than " + disableSculkTPS); - } - } -} diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/BlockFormOrGrow.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/BlockFormOrGrow.java new file mode 100755 index 000000000..73d4333f1 --- /dev/null +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/BlockFormOrGrow.java @@ -0,0 +1,53 @@ +package me.xginko.aef.modules.lagpreventions.regionalactivity; + +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.BlockFormEvent; +import org.bukkit.event.block.BlockSpreadEvent; + +public class BlockFormOrGrow extends RegionalActivityModule { + + private final int limit; + + public BlockFormOrGrow() { + super( + "block-spread", + false, + 1500.0, + 6000, + 10000, + 10.0, + 120.0 + ); + config.addComment(configPath+".enable", """ + Limits blocks spreading or forming based on world conditions within a\s + configurable radius and timeframe to help reduce lag by cancelling burst\s + activity hotspots.\s + \s + Examples:\s + \s + - Snow forming due to a snow storm.\s + - Ice forming in a snowy Biome like Taiga or Tundra.\s + - Obsidian / Cobblestone forming due to contact with water.\s + - Concrete forming due to mixing of concrete powder and water.\s + - Mushrooms spreading.\s + - Fire spreading."""); + this.limit = config.getInt(configPath + ".block-form-event-limit", 800, + "Maximum number of times a block can form or spread within the configured\n" + + "timeframe before activity will be put on cooldown."); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onBlockForm(BlockFormEvent event) { + if (shouldCancelActivity(event, event.getBlock().getLocation(), limit)) { + event.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onBlockSpread(BlockSpreadEvent event) { + if (shouldCancelActivity(event, event.getBlock().getLocation(), limit)) { + event.setCancelled(true); + } + } +} diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/BlockPhysics.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/BlockPhysics.java new file mode 100755 index 000000000..c328a2d80 --- /dev/null +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/BlockPhysics.java @@ -0,0 +1,44 @@ +package me.xginko.aef.modules.lagpreventions.regionalactivity; + +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.BlockPhysicsEvent; + +public class BlockPhysics extends RegionalActivityModule { + + private final int limit; + + public BlockPhysics() { + super( + "block-physics", + true, + 1500.0, + 18000, + 20000, + 10.0, + 120.0 + ); + this.config.addComment(configPath+".enable", """ + Limits block physics within a configurable radius and timeframe\s + to help reduce lag by cancelling burst activity hotspots.\s + \s + Note:\s + \s + The event used for this check (BlockPhysicsEvent) is a high frequency event,\s + it may be called thousands of times per a second on a busy server.\s + Where possible the event may also only be called for the "root" block of\s + physics updates in order to limit event spam.\s + Physics updates that cause other blocks to change their state may not result\s + in an event for each of those blocks (usually adjacent)."""); + this.limit = config.getInt(configPath + ".block-physics-event-limit", 256000, + "Maximum number of times a physics check can be performed within the configured\n" + + "timeframe before they will be put on cooldown."); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onBlockPhysics(BlockPhysicsEvent event) { + if (shouldCancelActivity(event, event.getBlock().getLocation(), limit)) { + event.setCancelled(true); + } + } +} diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/EntitySpawns.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/EntitySpawns.java new file mode 100644 index 000000000..16b4451e8 --- /dev/null +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/EntitySpawns.java @@ -0,0 +1,40 @@ +package me.xginko.aef.modules.lagpreventions.regionalactivity; + +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.entity.EntitySpawnEvent; + +public class EntitySpawns extends RegionalActivityModule { + + private final int limit; + + public EntitySpawns() { + super( + "entity-spawn", + true, + 1500.0, + 18000, + 20000, + 10.0, + 120.0 + ); + this.config.addComment(configPath+".enable", """ + Limits entity spawning activity within a configurable radius and timeframe\s + to help reduce lag by cancelling high activity hotspots.\s + \s + Examples:\s + \s + - A creature gets spawned naturally, by spawner or other reasons.\s + - An entity gets spawned naturally, by spawner or other reasons.\s + This does not include tile entities."""); + this.limit = config.getInt(configPath + ".spawn-event-limit", 6000, + "Maximum number of entity spawns within configured timeframe."); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onEntitySpawn(EntitySpawnEvent event) { + if (shouldCancelActivity(event, event.getLocation(), limit)) { + event.setCancelled(true); + } + } +} diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/TargetDistanceLimit.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/EntityTargeting.java similarity index 61% rename from AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/TargetDistanceLimit.java rename to AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/EntityTargeting.java index 987a088fb..bd60ab6a0 100755 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/TargetDistanceLimit.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/EntityTargeting.java @@ -1,14 +1,13 @@ -package me.xginko.aef.modules.lagpreventions; +package me.xginko.aef.modules.lagpreventions.regionalactivity; import com.cryptomorin.xseries.XEntityType; import io.github.thatsmusic99.configurationmaster.api.ConfigSection; -import me.xginko.aef.modules.AEFModule; import me.xginko.aef.utils.LocationUtil; +import org.bukkit.Location; import org.bukkit.entity.Entity; import org.bukkit.entity.EntityType; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; -import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.entity.EntityTargetEvent; import org.bukkit.util.NumberConversions; @@ -17,23 +16,37 @@ import java.util.Map; import java.util.TreeMap; -public class TargetDistanceLimit extends AEFModule implements Listener { +public class EntityTargeting extends RegionalActivityModule implements Listener { private final Map limitedTypes = new EnumMap<>(EntityType.class); private final double globalMaxDistanceSquared; - private final boolean logIsEnabled, globalDistanceEnabled, perTypeDistanceEnabled; + private final int limit; + private final boolean globalDistanceEnabled, perTypeDistanceEnabled; - public TargetDistanceLimit() { - super("lag-preventions.target-distance-limits"); - this.logIsEnabled = config.getBoolean(configPath + ".log", false); - this.globalDistanceEnabled = config.getBoolean(configPath + ".global-limit.enable", false); + public EntityTargeting() { + super("entity-targeting", + true, + 1500.0, + 18000, + 20000, + 14.0, + 120.0 + ); + this.config.addComment(configPath+".enable", """ + Limits entities targeting other entities within a configurable radius\s + and timeframe to help reduce lag by cancelling burst activity hotspots."""); + this.limit = config.getInt(configPath + ".entity-target-event-limit", 8000, """ + Maximum number of times an entity can target another entity within the\s + configured timeframe before the area will be put on cooldown."""); + + this.globalDistanceEnabled = config.getBoolean(configPath + ".distance-limit.global-limit.enable", false); this.globalMaxDistanceSquared = NumberConversions.square( - config.getDouble(configPath + ".global-limit.max-target-distance", 20.0, """ + config.getDouble(configPath + ".distance-limit.global-limit.max-target-distance", 20.0, """ The max distance no target should exceed.\s You want this to be higher than your highest max distance\s for a specific mob.""")); - this.perTypeDistanceEnabled = config.getBoolean(configPath + ".custom-limits.enable", true); + this.perTypeDistanceEnabled = config.getBoolean(configPath + ".distance-limit.custom-limits.enable", true); Map defaults = new EnumMap<>(XEntityType.class); defaults.put(XEntityType.ZOMBIE, 6.0); defaults.put(XEntityType.SKELETON, 6.0); @@ -41,15 +54,13 @@ public TargetDistanceLimit() { defaults.put(XEntityType.ZOMBIE_VILLAGER, 10.0); defaults.put(XEntityType.ZOMBIFIED_PIGLIN, 8.0); defaults.put(XEntityType.WITHER, 8.0); - Map versionDefaults = new TreeMap<>(); for (Map.Entry entry : defaults.entrySet()) { if (entry.getKey().isSupported()) { versionDefaults.put(entry.getKey().get().name(), entry.getValue()); } } - - ConfigSection section = config.getConfigSection(configPath + ".custom-limits.entities", versionDefaults); + ConfigSection section = config.getConfigSection(configPath + ".distance-limit.custom-limits.entities", versionDefaults); for (String configuredEntity : section.getKeys(false)) { try { Double maxDistanceSquared = NumberConversions.square(Double.parseDouble(section.getString(configuredEntity))); @@ -63,34 +74,35 @@ public TargetDistanceLimit() { } } - @Override - public void enable() { - plugin.getServer().getPluginManager().registerEvents(this, plugin); - } - - @Override - public boolean shouldEnable() { - return config.getBoolean(configPath + ".enable", false); - } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onEntityTarget(EntityTargetEvent event) { + Entity targetEntity = event.getTarget(); + if (targetEntity == null) return; // If entity un-targets an entity, it's good for us. - @Override - public void disable() { - HandlerList.unregisterAll(this); - } + Location location = event.getEntity().getLocation(); + if (shouldCancelActivity(event, location, limit)) { + event.setCancelled(true); + return; + } - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - private void onTargetAcquire(EntityTargetEvent event) { - final Entity targetEntity = event.getTarget(); - if (targetEntity == null) return; + if (!globalDistanceEnabled && !perTypeDistanceEnabled) { + return; + } - final double targetDistanceSquared = event.getEntity().getLocation().distanceSquared(targetEntity.getLocation()); + double targetDistanceSquared; + try { + targetDistanceSquared = event.getEntity().getLocation().distanceSquared(targetEntity.getLocation()); + } catch (IllegalArgumentException e) { + if (logIsEnabled) error("Unable to measure distance between entity and target.", e); + return; + } if (globalDistanceEnabled) { if (targetDistanceSquared > globalMaxDistanceSquared) { event.setCancelled(true); event.setTarget(null); if (logIsEnabled) info("Cancelled target acquire for entity " + event.getEntityType().name() + " at " + - LocationUtil.toString(event.getEntity().getLocation()) + + LocationUtil.toString(location) + " because target is further than the global limit. Distance: " + Math.sqrt(targetDistanceSquared)); return; } @@ -101,7 +113,7 @@ private void onTargetAcquire(EntityTargetEvent event) { event.setCancelled(true); event.setTarget(null); if (logIsEnabled) info("Cancelled target acquire for entity " + event.getEntityType().name() + " at " + - LocationUtil.toString(event.getEntity().getLocation()) + + LocationUtil.toString(location) + " because target further than its configured limit. Distance: " + Math.sqrt(targetDistanceSquared)); } } diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/Explosions.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/Explosions.java new file mode 100644 index 000000000..47aae4c07 --- /dev/null +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/Explosions.java @@ -0,0 +1,57 @@ +package me.xginko.aef.modules.lagpreventions.regionalactivity; + +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.BlockExplodeEvent; +import org.bukkit.event.entity.EntityExplodeEvent; +import org.bukkit.event.entity.ExplosionPrimeEvent; + +public class Explosions extends RegionalActivityModule { + + private final int limit; + + public Explosions() { + super( + "explosions", + false, + 1500.0, + 6000, + 10000, + 10.0, + 120.0 + ); + this.config.addComment(configPath+".enable", """ + Limits explosions within a configurable radius and timeframe\s + to help reduce lag by cancelling high activity hotspots.\s + \s + Examples:\s + \s + - A block exploding.\s + - An entity exploding.\s + - An entity making the decision to explode."""); + this.limit = config.getInt(configPath + ".explode-event-limit", 500, + "Maximum number of explode events within the configured timeframe\n" + + "before the region will be put on cooldown."); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onExplosionPrime(ExplosionPrimeEvent event) { + if (shouldCancelActivity(event, event.getEntity().getLocation(), limit)) { + event.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onEntityExplode(EntityExplodeEvent event) { + if (shouldCancelActivity(event, event.getLocation(), limit)) { + event.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onBlockExplode(BlockExplodeEvent event) { + if (shouldCancelActivity(event, event.getBlock().getLocation(), limit)) { + event.setCancelled(true); + } + } +} diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/LiquidSpread.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/LiquidSpread.java new file mode 100755 index 000000000..480ba4fae --- /dev/null +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/LiquidSpread.java @@ -0,0 +1,46 @@ +package me.xginko.aef.modules.lagpreventions.regionalactivity; + +import com.cryptomorin.xseries.XMaterial; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.BlockFromToEvent; + +public class LiquidSpread extends RegionalActivityModule { + + private final int limit; + private final boolean ignoreDragonEgg; + + public LiquidSpread() { + super( + "liquid-spread", + true, + 1500.0, + 18000, + 20000, + 12.0, + 100.0 + ); + this.config.addComment(configPath+".enable", """ + Limits liquid spreading within a configurable radius and timeframe\s + to help reduce lag by cancelling high activity hotspots.\s + \s + Examples:\s + \s + - A lava block spreading by flowing.\s + - A water block spreading by flowing.\s + - (optional) A dragon egg is teleporting from one position to another."""); + this.limit = config.getInt(configPath + ".liquid-spread-event-limit", 2400, + "Maximum number of times liquids are allowed to spread within the configured\n" + + "timeframe before they will be put on cooldown."); + this.ignoreDragonEgg = config.getBoolean(configPath + ".ignore-dragon-egg", true); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onBlockFromTo(BlockFromToEvent event) { + if (ignoreDragonEgg && event.getBlock().getType() == XMaterial.DRAGON_EGG.parseMaterial()) return; + + if (shouldCancelActivity(event, event.getBlock().getLocation(), limit)) { + event.setCancelled(true); + } + } +} diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/Noteblocks.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/Noteblocks.java new file mode 100755 index 000000000..7c3945916 --- /dev/null +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/Noteblocks.java @@ -0,0 +1,40 @@ +package me.xginko.aef.modules.lagpreventions.regionalactivity; + +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.NotePlayEvent; + +public class Noteblocks extends RegionalActivityModule { + + private final int limit; + + public Noteblocks() { + super( + "noteblocks", + false, + 1500.0, + 6000, + 10000, + 10.0, + 120.0 + ); + this.config.addComment(configPath+".enable", """ + Limits noteblocks being played within a configurable radius and timeframe\s + to help reduce lag by cancelling high activity hotspots.\s + \s + Examples:\s + \s + - A noteblock is being played through player interaction.\s + - A noteblock is being played through a redstone current."""); + this.limit = config.getInt(configPath + ".noteblock-play-limit", 2800, + "Maximum number of times a noteblock can be played within the configured\n" + + "timeframe before they will be put on cooldown."); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onNotePlay(NotePlayEvent event) { + if (shouldCancelActivity(event, event.getBlock().getLocation(), limit)) { + event.setCancelled(true); + } + } +} diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/PathfindingLimits.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/Pathfinding.java similarity index 64% rename from AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/PathfindingLimits.java rename to AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/Pathfinding.java index 93a6b91ac..370e00fe3 100755 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/PathfindingLimits.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/Pathfinding.java @@ -1,14 +1,12 @@ -package me.xginko.aef.modules.lagpreventions; +package me.xginko.aef.modules.lagpreventions.regionalactivity; import com.cryptomorin.xseries.XEntityType; import com.destroystokyo.paper.event.entity.EntityPathfindEvent; import io.github.thatsmusic99.configurationmaster.api.ConfigSection; -import me.xginko.aef.modules.AEFModule; import me.xginko.aef.utils.LocationUtil; import org.bukkit.entity.EntityType; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; -import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.util.NumberConversions; @@ -16,24 +14,40 @@ import java.util.Map; import java.util.TreeMap; -public class PathfindingLimits extends AEFModule implements Listener { +public class Pathfinding extends RegionalActivityModule implements Listener { private final Map limitedTypes = new EnumMap<>(EntityType.class); - private final boolean logIsEnabled, globalDistanceEnabled, perTypeDistanceEnabled; private final double globalMaxDistanceSquared; + private final int limit; + private final boolean globalDistanceEnabled, perTypeDistanceEnabled; - public PathfindingLimits() { - super("lag-preventions.pathfinding-limits"); - this.logIsEnabled = config.getBoolean(configPath + ".log", false, - "Only meant for debug."); - this.globalDistanceEnabled = config.getBoolean(configPath + ".global-limit.enable", false); + public Pathfinding() { + super( + "entity-pathfinding", + false, + 1500.0, + 6000, + 10000, + 14.0, + 120.0 + ); + this.config.addComment(configPath+".enable", """ + Limits entities deciding to pathfind to a specific location\s + within a configurable radius and timeframe to help reduce lag\s + by cancelling burst activity hotspots."""); + this.limit = config.getInt(configPath + ".entity-pathfind-event-limit", 4000, """ + Maximum number of times an entity can decide to start moving\s + towards a location within the configured timeframe before the\s + area will be put on cooldown."""); + + this.globalDistanceEnabled = config.getBoolean(configPath + ".distance-limit.global-limit.enable", false); this.globalMaxDistanceSquared = NumberConversions.square( - config.getDouble(configPath + ".global-limit.max-target-distance", 20.0, """ + config.getDouble(configPath + ".distance-limit.global-limit.max-target-distance", 20.0, """ The max distance no mob pathfinding should exceed.\s You always want this to be higher than your highest max distance\s for a specific mob.""")); - this.perTypeDistanceEnabled = config.getBoolean(configPath + ".custom-limits.enable", true); + this.perTypeDistanceEnabled = config.getBoolean(configPath + ".distance-limit.custom-limits.enable", true); Map defaults = new EnumMap<>(XEntityType.class); defaults.put(XEntityType.ZOMBIE, 6.0); defaults.put(XEntityType.SKELETON, 6.0); @@ -41,15 +55,13 @@ public PathfindingLimits() { defaults.put(XEntityType.ZOMBIE_VILLAGER, 10.0); defaults.put(XEntityType.ZOMBIFIED_PIGLIN, 8.0); defaults.put(XEntityType.WITHER, 8.0); - Map versionDefaults = new TreeMap<>(); for (Map.Entry entry : defaults.entrySet()) { if (entry.getKey().isSupported()) { versionDefaults.put(entry.getKey().get().name(), entry.getValue()); } } - - ConfigSection section = config.getConfigSection(configPath + ".custom-limits.entities", versionDefaults); + ConfigSection section = config.getConfigSection(configPath + ".distance-limit.custom-limits.entities", versionDefaults); for (String configuredEntity : section.getKeys(false)) { try { Double maxDistanceSquared = NumberConversions.square(Double.parseDouble(section.getString(configuredEntity))); @@ -63,24 +75,24 @@ public PathfindingLimits() { } } - @Override - public void enable() { - plugin.getServer().getPluginManager().registerEvents(this, plugin); - } - - @Override - public boolean shouldEnable() { - return config.getBoolean(configPath + ".enable", false); - } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onEntityPathfind(EntityPathfindEvent event) { + if (shouldCancelActivity(event, event.getEntity().getLocation(), limit)) { + event.setCancelled(true); + return; + } - @Override - public void disable() { - HandlerList.unregisterAll(this); - } + if (!globalDistanceEnabled && !perTypeDistanceEnabled) { + return; + } - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - private void onPathfind(EntityPathfindEvent event) { - final double targetDistanceSquared = event.getEntity().getLocation().distanceSquared(event.getLoc()); + double targetDistanceSquared; + try { + targetDistanceSquared = event.getEntity().getLocation().distanceSquared(event.getLoc()); + } catch (IllegalArgumentException e) { + if (logIsEnabled) error("Unable to measure distance between entity and target.", e); + return; + } if (globalDistanceEnabled) { if (targetDistanceSquared > globalMaxDistanceSquared) { diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/Pistons.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/Pistons.java new file mode 100644 index 000000000..9b5a6b2cf --- /dev/null +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/Pistons.java @@ -0,0 +1,48 @@ +package me.xginko.aef.modules.lagpreventions.regionalactivity; + +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.BlockPistonExtendEvent; +import org.bukkit.event.block.BlockPistonRetractEvent; + +public class Pistons extends RegionalActivityModule { + + private final int limit; + + public Pistons() { + super( + "pistons", + false, + 1500.0, + 6000, + 10000, + 10.0, + 120.0 + ); + this.config.addComment(configPath+".enable", """ + Limits piston movement within a configurable radius and timeframe\s + to help reduce lag by cancelling high activity hotspots.\s + \s + Examples:\s + \s + - A piston extends.\s + - A piston retracts."""); + this.limit = config.getInt(configPath + ".piston-movement-limit", 1000, + "Maximum number of piston extend and/or retracts within the\n" + + "configured timeframe."); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onPistonExtend(BlockPistonExtendEvent event) { + if (shouldCancelActivity(event, event.getBlock().getLocation(), limit)) { + event.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onPistonRetract(BlockPistonRetractEvent event) { + if (shouldCancelActivity(event, event.getBlock().getLocation(), limit)) { + event.setCancelled(true); + } + } +} diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/Redstone.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/Redstone.java new file mode 100644 index 000000000..4d34efe38 --- /dev/null +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/Redstone.java @@ -0,0 +1,40 @@ +package me.xginko.aef.modules.lagpreventions.regionalactivity; + +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.BlockRedstoneEvent; + +public class Redstone extends RegionalActivityModule { + + private final int limit; + + public Redstone() { + super( + "redstone", + true, + 1500.0, + 18000, + 20000, + 10.0, + 120.0 + ); + this.config.addComment(configPath+".enable", """ + Limits redstone activity within a configurable radius and timeframe\s + to help reduce lag by cancelling high activity hotspots.\s + \s + Examples:\s + \s + - A redstone current changes.\s + - A redstone block gets powered on.\s + - A redstone block gets powered off."""); + this.limit = config.getInt(configPath + ".redstone-event-limit", 6000, + "Maximum number of redstone events within configured timeframe."); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onBlockRedstone(BlockRedstoneEvent event) { + if (shouldCancelActivity(event, event.getBlock().getLocation(), limit)) { + event.setNewCurrent(0); + } + } +} diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/RegionalActivityModule.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/RegionalActivityModule.java new file mode 100644 index 000000000..cca7ae78d --- /dev/null +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/RegionalActivityModule.java @@ -0,0 +1,130 @@ +package me.xginko.aef.modules.lagpreventions.regionalactivity; + +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; +import me.xginko.aef.AnarchyExploitFixes; +import me.xginko.aef.modules.AEFModule; +import me.xginko.aef.utils.models.BlockRegion2D; +import org.bukkit.Location; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.jetbrains.annotations.NotNull; + +import java.time.Duration; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; + +/** + * Credits to the initial idea of measuring burst activity within a certain region + * of the world go to kumori (Soft1k) of 3b3t.org. + */ +public abstract class RegionalActivityModule extends AEFModule implements Listener { + + protected final Cache regionDataCache; + protected final long pauseTimeMillis; + protected final double checkRadius, pauseTPS, pauseMSPT; + protected final boolean logIsEnabled; + + public RegionalActivityModule( + String subConfigPath, boolean deflogEnabled, double defCheckRadius, + int defPauseMillis, int defCacheMillis, double defPauseTPS, double defPauseMSPT + ) { + super("lag-preventions.regional-activity."+subConfigPath); + String configPath = "lag-preventions.regional-activity."+subConfigPath; + this.logIsEnabled = config.getBoolean(configPath + ".log", deflogEnabled); + this.checkRadius = config.getDouble(configPath + ".check-radius-blocks", defCheckRadius, + "The radius in blocks in which activity will be grouped together and measured."); + this.pauseTimeMillis = config.getInt(configPath + ".pause-time-millis", defPauseMillis, + "The time in milliseconds all related activity will be blocked if it exceeded\n" + + "the configured limit."); + this.regionDataCache = Caffeine.newBuilder().expireAfterWrite(Duration.ofMillis( + config.getInt(configPath + ".data-keep-time-millis", defCacheMillis, + "The time in milliseconds before a region and its data will be expired\n" + + "if no activity has been detected.\n" + + "For proper functionality, needs to be at least as long as your pause time."))).build(); + this.pauseTPS = config.getDouble(configPath + ".pause-TPS", defPauseTPS, + "The TPS at which to cancel the physics entirely."); + this.pauseMSPT = config.getDouble(configPath + ".pause-MSPT", defPauseMSPT, + "The MSPT at which to cancel the physics entirely."); + } + + @Override + public boolean shouldEnable() { + return config.getBoolean(configPath + ".enable", false); + } + + @Override + public void enable() { + plugin.getServer().getPluginManager().registerEvents(this, plugin); + } + + @Override + public void disable() { + HandlerList.unregisterAll(this); + regionDataCache.invalidateAll(); + } + + protected @NotNull RegionalActivityModule.RegionData getRegionData(Location location) { + return regionDataCache.get(getRegion(location), RegionData::new); + } + + public boolean shouldCancelActivity(Event event, Location location, int limit) { + double tps = AnarchyExploitFixes.getTickReporter().getTPS(); + double mspt = AnarchyExploitFixes.getTickReporter().getMSPT(); + + if (tps <= pauseTPS || mspt >= pauseMSPT) { + if (logIsEnabled) info("Cancelling " + event.getClass().getSimpleName() + " because server is lagging." + + " (tps="+String.format("%.4f", tps)+" | mspt="+String.format("%.4f", mspt)+")"); + return true; + } + + RegionData regionData = getRegionData(location); + + if (regionData.resumeTime.get() > System.currentTimeMillis()) { + return true; + } + + if (regionData.activityCount.incrementAndGet() > limit) { + if (logIsEnabled) { + info( "Disabling in a radius of " + checkRadius + " blocks from center at " + + "x=" + regionData.region.getCenterX() + ", z=" + regionData.region.getCenterZ() + + " in world " + location.getWorld().getName() + " for " + pauseTimeMillis + "ms, " + + "because of too high activity within the configured timeframe: " + + regionData.activityCount + " (limit: " + limit + ")"); + } + regionData.resumeTime.set(System.currentTimeMillis() + pauseTimeMillis); + regionData.activityCount.set(0); // Reset count when region is cooling down + return true; + } + + return false; + } + + public @NotNull BlockRegion2D getRegion(Location location) { + // Find and return region containing this location + for (Map.Entry regionDataEntry : regionDataCache.asMap().entrySet()) { + if (regionDataEntry.getKey().contains(location)) { + return regionDataEntry.getKey(); + } + } + // Create and cache region if none exists + BlockRegion2D region = BlockRegion2D.of(location.getWorld(), location.getX(), location.getZ(), checkRadius); + regionDataCache.put(region, new RegionData(region)); + return region; + } + + protected static class RegionData { + + public final BlockRegion2D region; + public final AtomicLong resumeTime; + public final AtomicInteger activityCount; + + public RegionData(BlockRegion2D region) { + this.region = region; + this.activityCount = new AtomicInteger(0); + this.resumeTime = new AtomicLong(0L); + } + } +} diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/SculkActivity.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/SculkActivity.java new file mode 100644 index 000000000..0d4896f80 --- /dev/null +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/SculkActivity.java @@ -0,0 +1,79 @@ +package me.xginko.aef.modules.lagpreventions.regionalactivity; + +import com.cryptomorin.xseries.XMaterial; +import org.bukkit.Material; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.BlockPhysicsEvent; +import org.bukkit.event.block.BlockRedstoneEvent; + +import java.util.EnumSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class SculkActivity extends RegionalActivityModule { + + private final Set sculkBlocks; + private final int limit; + + public SculkActivity() { + super( + "sculk-sensor", + true, + 1500.0, + 18000, + 20000, + 10.0, + 120.0 + ); + this.config.addComment(configPath+".enable", + "Limits sculk activity within a configurable radius and timeframe\n" + + "to help reduce lag by cancelling high activity hotspots.\n" + + "\n" + + "Examples:\n" + + "\n" + + "- A redstone current changes for a sculk sensor.\n" + + "- A physics check is being performed for a sculk sensor."); + this.limit = config.getInt(configPath + ".sculk-event-limit", 800, + "Maximum number of sculk events within configured timeframe."); + List defaults = Stream.of( + XMaterial.SCULK_SENSOR, + XMaterial.SCULK_SHRIEKER, + XMaterial.CALIBRATED_SCULK_SENSOR) + .filter(XMaterial::isSupported) + .map(XMaterial::parseMaterial) + .map(Enum::name) + .collect(Collectors.toList()); + this.sculkBlocks = config.getList(configPath + ".sculk-blocks", defaults) + .stream() + .map(configuredType -> { + try { + return Material.valueOf(configuredType); + } catch (IllegalArgumentException e) { + notRecognized(Material.class, configuredType); + return null; + } + }) + .collect(Collectors.toCollection(() -> EnumSet.noneOf(Material.class))); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onBlockRedstone(BlockRedstoneEvent event) { + if (!sculkBlocks.contains(event.getBlock().getType())) return; + + if (shouldCancelActivity(event, event.getBlock().getLocation(), limit)) { + event.setNewCurrent(0); + } + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onBlockPhysics(BlockPhysicsEvent event) { + if (!sculkBlocks.contains(event.getBlock().getType())) return; + + if (shouldCancelActivity(event, event.getBlock().getLocation(), limit)) { + event.setCancelled(true); + } + } +} diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/SculkBloom.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/SculkBloom.java new file mode 100755 index 000000000..0997ab64c --- /dev/null +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/SculkBloom.java @@ -0,0 +1,40 @@ +package me.xginko.aef.modules.lagpreventions.regionalactivity; + +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.SculkBloomEvent; + +public class SculkBloom extends RegionalActivityModule { + + private final int limit; + + public SculkBloom() { + super( + "sculk-bloom", + false, + 1500.0, + 6000, + 10000, + 10.0, + 120.0 + ); + this.config.addComment(configPath+".enable", """ + Limits sculk blooming within a configurable radius and timeframe\s + to help reduce lag by cancelling high activity hotspots.\s + \s + Examples:\s + \s + - An entity was killed and dropped experience within an 8-block\s + radius of a SculkCatalyst.\s + - A plugin used SculkCatalyst.bloom(Block, int)"""); + this.limit = config.getInt(configPath + ".sculk-bloom-limit", 300, + "Maximum number of sculk bloom events within the configured timeframe."); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onSkulkBloom(SculkBloomEvent event) { + if (shouldCancelActivity(event, event.getBlock().getLocation(), limit)) { + event.setCancelled(true); + } + } +} diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/packets/BeehiveCoordinates.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/packets/BeehiveCoordinates.java index cb4fe1ecf..f67519012 100755 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/packets/BeehiveCoordinates.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/packets/BeehiveCoordinates.java @@ -7,6 +7,7 @@ import com.github.retrooper.packetevents.protocol.nbt.NBTList; import com.github.retrooper.packetevents.protocol.packettype.PacketType; import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerSetSlot; +import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerWindowItems; import java.util.List; @@ -41,23 +42,40 @@ public boolean shouldEnable() { @Override public void onPacketSend(PacketSendEvent event) { - if (event.getPacketType() != PacketType.Play.Server.SET_SLOT) return; - WrapperPlayServerSetSlot packet = new WrapperPlayServerSetSlot(event); - ItemStack filtered = filterItemStack(packet.getItem()); - if (filtered == null) return; - packet.setItem(filtered); - event.markForReEncode(true); - } + if (event.isCancelled()) return; + + if (event.getPacketType() == PacketType.Play.Server.SET_SLOT) { + WrapperPlayServerSetSlot packet = new WrapperPlayServerSetSlot(event); + + ItemStack itemStack = packet.getItem(); + if (itemStack == null || itemStack.isEmpty()) return; + + NBTCompound nbt = itemStack.getNBT(); + if (fixCompound(nbt)) { + itemStack.setNBT(nbt); + packet.setItem(itemStack); + event.markForReEncode(true); + } + } - private ItemStack filterItemStack(ItemStack itemStack) { - if (itemStack == null || itemStack.isEmpty()) return null; - final NBTCompound rootCompound = itemStack.getNBT(); - if (!filterCompound(rootCompound)) return null; - itemStack.setNBT(rootCompound); - return itemStack; + if (event.getPacketType() == PacketType.Play.Server.WINDOW_ITEMS) { + WrapperPlayServerWindowItems packet = new WrapperPlayServerWindowItems(event); + + for (int i = 0; i < packet.getItems().size(); i++) { + ItemStack itemStack = packet.getItems().get(i); + if (itemStack == null || itemStack.isEmpty()) return; + + NBTCompound nbt = itemStack.getNBT(); + if (fixCompound(nbt)) { + itemStack.setNBT(nbt); + packet.getItems().set(i, itemStack); + event.markForReEncode(true); + } + } + } } - private boolean filterCompound(NBTCompound compound) { + private boolean fixCompound(NBTCompound compound) { if (compound == null || compound.isEmpty()) return false; boolean needsReEncode = false; @@ -93,7 +111,8 @@ private boolean filterCompound(NBTCompound compound) { for (int i = 0; i < items.size(); i++) { NBTCompound item = items.getTag(i); NBTCompound itemRootCompound = item.getCompoundTagOrNull("tag"); - if (filterCompound(itemRootCompound)) { + if (fixCompound(itemRootCompound)) { + item.setTag("tag", itemRootCompound); items.setTag(i, item); needsReEncode = true; } @@ -105,7 +124,7 @@ private boolean filterCompound(NBTCompound compound) { if (compound.getTags().containsKey("BlockEntityTag")) { NBTCompound blockEntityTag = compound.getCompoundTagOrNull("BlockEntityTag"); - if (filterCompound(blockEntityTag)) { + if (fixCompound(blockEntityTag)) { compound.setTag("BlockEntityTag", blockEntityTag); needsReEncode = true; } diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/packets/InventoryLag.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/packets/InventoryLag.java new file mode 100644 index 000000000..e1e2bd18d --- /dev/null +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/packets/InventoryLag.java @@ -0,0 +1,195 @@ +package me.xginko.aef.modules.packets; + +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; +import com.github.retrooper.packetevents.PacketEvents; +import com.github.retrooper.packetevents.event.PacketListenerPriority; +import com.github.retrooper.packetevents.event.PacketSendEvent; +import com.github.retrooper.packetevents.netty.buffer.ByteBufHelper; +import com.github.retrooper.packetevents.protocol.packettype.PacketType; +import com.github.retrooper.packetevents.protocol.packettype.PacketTypeCommon; +import me.xginko.aef.utils.EntityUtil; +import me.xginko.aef.utils.MaterialUtil; +import org.bukkit.entity.HumanEntity; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.event.block.Action; +import org.bukkit.event.inventory.InventoryOpenEvent; +import org.bukkit.event.player.PlayerInteractEntityEvent; +import org.bukkit.event.player.PlayerInteractEvent; + +import java.time.Duration; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; +import java.util.stream.Collectors; + +public class InventoryLag extends PacketModule implements Listener { + + private final Cache playerDataCache; + private final Set measuredPacketTypes; + private final long rateLimitBytes, lockoutBytes, lockoutMillis; + private final int screenOpenLimit, screenOpenDelay; + private final boolean closeInventory, log; + + public InventoryLag() { + super("patches.inventory-lag", PacketListenerPriority.HIGHEST); + config.addComment(configPath + ".enable", """ + Checks if a player is requesting unusual amounts of traffic from the server\s + using ItemStacks.\s + If a player exceeds the limit, they will be put on a cooldown, during which\s + they will be very limited in terms of ItemStack or Inventory interactions."""); + this.log = config.getBoolean(configPath + ".log", false, """ + For debug purposes. Don't leave enabled for too long as it is very spammy."""); + this.closeInventory = config.getBoolean(configPath + ".close-open-inventory", true, """ + Whether to immediately close any open inventory of the player on limit exceed\s + Note: Closing has to be scheduled so it will take a bit if the server is heavily\s + lagging."""); + this.playerDataCache = Caffeine.newBuilder().expireAfterWrite(Duration.ofMillis(Math.max(1L, + config.getLong(configPath + ".byte-data-keep-time-millis", 30_000, """ + The time in millis in which to check if the player exceeded the limit.\s + Needs to be at least as long as your lockout duration millis.""")))).build(); + this.rateLimitBytes = config.getLong(configPath + ".rate-limit.bytesize-limit", 8_000_000, """ + The limit in bytes the server has sent the server in the form of ItemStacks,\s + before the player will be put on a rate-limit.\s + Should always be lower than your lockout bytesize limit."""); + this.screenOpenDelay = config.getInt(configPath + ".rate-limit.timeframe-millis", 2500, """ + The time in millis in which a player is allowed to open x amounts of windows\s + but not more."""); + this.screenOpenLimit = config.getInt(configPath + ".rate-limit.max-window-opens-per-timeframe", 2, """ + The amount of windows that can be opened during the timeframe-millis."""); + this.lockoutBytes = config.getLong(configPath + ".lockout.bytesize-limit", 24_000_000, """ + The upper limit in bytes a player is allowed to request from the server\s + within the configured timeframe before he will be put on cooldown.\s + During the cooldown, he will not be able to open any inventory screens\s + or interact with items."""); + this.lockoutMillis = config.getLong(configPath + ".lockout.duration-millis", 15_000, """ + The time in milliseconds the player will have to wait before\s + being able to open an inventory again after he exceeded the limit."""); + this.measuredPacketTypes = config.getList(configPath + ".check-packets", List.of("SET_SLOT", "WINDOW_ITEMS")) + .stream() + .map(configuredPacketType -> { + try { + return PacketType.Play.Server.valueOf(configuredPacketType); + } catch (IllegalArgumentException e) { + notRecognized(PacketType.Play.Server.class, configuredPacketType); + return null; + } + }) + .filter(Objects::nonNull) + .collect(Collectors.toCollection(HashSet::new)); + } + + @Override + public boolean shouldEnable() { + return config.getBoolean(configPath + ".enable", false); + } + + @Override + public void enable() { + plugin.getServer().getPluginManager().registerEvents(this, plugin); + PacketEvents.getAPI().getEventManager().registerListener(asAbstract); + } + + @Override + public void disable() { + HandlerList.unregisterAll(this); + PacketEvents.getAPI().getEventManager().unregisterListener(asAbstract); + } + + @Override + public void onPacketSend(PacketSendEvent event) { + if (event.isCancelled() || !measuredPacketTypes.contains(event.getPacketType()) || event.getUser() == null) return; + + PlayerData data = playerDataCache.get(event.getUser().getUUID(), PlayerData::new); + + long servedBytes = data.servedSetSlotBytes.addAndGet(ByteBufHelper.readableBytes(event.getByteBuf())); + if (log) info("Player '" + event.getUser().getName() + "' requested " + servedBytes + " bytes in ItemStacks. " + + "(PacketType: " + event.getPacketType() + ")"); + + if (servedBytes <= lockoutBytes) { + return; + } + + if (data.cooldownResumeTime.get() > System.currentTimeMillis()) { + event.setCancelled(true); + return; + } + + data.cooldownResumeTime.set(System.currentTimeMillis() + lockoutMillis); + + if (log) warn("Player '" + event.getUser().getName() + "' is now on LOCKOUT as they exceeded" + + " the set limit (now " + servedBytes + " bytes)."); + + if (closeInventory && event.getPlayer() != null) { + Player player = (Player) event.getPlayer(); + player.getScheduler().execute(plugin, player::closeInventory, null, 1L); + } + } + + private void onInventoryOpen(Cancellable event, HumanEntity player) { + PlayerData data = playerDataCache.get(player.getUniqueId(), PlayerData::new); + + if (data.cooldownResumeTime.get() > System.currentTimeMillis()) { + event.setCancelled(true); + if (log) info("Player '" + player.getName() + "' could not open screen because they are on cooldown."); + return; + } + + long servedBytes = data.servedSetSlotBytes.get(); + + if ( + servedBytes > rateLimitBytes && servedBytes < lockoutBytes + && data.screenOpenCount.getAndIncrement() > screenOpenLimit + ) { + event.setCancelled(true); + data.cooldownResumeTime.set(System.currentTimeMillis() + screenOpenDelay); + data.screenOpenCount.set(0); + if (log) warn("Player '" + player.getName() + "' is now on RATE-LIMIT as they exceeded" + + " the set limit (now " + servedBytes + " bytes)."); + } + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onPlayerInteract(PlayerInteractEvent event) { + if (event.getAction() != Action.RIGHT_CLICK_BLOCK) return; + + if (MaterialUtil.INVENTORY_HOLDERS.contains(event.getClickedBlock().getType())) { + onInventoryOpen(event, event.getPlayer()); + } + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onPlayerInteract(PlayerInteractEntityEvent event) { + if (EntityUtil.isInventoryHolder(event.getRightClicked())) { + onInventoryOpen(event, event.getPlayer()); + } + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onInventoryOpen(InventoryOpenEvent event) { + onInventoryOpen(event, event.getPlayer()); + } + + private static class PlayerData { + + public final UUID uuid; + public final AtomicLong servedSetSlotBytes, cooldownResumeTime; + public final AtomicInteger screenOpenCount; + + public PlayerData(UUID uuid) { + this.uuid = uuid; + this.servedSetSlotBytes = new AtomicLong(0L); + this.cooldownResumeTime = new AtomicLong(0L); + this.screenOpenCount = new AtomicInteger(0); + } + } +} \ No newline at end of file diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/patches/MapCursorLag.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/packets/MapCursorLag.java similarity index 50% rename from AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/patches/MapCursorLag.java rename to AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/packets/MapCursorLag.java index 3b95c860b..609406f46 100644 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/patches/MapCursorLag.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/packets/MapCursorLag.java @@ -1,8 +1,12 @@ -package me.xginko.aef.modules.patches; +package me.xginko.aef.modules.packets; -import me.xginko.aef.modules.AEFModule; +import com.github.retrooper.packetevents.PacketEvents; +import com.github.retrooper.packetevents.event.PacketListenerPriority; +import com.github.retrooper.packetevents.event.PacketSendEvent; +import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes; +import com.github.retrooper.packetevents.protocol.packettype.PacketType; +import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerSpawnEntity; import me.xginko.aef.utils.EntityUtil; -import me.xginko.aef.utils.FramedMapUtil; import org.bukkit.entity.Entity; import org.bukkit.entity.ItemFrame; import org.bukkit.event.EventHandler; @@ -12,10 +16,10 @@ import org.bukkit.event.player.PlayerInteractEntityEvent; import org.bukkit.event.world.ChunkLoadEvent; -public class MapCursorLag extends AEFModule implements Listener { +public class MapCursorLag extends PacketModule implements Listener { public MapCursorLag() { - super("patches.map-cursor-lag-patch"); + super("patches.map-cursor-lag-patch", PacketListenerPriority.HIGHEST); config.addComment(configPath + ".enable", """ Patches the famous stacked map cursor lag that causes both\s client and server crashes."""); @@ -23,7 +27,9 @@ public MapCursorLag() { @Override public void enable() { - plugin.getServer().getPluginManager().registerEvents(this, plugin); + if (EntityUtil.canDisableMapPositionCursor()) + plugin.getServer().getPluginManager().registerEvents(this, plugin); + PacketEvents.getAPI().getEventManager().registerListener(asAbstract); } @Override @@ -34,15 +40,28 @@ public boolean shouldEnable() { @Override public void disable() { HandlerList.unregisterAll(this); + PacketEvents.getAPI().getEventManager().unregisterListener(asAbstract); } + @Override + public void onPacketSend(PacketSendEvent event) { + if (event.getPacketType() != PacketType.Play.Server.SPAWN_ENTITY) return; + + if (new WrapperPlayServerSpawnEntity(event).getEntityType() == EntityTypes.MARKER) { + event.setCancelled(true); + } + } + + /* + For 1.16 and up + */ @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) private void onChunkLoad(ChunkLoadEvent event) { if (event.isNewChunk()) return; for (Entity entity : event.getChunk().getEntities()) { if (EntityUtil.ITEM_FRAMES.contains(entity.getType())) { - FramedMapUtil.disableTracker((ItemFrame) entity); + EntityUtil.disableMapPositionCursor((ItemFrame) entity); } } } @@ -51,7 +70,7 @@ private void onChunkLoad(ChunkLoadEvent event) { private void onInteract(PlayerInteractEntityEvent event) { Entity rightClicked = event.getRightClicked(); if (EntityUtil.ITEM_FRAMES.contains(rightClicked.getType())) { - FramedMapUtil.disableTracker((ItemFrame) rightClicked); + EntityUtil.disableMapPositionCursor((ItemFrame) rightClicked); } } } diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/packets/PurpurBeehiveCrash.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/packets/PurpurBeehiveCrash.java index 9ef878be3..4f6967ee9 100755 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/packets/PurpurBeehiveCrash.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/packets/PurpurBeehiveCrash.java @@ -5,19 +5,20 @@ import com.github.retrooper.packetevents.protocol.packettype.PacketType; import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPluginMessage; import com.google.common.io.ByteStreams; +import me.xginko.aef.utils.PlatformUtil; import org.bukkit.Location; -import org.bukkit.entity.Player; +import org.bukkit.entity.Entity; import org.bukkit.util.NumberConversions; public class PurpurBeehiveCrash extends PacketModule { - private static final String BEEHIVE_C2S_CHANNEL = "purpur:beehive_c2s"; private static final int SIZE_BITS_X = 26; private static final int SIZE_BITS_Z = SIZE_BITS_X; private static final int SIZE_BITS_Y = 64 - SIZE_BITS_X - SIZE_BITS_Z; private static final int BIT_SHIFT_Z = SIZE_BITS_Y; private static final int BIT_SHIFT_X = SIZE_BITS_Y + SIZE_BITS_Z; + private final String beehive_c2s_channel; private final double maxDistanceSquared; private final boolean log, kick; @@ -31,6 +32,7 @@ public PurpurBeehiveCrash() { beehive is away from the client enabling a malicious sender\s to load chunks very fast at far away locations by telling\s the server it clicked a beehive there."""); + this.beehive_c2s_channel = config.getString(configPath + ".channel", "purpur:beehive_c2s"); this.maxDistanceSquared = NumberConversions.square(config.getInt(configPath + ".max-distance", 24)); this.log = config.getBoolean(configPath + ".log", false); this.kick = config.getBoolean(configPath + ".kick-player", false); @@ -38,19 +40,18 @@ public PurpurBeehiveCrash() { @Override public boolean shouldEnable() { - return config.getBoolean(configPath + ".enable", true); + return config.getBoolean(configPath + ".enable", PlatformUtil.isPurpur()); } @Override public void onPacketReceive(PacketReceiveEvent event) { + if (event.isCancelled() || event.getPlayer() == null) return; if (event.getPacketType() != PacketType.Play.Client.PLUGIN_MESSAGE) return; - WrapperPlayClientPluginMessage packet = new WrapperPlayClientPluginMessage(event); - if (!packet.getChannelName().equalsIgnoreCase(BEEHIVE_C2S_CHANNEL)) return; - Player player = (Player) event.getPlayer(); - if (player == null) return; + WrapperPlayClientPluginMessage packet = new WrapperPlayClientPluginMessage(event); + if (!packet.getChannelName().equalsIgnoreCase(beehive_c2s_channel)) return; - if (distanceSquared(ByteStreams.newDataInput(packet.getData()).readLong(), player.getLocation()) > maxDistanceSquared) { + if (distanceSquared(ByteStreams.newDataInput(packet.getData()).readLong(), ((Entity) event.getPlayer()).getLocation()) > maxDistanceSquared) { event.setCancelled(true); onCancel(log, kick, event.getUser()); } diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/packets/SequenceCrash.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/packets/SequenceCrash.java index db99aa15f..6a4fe8a49 100755 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/packets/SequenceCrash.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/packets/SequenceCrash.java @@ -6,7 +6,7 @@ import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerBlockPlacement; import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerDigging; import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientUseItem; -import io.papermc.lib.PaperLib; +import me.xginko.aef.utils.PlatformUtil; public class SequenceCrash extends PacketModule { @@ -23,11 +23,13 @@ public SequenceCrash() { @Override public boolean shouldEnable() { - return config.getBoolean(configPath + ".enable", true) && PaperLib.getMinecraftVersion() >= 19; + return config.getBoolean(configPath + ".enable", true) && PlatformUtil.getMinecraftVersion() >= 19; } @Override public void onPacketReceive(PacketReceiveEvent event) { + if (event.isCancelled()) return; + int sequence; if (event.getPacketType() == PacketType.Play.Client.PLAYER_BLOCK_PLACEMENT) { sequence = new WrapperPlayClientPlayerBlockPlacement(event).getSequence(); diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/packets/SignLag.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/packets/SignLag.java index 22903a026..8d77c00e6 100755 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/packets/SignLag.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/packets/SignLag.java @@ -4,10 +4,14 @@ import com.github.retrooper.packetevents.event.PacketReceiveEvent; import com.github.retrooper.packetevents.protocol.packettype.PacketType; import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientUpdateSign; -import me.xginko.aef.AnarchyExploitFixes; +import me.xginko.aef.utils.models.ExpiringSet; + +import java.time.Duration; +import java.util.UUID; public class SignLag extends PacketModule { + private final ExpiringSet cooldowns; private final int line_char_limit, total_char_limit; private final boolean log, kick; @@ -16,6 +20,10 @@ public SignLag() { config.addComment(configPath + ".enable", """ Patches a lag exploit that involves sending specific oversized\s sign edit packets."""); + this.cooldowns = new ExpiringSet<>(Duration.ofMillis(Math.max(1, + config.getInt(configPath + ".packet-delay-in-ticks", 10, + "How many ticks a player needs to wait to be able to send\n" + + "another sign update packet (renaming or writing).")) * 50L)); this.line_char_limit = config.getInt(configPath + ".line-character-limit", 80, "Vanilla limit is 384 characters per line, which can be too much."); this.total_char_limit = config.getInt(configPath + ".total-char-limit", 384, @@ -26,13 +34,17 @@ public SignLag() { @Override public boolean shouldEnable() { - return AnarchyExploitFixes.config().getBoolean(configPath + ".enable", true); + return config.getBoolean(configPath + ".enable", true); } @Override public void onPacketReceive(PacketReceiveEvent event) { + if (event.isCancelled()) return; if (event.getPacketType() != PacketType.Play.Client.UPDATE_SIGN) return; + if (cooldowns.contains(event.getUser().getUUID())) return; + cooldowns.add(event.getUser().getUUID()); + int sum = 0; for (String line : new WrapperPlayClientUpdateSign(event).getTextLines()) { diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/packets/TabCompleteCrash.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/packets/TabCompleteCrash.java index 49f4528aa..399e7773f 100755 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/packets/TabCompleteCrash.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/packets/TabCompleteCrash.java @@ -4,7 +4,7 @@ import com.github.retrooper.packetevents.event.PacketReceiveEvent; import com.github.retrooper.packetevents.protocol.packettype.PacketType; import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientTabComplete; -import org.bukkit.entity.Player; +import org.bukkit.permissions.ServerOperator; public class TabCompleteCrash extends PacketModule { @@ -28,6 +28,7 @@ public boolean shouldEnable() { @Override public void onPacketReceive(PacketReceiveEvent event) { + if (event.isCancelled()) return; if (event.getPacketType() != PacketType.Play.Client.TAB_COMPLETE) return; final String text = new WrapperPlayClientTabComplete(event).getText(); @@ -48,8 +49,7 @@ public void onPacketReceive(PacketReceiveEvent event) { } } - final Player player = (Player) event.getPlayer(); - if (player != null && player.isOp()) return; + if (event.getPlayer() == null || ((ServerOperator) event.getPlayer()).isOp()) return; for (String sequence : ABUSABLE_SEQUENCES) { if (text.indexOf(sequence) != -1) { diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/packets/WindowClickCrash.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/packets/WindowClickCrash.java index db0c4095f..e33595b7b 100755 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/packets/WindowClickCrash.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/packets/WindowClickCrash.java @@ -4,7 +4,7 @@ import com.github.retrooper.packetevents.event.PacketReceiveEvent; import com.github.retrooper.packetevents.protocol.packettype.PacketType; import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientClickWindow; -import org.bukkit.entity.Player; +import org.bukkit.entity.HumanEntity; public class WindowClickCrash extends PacketModule { @@ -27,10 +27,11 @@ public boolean shouldEnable() { @Override public void onPacketReceive(PacketReceiveEvent event) { + if (event.isCancelled()) return; if (event.getPacketType() != PacketType.Play.Client.CLICK_WINDOW) return; - final WrapperPlayClientClickWindow packet = new WrapperPlayClientClickWindow(event); + WrapperPlayClientClickWindow packet = new WrapperPlayClientClickWindow(event); - final int button = packet.getButton(); + int button = packet.getButton(); if (button < 0 || button > 10 && button != 40 && button != 99) { event.setCancelled(true); @@ -38,12 +39,14 @@ public void onPacketReceive(PacketReceiveEvent event) { return; } - final int slot = packet.getSlot(); + int slot = packet.getSlot(); - final Player player = (Player) event.getPlayer(); - if (player != null && slot >= player.getOpenInventory().countSlots()) { - event.setCancelled(true); - onCancel(log, kick, event.getUser()); + if (event.getPlayer() != null) { + if (((HumanEntity) event.getPlayer()).getOpenInventory().countSlots() < slot) { + event.setCancelled(true); + onCancel(log, kick, event.getUser()); + return; + } } if (slot != -999 && slot != -1) { diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/patches/PearlPhase.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/patches/PearlPhase.java index 4bd252d80..1c65dd580 100644 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/patches/PearlPhase.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/patches/PearlPhase.java @@ -1,9 +1,10 @@ package me.xginko.aef.modules.patches; import com.cryptomorin.xseries.XMaterial; -import me.xginko.aef.AnarchyExploitFixes; import me.xginko.aef.modules.AEFModule; +import me.xginko.aef.utils.LocationUtil; import me.xginko.aef.utils.MaterialUtil; +import me.xginko.aef.utils.PlatformUtil; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.World; @@ -28,6 +29,7 @@ public class PearlPhase extends AEFModule implements Listener { private final Set glitchyMaterial; + private final double maxDistance; private final int radius; private long schedulingTimeoutMillis; @@ -35,10 +37,13 @@ public PearlPhase() { super("patches.pearl-phase"); config.addComment(configPath+ ".enable", "Attempts to patch a pearl phasing exploit by cancelling the teleport\n" + - "if the pearl is thrown at or near a specific block type."); + "if the pearl is thrown at or near a specific block.\n" + + "At the time of the creation of this module, this is an issue with NoCheatPlus."); this.radius = Math.min(1, config.getInt(configPath + ".search-radius", 2, "How many blocks around the teleport location should be searched\n" + "for potential glitch blocks if the teleport location isn't one itself.")); + this.maxDistance = config.getDouble(configPath + ".maximum-distance-to-cancel-teleport", 3.0); + Stream concatA = Stream.concat(MaterialUtil.SLAB_LIKE.stream(), Stream.of(XMaterial.COBWEB, XMaterial.POWDER_SNOW).filter(XMaterial::isSupported).map(XMaterial::parseMaterial)); Stream concatB = Stream.concat(MaterialUtil.PRESSURE_PLATES.stream(), MaterialUtil.TRAPDOORS.stream()); @@ -58,7 +63,7 @@ public PearlPhase() { }) .filter(Objects::nonNull) .collect(Collectors.toCollection(() -> EnumSet.noneOf(Material.class))); - if (AnarchyExploitFixes.isServerFolia()) { + if (PlatformUtil.isFolia()) { this.schedulingTimeoutMillis = config.getInt(configPath + ".check-timeout-millis", 800, "We will have to schedule the check on folia, meaning theres a chance\n" + "the task might take longer than expected. To make sure that does not cause\n" + @@ -86,8 +91,9 @@ private void onPlayerTeleport(PlayerTeleportEvent event) { if (event.getCause() != PlayerTeleportEvent.TeleportCause.ENDER_PEARL) return; Location destination = event.getTo(); + if (LocationUtil.getRelDistance2D(event.getFrom(), destination) >= maxDistance) return; - if (!AnarchyExploitFixes.isServerFolia()) { + if (!PlatformUtil.isFolia()) { if (isPotentialPhase(destination)) event.setCancelled(true); return; diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/preventions/BedTrap.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/preventions/BedTrap.java index c83cff80c..549081e34 100755 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/preventions/BedTrap.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/preventions/BedTrap.java @@ -3,7 +3,7 @@ import com.destroystokyo.paper.event.player.PlayerPostRespawnEvent; import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; -import me.xginko.aef.AnarchyExploitFixes; +import me.xginko.aef.events.PacketPlayerRespawnEvent; import me.xginko.aef.modules.AEFModule; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -13,10 +13,11 @@ import java.time.Duration; import java.util.UUID; +import java.util.concurrent.atomic.AtomicInteger; public class BedTrap extends AEFModule implements Listener { - private final Cache playerDeathNearBedCount; + private final Cache playerDeathNearBedCount; private final int maxDeathsPerTime; private final boolean shouldLog; @@ -30,7 +31,7 @@ public BedTrap() { this.playerDeathNearBedCount = Caffeine.newBuilder().expireAfterWrite(Duration.ofSeconds(Math.max(1, config.getInt(configPath + ".time-in-seconds", 5, """ "Time until death counter will be reset again.""")))).build(); - this.shouldLog = AnarchyExploitFixes.config().getBoolean(configPath+".log", false); + this.shouldLog = config.getBoolean(configPath+".log", false); } @Override @@ -50,25 +51,40 @@ public void disable() { @SuppressWarnings("deprecation") @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - private void onRespawn(PlayerPostRespawnEvent event) { - final Player player = event.getPlayer(); + private void onPlayerPostRespawn(PlayerPostRespawnEvent event) { + Player player = event.getPlayer(); if (!event.isBedSpawn()) return; - int nearBedDeaths = playerDeathNearBedCount.get(player.getUniqueId(), k -> 0); - nearBedDeaths++; + if (playerDeathNearBedCount.get(player.getUniqueId(), k -> new AtomicInteger()).incrementAndGet() <= maxDeathsPerTime) { + return; + } - if (nearBedDeaths > maxDeathsPerTime) { - player.getScheduler().execute(plugin, () -> { - try { - player.setRespawnLocation(null, true); - } catch (NoSuchMethodError e) { - player.setBedSpawnLocation(null, true); - } - if (shouldLog) info("Reset bed respawn of potentially bed-trapped player '" + player.getName() + "'"); - }, null, 1L); + player.getScheduler().execute(plugin, () -> { + try { + player.setRespawnLocation(null, true); + } catch (NoSuchMethodError e) { + player.setBedSpawnLocation(null, true); + } + if (shouldLog) info("Reset bed respawn of potentially bed-trapped player '" + player.getName() + "'"); + }, null, 1L); + } + + @SuppressWarnings("deprecation") + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onPacketPlayerRespawn(PacketPlayerRespawnEvent event) { + Player player = event.getPlayer(); + if (!event.isPotentialBedSpawn()) return; + + if (playerDeathNearBedCount.get(player.getUniqueId(), k -> new AtomicInteger()).incrementAndGet() <= maxDeathsPerTime) { return; } - playerDeathNearBedCount.put(player.getUniqueId(), nearBedDeaths); + try { + player.setRespawnLocation(null, true); + } catch (NoSuchMethodError e) { + player.setBedSpawnLocation(null, true); + } + + if (shouldLog) info("Reset bed respawn of potentially bed-trapped player '" + player.getName() + "'"); } } \ No newline at end of file diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/preventions/blockbreak/PistonExplodePermBlockRemoval.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/preventions/blockbreak/PistonExplodePermBlockRemoval.java index 38352728b..1f70fe0d3 100755 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/preventions/blockbreak/PistonExplodePermBlockRemoval.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/preventions/blockbreak/PistonExplodePermBlockRemoval.java @@ -1,6 +1,5 @@ package me.xginko.aef.modules.preventions.blockbreak; -import com.destroystokyo.paper.MaterialTags; import me.xginko.aef.modules.AEFModule; import me.xginko.aef.utils.MaterialUtil; import org.bukkit.Material; @@ -42,19 +41,22 @@ public void disable() { } @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - private void onPistonExplode(EntityExplodeEvent event) { + private void onEntityExplode(EntityExplodeEvent event) { if (whitelistedWorlds.contains(event.getEntity().getWorld().getName())) return; event.blockList().removeIf(block -> { - if (MaterialTags.PISTONS.isTagged(block.getType())) { - for (BlockFace face : BlockFace.values()) { - if (MaterialUtil.INDESTRUCTIBLES.contains(block.getRelative(face).getType())) { - plugin.getServer().getRegionScheduler().runDelayed(plugin, block.getLocation(), - removePiston -> block.setType(Material.AIR), 5); - return true; - } + if (!MaterialUtil.PISTONS.contains(block.getType())) return false; + + for (BlockFace face : BlockFace.values()) { + // Check if any side of the piston touches an indestructible block + if (MaterialUtil.INDESTRUCTIBLES.contains(block.getRelative(face).getType())) { + // Schedule remove task for each piston location to ensure we are always on the correct thread + plugin.getServer().getRegionScheduler().runDelayed(plugin, block.getLocation(), remove -> + block.setType(Material.AIR), 5); + return true; // Remove piston from the list of blocks to be affected by the explosion } } + return false; }); } diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/preventions/blockbreak/StructureGrowPermBlockRemoval.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/preventions/blockbreak/StructureGrowPermBlockRemoval.java index 60f3cfd54..b67e207be 100644 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/preventions/blockbreak/StructureGrowPermBlockRemoval.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/preventions/blockbreak/StructureGrowPermBlockRemoval.java @@ -37,12 +37,12 @@ public void disable() { @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void onStructureGrow(StructureGrowEvent event) { - final World world = event.getWorld(); + World world = event.getWorld(); + for (BlockState blockState : event.getBlocks()) { if (MaterialUtil.INDESTRUCTIBLES.contains(world.getBlockAt(blockState.getLocation()).getType())) { event.setCancelled(true); - info("Prevented permanent block break by not growing a structure at " + - LocationUtil.toString(blockState.getLocation())); + info("Prevented permanent block break by "+ event.getSpecies().name() +" at " + LocationUtil.toString(blockState.getLocation())); return; } } diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/preventions/portals/PreventAllEntitiesInPortals.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/preventions/portals/PreventAllEntitiesInPortals.java index 2e2902980..0eaba2ead 100755 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/preventions/portals/PreventAllEntitiesInPortals.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/preventions/portals/PreventAllEntitiesInPortals.java @@ -1,8 +1,8 @@ package me.xginko.aef.modules.preventions.portals; import com.cryptomorin.xseries.XEntityType; -import me.xginko.aef.AnarchyExploitFixes; import me.xginko.aef.modules.AEFModule; +import me.xginko.aef.utils.PlatformUtil; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.HandlerList; @@ -43,7 +43,7 @@ private void onPortalUse(EntityPortalEvent event) { // Does not fire on folia du @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onPortalEnter(EntityPortalEnterEvent event) { // Only portal event that can be listened to on folia - if (!AnarchyExploitFixes.isServerFolia()) return; // Avoid fallback logic on non-folia + if (!PlatformUtil.isFolia()) return; // Avoid fallback logic on non-folia if (event.getEntityType() != XEntityType.PLAYER.get()) { event.getEntity().getScheduler().execute(plugin, () -> event.getEntity().remove(), null, 1L); diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/preventions/portals/PreventPortalTraps.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/preventions/portals/PreventPortalTraps.java index a3bb26117..d993a11ce 100755 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/preventions/portals/PreventPortalTraps.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/preventions/portals/PreventPortalTraps.java @@ -4,8 +4,8 @@ import com.cryptomorin.xseries.XSound; import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; -import me.xginko.aef.AnarchyExploitFixes; import me.xginko.aef.modules.AEFModule; +import me.xginko.aef.utils.PlatformUtil; import org.bukkit.Location; import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; @@ -31,7 +31,7 @@ public PreventPortalTraps() { standing in a portal for too long."""); int tpBackDelaySeconds = Math.max(1, config.getInt(configPath + ".wait-time-until-tp-back-in-seconds", 10)); this.tpBackDelayTicks = tpBackDelaySeconds * 20L; - if (AnarchyExploitFixes.isServerFolia()) { + if (PlatformUtil.isFolia()) { this.portalEnterLocations = Caffeine.newBuilder().expireAfterWrite(Duration.ofSeconds(tpBackDelaySeconds + 2)).build(); } else { this.portalEnterLocations = null; @@ -69,7 +69,7 @@ private void onPortalUse(PlayerPortalEvent event) { // Does not fire on folia du @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onPortalEnter(EntityPortalEnterEvent event) { // Only portal event that can be listened to on folia - if (!AnarchyExploitFixes.isServerFolia()) return; // Avoid fallback logic on non-folia + if (!PlatformUtil.isFolia()) return; // Avoid fallback logic on non-folia if (event.getEntityType() != EntityType.PLAYER) return; final Player player = (Player) event.getEntity(); diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/preventions/portals/PreventProjectilesInPortals.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/preventions/portals/PreventProjectilesInPortals.java index a1ff5baf7..032d621ef 100755 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/preventions/portals/PreventProjectilesInPortals.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/preventions/portals/PreventProjectilesInPortals.java @@ -1,8 +1,8 @@ package me.xginko.aef.modules.preventions.portals; -import me.xginko.aef.AnarchyExploitFixes; import me.xginko.aef.modules.AEFModule; import me.xginko.aef.utils.EntityUtil; +import me.xginko.aef.utils.PlatformUtil; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.HandlerList; @@ -41,7 +41,7 @@ private void onPortalUse(EntityPortalEvent event) { // Does not fire on folia du @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onPortalEnter(EntityPortalEnterEvent event) { // Only portal event that can be listened to on folia - if (!AnarchyExploitFixes.isServerFolia()) return; // Avoid fallback logic on non-folia + if (!PlatformUtil.isFolia()) return; // Avoid fallback logic on non-folia if (EntityUtil.isProjectile(event.getEntity())) { event.getEntity().getScheduler().execute(plugin, event.getEntity()::remove, null, 1L); diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/preventions/portals/PreventSpecificEntitiesInPortals.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/preventions/portals/PreventSpecificEntitiesInPortals.java index 7977c4f0b..4a9befc3d 100755 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/preventions/portals/PreventSpecificEntitiesInPortals.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/preventions/portals/PreventSpecificEntitiesInPortals.java @@ -1,8 +1,8 @@ package me.xginko.aef.modules.preventions.portals; import com.cryptomorin.xseries.XEntityType; -import me.xginko.aef.AnarchyExploitFixes; import me.xginko.aef.modules.AEFModule; +import me.xginko.aef.utils.PlatformUtil; import org.bukkit.entity.EntityType; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; @@ -68,7 +68,7 @@ private void onPortalUse(EntityPortalEvent event) { // Does not fire on folia du @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onPortalEnter(EntityPortalEnterEvent event) { // Only portal event that can be listened to on folia - if (!AnarchyExploitFixes.isServerFolia()) return; // Avoid fallback logic on non-folia + if (!PlatformUtil.isFolia()) return; // Avoid fallback logic on non-folia if (forbiddenTypes.contains(event.getEntityType()) && event.getEntityType() != XEntityType.PLAYER.get()) { event.getEntity().getScheduler().execute(plugin, event.getEntity()::remove, null, 1L); diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/preventions/withers/WitherSpawningAtSpawn.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/preventions/withers/WitherSpawningAtSpawn.java index 829a60f84..a0811b950 100755 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/preventions/withers/WitherSpawningAtSpawn.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/preventions/withers/WitherSpawningAtSpawn.java @@ -37,7 +37,7 @@ public WitherSpawningAtSpawn() { ConfigSection section = config.getConfigSection(configPath + ".worlds", defaults); for (String world : section.getKeys(false)) { try { - Integer radius = Integer.valueOf(section.getString(world)); + Integer radius = Integer.parseInt(section.getString(world)); this.worldsAndTheirRadiuses.put(world, radius); } catch (NumberFormatException e) { warn("Radius for world '" + world + "' is not a valid integer."); diff --git a/AnarchyExploitFixesFolia/src/main/resources/config.yml b/AnarchyExploitFixesFolia/src/main/resources/config.yml new file mode 100644 index 000000000..622889ecd --- /dev/null +++ b/AnarchyExploitFixesFolia/src/main/resources/config.yml @@ -0,0 +1,2273 @@ +plugin-version: 2.7.2 +server-version: '1.21-127-4e6a2a1 (MC: 1.21)' + +############## +# Language # +############## +language: + # The default language that will be used if auto-language is set to false + # or no matching language file was found. + default-language: en_us + # If set to true, will display messages based on client language. + auto-language: true + +############# +# General # +############# +general: + # The time in ticks (1 sec = 20 ticks) a checked tps will be cached + # by the plugin. + max-tps-check-interval-in-ticks: 20 + # In case packet modules are causing trouble, you can disable them here. + disable-all-packet-listeners: false + # The Y-level at which the nether ceiling generates the last layer of bedrock + # on your server. + nether-ceiling-y: 127 + # A server restart is required when changing a command's enable status! + commands: + say: + enable: false + # Uses MiniMessage format: https://docs.advntr.dev/minimessage/format.html. + format: 'SERVER: %message%' + help: + # Help command that shows a small command overview for players. + enable: false + toggleconnectionmsgs: + # If you don't use join leave/messages, you can set this to false. + enable: true + +################### +# Miscellaneous # +################### +misc: + join-leave-messages: + # If you want to hide yourself or someone else when logging + # into the game, use these permissions: + # aef.silentJoin, aef.silentLeave + enable: true + # If set to true, players will see join/leave messages by default + # and enter /toggleconnectionmsgs to disable them. + # If set to false will work the other way around. + connection-messages-on-by-default: true + show-in-console: false + first-join-messages: + # Configure message in lang folder. + # You can hide yourself and other players using the permission: + # aef.silentJoin + enable: false + show-in-console: true + kicks: + # Configure mask message in lang folder. + mask-kick-messages: false + prevent-message-kick: + # Cancels the kick for specific kick messages. + enable: false + kick-messages-to-listen-to: + - Kicked for spamming + - Stop spamming! + auto-bed: + # Re-enables SPIGOT-5988, also known as 'auto-bed' + # From Minecraft version 1.16 (≈June 2020) to version 1.17.1(≈October 2021) + # there was a bug (SPIGOT-5988) which did not reset the respawn point of the + # player after death, if his bed was blocked with a shulker. + # After dying a second time, the player will be back at his bed again. + # This bug persisted from the Spigot server to Paper and all its forks until + # October 2021, after which it was fixed by the Spigot development team. + # Attempts by players to reach out to Spigot to allow them to disable the patch + # that fixes the SPIGOT-5988 bug have failed. + # Demonstration of how the patch works: + # https://www.youtube.com/watch?v=3y5SbQXzMss + enable: false + +########## +# Chat # +########## +chat: + command-whitelist: + # This will make it pretty much impossible to find your plugins as + # only the commands you specify will be able to work. + # Allow bypass using permission: aef.bypass.commandwhitelist + enable: false + # Will show logs when a command was denied. + log: false + # Enable only if you have problems with the default commandwhitelist. + # Otherwise not recommended to use. + use-packets: false + # Add all commands you WANT your players to be able to access + # WITHOUT the '/'. Not case sensitive. + whitelisted-commands: + - help + - vote + - kill + - discord + - togglechat + - toggleconnectionmsgs + - toggletells + - togglewhispering + - toggleprivatemsgs + - ignore + - ignorelist + - ignorehard + - toggledeathmsg + - dmt + - worldstats + - stats + - tps + - msg + - whisper + - w + - m + - t + - pm + - tell + - r + - reply + - last + # Add all subcommands you DON'T want your players to be able + # to access. Case sensitive! + blacklisted-subcommands: + - help about + - vote List + - vote Best + - vote Total + - worldstats reload + - stats reload + prevent-scanning-server-plugins: + # Prevents hacked clients running .plugins to find out what plugins + # the server is using. + # Recommended to use in combination with command whitelist. + enable: true + +############ +# Elytra # +############ +elytra: + # NOTE: Set nocheatplus horizontal elytra settings to 500 or higher. + elytra-speed: + # Time in ticks that a chunk has to have been inhabited to count as old chunk. + # Note that the time is incremented once per tick per player within mob spawning + # distance of a chunk. + old-chunk-inhabited-ticks: 200 + # The period in millis players will be checked to determine their speed. + # If you have lagging players with consistent high ping, you can increase this number. + check-period-millis: 500 + # If set to false, will only calculate 2-Dimensional speed without taking height + # changes into consideration. + calculate-3D-speed: false + # Display info in Actionbar while flying. + display-actionbar: true + # Inform flying player if they are in old or new chunks. + display-chunk-info-in-actionbar: true + # Plays XP pickup sound to alert players when theyre going above the limit. + play-sound-when-too-fast: true + sound: ENTITY_EXPERIENCE_ORB_PICKUP + # Recommended to leave false if you dont experience any issues. + teleport-instead-of-canceling-movement: false + Global-Settings: + # Global settings. If nothing else is enabled, this will be used for all environments. + enable: true + # Can be slow with a lot of players. Enable only if needed. + use-bypass-permission: false + deny-elytra-usage: false + speed-old-chunks: 1.81 + speed-new-chunks: 1.81 + enable-bursting: true + burst-speed-old-chunks: 5.0 + burst-speed-old-chunk-TPS: 18.0 + burst-speed-new-chunks: 3.12 + burst-speed-new-chunk-TPS: 19.0 + deny-elytra-on-low-TPS: true + deny-elytra-TPS: 12.0 + also-remove-elytra-on-low-TPS: true + At-Spawn: + # Use separate values for players at spawn. + enable: false + # Radius in blocks around 00 that should count as spawn. + radius: 3000 + # Can be slow with a lot of players. Enable only if needed. + use-bypass-permission: false + deny-elytra-usage: false + speed-old-chunks: 1.0 + speed-new-chunks: 0.8 + deny-elytra-on-low-TPS: true + deny-elytra-TPS: 10.0 + also-remove-elytra-on-low-TPS: true + Nether-Ceiling: + # Use separate values for players above the nether ceiling. + enable: true + # Can be slow with a lot of players. Enable only if needed. + use-bypass-permission: false + deny-elytra-usage: false + speed-old-chunks: 0.5 + speed-new-chunks: 0.5 + enable-bursting: true + burst-speed-old-chunks: 1.0 + burst-speed-old-chunk-TPS: 18.0 + burst-speed-new-chunks: 1.0 + burst-speed-new-chunk-TPS: 18.0 + deny-elytra-on-low-TPS: true + deny-elytra-TPS: 12.0 + also-remove-elytra-on-low-TPS: true + packet-elytra-fly: + # Patches the future/rusherhack/kamiblue 2b2t elytra fly exploit. + patch-packet-elytra-fly: false + # The fly exploit causes the player to constantly toggle gliding. + # If too many glide toggles occur within a timeframe, they are + # most likely using PacketFly. + # Still may trigger false positives when players are jumping and + # sprinting with elytra equipped, so recommended to play around + # with the values. + max-elytra-opens-per-time: 25 + # Time in seconds a elytra open count will be remembered by the plugin. + time-in-seconds: 8 + # Configure message in lang folder. + notify-player-to-disable-packetfly: true + # If enabled, player will be kicked with a message instead of + # getting their elytra dropped. + kick-instead-of-remove-elytra: false + +################## +# Chunk Limits # +################## +chunk-limits: + entity-limits: + dropped-item-limit: + # Limit the amount of dropped items in a chunk to combat lag. + # Be aware this does not prioritize items by value or anything, + # it just deletes whatever happens to get over the limit during + # counting. + enable: false + log-removals: true + max-dropped-items-per-chunk: 200 + # The delay in ticks the plugin will wait after an item in a chunk + # has dropped before the check logic will run. + # This improves performance as there will be no check for each single + # item entity that spawns. + post-item-drop-check-delay-ticks: 60 + # The period in ticks in which all loaded chunks should be regularly + # checked. Keep in mind: A lower number provides more accuracy but is + # also worse for performance. + check-period-in-ticks: 1200 + # Runs item check when a chunk is loaded. + check-on-chunk-load: true + whitelist-specific-item-types: false + # Check the paper api for correct Material enums: + # https://jd.papermc.io/paper/1.20.6/org/bukkit/Material.html + # Make sure your minecraft version is matching as well. + whitelisted-types: + - BLACK_SHULKER_BOX + - BLUE_SHULKER_BOX + - BROWN_SHULKER_BOX + - CYAN_SHULKER_BOX + - GRAY_SHULKER_BOX + - GREEN_SHULKER_BOX + - LIGHT_BLUE_SHULKER_BOX + - LIGHT_GRAY_SHULKER_BOX + - LIME_SHULKER_BOX + - MAGENTA_SHULKER_BOX + - ORANGE_SHULKER_BOX + - PINK_SHULKER_BOX + - PURPLE_SHULKER_BOX + - RED_SHULKER_BOX + - SHULKER_BOX + - WHITE_SHULKER_BOX + - YELLOW_SHULKER_BOX + tile-entity-limit: + # Limit the amount of tile entities in a chunk to prevent lag. + enable: false + log-removals: true + max-tile-entities-per-chunk: 100 + check-period-in-ticks: 20 + villager-limit: + enable: false + max-villagers-per-chunk: 25 + log-removals: false + # Check all chunks every x ticks. + check-period-in-ticks: 600 + # Professions that are in the top of the list are going to be scheduled for + # removal first. + removal-priority: + - NONE + - NITWIT + - SHEPHERD + - FISHERMAN + - BUTCHER + - CARTOGRAPHER + - LEATHERWORKER + - FLETCHER + - MASON + - FARMER + - ARMORER + - TOOLSMITH + - WEAPONSMITH + - CLERIC + - LIBRARIAN + whitelist: + enable: false + professions: + - NONE + - NITWIT + - SHEPHERD + - FISHERMAN + - BUTCHER + - CARTOGRAPHER + - LEATHERWORKER + - FLETCHER + - MASON + - FARMER + - ARMORER + - TOOLSMITH + - WEAPONSMITH + - CLERIC + - LIBRARIAN + non-living-limit: + # Limit the amount of non living entities in a chunk to prevent lag. + # Ignores dropped items. + enable: false + log-removals: true + max-non-living-per-chunk: 100 + check-period-in-ticks: 20 + custom-limit: + # Limit specific entity types per chunk. + enable: false + log-removals: true + # Check all loaded chunks every x ticks. + check-period-in-ticks: 1200 + chunk-age-skip: + enable: true + # How long a chunk has to have been inhabited for it to be checked. + # 1 second = 20 ticks. + min-age-in-ticks: 800 + # When a chunk is loaded, entities inside of it are not necessarily + # loaded as well. Force loading is worse for performance, but there + # might be a scenario where this turns out to be useful. + forceload-entities: false + # Check the paper api for correct EntityType enums: + # https://jd.papermc.io/paper/1.20.6/org/bukkit/entity/EntityType.html + # Make sure your minecraft version is matching as well. + limited-types: + ALLAY: 20 + ARROW: 20 + AXOLOTL: 10 + BAT: 3 + BEE: 15 + BLAZE: 10 + CAT: 10 + CAVE_SPIDER: 10 + CHICKEN: 10 + COD: 6 + COW: 10 + CREEPER: 10 + DOLPHIN: 4 + DONKEY: 10 + DROWNED: 10 + ENDERMAN: 10 + ENDERMITE: 3 + EVOKER: 15 + FIREBALL: 5 + FOX: 10 + FROG: 20 + GLOW_SQUID: 20 + GOAT: 10 + GUARDIAN: 20 + HOGLIN: 10 + HORSE: 10 + HUSK: 10 + IRON_GOLEM: 15 + LLAMA: 10 + MAGMA_CUBE: 10 + MOOSHROOM: 10 + MULE: 10 + OCELOT: 3 + PANDA: 5 + PARROT: 10 + PHANTOM: 10 + PIG: 10 + PIGLIN: 25 + PIGLIN_BRUTE: 10 + PILLAGER: 15 + POLAR_BEAR: 5 + PUFFERFISH: 3 + RABBIT: 5 + RAVAGER: 15 + SALMON: 6 + SHEEP: 10 + SILVERFISH: 3 + SKELETON: 10 + SKELETON_HORSE: 10 + SLIME: 10 + SMALL_FIREBALL: 5 + SNOWBALL: 5 + SPIDER: 10 + SQUID: 20 + STRAY: 10 + STRIDER: 3 + TADPOLE: 20 + TRADER_LLAMA: 10 + TROPICAL_FISH: 6 + TURTLE: 20 + VEX: 15 + VINDICATOR: 15 + WANDERING_TRADER: 10 + WITCH: 15 + WITHER: 16 + WITHER_SKELETON: 10 + WITHER_SKULL: 10 + WOLF: 10 + ZOGLIN: 10 + ZOMBIE: 10 + ZOMBIE_HORSE: 10 + ZOMBIE_VILLAGER: 25 + ZOMBIFIED_PIGLIN: 20 + vehicle-limit: + # Limit the amount of vehicles to prevent some lag machines. + # ex. dispenser that spawns a lot of boats into a single location + # then hitting it, causing all boats to explode in every direction. + enable: false + log-removals: false + max-vehicles-per-chunk: 25 + # 200 ticks = 10 seconds. + check-period-in-ticks: 400 + falling-block-limit: + # Prevent players from placing massive sand chunks, then collapsing + # them to kill the server. + enable: true + log: false + # Removes any falling block if there is more than x blocks actively + # falling in a chunk. + max-falling-gravity-blocks-per-chunk: 60 + # Delay in ticks until the same chunk can be checked again. + # Prevents overchecking, because physics events can be called many + # times in a short time for the same chunk. + chunk-check-delay-in-ticks: 20 + block-limit: + enable: false + # Attempt to prevent ChunkBan / Client FPS Lag + max-blocks-per-chunk: + ACACIA_HANGING_SIGN: 8 + ACACIA_SIGN: 8 + ACACIA_WALL_HANGING_SIGN: 8 + ACACIA_WALL_SIGN: 8 + BAMBOO_HANGING_SIGN: 8 + BAMBOO_SIGN: 8 + BAMBOO_WALL_HANGING_SIGN: 8 + BAMBOO_WALL_SIGN: 8 + BEACON: 32 + BIRCH_HANGING_SIGN: 8 + BIRCH_SIGN: 8 + BIRCH_WALL_HANGING_SIGN: 8 + BIRCH_WALL_SIGN: 8 + BLACK_BANNER: 12 + BLACK_WALL_BANNER: 12 + BLUE_BANNER: 12 + BLUE_WALL_BANNER: 12 + BROWN_BANNER: 12 + BROWN_WALL_BANNER: 12 + CHEST: 500 + CREEPER_HEAD: 16 + CREEPER_WALL_HEAD: 16 + CRIMSON_HANGING_SIGN: 8 + CRIMSON_SIGN: 8 + CRIMSON_WALL_HANGING_SIGN: 8 + CRIMSON_WALL_SIGN: 8 + CYAN_BANNER: 12 + CYAN_WALL_BANNER: 12 + DARK_OAK_HANGING_SIGN: 8 + DARK_OAK_SIGN: 8 + DARK_OAK_WALL_HANGING_SIGN: 8 + DARK_OAK_WALL_SIGN: 8 + DISPENSER: 100 + DRAGON_HEAD: 16 + DRAGON_WALL_HEAD: 16 + ENCHANTING_TABLE: 16 + ENDER_CHEST: 64 + GLOWSTONE: 5000 + GRAY_BANNER: 12 + GRAY_WALL_BANNER: 12 + GREEN_BANNER: 12 + GREEN_WALL_BANNER: 12 + JUNGLE_HANGING_SIGN: 8 + JUNGLE_SIGN: 8 + JUNGLE_WALL_HANGING_SIGN: 8 + JUNGLE_WALL_SIGN: 8 + LIGHT_BLUE_BANNER: 12 + LIGHT_BLUE_WALL_BANNER: 12 + LIGHT_GRAY_BANNER: 12 + LIGHT_GRAY_WALL_BANNER: 12 + LIME_BANNER: 12 + LIME_WALL_BANNER: 12 + MAGENTA_BANNER: 12 + MAGENTA_WALL_BANNER: 12 + MANGROVE_HANGING_SIGN: 8 + MANGROVE_SIGN: 8 + MANGROVE_WALL_HANGING_SIGN: 8 + MANGROVE_WALL_SIGN: 8 + MOVING_PISTON: 32 + OAK_HANGING_SIGN: 8 + OAK_SIGN: 8 + OAK_WALL_HANGING_SIGN: 8 + OAK_WALL_SIGN: 8 + ORANGE_BANNER: 12 + ORANGE_WALL_BANNER: 12 + PIGLIN_HEAD: 16 + PIGLIN_WALL_HEAD: 16 + PINK_BANNER: 12 + PINK_WALL_BANNER: 12 + PISTON: 32 + PISTON_HEAD: 32 + PLAYER_HEAD: 16 + PLAYER_WALL_HEAD: 16 + PURPLE_BANNER: 12 + PURPLE_WALL_BANNER: 12 + RED_BANNER: 12 + RED_WALL_BANNER: 12 + SLIME_BLOCK: 128 + SPRUCE_HANGING_SIGN: 8 + SPRUCE_SIGN: 8 + SPRUCE_WALL_HANGING_SIGN: 8 + SPRUCE_WALL_SIGN: 8 + STICKY_PISTON: 32 + TRAPPED_CHEST: 200 + WARPED_HANGING_SIGN: 8 + WARPED_SIGN: 8 + WARPED_WALL_HANGING_SIGN: 8 + WARPED_WALL_SIGN: 8 + YELLOW_BANNER: 12 + YELLOW_WALL_BANNER: 12 + ZOMBIE_HEAD: 16 + ZOMBIE_WALL_HEAD: 16 + minecart-limit: + # Limit the amount of minecarts to prevent lag caused by collisions. + enable: false + log-removals: false + max-minecarts-per-chunk: 25 + check-period-in-ticks: 400 + exp-bottle-limit: + # Prevent players from crashing the server or other players by + # creating a ton of THROWN_EXP_BOTTLE entities, then loading + # them at once. + # Does not limit the EXP_ORBS, just the bottle entities. + enable: true + log: false + max-exp-bottle-per-chunk: 25 + # 20 ticks = 1 second + check-period-in-ticks: 800 + +##################### +# Lag Preventions # +##################### +lag-preventions: + keep-stash-chunks-loaded: + # Idea by 3b3t admin kumori (Soft1k) + # Improves lag generated by large stash chunks constantly loading and + # unloading by setting them force loaded. This might cause increased ram + # usage, so keep an eye out for that. + enable: false + log: false + # How many container blocks have to be in a chunk for it to be seen + # as a stash chunk to keep force loaded. + container-block-threshold: 50 + # The minimum time in ticks a chunk has to have been inhabited to be checked. + min-chunk-inhabited-time-ticks: 1000 + # The time in minutes a stash chunks will be kept force loaded before + # setting it back to normal. + keep-loaded-minutes: 120 + # Set to false if you want to check more blocks than just tile entities. + # Makes the overall speed of the module faster if set to true. + only-check-tile-entities: true + container-types: + - CHISELED_BOOKSHELF + - DECORATED_POT + - CHEST + - FURNACE + - JUKEBOX + - SHULKER_BOX + - WHITE_SHULKER_BOX + - ORANGE_SHULKER_BOX + - MAGENTA_SHULKER_BOX + - LIGHT_BLUE_SHULKER_BOX + - YELLOW_SHULKER_BOX + - LIME_SHULKER_BOX + - PINK_SHULKER_BOX + - GRAY_SHULKER_BOX + - LIGHT_GRAY_SHULKER_BOX + - CYAN_SHULKER_BOX + - PURPLE_SHULKER_BOX + - BLUE_SHULKER_BOX + - BROWN_SHULKER_BOX + - GREEN_SHULKER_BOX + - RED_SHULKER_BOX + - BLACK_SHULKER_BOX + - HOPPER + - DISPENSER + - DROPPER + - LECTERN + - TRAPPED_CHEST + - CRAFTER + - BREWING_STAND + - BARREL + - SMOKER + - BLAST_FURNACE + # Radiuses around spawn in chunks (not blocks) that should not be checked. + # Worlds not on this list are exempt from all checking. + worlds: + world: 100 + world_the_end: 100 + world_nether: 100 + disable-item-drops-during-large-stash-explosions: + # Explodes containers without dropping items after a certain amount + # of exploded containers per chunk. + enable: false + log: false + # How many container blocks in a chunk can be blown up until items + # no longer drop from them. + min-explosions-before-drops-disable: 6 + # The time in seconds to wait after an explosion for another one to happen. + # If no explosion happens within x seconds after the first one, the count resets to 0. + time-in-seconds: 3 + container-types: + - CHISELED_BOOKSHELF + - DECORATED_POT + - CHEST + - FURNACE + - JUKEBOX + - HOPPER + - DISPENSER + - DROPPER + - LECTERN + - TRAPPED_CHEST + - CRAFTER + - BREWING_STAND + - BARREL + - SMOKER + - BLAST_FURNACE + prevent-lever-spam: + # Rate Limit levers to prevent a lag exploit. + enable: false + show-actionbar: true + kick-player: false + max-lever-usages-per-time: 15 + lever-time-in-ticks: 40 + entity-age-limits: + custom-limits: + # Kill certain entities after a custom amount of ticks lived. + enable: false + log-removals: false + # Check all loaded chunks every x ticks. + check-period-in-ticks: 1200 + # When a chunk is loaded, entities inside of it are not necessarily + # loaded as well. Force loading is worse for performance, but there + # might be a scenario where this turns out to be useful. + forceload-entities: false + # Check the paper api for correct EntityType enums: + # https://jd.papermc.io/paper/1.20/org/bukkit/entity/EntityType.html + # Make sure your minecraft version is matching as well. + limited-types: + ARROW: 120 + FALLING_BLOCK: 160 + SNOWBALL: 100 + SPECTRAL_ARROW: 120 + WITHER_SKULL: 100 + projectile-limit: + # Patches any lag exploit that abuses spawning a ton of projectile entities + # (ex. Snowball exploit).Skips over the following entities: ENDER_PEARL, FISHING_HOOK, WITHER_SKULL + # and ENDER_SIGNAL. You can configure those separately in the custom entity age + # limit section. + enable: false + # (20 ticks = 1 second) Will not touch Ender Pearls + max-alive-time-ticks: 300 + # How frequently we should check all projectiles for their alive time + check-period-seconds: 20 + prevent-flooding-machines: + # Will prevent pistons from pushing waterlogged blocks. + # Stops players from using waterlogged blocks and piston flying + # machines to generate large walls of water that generate lag + # due to the fluid physics. + enable: false + delete-waterlogged-blocks: true + anti-shulker-drops: + # Disables shulkers dropping stored items when blown up. + # This helps fix client- and serverside lag when done often and fast. + enable: false + regional-activity: + block-spread: + # Limits blocks spreading or forming based on world conditions within a + # configurable radius and timeframe to help reduce lag by cancelling burst + # activity hotspots. + # + # Examples: + # + # - Snow forming due to a snow storm. + # - Ice forming in a snowy Biome like Taiga or Tundra. + # - Obsidian / Cobblestone forming due to contact with water. + # - Concrete forming due to mixing of concrete powder and water. + # - Mushrooms spreading. + # - Fire spreading. + enable: false + log: false + # The radius in blocks in which activity will be grouped together and measured. + check-radius-blocks: 1500.0 + # The time in milliseconds all related activity will be blocked if it exceeded + # the configured limit. + pause-time-millis: 6000 + # The time in milliseconds before a region and its data will be expired + # if no activity has been detected. + # For proper functionality, needs to be at least as long as your pause time. + data-keep-time-millis: 10000 + # The TPS at which to cancel the physics entirely. + pause-TPS: 10.0 + # The MSPT at which to cancel the physics entirely. + pause-MSPT: 120.0 + # Maximum number of times a block can form or spread within the configured + # timeframe before activity will be put on cooldown. + block-form-event-limit: 800 + entity-spawn: + # Limits entity spawning activity within a configurable radius and timeframe + # to help reduce lag by cancelling high activity hotspots. + # + # Examples: + # + # - A creature gets spawned naturally, by spawner or other reasons. + # - An entity gets spawned naturally, by spawner or other reasons. + # This does not include tile entities. + enable: false + log: true + # The radius in blocks in which activity will be grouped together and measured. + check-radius-blocks: 1500.0 + # The time in milliseconds all related activity will be blocked if it exceeded + # the configured limit. + pause-time-millis: 18000 + # The time in milliseconds before a region and its data will be expired + # if no activity has been detected. + # For proper functionality, needs to be at least as long as your pause time. + data-keep-time-millis: 20000 + # The TPS at which to cancel the physics entirely. + pause-TPS: 10.0 + # The MSPT at which to cancel the physics entirely. + pause-MSPT: 120.0 + # Maximum number of entity spawns within configured timeframe. + spawn-event-limit: 6000 + entity-targeting: + # Limits entities targeting other entities within a configurable radius + # and timeframe to help reduce lag by cancelling burst activity hotspots. + enable: false + log: true + # The radius in blocks in which activity will be grouped together and measured. + check-radius-blocks: 1500.0 + # The time in milliseconds all related activity will be blocked if it exceeded + # the configured limit. + pause-time-millis: 18000 + # The time in milliseconds before a region and its data will be expired + # if no activity has been detected. + # For proper functionality, needs to be at least as long as your pause time. + data-keep-time-millis: 20000 + # The TPS at which to cancel the physics entirely. + pause-TPS: 14.0 + # The MSPT at which to cancel the physics entirely. + pause-MSPT: 120.0 + # Maximum number of times an entity can target another entity within the + # configured timeframe before the area will be put on cooldown. + entity-target-event-limit: 8000 + distance-limit: + global-limit: + enable: false + # The max distance no target should exceed. + # You want this to be higher than your highest max distance + # for a specific mob. + max-target-distance: 20.0 + custom-limits: + enable: true + entities: + SKELETON: 6.0 + WITHER: 8.0 + WITHER_SKELETON: 8.0 + ZOMBIE: 6.0 + ZOMBIE_VILLAGER: 10.0 + ZOMBIFIED_PIGLIN: 8.0 + entity-pathfinding: + # Limits entities deciding to pathfind to a specific location + # within a configurable radius and timeframe to help reduce lag + # by cancelling burst activity hotspots. + enable: false + log: false + # The radius in blocks in which activity will be grouped together and measured. + check-radius-blocks: 1500.0 + # The time in milliseconds all related activity will be blocked if it exceeded + # the configured limit. + pause-time-millis: 6000 + # The time in milliseconds before a region and its data will be expired + # if no activity has been detected. + # For proper functionality, needs to be at least as long as your pause time. + data-keep-time-millis: 10000 + # The TPS at which to cancel the physics entirely. + pause-TPS: 14.0 + # The MSPT at which to cancel the physics entirely. + pause-MSPT: 120.0 + # Maximum number of times an entity can decide to start moving + # towards a location within the configured timeframe before the + # area will be put on cooldown. + entity-pathfind-event-limit: 4000 + distance-limit: + global-limit: + enable: false + # The max distance no mob pathfinding should exceed. + # You always want this to be higher than your highest max distance + # for a specific mob. + max-target-distance: 20.0 + custom-limits: + enable: true + entities: + SKELETON: 6.0 + WITHER: 8.0 + WITHER_SKELETON: 8.0 + ZOMBIE: 6.0 + ZOMBIE_VILLAGER: 10.0 + ZOMBIFIED_PIGLIN: 8.0 + sculk-bloom: + # Limits sculk blooming within a configurable radius and timeframe + # to help reduce lag by cancelling high activity hotspots. + # + # Examples: + # + # - An entity was killed and dropped experience within an 8-block + # radius of a SculkCatalyst. + # - A plugin used SculkCatalyst.bloom(Block, int) + enable: false + log: false + # The radius in blocks in which activity will be grouped together and measured. + check-radius-blocks: 1500.0 + # The time in milliseconds all related activity will be blocked if it exceeded + # the configured limit. + pause-time-millis: 6000 + # The time in milliseconds before a region and its data will be expired + # if no activity has been detected. + # For proper functionality, needs to be at least as long as your pause time. + data-keep-time-millis: 10000 + # The TPS at which to cancel the physics entirely. + pause-TPS: 10.0 + # The MSPT at which to cancel the physics entirely. + pause-MSPT: 120.0 + # Maximum number of sculk bloom events within the configured timeframe. + sculk-bloom-limit: 300 + liquid-spread: + # Limits liquid spreading within a configurable radius and timeframe + # to help reduce lag by cancelling high activity hotspots. + # + # Examples: + # + # - A lava block spreading by flowing. + # - A water block spreading by flowing. + # - (optional) A dragon egg is teleporting from one position to another. + enable: false + log: true + # The radius in blocks in which activity will be grouped together and measured. + check-radius-blocks: 1500.0 + # The time in milliseconds all related activity will be blocked if it exceeded + # the configured limit. + pause-time-millis: 18000 + # The time in milliseconds before a region and its data will be expired + # if no activity has been detected. + # For proper functionality, needs to be at least as long as your pause time. + data-keep-time-millis: 20000 + # The TPS at which to cancel the physics entirely. + pause-TPS: 12.0 + # The MSPT at which to cancel the physics entirely. + pause-MSPT: 100.0 + # Maximum number of times liquids are allowed to spread within the configured + # timeframe before they will be put on cooldown. + liquid-spread-event-limit: 2400 + ignore-dragon-egg: true + redstone: + # Limits redstone activity within a configurable radius and timeframe + # to help reduce lag by cancelling high activity hotspots. + # + # Examples: + # + # - A redstone current changes. + # - A redstone block gets powered on. + # - A redstone block gets powered off. + enable: false + log: true + # The radius in blocks in which activity will be grouped together and measured. + check-radius-blocks: 1500.0 + # The time in milliseconds all related activity will be blocked if it exceeded + # the configured limit. + pause-time-millis: 18000 + # The time in milliseconds before a region and its data will be expired + # if no activity has been detected. + # For proper functionality, needs to be at least as long as your pause time. + data-keep-time-millis: 20000 + # The TPS at which to cancel the physics entirely. + pause-TPS: 10.0 + # The MSPT at which to cancel the physics entirely. + pause-MSPT: 120.0 + # Maximum number of redstone events within configured timeframe. + redstone-event-limit: 6000 + noteblocks: + # Limits noteblocks being played within a configurable radius and timeframe + # to help reduce lag by cancelling high activity hotspots. + # + # Examples: + # + # - A noteblock is being played through player interaction. + # - A noteblock is being played through a redstone current. + enable: false + log: false + # The radius in blocks in which activity will be grouped together and measured. + check-radius-blocks: 1500.0 + # The time in milliseconds all related activity will be blocked if it exceeded + # the configured limit. + pause-time-millis: 6000 + # The time in milliseconds before a region and its data will be expired + # if no activity has been detected. + # For proper functionality, needs to be at least as long as your pause time. + data-keep-time-millis: 10000 + # The TPS at which to cancel the physics entirely. + pause-TPS: 10.0 + # The MSPT at which to cancel the physics entirely. + pause-MSPT: 120.0 + # Maximum number of times a noteblock can be played within the configured + # timeframe before they will be put on cooldown. + noteblock-play-limit: 2800 + explosions: + # Limits explosions within a configurable radius and timeframe + # to help reduce lag by cancelling high activity hotspots. + # + # Examples: + # + # - A block exploding. + # - An entity exploding. + # - An entity making the decision to explode. + enable: false + log: false + # The radius in blocks in which activity will be grouped together and measured. + check-radius-blocks: 1500.0 + # The time in milliseconds all related activity will be blocked if it exceeded + # the configured limit. + pause-time-millis: 6000 + # The time in milliseconds before a region and its data will be expired + # if no activity has been detected. + # For proper functionality, needs to be at least as long as your pause time. + data-keep-time-millis: 10000 + # The TPS at which to cancel the physics entirely. + pause-TPS: 10.0 + # The MSPT at which to cancel the physics entirely. + pause-MSPT: 120.0 + # Maximum number of explode events within the configured timeframe + # before the region will be put on cooldown. + explode-event-limit: 500 + pistons: + # Limits piston movement within a configurable radius and timeframe + # to help reduce lag by cancelling high activity hotspots. + # + # Examples: + # + # - A piston extends. + # - A piston retracts. + enable: false + log: false + # The radius in blocks in which activity will be grouped together and measured. + check-radius-blocks: 1500.0 + # The time in milliseconds all related activity will be blocked if it exceeded + # the configured limit. + pause-time-millis: 6000 + # The time in milliseconds before a region and its data will be expired + # if no activity has been detected. + # For proper functionality, needs to be at least as long as your pause time. + data-keep-time-millis: 10000 + # The TPS at which to cancel the physics entirely. + pause-TPS: 10.0 + # The MSPT at which to cancel the physics entirely. + pause-MSPT: 120.0 + # Maximum number of piston extend and/or retracts within the + # configured timeframe. + piston-movement-limit: 1000 + block-physics: + # Limits block physics within a configurable radius and timeframe + # to help reduce lag by cancelling burst activity hotspots. + # + # Note: + # + # The event used for this check (BlockPhysicsEvent) is a high frequency event, + # it may be called thousands of times per a second on a busy server. + # Where possible the event may also only be called for the "root" block of + # physics updates in order to limit event spam. + # Physics updates that cause other blocks to change their state may not result + # in an event for each of those blocks (usually adjacent). + enable: false + log: true + # The radius in blocks in which activity will be grouped together and measured. + check-radius-blocks: 1500.0 + # The time in milliseconds all related activity will be blocked if it exceeded + # the configured limit. + pause-time-millis: 18000 + # The time in milliseconds before a region and its data will be expired + # if no activity has been detected. + # For proper functionality, needs to be at least as long as your pause time. + data-keep-time-millis: 20000 + # The TPS at which to cancel the physics entirely. + pause-TPS: 10.0 + # The MSPT at which to cancel the physics entirely. + pause-MSPT: 120.0 + # Maximum number of times a physics check can be performed within the configured + # timeframe before they will be put on cooldown. + block-physics-event-limit: 256000 + sculk-sensor: + # Limits sculk activity within a configurable radius and timeframe + # to help reduce lag by cancelling high activity hotspots. + # + # Examples: + # + # - A redstone current changes for a sculk sensor. + # - A physics check is being performed for a sculk sensor. + enable: false + log: true + # The radius in blocks in which activity will be grouped together and measured. + check-radius-blocks: 1500.0 + # The time in milliseconds all related activity will be blocked if it exceeded + # the configured limit. + pause-time-millis: 18000 + # The time in milliseconds before a region and its data will be expired + # if no activity has been detected. + # For proper functionality, needs to be at least as long as your pause time. + data-keep-time-millis: 20000 + # The TPS at which to cancel the physics entirely. + pause-TPS: 10.0 + # The MSPT at which to cancel the physics entirely. + pause-MSPT: 120.0 + # Maximum number of sculk events within configured timeframe. + sculk-event-limit: 800 + sculk-blocks: + - SCULK_SENSOR + - SCULK_SHRIEKER + - CALIBRATED_SCULK_SENSOR + +############# +# Patches # +############# +patches: + anti-book-ban: + enable: false + # If set to false, will use UTF-8. + # Charset to use to encode the result of NBTCompound#toString into + # a sequence of bytes. The length of that sequence is then used to + # get the approximate Byte-size of an ItemStack. + # Use the /aef bytesize command to get a better understanding. + use-UTF-16: false + max-book-size: 56000 + # Kicks players when they try to create a book bigger than the limit. + kick-on-too-large-book-edit: true + max-author-chars: 32 + max-title-chars: 32 + max-pages: 100 + max-item-size: 56000 + max-inventory-size: 2050000 + # How long in ticks a dropped item's size should be cached after + # checking. + dropped-items-size-cache-ticks: 120 + # How long in ticks a player's inventory size should be cached after + # checking. + player-inventory-size-cache-ticks: 20 + pearl-phase: + # Attempts to patch a pearl phasing exploit by cancelling the teleport + # if the pearl is thrown at or near a specific block. + # At the time of the creation of this module, this is an issue with NoCheatPlus. + enable: false + # How many blocks around the teleport location should be searched + # for potential glitch blocks if the teleport location isn't one itself. + search-radius: 2 + maximum-distance-to-cancel-teleport: 3.0 + glitchy-materials: + - ACACIA_PRESSURE_PLATE + - ACACIA_SLAB + - ACACIA_TRAPDOOR + - ANDESITE_SLAB + - BAMBOO_MOSAIC_SLAB + - BAMBOO_PRESSURE_PLATE + - BAMBOO_SLAB + - BAMBOO_TRAPDOOR + - BIRCH_PRESSURE_PLATE + - BIRCH_SLAB + - BIRCH_TRAPDOOR + - BLACKSTONE_SLAB + - BRICK_SLAB + - CALIBRATED_SCULK_SENSOR + - CHERRY_PRESSURE_PLATE + - CHERRY_SLAB + - CHERRY_TRAPDOOR + - COBBLED_DEEPSLATE_SLAB + - COBBLESTONE_SLAB + - COBWEB + - COPPER_TRAPDOOR + - CRIMSON_PRESSURE_PLATE + - CRIMSON_SLAB + - CRIMSON_TRAPDOOR + - CUT_COPPER_SLAB + - CUT_RED_SANDSTONE_SLAB + - CUT_SANDSTONE_SLAB + - DARK_OAK_PRESSURE_PLATE + - DARK_OAK_SLAB + - DARK_OAK_TRAPDOOR + - DARK_PRISMARINE_SLAB + - DEEPSLATE_BRICK_SLAB + - DEEPSLATE_TILE_SLAB + - DIORITE_SLAB + - END_STONE_BRICK_SLAB + - EXPOSED_COPPER_TRAPDOOR + - EXPOSED_CUT_COPPER_SLAB + - GRANITE_SLAB + - HEAVY_WEIGHTED_PRESSURE_PLATE + - IRON_TRAPDOOR + - JUNGLE_PRESSURE_PLATE + - JUNGLE_SLAB + - JUNGLE_TRAPDOOR + - LIGHT_WEIGHTED_PRESSURE_PLATE + - MANGROVE_PRESSURE_PLATE + - MANGROVE_SLAB + - MANGROVE_TRAPDOOR + - MOSSY_COBBLESTONE_SLAB + - MOSSY_STONE_BRICK_SLAB + - MUD_BRICK_SLAB + - NETHER_BRICK_SLAB + - OAK_PRESSURE_PLATE + - OAK_SLAB + - OAK_TRAPDOOR + - OXIDIZED_COPPER_TRAPDOOR + - OXIDIZED_CUT_COPPER_SLAB + - PETRIFIED_OAK_SLAB + - POLISHED_ANDESITE_SLAB + - POLISHED_BLACKSTONE_BRICK_SLAB + - POLISHED_BLACKSTONE_PRESSURE_PLATE + - POLISHED_BLACKSTONE_SLAB + - POLISHED_DEEPSLATE_SLAB + - POLISHED_DIORITE_SLAB + - POLISHED_GRANITE_SLAB + - POLISHED_TUFF_SLAB + - POWDER_SNOW + - PRISMARINE_BRICK_SLAB + - PRISMARINE_SLAB + - PURPUR_SLAB + - QUARTZ_SLAB + - RED_NETHER_BRICK_SLAB + - RED_SANDSTONE_SLAB + - SANDSTONE_SLAB + - SCULK_SENSOR + - SCULK_SHRIEKER + - SMOOTH_QUARTZ_SLAB + - SMOOTH_RED_SANDSTONE_SLAB + - SMOOTH_SANDSTONE_SLAB + - SMOOTH_STONE_SLAB + - SPRUCE_PRESSURE_PLATE + - SPRUCE_SLAB + - SPRUCE_TRAPDOOR + - STONE_BRICK_SLAB + - STONE_PRESSURE_PLATE + - STONE_SLAB + - TUFF_BRICK_SLAB + - TUFF_SLAB + - WARPED_PRESSURE_PLATE + - WARPED_SLAB + - WARPED_TRAPDOOR + - WAXED_COPPER_TRAPDOOR + - WAXED_CUT_COPPER_SLAB + - WAXED_EXPOSED_COPPER_TRAPDOOR + - WAXED_EXPOSED_CUT_COPPER_SLAB + - WAXED_OXIDIZED_COPPER_TRAPDOOR + - WAXED_OXIDIZED_CUT_COPPER_SLAB + - WAXED_WEATHERED_COPPER_TRAPDOOR + - WAXED_WEATHERED_CUT_COPPER_SLAB + - WEATHERED_COPPER_TRAPDOOR + - WEATHERED_CUT_COPPER_SLAB + prevent-command-sign: + # Patch signs that have run_command NBT tags attached, allowing the + # to run a command with operator permissions on click. + # Recommended to enable if you had a rogue admin or backdoor incident. + enable: true + prevent-fast-world-teleport-crash: + # Prevents crash methods that involve very fast teleporting between + # different worlds in a short time. + enable: false + # Time in milliseconds until an entity can teleport to another + # world again. + teleport-delay-millis: 1000 + log: false + prevent-teleport-coordinate-exploit: + # Patches coordinate exploit for teleportation commands such as /tpa, + # /home AS WELL as respawn exploits. + # This is done by vanishing the player for x ticks before teleporting. + enable: false + min-distance-to-vanish-player: 100 + teleport-vanish-time-in-ticks: 10 + # Removes entities or players if they are invalid, dead or not located + # within a ticking chunk. Not sure if this works. + experimental-godmode-patch: false + tab-complete-crash-patch: + # Patches two lag exploits and an instant server shutdown exploit that + # works by sending a malicious TabComplete packet that triggers a + # StackOverflowError inside the TagParser class. + enable: true + log: false + kick-player: false + remove-beehive-coordinates: + # Patches an exploit that allows players to obtain another player's + # coordinates by trading them for Beehives or Beenests. + # If the traded item contains any bees, the stored bee's NBT data can + # then be read from the item. + # This data includes, but is not limited to: + # - XYZ coordinates of where the bee has its hive + # - XYZ of the bee's last coordinates before entering it's hive + # - XYZ coordinates of where the bee last visited a flower + # - XYZ coordinates of where the bee was first spawned into existence + # - UID of the world the bee was first spawned into existence + enable: true + # The NBT tags to filter from the item. These are the Keys that hold + # the position data. You may add more tags you want removed here. + tags: + - Pos + - HivePos + - FlowerPos + - Paper.Origin + - Paper.OriginWorld + - WorldUUIDMost + - WorldUUIDLeast + window-click-crash-patch: + # Patches a variety of different lag and crash methods that work + # by sending invalid Window Click packets, causing the server to + # dump error logs until it runs out of memory. + enable: true + log: false + kick-player: false + map-cursor-lag-patch: + # Patches the famous stacked map cursor lag that causes both + # client and server crashes. + enable: false + inventory-lag: + # Checks if a player is requesting unusual amounts of traffic from the server + # using ItemStacks. + # If a player exceeds the limit, they will be put on a cooldown, during which + # they will be very limited in terms of ItemStack or Inventory interactions. + enable: false + # For debug purposes. Don't leave enabled for too long as it is very spammy. + log: false + # Whether to immediately close any open inventory of the player on limit exceed + # Note: Closing has to be scheduled so it will take a bit if the server is heavily + # lagging. + close-open-inventory: true + # The time in millis in which to check if the player exceeded the limit. + # Needs to be at least as long as your lockout duration millis. + byte-data-keep-time-millis: 30000 + rate-limit: + # The limit in bytes the server has sent the server in the form of ItemStacks, + # before the player will be put on a rate-limit. + # Should always be lower than your lockout bytesize limit. + bytesize-limit: 8000000 + # The time in millis in which a player is allowed to open x amounts of windows + # but not more. + timeframe-millis: 2500 + # The amount of windows that can be opened during the timeframe-millis. + max-window-opens-per-timeframe: 2 + lockout: + # The upper limit in bytes a player is allowed to request from the server + # within the configured timeframe before he will be put on cooldown. + # During the cooldown, he will not be able to open any inventory screens + # or interact with items. + bytesize-limit: 24000000 + # The time in milliseconds the player will have to wait before + # being able to open an inventory again after he exceeded the limit. + duration-millis: 15000 + check-packets: + - SET_SLOT + - WINDOW_ITEMS + sequence-crash-patch: + # Patches a variety of lag/crash exploits that involves sending packets + # with invalid sequences. + enable: true + log: false + kick-player: false + sign-lag: + # Patches a lag exploit that involves sending specific oversized + # sign edit packets. + enable: true + # How many ticks a player needs to wait to be able to send + # another sign update packet (renaming or writing). + packet-delay-in-ticks: 10 + # Vanilla limit is 384 characters per line, which can be too much. + line-character-limit: 80 + # General char limit for all lines combined. + total-char-limit: 384 + log: false + kick-player: false + beehive-crash-patch: + # Patches a server crash exploit exclusive to Purpur servers. + # This exploit works due to PurpurClient having a feature that + # lets clients request stored data of a clicked beehive from + # the server. The server does not check how far the clicked + # beehive is away from the client enabling a malicious sender + # to load chunks very fast at far away locations by telling + # the server it clicked a beehive there. + enable: false + channel: purpur:beehive_c2s + max-distance: 24 + log: false + kick-player: false + +############## +# Illegals # +############## +illegals: + remove-placed-blocks: + on-chunkload: + # Remove illegally placed blocks on chunkload. + enable: false + # Enter PLAYER_HEAD here if you want to remove placed playerheads. + blocks-to-remove: + - PLAYER_HEAD + - CHAIN_COMMAND_BLOCK + - COMMAND_BLOCK + - COMMAND_BLOCK_MINECART + - REPEATING_COMMAND_BLOCK + - BEDROCK + - BARRIER + exempted-worlds: + - exampleworld1 + - exampleworld2 + pause-on-low-TPS: false + pause-TPS: 14.0 + periodically: + enable: false + # Enter PLAYER_HEAD here if you want to remove placed playerheads. + blocks-to-remove: + - PLAYER_HEAD + - CHAIN_COMMAND_BLOCK + - COMMAND_BLOCK + - COMMAND_BLOCK_MINECART + - REPEATING_COMMAND_BLOCK + - BEDROCK + - BARRIER + exempted-worlds: + - exampleworld1 + - exampleworld2 + check-period-in-seconds: 10 + pause-on-low-TPS: false + pause-TPS: 14.0 + remove-unnatural-spawners-on-chunkload: + enable: false + pause-on-low-TPS: false + pause-TPS: 14.0 + # You can add or remove as much world names here as you want. + natural-spawner-types-per-world: + world: + - SKELETON + - ZOMBIE + - SILVERFISH + - SPIDER + - CAVE_SPIDER + world_the_end: + - SKELETON + - SPIDER + world_nether: + - BLAZE + - MAGMA_CUBE + nbt: + ban-custom-tags: + # Bypass permission: aef.bypass.illegal.nbt.custom + # Deletes items that have one or more of the configured tags. + enable: false + # Available options: + # STRICT - Deletes or reverts illegals even on chest open + # ACTION_ON_USE - Deletes or reverts illegals when interacted with + # PREVENT_USE_ONLY - Only prevents usage of illegals if possible + handling: PREVENT_USE_ONLY + # Enable this if you have problems with the plugin removing items from chest guis. + gui-plugins-supported: false + # Prevents Hopper32k mechanic of placing a shulker containing illegals on top + # of a hopper, then using the illegal out of the hopper's inventory. + # WARNING: Hooks into InventoryMoveItemEvent, which can become VERY resource + # intense as the event fires in high frequencies as soon as players start using + # farms or item sorters. Recommended to leave off if not necessary. + prevent-hopper32k-mechanic: false + check-on-chunkload: + # WARNING: CHECKING ON CHUNKLOAD IS NOT RECOMMENDED AS IT IS VERY RESOURCE INTENSE. + # BE VERY SURE YOU ACTUALLY NEED THIS. + # Iterates over all blocks in a chunk when it is loaded and checks any inventories + # for illegals. If a container with illegals is found, the container will be REMOVED. + enable: false + # If set to true, immediately replaces the container with air. Otherwise, will try + # to handle items separately. + remove-container: false + # The time in milliseconds to wait before performing another check, + # if a check was positive. Helps with lag resulting from repeatedly + # checking illegals. + check-rate-limit-millis: 3000 + check-stored-items: false + # The exact, case sensitive value of the nbt tag. + tags: + - dmg + item-whitelist-enabled: false + use-as-blacklist-instead: false + whitelisted-items: + - GOLDEN_APPLE + impossibly-stored-items: + # Bypass permission: aef.bypass.illegal.nbt.storeditems + # Prevents usage of or deletes storage items that have been pre-filled + # with items using NBT tags. These can only be created by players with + # creative access. + # Most commonly dispensers, droppers and chests containing kit shulkers + # are created but there are more combinations possible. + enable: false + # Available options: + # STRICT - Deletes or reverts illegals even on chest open + # ACTION_ON_USE - Deletes or reverts illegals when interacted with + # PREVENT_USE_ONLY - Only prevents usage of illegals if possible + handling: PREVENT_USE_ONLY + # Enable this if you have problems with the plugin removing items from chest guis. + gui-plugins-supported: false + # Prevents Hopper32k mechanic of placing a shulker containing illegals on top + # of a hopper, then using the illegal out of the hopper's inventory. + # WARNING: Hooks into InventoryMoveItemEvent, which can become VERY resource + # intense as the event fires in high frequencies as soon as players start using + # farms or item sorters. Recommended to leave off if not necessary. + prevent-hopper32k-mechanic: false + check-on-chunkload: + # WARNING: CHECKING ON CHUNKLOAD IS NOT RECOMMENDED AS IT IS VERY RESOURCE INTENSE. + # BE VERY SURE YOU ACTUALLY NEED THIS. + # Iterates over all blocks in a chunk when it is loaded and checks any inventories + # for illegals. If a container with illegals is found, the container will be REMOVED. + enable: false + # If set to true, immediately replaces the container with air. Otherwise, will try + # to handle items separately. + remove-container: false + # The time in milliseconds to wait before performing another check, + # if a check was positive. Helps with lag resulting from repeatedly + # checking illegals. + check-rate-limit-millis: 3000 + # The exact name of the nbt tag that signals items are stored inside. + tag: BlockEntityTag + check-stored-items: false + storage-types: + - BARREL + - BLAST_FURNACE + - BREWING_STAND + - CHEST + - CHISELED_BOOKSHELF + - CRAFTER + - DECORATED_POT + - DISPENSER + - DROPPER + - FURNACE + - HOPPER + - JUKEBOX + - LECTERN + - SMOKER + - TRAPPED_CHEST + command-items: + # Bypass permission: aef.bypass.illegal.nbt.commanditem + # Deletes items with commands in their NBT data that run as operator. + # These can only be created by players with creative access. + # Most common items are books, since it allows storing multiple commands. + enable: false + # Available options: + # STRICT - Deletes or reverts illegals even on chest open + # ACTION_ON_USE - Deletes or reverts illegals when interacted with + # PREVENT_USE_ONLY - Only prevents usage of illegals if possible + handling: PREVENT_USE_ONLY + # Enable this if you have problems with the plugin removing items from chest guis. + gui-plugins-supported: false + # Prevents Hopper32k mechanic of placing a shulker containing illegals on top + # of a hopper, then using the illegal out of the hopper's inventory. + # WARNING: Hooks into InventoryMoveItemEvent, which can become VERY resource + # intense as the event fires in high frequencies as soon as players start using + # farms or item sorters. Recommended to leave off if not necessary. + prevent-hopper32k-mechanic: false + check-on-chunkload: + # WARNING: CHECKING ON CHUNKLOAD IS NOT RECOMMENDED AS IT IS VERY RESOURCE INTENSE. + # BE VERY SURE YOU ACTUALLY NEED THIS. + # Iterates over all blocks in a chunk when it is loaded and checks any inventories + # for illegals. If a container with illegals is found, the container will be REMOVED. + enable: false + # If set to true, immediately replaces the container with air. Otherwise, will try + # to handle items separately. + remove-container: false + # The time in milliseconds to wait before performing another check, + # if a check was positive. Helps with lag resulting from repeatedly + # checking illegals. + check-rate-limit-millis: 3000 + check-stored-items: false + enchantments: + inapplicable-enchants: + # Bypass permission: aef.bypass.illegal.enchants.inapplicable + # Reverts or prevents usage of ItemStacks with Enchantments that + # cannot be applied to that ItemStack in vanilla survival minecraft. + # Examples: A helmet with sharpness or a block of stone with fortune. + enable: false + # Available options: + # STRICT - Deletes or reverts illegals even on chest open + # ACTION_ON_USE - Deletes or reverts illegals when interacted with + # PREVENT_USE_ONLY - Only prevents usage of illegals if possible + handling: PREVENT_USE_ONLY + # Enable this if you have problems with the plugin removing items from chest guis. + gui-plugins-supported: false + # Prevents Hopper32k mechanic of placing a shulker containing illegals on top + # of a hopper, then using the illegal out of the hopper's inventory. + # WARNING: Hooks into InventoryMoveItemEvent, which can become VERY resource + # intense as the event fires in high frequencies as soon as players start using + # farms or item sorters. Recommended to leave off if not necessary. + prevent-hopper32k-mechanic: false + check-on-chunkload: + # WARNING: CHECKING ON CHUNKLOAD IS NOT RECOMMENDED AS IT IS VERY RESOURCE INTENSE. + # BE VERY SURE YOU ACTUALLY NEED THIS. + # Iterates over all blocks in a chunk when it is loaded and checks any inventories + # for illegals. If a container with illegals is found, the container will be REMOVED. + enable: false + # If set to true, immediately replaces the container with air. Otherwise, will try + # to handle items separately. + remove-container: false + # The time in milliseconds to wait before performing another check, + # if a check was positive. Helps with lag resulting from repeatedly + # checking illegals. + check-rate-limit-millis: 3000 + check-stored-items: false + item-whitelist-enabled: true + use-as-blacklist-instead: false + whitelisted-items: + - GOLDEN_APPLE + higher-enchants: + # Bypass permission: aef.bypass.illegal.enchants.higher + # Reverts or prevents usage of ItemStacks with Enchantments higher + # than the natural, in vanilla survival obtainable level (aka 32ks / 255s). + enable: false + # Available options: + # STRICT - Deletes or reverts illegals even on chest open + # ACTION_ON_USE - Deletes or reverts illegals when interacted with + # PREVENT_USE_ONLY - Only prevents usage of illegals if possible + handling: PREVENT_USE_ONLY + # Enable this if you have problems with the plugin removing items from chest guis. + gui-plugins-supported: false + # Prevents Hopper32k mechanic of placing a shulker containing illegals on top + # of a hopper, then using the illegal out of the hopper's inventory. + # WARNING: Hooks into InventoryMoveItemEvent, which can become VERY resource + # intense as the event fires in high frequencies as soon as players start using + # farms or item sorters. Recommended to leave off if not necessary. + prevent-hopper32k-mechanic: false + check-on-chunkload: + # WARNING: CHECKING ON CHUNKLOAD IS NOT RECOMMENDED AS IT IS VERY RESOURCE INTENSE. + # BE VERY SURE YOU ACTUALLY NEED THIS. + # Iterates over all blocks in a chunk when it is loaded and checks any inventories + # for illegals. If a container with illegals is found, the container will be REMOVED. + enable: false + # If set to true, immediately replaces the container with air. Otherwise, will try + # to handle items separately. + remove-container: false + # The time in milliseconds to wait before performing another check, + # if a check was positive. Helps with lag resulting from repeatedly + # checking illegals. + check-rate-limit-millis: 3000 + check-stored-items: false + only-specific-enchants: false + specific-enchants: + - DIG_SPEED + item-whitelist-enabled: true + use-as-blacklist-instead: false + whitelisted-items: + - GOLDEN_APPLE + incompatible-enchants: + # Bypass permission: aef.bypass.illegal.enchants.incompatible + # Reverts or prevents usage of ItemStacks with Enchantments that + # cannot coexist in vanilla survival minecraft. + # Examples: A bow with mending and infinity or armor with every + # protection enchantment. + enable: false + # Available options: + # STRICT - Deletes or reverts illegals even on chest open + # ACTION_ON_USE - Deletes or reverts illegals when interacted with + # PREVENT_USE_ONLY - Only prevents usage of illegals if possible + handling: PREVENT_USE_ONLY + # Enable this if you have problems with the plugin removing items from chest guis. + gui-plugins-supported: false + # Prevents Hopper32k mechanic of placing a shulker containing illegals on top + # of a hopper, then using the illegal out of the hopper's inventory. + # WARNING: Hooks into InventoryMoveItemEvent, which can become VERY resource + # intense as the event fires in high frequencies as soon as players start using + # farms or item sorters. Recommended to leave off if not necessary. + prevent-hopper32k-mechanic: false + check-on-chunkload: + # WARNING: CHECKING ON CHUNKLOAD IS NOT RECOMMENDED AS IT IS VERY RESOURCE INTENSE. + # BE VERY SURE YOU ACTUALLY NEED THIS. + # Iterates over all blocks in a chunk when it is loaded and checks any inventories + # for illegals. If a container with illegals is found, the container will be REMOVED. + enable: false + # If set to true, immediately replaces the container with air. Otherwise, will try + # to handle items separately. + remove-container: false + # The time in milliseconds to wait before performing another check, + # if a check was positive. Helps with lag resulting from repeatedly + # checking illegals. + check-rate-limit-millis: 3000 + check-stored-items: false + item-whitelist-enabled: true + use-as-blacklist-instead: false + whitelisted-items: + - BOW + attribute-modifiers: + # Bypass permission: aef.bypass.illegal.attributes + # Prevents usage of or reverts items with any attribute modifiers + # or item flags. + enable: false + # Available options: + # STRICT - Deletes or reverts illegals even on chest open + # ACTION_ON_USE - Deletes or reverts illegals when interacted with + # PREVENT_USE_ONLY - Only prevents usage of illegals if possible + handling: PREVENT_USE_ONLY + # Enable this if you have problems with the plugin removing items from chest guis. + gui-plugins-supported: false + # Prevents Hopper32k mechanic of placing a shulker containing illegals on top + # of a hopper, then using the illegal out of the hopper's inventory. + # WARNING: Hooks into InventoryMoveItemEvent, which can become VERY resource + # intense as the event fires in high frequencies as soon as players start using + # farms or item sorters. Recommended to leave off if not necessary. + prevent-hopper32k-mechanic: false + check-on-chunkload: + # WARNING: CHECKING ON CHUNKLOAD IS NOT RECOMMENDED AS IT IS VERY RESOURCE INTENSE. + # BE VERY SURE YOU ACTUALLY NEED THIS. + # Iterates over all blocks in a chunk when it is loaded and checks any inventories + # for illegals. If a container with illegals is found, the container will be REMOVED. + enable: false + # If set to true, immediately replaces the container with air. Otherwise, will try + # to handle items separately. + remove-container: false + # The time in milliseconds to wait before performing another check, + # if a check was positive. Helps with lag resulting from repeatedly + # checking illegals. + check-rate-limit-millis: 3000 + item-whitelist-enabled: false + use-as-blacklist-instead: true + check-stored-items: false + whitelisted-items: + - TOTEM_OF_UNDYING + ban-player-heads: + # Bypass permission: aef.bypass.illegal.playerhead + # Deletes or prevents usage of player heads. + enable: false + # Available options: + # STRICT - Deletes or reverts illegals even on chest open + # ACTION_ON_USE - Deletes or reverts illegals when interacted with + # PREVENT_USE_ONLY - Only prevents usage of illegals if possible + handling: PREVENT_USE_ONLY + # Enable this if you have problems with the plugin removing items from chest guis. + gui-plugins-supported: false + # Prevents Hopper32k mechanic of placing a shulker containing illegals on top + # of a hopper, then using the illegal out of the hopper's inventory. + # WARNING: Hooks into InventoryMoveItemEvent, which can become VERY resource + # intense as the event fires in high frequencies as soon as players start using + # farms or item sorters. Recommended to leave off if not necessary. + prevent-hopper32k-mechanic: false + check-on-chunkload: + # WARNING: CHECKING ON CHUNKLOAD IS NOT RECOMMENDED AS IT IS VERY RESOURCE INTENSE. + # BE VERY SURE YOU ACTUALLY NEED THIS. + # Iterates over all blocks in a chunk when it is loaded and checks any inventories + # for illegals. If a container with illegals is found, the container will be REMOVED. + enable: false + # If set to true, immediately replaces the container with air. Otherwise, will try + # to handle items separately. + remove-container: false + # The time in milliseconds to wait before performing another check, + # if a check was positive. Helps with lag resulting from repeatedly + # checking illegals. + check-rate-limit-millis: 3000 + # Will delete shulker/bundle if they contain any player heads. + check-stored-items: false + ban-specific-materials: + # Bypass permission: aef.bypass.illegal.bannedmaterial + # Prevents usage of or deletes items with material that you do not want + # your players to be able to use. + # Useful if your players have blocks that shouldn't be obtainable in survival. + enable: false + # Available options: + # STRICT - Deletes or reverts illegals even on chest open + # ACTION_ON_USE - Deletes or reverts illegals when interacted with + # PREVENT_USE_ONLY - Only prevents usage of illegals if possible + handling: PREVENT_USE_ONLY + # Enable this if you have problems with the plugin removing items from chest guis. + gui-plugins-supported: false + # Prevents Hopper32k mechanic of placing a shulker containing illegals on top + # of a hopper, then using the illegal out of the hopper's inventory. + # WARNING: Hooks into InventoryMoveItemEvent, which can become VERY resource + # intense as the event fires in high frequencies as soon as players start using + # farms or item sorters. Recommended to leave off if not necessary. + prevent-hopper32k-mechanic: false + check-on-chunkload: + # WARNING: CHECKING ON CHUNKLOAD IS NOT RECOMMENDED AS IT IS VERY RESOURCE INTENSE. + # BE VERY SURE YOU ACTUALLY NEED THIS. + # Iterates over all blocks in a chunk when it is loaded and checks any inventories + # for illegals. If a container with illegals is found, the container will be REMOVED. + enable: false + # If set to true, immediately replaces the container with air. Otherwise, will try + # to handle items separately. + remove-container: false + # The time in milliseconds to wait before performing another check, + # if a check was positive. Helps with lag resulting from repeatedly + # checking illegals. + check-rate-limit-millis: 3000 + check-stored-items: false + banned-materials: + - CHAIN_COMMAND_BLOCK + - COMMAND_BLOCK + - COMMAND_BLOCK_MINECART + - REPEATING_COMMAND_BLOCK + - BEDROCK + - BARRIER + - STRUCTURE_BLOCK + - STRUCTURE_VOID + - END_PORTAL_FRAME + - END_PORTAL + - NETHER_PORTAL + - LIGHT + potions: + # Bypass permission: aef.bypass.illegal.potions + # Prevents usage of or reverts items with any attribute modifiers + # or item flags. + enable: false + # Available options: + # STRICT - Deletes or reverts illegals even on chest open + # ACTION_ON_USE - Deletes or reverts illegals when interacted with + # PREVENT_USE_ONLY - Only prevents usage of illegals if possible + handling: PREVENT_USE_ONLY + # Enable this if you have problems with the plugin removing items from chest guis. + gui-plugins-supported: false + # Prevents Hopper32k mechanic of placing a shulker containing illegals on top + # of a hopper, then using the illegal out of the hopper's inventory. + # WARNING: Hooks into InventoryMoveItemEvent, which can become VERY resource + # intense as the event fires in high frequencies as soon as players start using + # farms or item sorters. Recommended to leave off if not necessary. + prevent-hopper32k-mechanic: false + check-on-chunkload: + # WARNING: CHECKING ON CHUNKLOAD IS NOT RECOMMENDED AS IT IS VERY RESOURCE INTENSE. + # BE VERY SURE YOU ACTUALLY NEED THIS. + # Iterates over all blocks in a chunk when it is loaded and checks any inventories + # for illegals. If a container with illegals is found, the container will be REMOVED. + enable: false + # If set to true, immediately replaces the container with air. Otherwise, will try + # to handle items separately. + remove-container: false + # The time in milliseconds to wait before performing another check, + # if a check was positive. Helps with lag resulting from repeatedly + # checking illegals. + check-rate-limit-millis: 3000 + check-stored-items: false + ban-spawn-eggs: + # Bypass permission: aef.bypass.illegal.spawnegg + # Deletes or prevents usage of spawn eggs. + enable: false + # Available options: + # STRICT - Deletes or reverts illegals even on chest open + # ACTION_ON_USE - Deletes or reverts illegals when interacted with + # PREVENT_USE_ONLY - Only prevents usage of illegals if possible + handling: PREVENT_USE_ONLY + # Enable this if you have problems with the plugin removing items from chest guis. + gui-plugins-supported: false + # Prevents Hopper32k mechanic of placing a shulker containing illegals on top + # of a hopper, then using the illegal out of the hopper's inventory. + # WARNING: Hooks into InventoryMoveItemEvent, which can become VERY resource + # intense as the event fires in high frequencies as soon as players start using + # farms or item sorters. Recommended to leave off if not necessary. + prevent-hopper32k-mechanic: false + check-on-chunkload: + # WARNING: CHECKING ON CHUNKLOAD IS NOT RECOMMENDED AS IT IS VERY RESOURCE INTENSE. + # BE VERY SURE YOU ACTUALLY NEED THIS. + # Iterates over all blocks in a chunk when it is loaded and checks any inventories + # for illegals. If a container with illegals is found, the container will be REMOVED. + enable: false + # If set to true, immediately replaces the container with air. Otherwise, will try + # to handle items separately. + remove-container: false + # The time in milliseconds to wait before performing another check, + # if a check was positive. Helps with lag resulting from repeatedly + # checking illegals. + check-rate-limit-millis: 3000 + # If remove-spawn-eggs is set to true Will delete shulker/bundle + # should they contain any spawneggs. + check-stored-items: false + whitelisted-items: + - VILLAGER_SPAWN_EGG + banned-item-names: + # Bypass permission: aef.bypass.illegal.bannedname + # Resets an item's name (or deletes the item) if it matches one of + # the configured regexes. + # Regexes can be complex. Use a tool like https://regex101.com/ or + # ChatGPT for good results. + enable: false + # Available options: + # STRICT - Deletes or reverts illegals even on chest open + # ACTION_ON_USE - Deletes or reverts illegals when interacted with + # PREVENT_USE_ONLY - Only prevents usage of illegals if possible + handling: PREVENT_USE_ONLY + # Enable this if you have problems with the plugin removing items from chest guis. + gui-plugins-supported: false + # Prevents Hopper32k mechanic of placing a shulker containing illegals on top + # of a hopper, then using the illegal out of the hopper's inventory. + # WARNING: Hooks into InventoryMoveItemEvent, which can become VERY resource + # intense as the event fires in high frequencies as soon as players start using + # farms or item sorters. Recommended to leave off if not necessary. + prevent-hopper32k-mechanic: false + check-on-chunkload: + # WARNING: CHECKING ON CHUNKLOAD IS NOT RECOMMENDED AS IT IS VERY RESOURCE INTENSE. + # BE VERY SURE YOU ACTUALLY NEED THIS. + # Iterates over all blocks in a chunk when it is loaded and checks any inventories + # for illegals. If a container with illegals is found, the container will be REMOVED. + enable: false + # If set to true, immediately replaces the container with air. Otherwise, will try + # to handle items separately. + remove-container: false + # The time in milliseconds to wait before performing another check, + # if a check was positive. Helps with lag resulting from repeatedly + # checking illegals. + check-rate-limit-millis: 3000 + # Will delete the item instead of resetting the name. + delete-item: false + regex: + - (?i)illegalstring + whitelisted-items: + - DIRT + illegally-stacked-items: + # Bypass permission: aef.bypass.illegal.overstacked + # Prevents usage of or reverts items with a higher or lower + # stack size than their vanilla limit. + enable: false + # Available options: + # STRICT - Deletes or reverts illegals even on chest open + # ACTION_ON_USE - Deletes or reverts illegals when interacted with + # PREVENT_USE_ONLY - Only prevents usage of illegals if possible + handling: PREVENT_USE_ONLY + # Enable this if you have problems with the plugin removing items from chest guis. + gui-plugins-supported: false + # Prevents Hopper32k mechanic of placing a shulker containing illegals on top + # of a hopper, then using the illegal out of the hopper's inventory. + # WARNING: Hooks into InventoryMoveItemEvent, which can become VERY resource + # intense as the event fires in high frequencies as soon as players start using + # farms or item sorters. Recommended to leave off if not necessary. + prevent-hopper32k-mechanic: false + check-on-chunkload: + # WARNING: CHECKING ON CHUNKLOAD IS NOT RECOMMENDED AS IT IS VERY RESOURCE INTENSE. + # BE VERY SURE YOU ACTUALLY NEED THIS. + # Iterates over all blocks in a chunk when it is loaded and checks any inventories + # for illegals. If a container with illegals is found, the container will be REMOVED. + enable: false + # If set to true, immediately replaces the container with air. Otherwise, will try + # to handle items separately. + remove-container: false + # The time in milliseconds to wait before performing another check, + # if a check was positive. Helps with lag resulting from repeatedly + # checking illegals. + check-rate-limit-millis: 3000 + item-whitelist-enabled: false + use-as-blacklist-instead: true + check-stored-items: false + whitelisted-items: + - TOTEM_OF_UNDYING + revert-unbreakables: + # Bypass permission: aef.bypass.illegal.unbreakable + # Deletes and prevents usage of unbreakable items. + # This can be anything from items with illegal damage attributes to + # Metadata/NBT tags. + # Note: Due to the limitations of the API, we can only fully prevent + # usage of these items by deleting them. + enable: false + # Available options: + # STRICT - Deletes or reverts illegals even on chest open + # ACTION_ON_USE - Deletes or reverts illegals when interacted with + # PREVENT_USE_ONLY - Only prevents usage of illegals if possible + handling: PREVENT_USE_ONLY + # Enable this if you have problems with the plugin removing items from chest guis. + gui-plugins-supported: false + # Prevents Hopper32k mechanic of placing a shulker containing illegals on top + # of a hopper, then using the illegal out of the hopper's inventory. + # WARNING: Hooks into InventoryMoveItemEvent, which can become VERY resource + # intense as the event fires in high frequencies as soon as players start using + # farms or item sorters. Recommended to leave off if not necessary. + prevent-hopper32k-mechanic: false + check-on-chunkload: + # WARNING: CHECKING ON CHUNKLOAD IS NOT RECOMMENDED AS IT IS VERY RESOURCE INTENSE. + # BE VERY SURE YOU ACTUALLY NEED THIS. + # Iterates over all blocks in a chunk when it is loaded and checks any inventories + # for illegals. If a container with illegals is found, the container will be REMOVED. + enable: false + # If set to true, immediately replaces the container with air. Otherwise, will try + # to handle items separately. + remove-container: false + # The time in milliseconds to wait before performing another check, + # if a check was positive. Helps with lag resulting from repeatedly + # checking illegals. + check-rate-limit-millis: 3000 + item-whitelist-enabled: false + use-as-blacklist-instead: false + # Will delete shulkers and bundles if they contain unbreakables. + check-stored-items: false + whitelisted-items: + - DIAMOND_CHESTPLATE + +###################### +# Dupe Preventions # +###################### +dupe-preventions: + # Prevent any possible dupes involving chested entities by making + # it impossible to put a chest on them. + # Only do this if you have reason to believe a dupe like that exists + # on your server. + prevent-chests-on-living-entities: false + # Closes open inventories of all entities that are in a chunk + # that will be unloaded. + close-entity-inventories-on-chunk-unload: false + # Prevents entities that can carry chests from using portals to + # block some common dupe tactics. + # CAUTION: Will remove chests and their contents from a chested + # entity if it touches a portal on Folia! + prevent-chested-living-entities-in-portals: false + # Closes open inventories of entities that disappeared when the + # player riding it disconnects. + close-entity-inventories-on-player-disconnect: false + +################# +# Preventions # +################# +preventions: + withers: + disable-wither-spawning-at-spawn: + # Disables spawning withers near a configurable radius around + # spawn. Helps if players are generating endless amounts of withers + # to lag the server. + enable: false + inform-players: true + worlds: + world: 5000 + world_the_end: 5000 + world_nether: 5000 + remove-flying-wither-skulls: + # Removes wither skulls when the chunk gets unloaded. + # Use if you have a ton of them at spawn and they are causing lag. + on-chunk-unload: false + # Removes wither skulls when the chunk gets loaded. + # Use if you have a ton of them at spawn and they are causing lag. + on-chunk-load: true + periodically-remove-all-flying-skulls: + # Enable if a lot of wither skulls at spawn are causing lag. + enable: false + check-period-in-ticks: 80 + # Prevents wither skulls from being shot. + disable-withers-from-shooting-skulls: false + rate-limit-wither-skulls: + # This can help combat lag caused by a ton of wither skulls + # spawning but weakens withers. + enable: false + # Cooldown until another skull can be shot at a player. + player-target-cooldown-in-ticks: 20 + # Cooldown until another skull can be shot at anything + # else other than a player. + other-target-cooldown-in-ticks: 40 + # Cooldown when wither has no target. + no-target-cooldown-in-ticks: 100 + portals: + prevent-specific-types: + # Configure entities here that you suspect might be used in a dupe + # with portals. + # CAUTION: Will kill the entity on folia due to broken portal event. + # There is sadly no other efficient way. + enable: true + # Defaults prevent common lag methods. + entities: + - DROPPED_ITEM + - FIREWORK + - PRIMED_TNT + - THROWN_EXP_BOTTLE + - EXPERIENCE_ORB + - ARMOR_STAND + # Only enable if you must. Does not affect players. + # CAUTION: Will kill the entity on folia due to broken portal event. + prevent-all-entities-in-portals: false + prevent-destroying-end-portals: + enable: true + show-logs: true + end: + bedrock-protection-radius-blocks: 8 + # Add block locations that should be protected as well. + # Format: ::: + # If you don't want to use this, just configure an empty list: + # pillar-blocks: [] + pillar-blocks: + - world_the_end:143:140:-50 + - world_the_end:112:90:-90 + prevent-portal-traps: + # Teleports a player back to the original location if they have been + # standing in a portal for too long. + enable: false + wait-time-until-tp-back-in-seconds: 10 + # Prevents a lag exploit. Might disable some chunk loader designs. + prevent-projectiles-in-portals: false + permanent-block-breaking: + by-placing-piston-on-retract: + enable: true + whitelisted-worlds: + - example_world_name + by-exploding-pistons: + enable: true + whitelisted-worlds: + - example_world_name + # If enabled, will only protect portals and end gateways + only-for-portals-and-gateways: false + by-growing-structures: + # Prevents removal of permanent blocks by growing structures + # like mushrooms into them. + enable: true + prevent-opped-players: + # Useful if you suspect a backdoor has happened. + enable: false + log: false + whitelisted-players: + - Notch + prevent-nether-roof: + # Prevent players from going above the nether roof. + enable: true + safely-teleport-players: true + anti-bed-trap: + # Resets a players bed respawn they die too many times within + # a certain timeframe. + enable: false + # Amount of times player can die until he is determined as bed-trapped. + max-deaths-per-time: 7 + # "Time until death counter will be reset again. + time-in-seconds: 5 + log: false + prevent-non-survival-players: + # Checks if player is in survival and if not, puts him back into survival. + # Useful if you had a backdoor incident. + enable: false + log: false + whitelisted-players: + - Notch + +############ +# Combat # +############ +combat: + silent-swap-delay: + enable: false + # The delay in millis a player cant swap hotbar items after placing + # a block, clicking a block (for example to place a crystal) or + # damaging an entity. (50 ms = 1 tick) + min-swap-delay-millis: 40 + prevent-bow-bomb: + enable: false + # Fully pulled bow is ~9-10. 15 is default just to be safe. + max-bow-squared-velocity: 15 + portal-god-mode-patch: + # Prevents an exploit that allows players to stand in nether portals and not + # take damage indefinitely by just never sending a TeleportConfirm packet to + # the server. + # A similar method is used for the chorus tp exploit, which is not covered + # by this module. + enable: false + # If the player stays inside the nether portal for this time without teleporting, + # the portal will be broken, making the player inside vulnerable again. + # Nether portal teleports normally happen within ~3s after enter, so 5s (100ticks) + # should be a safe value. + break-portal-delay-ticks: 100 + crystal-aura: + piston-aura-delay: + # Rate-limits pistons that extend into crystals. + enable: false + piston-extend-delay-in-ticks: 40 + regular-delay: + enable: false + # Can help with desync but recommended to leave off unless you have issues. + update-inventory-on-cancel: false + global: + enable: false + # 1 tick = 50 ms + place-delay-millis: 0 + break-delay-millis: 200 + main-hand: + enable: false + # 1 tick = 50 ms + place-delay-millis: 0 + break-delay-millis: 200 + off-hand: + enable: false + # 1 tick = 50 ms + place-delay-millis: 0 + break-delay-millis: 200 + prevent-burrow: + enable: false + # 1.0 = Half a heart of damage every time you move. + damage-when-moving: 1.0 + teleport-above-block: true + # Prevent burrow even if there is a block above the block they + # are burrowing in. + # Please note this may allow creating an "elevator", players will + # keep teleporting up until they hit air. + prevent-if-block-above-burrow: false + break-anvil-instead-of-teleport: true + # Needs to be enabled to prevent a bug where players are teleported + # above a slab when the slab is underwater. + allow-slabs-in-burrow: true + ignored-materials: + - BLACK_SHULKER_BOX + - BLUE_SHULKER_BOX + - BROWN_SHULKER_BOX + - CYAN_SHULKER_BOX + - GRAY_SHULKER_BOX + - GREEN_SHULKER_BOX + - LIGHT_BLUE_SHULKER_BOX + - LIGHT_GRAY_SHULKER_BOX + - LIME_SHULKER_BOX + - MAGENTA_SHULKER_BOX + - ORANGE_SHULKER_BOX + - PINK_SHULKER_BOX + - PURPLE_SHULKER_BOX + - RED_SHULKER_BOX + - SHULKER_BOX + - WHITE_SHULKER_BOX + - YELLOW_SHULKER_BOX + - AIR + - DIRT + - DIRT_PATH + - SAND + - GRAVEL + multi-task-patch: + enable: false + piston-push: + # Disables pistons from extending if it would push certain configured entities. + # This can be used to prevent players from pushing other players out of burrows, by + # configuring PLAYER, or to disable piston-crystal by adding ENDER_CRYSTAL to the list. + enable: false + piston-push-blocked-entities: + - PLAYER + anchor-aura-delay: + enable: false + # Can help with desync but recommended to leave off unless you have issues. + update-inventory-on-cancel: false + global: + enable: false + # 1 tick = 50 ms + place-delay-millis: 400 + break-delay-millis: 0 + main-hand: + enable: false + # 1 tick = 50 ms + place-delay-millis: 400 + break-delay-millis: 0 + off-hand: + enable: false + # 1 tick = 50 ms + place-delay-millis: 400 + break-delay-millis: 0 + bed-aura-delay: + enable: false + # Can help with desync but recommended to leave off unless you have issues. + update-inventory-on-cancel: false + global: + enable: false + # 1 tick = 50 ms + place-delay-millis: 250 + break-delay-millis: 0 + main-hand: + enable: false + # 1 tick = 50 ms + place-delay-millis: 250 + break-delay-millis: 0 + off-hand: + enable: false + # 1 tick = 50 ms + place-delay-millis: 250 + break-delay-millis: 0 + +############# +# Bedrock # +############# +bedrock: + fill-in-bedrock: + overworld-floor: + periodically-check-and-fill: + # Only checks loaded chunks. + enable: false + check-period-in-seconds: 10 + # Uses the exact name of the world's folder in your server directory. + exempted-worlds: + - exampleworld + - exampleworld2 + # Pauses the task during low tps to avoid lag. + pause-on-low-tps: true + # The TPS at which bedrock filling will pause to avoid adding to the lag. + pause-tps: 16.0 + fill-on-chunkload: + enable: false + # Recommended to leave off. Only useful if world generation is broken. + also-check-new-chunks: false + # Uses the exact name of the world's folder in your server directory. + exempted-worlds: + - exampleworld + - exampleworld2 + pause-on-low-tps: true + # The TPS at which bedrock filling will pause to avoid adding to the lag. + pause-tps: 16.0 + nether-ceiling: + periodically-check-and-fill: + # Only checks loaded chunks. + enable: false + check-period-in-seconds: 10 + # Uses the exact name of the world's folder in your server directory. + exempted-worlds: + - exampleworld + - exampleworld2 + pause-on-low-tps: true + # The TPS at which bedrock filling will pause to avoid adding to the lag. + pause-tps: 16.0 + fill-on-chunkload: + enable: false + # Recommended to leave off. Only useful if world generation is broken. + also-check-new-chunks: false + # Uses the exact name of the world's folder in your server directory. + exempted-worlds: + - exampleworld + - exampleworld2 + pause-on-low-tps: true + # The TPS at which bedrock filling will pause to avoid adding to the lag. + pause-tps: 16.0 + nether-floor: + periodically-check-and-fill: + # Only checks loaded chunks. + enable: false + check-period-in-seconds: 10 + # Uses the exact name of the world's folder in your server directory. + exempted-worlds: + - exampleworld + - exampleworld2 + pause-on-low-tps: true + # The TPS at which bedrock filling will pause to avoid adding to the lag. + pause-tps: 16.0 + fill-on-chunkload: + enable: false + # Recommended to leave off. Only useful if world generation is broken. + also-check-new-chunks: false + # Uses the exact name of the world's folder in your server directory. + exempted-worlds: + - exampleworld + - exampleworld2 + pause-on-low-tps: true + # The TPS at which bedrock filling will pause to avoid adding to the lag. + pause-tps: 16.0 + prevent-going-below-bedrock-floor: + # Prevents players from going below the bedrock floor. + enable: true + # Whether to make player leave their vehicle. + leave-vehicle: true + # Whether to close the player's elytra if they were flying. + stop-elytra: true + # Teleport player on top of that bedrock + teleport: true + # 1.0 = Half a heart of damage every time you move. Set 0 to disable + damage-when-moving: 8.0 + # Whether the bedrock hole should be filled or not. + fill-bedrock-hole: true + exempted-worlds: + - world_the_end + - skyblock_world + filler-material: BEDROCK diff --git a/AnarchyExploitFixesLegacy/build.gradle.kts b/AnarchyExploitFixesLegacy/build.gradle.kts index 363ea4cf3..66dd01ba9 100755 --- a/AnarchyExploitFixesLegacy/build.gradle.kts +++ b/AnarchyExploitFixesLegacy/build.gradle.kts @@ -1,6 +1,3 @@ -import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar -import net.raphimc.javadowngrader.gradle.task.DowngradeJarTask - plugins { id("me.xginko.aef.wrapper") alias(libs.plugins.downgradeJava) @@ -21,7 +18,7 @@ configure { tasks { shadowJar { - archiveFileName = "${rootProject.name}-${project.name}-${project.version}-java17.${archiveExtension.get()}" + archiveFileName = "${rootProject.name}-${project.name}-${project.version}.${archiveExtension.get()}" exclude( "com/cryptomorin/xseries/XBiome*", "com/cryptomorin/xseries/XPotion*", @@ -34,7 +31,6 @@ tasks { relocate("com.zaxxer", "me.xginko.aef.libs.zaxxer") relocate("org.apache.commons.math3", "me.xginko.aef.libs.fastmath") relocate("com.github.benmanes.caffeine", "me.xginko.aef.libs.caffeine") - relocate("io.papermc.lib", "me.xginko.aef.libs.paperlib") relocate("de.tr7zw.changeme.nbtapi", "me.xginko.aef.libs.nbtapi") relocate("org.bstats", "me.xginko.aef.libs.bstats") relocate("com.cryptomorin.xseries", "me.xginko.aef.libs.xseries") @@ -44,21 +40,14 @@ tasks { duplicatesStrategy = DuplicatesStrategy.EXCLUDE dependsOn(shadowJar.get()) from(zipTree(shadowJar.get().archiveFile)) - finalizedBy("java8Jar") - } - val java8Jar = register("java8Jar") { - input.set(shadowJar.get().archiveFile.get().asFile) - dependsOn(jar) - finalizedBy("fixJava8FileName") + finalizedBy("shadeDowngradedApi") } - register("fixJava8FileName") { - val outputFolder = projectDir.resolve("build/libs") - val inputName = "${rootProject.name}-${project.name}-${project.version}-java17-downgraded.jar" - val outputName = "${rootProject.name}-${project.name}-${project.version}.jar" - from(outputFolder) - include(inputName) - destinationDir = outputFolder - rename(inputName, outputName) - dependsOn(java8Jar) + + shadeDowngradedApi { + archiveFileName = shadowJar.get().archiveFileName + destinationDirectory = projectDir.resolve("build/libs") + + downgradeTo = JavaVersion.VERSION_1_8 + shadePath = { _ -> "me/xginko/shadow/jvmdowngrader" } } } diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/AnarchyExploitFixes.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/AnarchyExploitFixes.java index 0db1c755e..7253574de 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/AnarchyExploitFixes.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/AnarchyExploitFixes.java @@ -2,7 +2,6 @@ import com.github.retrooper.packetevents.PacketEvents; import io.github.retrooper.packetevents.factory.spigot.SpigotPacketEventsBuilder; -import io.papermc.lib.PaperLib; import me.xginko.aef.commands.AEFCommand; import me.xginko.aef.config.Config; import me.xginko.aef.config.Datastore; @@ -10,6 +9,7 @@ import me.xginko.aef.enums.AEFPermission; import me.xginko.aef.modules.AEFModule; import me.xginko.aef.utils.CachingPermTool; +import me.xginko.aef.utils.PlatformUtil; import me.xginko.aef.utils.tickdata.TickReporter; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.core.config.Configurator; @@ -53,6 +53,7 @@ public final class AnarchyExploitFixes extends JavaPlugin { @Override public void onLoad() { + PlatformUtil.load(); prefixedLogger = LoggerFactory.getLogger(getLogger().getName()); unPrefixedLogger = LoggerFactory.getLogger(""); // Disable logging for some shaded libraries as those can get very verbose @@ -95,6 +96,7 @@ public void onEnable() { instance = this; cachingPermTool = CachingPermTool.enable(this); + metrics = new Metrics(this, 8700); Stream.of(" ", " ", @@ -107,18 +109,17 @@ public void onEnable() { " " ).forEach(prefixedLogger::info); - if (!PaperLib.isPaper()) { + if (!PlatformUtil.isPaper()) { prefixedLogger.error("This plugin depends on Paper's API, which is not present on your server."); - PaperLib.suggestPaper(this, java.util.logging.Level.SEVERE); getServer().getPluginManager().disablePlugin(this); return; } - prefixedLogger.info("Detected Version 1.{}.{}", PaperLib.getMinecraftVersion(), PaperLib.getMinecraftPatchVersion()); + prefixedLogger.info("Detected Version 1.{}.{}", PlatformUtil.getMinecraftVersion(), PlatformUtil.getMinecraftPatchVersion()); - if (PaperLib.getMinecraftVersion() < 12) { + if (PlatformUtil.getMinecraftVersion() < 12) { prefixedLogger.warn("This version is unsupported. Expect issues."); - } else if (PaperLib.getMinecraftVersion() > 19) { + } else if (PlatformUtil.getMinecraftVersion() > 19) { prefixedLogger.warn("Legacy is intended for Paper server versions 1.12 - 1.19.4."); prefixedLogger.warn("Its highly recommended to use the Folia jar for your server."); } @@ -154,12 +155,13 @@ public void onEnable() { @Override public void onDisable() { if (isPacketEventsInstalled) { - AEFModule.disableAll(); + AEFModule.ENABLED_MODULES.forEach(AEFModule::disable); + AEFModule.ENABLED_MODULES.clear(); PacketEvents.getAPI().terminate(); } - if (metrics != null) { - metrics.shutdown(); - metrics = null; + if (languageCacheMap != null) { + languageCacheMap.clear(); + languageCacheMap = null; } if (cachingPermTool != null) { cachingPermTool.disable(); @@ -173,37 +175,48 @@ public void onDisable() { datastore.disable(); datastore = null; } + if (metrics != null) { + metrics.shutdown(); + metrics = null; + } + unPrefixedLogger = null; + prefixedLogger = null; instance = null; config = null; - languageCacheMap = null; - prefixedLogger = null; - unPrefixedLogger = null; } public static AnarchyExploitFixes getInstance() { return instance; } + public static Config config() { return config; } + public static Datastore getDatastore() { return datastore; } + public static TickReporter getTickReporter() { return tickReporter; } + public static Logger prefixedLogger() { return prefixedLogger; } + public static Logger unprefixedLogger() { return unPrefixedLogger; } + public static LanguageCache getLang(Locale locale) { return getLang(locale.toString().toLowerCase()); } + public static LanguageCache getLang(CommandSender commandSender) { return commandSender instanceof Player ? getLang(((Player) commandSender).getLocale()) : getLang(config.default_lang); } + public static LanguageCache getLang(String lang) { if (!config.auto_lang) return languageCacheMap.get(config.default_lang.toString().toLowerCase()); return languageCacheMap.getOrDefault(lang.replace("-", "_"), languageCacheMap.get(config.default_lang.toString().toLowerCase())); @@ -229,8 +242,6 @@ private void reloadConfiguration() { if (tickReporter != null) tickReporter.disable(); tickReporter = TickReporter.create(this, config.tps_cache_duration); AEFModule.reloadModules(); - if (metrics != null) metrics.shutdown(); - metrics = new Metrics(this, 8700); config.saveConfig(); } catch (Throwable t) { prefixedLogger.error("Failed while loading config!", t); diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/commands/aef/AEFCmd.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/commands/aef/AEFCmd.java index 994defccb..8651e26ca 100644 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/commands/aef/AEFCmd.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/commands/aef/AEFCmd.java @@ -89,6 +89,8 @@ public void enable() { return subCommand.tabComplete(sender, alias, args); } } + + return tabCompletes.stream().filter(cmd -> cmd.startsWith(args[0])).collect(Collectors.toList()); } return Collections.emptyList(); diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/commands/aef/subcommands/DisableSubCmd.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/commands/aef/subcommands/DisableSubCmd.java index 079f130e9..1d3e1cf5a 100644 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/commands/aef/subcommands/DisableSubCmd.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/commands/aef/subcommands/DisableSubCmd.java @@ -37,7 +37,8 @@ public boolean execute(@NotNull CommandSender sender, @NotNull String commandLab sender.sendMessage(ChatColor.WHITE + " Disabling plugin."); AnarchyExploitFixes plugin = AnarchyExploitFixes.getInstance(); plugin.getServer().getScheduler().runTask(plugin, () -> { - AEFModule.disableAll(); + AEFModule.ENABLED_MODULES.forEach(AEFModule::disable); + AEFModule.ENABLED_MODULES.clear(); sender.sendMessage(ChatColor.AQUA + " All enabled plugin features have been disabled."); sender.sendMessage(ChatColor.AQUA + " Use /aef reload to enable the plugin again."); sender.sendMessage(""); diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/config/Config.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/config/Config.java index d076aaf2a..2fcf27443 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/config/Config.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/config/Config.java @@ -3,13 +3,16 @@ import com.cryptomorin.xseries.XSound; import io.github.thatsmusic99.configurationmaster.api.ConfigFile; import io.github.thatsmusic99.configurationmaster.api.ConfigSection; -import io.papermc.lib.PaperLib; import me.xginko.aef.AnarchyExploitFixes; +import me.xginko.aef.utils.PlatformUtil; +import me.xginko.aef.utils.WorldUtil; import org.bukkit.ChatColor; import org.bukkit.Sound; import java.io.File; import java.time.Duration; +import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; @@ -17,12 +20,13 @@ public class Config { private final ConfigFile config; + public final Map worldMinHeights; public final Locale default_lang; public final String cmd_say_format; public final Sound elytra_too_fast_sound; public final Duration tps_cache_duration; - public final long elytra_speed_calc_period; - public final int nether_ceiling_max_y, nether_floor_min_y, overworld_floor_min_y, elytra_spawn_radius; + public final long elytra_speed_calc_period_millis, elytra_old_chunk_limit; + public final int nether_ceiling_max_y, elytra_spawn_radius; public final boolean auto_lang, packets_disabled, connectionMsgsAreOnByDefault, cmd_say_enabled, cmd_help_enabled, cmd_toggleConMsgs_enabled, elytra_enable_at_spawn, elytra_enable_global, elytra_enable_netherceiling, @@ -30,9 +34,11 @@ public class Config { elytra_teleport_back, elytra_calculate_3D; public Config() throws Exception { + AnarchyExploitFixes plugin = AnarchyExploitFixes.getInstance(); // Load config.yml with ConfigMaster - this.config = ConfigFile.loadConfig(new File(AnarchyExploitFixes.getInstance().getDataFolder(), "config.yml")); - config.set("config-version", 1.1); + this.config = ConfigFile.loadConfig(new File(plugin.getDataFolder(), "config.yml")); + config.set("plugin-version", plugin.getDescription().getVersion()); + config.set("server-version", plugin.getServer().getVersion()); // Pre-structure to force order structureConfig(); @@ -51,15 +57,33 @@ public Config() throws Exception { "by the plugin.")) * 50L); this.packets_disabled = getBoolean("general.disable-all-packet-listeners", false, "In case packet modules are causing trouble, you can disable them here."); + this.nether_ceiling_max_y = getInt("general.nether-ceiling-y", 127, "The Y-level at which the nether ceiling generates the last layer\n" + "of bedrock on your server."); - this.nether_floor_min_y = getInt("general.nether-floor-y", 0, - "The Y-level at which the nether floor generates the last layer\n" + - "of bedrock on your server."); - this.overworld_floor_min_y = getInt("general.overworld-floor-y", PaperLib.getMinecraftVersion() > 17 ? -64 : 0, - "The Y-level at which the overworld floor generates the last layer\n" + - "of bedrock on your server."); + + if (WorldUtil.GET_MIN_WORLD_HEIGHT_AVAILABLE) { + this.worldMinHeights = Collections.emptyMap(); + } else { + this.worldMinHeights = new HashMap<>(6); + Map defaults = new HashMap<>(); + defaults.put("world", PlatformUtil.getMinecraftVersion() > 17 ? -64 : 0); + defaults.put("world_nether", 0); + defaults.put("world_the_end", 0); + ConfigSection worldMinHeights = getConfigSection("general.world-min-heights", defaults, + "If you see this config option, AEF is unable to get the minimum height\n" + + "of your worlds from the API.\n" + + "Please enter them here manually for each world you're currently using.\n" + + "Use the exact same name as your world folder."); + for (String worldName : worldMinHeights.getKeys(false)) { + try { + worldMinHeights.put(worldName, Integer.parseInt(worldMinHeights.getString(worldName))); + } catch (NumberFormatException e) { + AnarchyExploitFixes.prefixedLogger().warn("Could not parse min height for world '{}'", worldName); + } + } + } + this.cmd_say_enabled = getBoolean("general.commands.say.enable", false); this.cmd_say_format = ChatColor.translateAlternateColorCodes('&', getString("general.commands.say.format", "&7Server: &6%message%")); this.cmd_help_enabled = getBoolean("general.commands.help.enable", false, @@ -70,8 +94,13 @@ public Config() throws Exception { "A server restart is required when changing a command's enable status!"); // Elytra Speed - this.elytra_speed_calc_period = getInt("elytra.elytra-speed.check-period-ticks", 10, - "The period in ticks players will be checked to determine their speed."); + this.elytra_old_chunk_limit = getLong("elytra.elytra-speed.old-chunk-inhabited-ticks", 200L, + "Time in ticks that a chunk has to have been inhabited to count as old chunk.\n" + + "Note that the time is incremented once per tick per player within mob spawning\n" + + "distance of a chunk."); + this.elytra_speed_calc_period_millis = getInt("elytra.elytra-speed.check-period-millis", 500, + "The period in millis players will be checked to determine their speed.\n" + + "If you have lagging players with consistent high ping, you can increase this number."); this.elytra_calculate_3D = getBoolean("elytra.elytra-speed.calculate-3D-speed", false, "If set to false, will only calculate 2-Dimensional speed\n" + "without taking height changes into consideration."); @@ -177,6 +206,16 @@ public int getInt(String path, int def) { return config.getInteger(path, def); } + public long getLong(String path, long def, String comment) { + config.addDefault(path, def, comment); + return config.getLong(path, def); + } + + public long getLong(String path, long def) { + config.addDefault(path, def); + return config.getLong(path, def); + } + public List getList(String path, List def, String comment) { config.addDefault(path, def, comment); return config.getStringList(path); diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/AEFModule.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/AEFModule.java index 47e8854e2..092826bb8 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/AEFModule.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/AEFModule.java @@ -3,9 +3,8 @@ import me.xginko.aef.AnarchyExploitFixes; import me.xginko.aef.config.Config; import me.xginko.aef.modules.packets.PacketModule; +import me.xginko.aef.utils.models.ConditionalEnableable; import me.xginko.aef.utils.models.Disableable; -import me.xginko.aef.utils.models.Enableable; -import org.bukkit.event.HandlerList; import org.reflections.Reflections; import org.reflections.scanners.Scanners; @@ -13,7 +12,7 @@ import java.util.HashSet; import java.util.Set; -public abstract class AEFModule implements Enableable { +public abstract class AEFModule implements ConditionalEnableable, Disableable { private static final Reflections MODULES_PACKAGE = new Reflections(AEFModule.class.getPackage().getName()); public static final Set ENABLED_MODULES = new HashSet<>(); @@ -23,8 +22,6 @@ public abstract class AEFModule implements Enableable { public final String configPath; private final String logFormat; - public abstract boolean shouldEnable(); - public AEFModule(String configPath) { this.plugin = AnarchyExploitFixes.getInstance(); this.config = AnarchyExploitFixes.config(); @@ -38,20 +35,9 @@ public AEFModule(String configPath) { } } - public static void disableAll() { - AnarchyExploitFixes plugin = AnarchyExploitFixes.getInstance(); - plugin.getServer().getScheduler().cancelTasks(plugin); - HandlerList.unregisterAll(plugin); - for (AEFModule module : ENABLED_MODULES) { - if (module instanceof Disableable) { - ((Disableable) module).disable(); - } - } - ENABLED_MODULES.clear(); - } - public static void reloadModules() { - disableAll(); + ENABLED_MODULES.forEach(AEFModule::disable); + ENABLED_MODULES.clear(); for (Class clazz : MODULES_PACKAGE.get(Scanners.SubTypes.of(AEFModule.class).asClass())) { if (clazz.isInterface() || Modifier.isAbstract(clazz.getModifiers())) continue; diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/bedrock/FillNetherCeilingOnChunkload.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/bedrock/FillNetherCeilingOnChunkload.java index aa337854c..1056a7f80 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/bedrock/FillNetherCeilingOnChunkload.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/bedrock/FillNetherCeilingOnChunkload.java @@ -6,6 +6,7 @@ import org.bukkit.World; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.world.ChunkLoadEvent; @@ -42,14 +43,20 @@ public boolean shouldEnable() { return config.getBoolean(configPath + ".enable", false); } + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) private void onChunkLoad(ChunkLoadEvent event) { if (!alsoCheckNewChunks && event.isNewChunk()) return; final World world = event.getWorld(); if (world.getEnvironment() != World.Environment.NETHER) return; if (exemptedWorlds.contains(world.getName())) return; - if (pauseOnLowTPS && (AnarchyExploitFixes.getTickReporter().getTPS() <= pauseTPS)) return; - ChunkUtil.createBedrockLayer(event.getChunk(), config.nether_ceiling_max_y); + if (!pauseOnLowTPS || AnarchyExploitFixes.getTickReporter().getTPS() >= pauseTPS) { + ChunkUtil.createBedrockLayer(event.getChunk(), config.nether_ceiling_max_y); + } } } diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/bedrock/FillNetherFloorOnChunkload.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/bedrock/FillNetherFloorOnChunkload.java index 99c6b6029..c35372c01 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/bedrock/FillNetherFloorOnChunkload.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/bedrock/FillNetherFloorOnChunkload.java @@ -7,6 +7,7 @@ import org.bukkit.World; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.world.ChunkLoadEvent; @@ -42,14 +43,20 @@ public boolean shouldEnable() { return config.getBoolean(configPath + ".enable", false); } + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) private void onChunkLoad(ChunkLoadEvent event) { final World world = event.getWorld(); if (world.getEnvironment() != World.Environment.NETHER) return; if (exemptedWorlds.contains(world.getName())) return; if (!alsoCheckNewChunks && event.isNewChunk()) return; - if (pauseOnLowTPS && (AnarchyExploitFixes.getTickReporter().getTPS() <= pauseTPS)) return; - ChunkUtil.createBedrockLayer(event.getChunk(), WorldUtil.getMinWorldHeight(world)); + if (!pauseOnLowTPS || AnarchyExploitFixes.getTickReporter().getTPS() >= pauseTPS) { + ChunkUtil.createBedrockLayer(event.getChunk(), WorldUtil.getMinWorldHeight(world)); + } } } diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/bedrock/FillOverworldFloorOnChunkload.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/bedrock/FillOverworldFloorOnChunkload.java index d900fb327..13e9613eb 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/bedrock/FillOverworldFloorOnChunkload.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/bedrock/FillOverworldFloorOnChunkload.java @@ -7,6 +7,7 @@ import org.bukkit.World; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.world.ChunkLoadEvent; @@ -42,14 +43,20 @@ public boolean shouldEnable() { return config.getBoolean(configPath + ".enable", false); } + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) private void onChunkLoad(ChunkLoadEvent event) { if (!alsoCheckNewChunks && event.isNewChunk()) return; final World world = event.getWorld(); if (world.getEnvironment() != World.Environment.NORMAL) return; if (exemptedWorlds.contains(world.getName())) return; - if (pauseOnLowTPS && (AnarchyExploitFixes.getTickReporter().getTPS() <= pauseTPS)) return; - ChunkUtil.createBedrockLayer(event.getChunk(), WorldUtil.getMinWorldHeight(world)); + if (!pauseOnLowTPS || AnarchyExploitFixes.getTickReporter().getTPS() >= pauseTPS) { + ChunkUtil.createBedrockLayer(event.getChunk(), WorldUtil.getMinWorldHeight(world)); + } } } diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/bedrock/PeriodicallyFillNetherCeiling.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/bedrock/PeriodicallyFillNetherCeiling.java index 11d7d32cc..6fffbeb59 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/bedrock/PeriodicallyFillNetherCeiling.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/bedrock/PeriodicallyFillNetherCeiling.java @@ -1,10 +1,11 @@ package me.xginko.aef.modules.bedrock; import me.xginko.aef.AnarchyExploitFixes; -import me.xginko.aef.utils.ChunkUtil; import me.xginko.aef.modules.AEFModule; +import me.xginko.aef.utils.ChunkUtil; import org.bukkit.Chunk; import org.bukkit.World; +import org.bukkit.scheduler.BukkitTask; import java.util.Arrays; import java.util.HashSet; @@ -16,6 +17,7 @@ public class PeriodicallyFillNetherCeiling extends AEFModule implements Runnable private final long checkPeriod; private final double pauseTPS; private final boolean checkShouldPauseOnLowTPS; + private BukkitTask bukkitTask; public PeriodicallyFillNetherCeiling() { super("bedrock.fill-in-bedrock.nether-ceiling.periodically-check-and-fill"); @@ -31,7 +33,7 @@ public PeriodicallyFillNetherCeiling() { @Override public void enable() { - plugin.getServer().getScheduler().scheduleSyncRepeatingTask(plugin, this, 20L, checkPeriod); + bukkitTask = plugin.getServer().getScheduler().runTaskTimer(plugin, this, 20L, checkPeriod); } @Override @@ -39,9 +41,14 @@ public boolean shouldEnable() { return config.getBoolean(configPath + ".enable", false); } + @Override + public void disable() { + if (bukkitTask != null) bukkitTask.cancel(); + } + @Override public void run() { - if (checkShouldPauseOnLowTPS && (AnarchyExploitFixes.getTickReporter().getTPS() <= pauseTPS)) return; + if (checkShouldPauseOnLowTPS && AnarchyExploitFixes.getTickReporter().getTPS() <= pauseTPS) return; for (World world : plugin.getServer().getWorlds()) { if (world.getEnvironment() == World.Environment.NETHER) { diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/bedrock/PeriodicallyFillNetherFloor.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/bedrock/PeriodicallyFillNetherFloor.java index a78ae950b..5117f34b2 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/bedrock/PeriodicallyFillNetherFloor.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/bedrock/PeriodicallyFillNetherFloor.java @@ -6,6 +6,7 @@ import me.xginko.aef.utils.WorldUtil; import org.bukkit.Chunk; import org.bukkit.World; +import org.bukkit.scheduler.BukkitTask; import java.util.Arrays; import java.util.HashSet; @@ -17,6 +18,7 @@ public class PeriodicallyFillNetherFloor extends AEFModule implements Runnable { private final long checkPeriod; private final double pauseTPS; private final boolean checkShouldPauseOnLowTPS; + private BukkitTask bukkitTask; public PeriodicallyFillNetherFloor() { super("bedrock.fill-in-bedrock.nether-floor.periodically-check-and-fill"); @@ -32,7 +34,7 @@ public PeriodicallyFillNetherFloor() { @Override public void enable() { - plugin.getServer().getScheduler().scheduleSyncRepeatingTask(plugin, this, 20L, checkPeriod); + bukkitTask = plugin.getServer().getScheduler().runTaskTimer(plugin, this, 20L, checkPeriod); } @Override @@ -40,9 +42,14 @@ public boolean shouldEnable() { return config.getBoolean(configPath + ".enable", false); } + @Override + public void disable() { + if (bukkitTask != null) bukkitTask.cancel(); + } + @Override public void run() { - if (checkShouldPauseOnLowTPS && (AnarchyExploitFixes.getTickReporter().getTPS() <= pauseTPS)) return; + if (checkShouldPauseOnLowTPS && AnarchyExploitFixes.getTickReporter().getTPS() <= pauseTPS) return; for (World world : plugin.getServer().getWorlds()) { if (world.getEnvironment() == World.Environment.NETHER) { diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/bedrock/PeriodicallyFillOverworldFloor.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/bedrock/PeriodicallyFillOverworldFloor.java index 2e144dd5d..06b61e5d3 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/bedrock/PeriodicallyFillOverworldFloor.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/bedrock/PeriodicallyFillOverworldFloor.java @@ -6,6 +6,7 @@ import me.xginko.aef.utils.WorldUtil; import org.bukkit.Chunk; import org.bukkit.World; +import org.bukkit.scheduler.BukkitTask; import java.util.Arrays; import java.util.HashSet; @@ -17,6 +18,7 @@ public class PeriodicallyFillOverworldFloor extends AEFModule implements Runnabl private final long checkPeriod; private final double pauseTPS; private final boolean checkShouldPauseOnLowTPS; + private BukkitTask bukkitTask; public PeriodicallyFillOverworldFloor() { super("bedrock.fill-in-bedrock.overworld-floor.periodically-check-and-fill"); @@ -32,7 +34,7 @@ public PeriodicallyFillOverworldFloor() { @Override public void enable() { - plugin.getServer().getScheduler().scheduleSyncRepeatingTask(plugin, this, 20L, checkPeriod); + bukkitTask = plugin.getServer().getScheduler().runTaskTimer(plugin, this, 20L, checkPeriod); } @Override @@ -40,9 +42,14 @@ public boolean shouldEnable() { return config.getBoolean(configPath + ".enable", false); } + @Override + public void disable() { + if (bukkitTask != null) bukkitTask.cancel(); + } + @Override public void run() { - if (checkShouldPauseOnLowTPS && (AnarchyExploitFixes.getTickReporter().getTPS() <= pauseTPS)) return; + if (checkShouldPauseOnLowTPS && AnarchyExploitFixes.getTickReporter().getTPS() <= pauseTPS) return; for (World world : plugin.getServer().getWorlds()) { if (world.getEnvironment() == World.Environment.NORMAL) { diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/bedrock/PreventGoingBelowBedrockFloor.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/bedrock/PreventGoingBelowBedrockFloor.java index 3222ace63..440caf3f5 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/bedrock/PreventGoingBelowBedrockFloor.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/bedrock/PreventGoingBelowBedrockFloor.java @@ -8,6 +8,7 @@ import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerMoveEvent; @@ -19,17 +20,22 @@ public class PreventGoingBelowBedrockFloor extends AEFModule implements Listener private final Set exemptedWorlds; private final Material fillMaterial; - private final boolean filling_enabled, eject_enabled, stop_elytra_enabled; + private final boolean fillHole, vehicleEject, closeElytra, teleport; + private final double damageOnMove; public PreventGoingBelowBedrockFloor() { super("bedrock.prevent-going-below-bedrock-floor"); config.addComment(configPath + ".enable", "Prevents players from going below the bedrock floor."); - this.eject_enabled = config.getBoolean(configPath + ".eject-player", true, + this.vehicleEject = config.getBoolean(configPath + ".eject-player", true, "Eject player from the vehicle"); - this.stop_elytra_enabled = config.getBoolean(configPath + ".stop-elytra", true, + this.closeElytra = config.getBoolean(configPath + ".stop-elytra", true, "Disables a player's elytra flight"); - this.filling_enabled = config.getBoolean(configPath + ".fill-bedrock-hole", true, + this.teleport = config.getBoolean(configPath + ".teleport", true, + "Teleport player on top of that bedrock"); + this.damageOnMove = config.getDouble(configPath + ".damage-when-moving", 8.0, + "1.0 = Half a heart of damage every time you move. Set 0 to disable"); + this.fillHole = config.getBoolean(configPath + ".fill-bedrock-hole", true, "Whether the bedrock hole should be filled or not"); this.exemptedWorlds = new HashSet<>(config.getList(configPath + ".exempted-worlds", Arrays.asList("world_the_end", "skyblock_world"))); @@ -53,6 +59,11 @@ public boolean shouldEnable() { return config.getBoolean(configPath + ".enable", true); } + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onPlayerMove(PlayerMoveEvent event) { final Player player = event.getPlayer(); @@ -61,18 +72,31 @@ private void onPlayerMove(PlayerMoveEvent event) { final Location playerLoc = player.getLocation(); if (playerLoc.getY() >= WorldUtil.getMinWorldHeight(world)) return; - if (eject_enabled && player.isInsideVehicle()) - player.leaveVehicle(); - if (stop_elytra_enabled && player.isGliding()) + if (vehicleEject && player.getVehicle() != null) { + player.getVehicle().eject(); + } + + if (closeElytra && player.isGliding()) { player.setGliding(false); + } - // place bedrock at the min world height - if (filling_enabled) - world.getBlockAt(playerLoc.getBlockX(), WorldUtil.getMinWorldHeight(world), playerLoc.getBlockZ()).setType(fillMaterial); + if (fillHole) { + // Place bedrock at the lowest possible valid location in the world + world.getBlockAt(playerLoc.getBlockX(), WorldUtil.getMinWorldHeight(world), playerLoc.getBlockZ()) + .setType(fillMaterial, false); + } - // teleport player on top of that bedrock - Location tploc = event.getFrom().clone().add(0, 2, 0); - event.setTo(tploc); - player.teleport(tploc); + if (teleport) { + // Teleport player up, so he lands on top of that bedrock + Location threeBlocksUp = event.getFrom().clone().add(0, 3, 0); + event.setTo(threeBlocksUp); + player.teleport(threeBlocksUp); + } + + if (damageOnMove > 0) { + // Deal damage when moving below bedrock. Just in case everything else fails + // to prevent players from moving below bedrock. + player.damage(damageOnMove); + } } } diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/chat/PreventPluginScanning.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/chat/PreventPluginScanning.java index e1de9ae82..ca7672f52 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/chat/PreventPluginScanning.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/chat/PreventPluginScanning.java @@ -5,6 +5,7 @@ import me.xginko.aef.modules.AEFModule; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.server.TabCompleteEvent; @@ -32,22 +33,29 @@ public boolean shouldEnable() { return config.getBoolean(configPath + ".enable", true); } + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + private boolean isSuspectedScanPacket(String buffer) { return (buffer.split(" ").length == 1 && !buffer.endsWith(" ")) || !buffer.startsWith("/"); } @EventHandler(priority = EventPriority.HIGHEST) private void onAsyncCommandTabComplete(AsyncTabCompleteEvent event) { - if (event.getSender().hasPermission(AEFPermission.BYPASS_CHAT.string())) return; - if (this.isSuspectedScanPacket(event.getBuffer())) { + if (event.getSender().hasPermission(AEFPermission.BYPASS_CHAT.bukkit())) return; + + if (isSuspectedScanPacket(event.getBuffer())) { event.setCancelled(true); } } @EventHandler(priority = EventPriority.HIGHEST) private void onCommandTabComplete(TabCompleteEvent event) { - if (event.getSender().hasPermission(AEFPermission.BYPASS_CHAT.string())) return; - if (this.isSuspectedScanPacket(event.getBuffer())) { + if (event.getSender().hasPermission(AEFPermission.BYPASS_CHAT.bukkit())) return; + + if (isSuspectedScanPacket(event.getBuffer())) { event.setCancelled(true); } } diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/chat/commandwhitelist/CommandWhitelist.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/chat/commandwhitelist/CommandWhitelist.java index f17b35be2..4754a75d5 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/chat/commandwhitelist/CommandWhitelist.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/chat/commandwhitelist/CommandWhitelist.java @@ -11,12 +11,10 @@ import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientChatMessage; import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientTabComplete; import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerTabComplete; -import io.papermc.lib.PaperLib; import me.xginko.aef.AnarchyExploitFixes; import me.xginko.aef.enums.AEFPermission; import me.xginko.aef.modules.AEFModule; import me.xginko.aef.utils.CommandUtil; -import me.xginko.aef.utils.models.Disableable; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; @@ -35,7 +33,7 @@ /** * Credits go to: YouHaveTrouble (https://github.com/YouHaveTrouble/CommandWhitelist) */ -public class CommandWhitelist extends AEFModule implements Disableable, PacketListener, Listener { +public class CommandWhitelist extends AEFModule implements PacketListener, Listener { private final PacketListenerAbstract abstractListener; private Listener commandSendListener; @@ -66,7 +64,7 @@ public CommandWhitelist() { Arrays.asList("help about", "vote List", "vote Best", "vote Total", "worldstats reload", "stats reload"), "Add all subcommands you DON'T want your players to be able\n" + "to access. Case sensitive!")); - if (CWCommandSendListener.isSupported()) + if (CWCommandSendListener.isSupported()) // Not available by default on 1.12 this.commandSendListener = new CWCommandSendListener(allowedCommands); this.abstractListener = asAbstract(PacketListenerPriority.HIGHEST); } @@ -100,11 +98,12 @@ public void disable() { @Override public void onPacketReceive(PacketReceiveEvent event) { + if (event.isCancelled()) return; if (event.getPacketType() == PacketType.Play.Client.TAB_COMPLETE) { - if (PaperLib.getMinecraftVersion() > 12) return; + if (CWCommandSendListener.isSupported()) return; final Player player = (Player) event.getPlayer(); - if (player != null && player.hasPermission(AEFPermission.BYPASS_CMD_WHITELIST.string())) return; + if (player != null && player.hasPermission(AEFPermission.BYPASS_CMD_WHITELIST.bukkit())) return; String text = new WrapperPlayClientTabComplete(event).getText(); if (!text.startsWith("/")) return; // We only care about the initial command, everything else is handled by the API @@ -145,7 +144,7 @@ else if (allowedCommand.startsWith(text.substring(1))) { } final Player player = (Player) event.getPlayer(); - if (player != null && player.hasPermission(AEFPermission.BYPASS_CMD_WHITELIST.string())) return; + if (player != null && player.hasPermission(AEFPermission.BYPASS_CMD_WHITELIST.bukkit())) return; if (!allowedCommands.contains(CommandUtil.getCommandLabel(message).toLowerCase())) { event.setCancelled(true); @@ -167,7 +166,7 @@ else if (allowedCommand.startsWith(text.substring(1))) { @EventHandler(priority = EventPriority.HIGHEST) private void onCommandPreProcess(PlayerCommandPreprocessEvent event) { final Player player = event.getPlayer(); - if (player.hasPermission(AEFPermission.BYPASS_CMD_WHITELIST.string())) return; + if (player.hasPermission(AEFPermission.BYPASS_CMD_WHITELIST.bukkit())) return; String message = event.getMessage(); String commandLabel = CommandUtil.getCommandLabel(message).toLowerCase(); @@ -196,16 +195,16 @@ private void onCommandPreProcess(PlayerCommandPreprocessEvent event) { private void onAsyncCommandTabComplete(AsyncTabCompleteEvent event) { if (event.getCompletions().isEmpty()) return; if (!(event.getSender() instanceof Player)) return; - if (event.getSender().hasPermission(AEFPermission.BYPASS_CMD_WHITELIST.string())) return; + if (event.getSender().hasPermission(AEFPermission.BYPASS_CMD_WHITELIST.bukkit())) return; event.setCompletions(getFilteredTabCompletions(event.getBuffer(), event.getCompletions())); } - @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onCommandTabComplete(TabCompleteEvent event) { if (event.getCompletions().isEmpty()) return; if (!(event.getSender() instanceof Player)) return; - if (event.getSender().hasPermission(AEFPermission.BYPASS_CMD_WHITELIST.string())) return; + if (event.getSender().hasPermission(AEFPermission.BYPASS_CMD_WHITELIST.bukkit())) return; event.setCompletions(getFilteredTabCompletions(event.getBuffer(), event.getCompletions())); } diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/chunklimits/BlockLimit.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/chunklimits/BlockLimit.java index 1e7deeb52..bf633d78a 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/chunklimits/BlockLimit.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/chunklimits/BlockLimit.java @@ -8,6 +8,7 @@ import org.bukkit.Material; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.block.BlockPlaceEvent; @@ -131,7 +132,7 @@ public BlockLimit() { for (String configuredMaterial : section.getKeys(false)) { try { Material blockMaterial = Material.valueOf(configuredMaterial); - Integer maxAmountPerChunk = Integer.valueOf(section.getString(configuredMaterial)); + Integer maxAmountPerChunk = Integer.parseInt(section.getString(configuredMaterial)); blockLimits.put(blockMaterial, maxAmountPerChunk); } catch (NumberFormatException e) { notRecognized(Integer.class, configuredMaterial); @@ -151,6 +152,11 @@ public boolean shouldEnable() { return config.getBoolean(configPath + ".enable", false); } + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onBlockPlace(BlockPlaceEvent event) { final Material placedType = event.getBlock().getType(); diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/chunklimits/CustomEntityLimit.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/chunklimits/CustomEntityLimit.java index 76589d2a5..6d44e7626 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/chunklimits/CustomEntityLimit.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/chunklimits/CustomEntityLimit.java @@ -11,9 +11,11 @@ import org.bukkit.entity.EntityType; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.entity.EntitySpawnEvent; import org.bukkit.event.world.ChunkUnloadEvent; +import org.bukkit.scheduler.BukkitTask; import java.util.EnumMap; import java.util.Map; @@ -24,6 +26,7 @@ public class CustomEntityLimit extends AEFModule implements Runnable, Listener { private final Map entityLimits = new EnumMap<>(EntityType.class); private final long checkPeriod; private final boolean logIsEnabled; + private BukkitTask bukkitTask; public CustomEntityLimit() { super("chunk-limits.entity-limits.custom-limit"); @@ -122,7 +125,7 @@ public CustomEntityLimit() { for (String configuredEntity : section.getKeys(false)) { try { EntityType limitedEntity = EntityType.valueOf(configuredEntity); - Integer maxAmountPerChunk = Integer.valueOf(section.getString(configuredEntity)); + Integer maxAmountPerChunk = Integer.parseInt(section.getString(configuredEntity)); entityLimits.put(limitedEntity, maxAmountPerChunk); } catch (NumberFormatException e) { notRecognized(Integer.class, configuredEntity); @@ -135,7 +138,7 @@ public CustomEntityLimit() { @Override public void enable() { plugin.getServer().getPluginManager().registerEvents(this, plugin); - plugin.getServer().getScheduler().scheduleSyncRepeatingTask(plugin, this, checkPeriod, checkPeriod); + bukkitTask = plugin.getServer().getScheduler().runTaskTimer(plugin, this, checkPeriod, checkPeriod); } @Override @@ -143,6 +146,12 @@ public boolean shouldEnable() { return config.getBoolean(configPath + ".enable", false); } + @Override + public void disable() { + HandlerList.unregisterAll(this); + if (bukkitTask != null) bukkitTask.cancel(); + } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onChunkUnload(ChunkUnloadEvent event) { for (Map.Entry limit : entityLimits.entrySet()) { diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/chunklimits/DroppedItemLimit.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/chunklimits/DroppedItemLimit.java index 0d255f1b3..ac4a1f31e 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/chunklimits/DroppedItemLimit.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/chunklimits/DroppedItemLimit.java @@ -14,9 +14,11 @@ import org.bukkit.entity.Item; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.entity.ItemSpawnEvent; import org.bukkit.event.world.ChunkLoadEvent; +import org.bukkit.scheduler.BukkitTask; import java.time.Duration; import java.util.EnumSet; @@ -31,6 +33,7 @@ public class DroppedItemLimit extends AEFModule implements Listener, Runnable { private final long checkPeriod, cleanupDelay; private final int maxDroppedItemsPerChunk; private final boolean logIsEnabled, usingWhitelist, onChunkLoad; + private BukkitTask bukkitTask; public DroppedItemLimit() { super("chunk-limits.entity-limits.dropped-item-limit"); @@ -75,7 +78,7 @@ public DroppedItemLimit() { @Override public void enable() { plugin.getServer().getPluginManager().registerEvents(this, plugin); - plugin.getServer().getScheduler().scheduleSyncRepeatingTask(plugin, this, checkPeriod, checkPeriod); + bukkitTask = plugin.getServer().getScheduler().runTaskTimer(plugin, this, checkPeriod, checkPeriod); } @Override @@ -83,6 +86,12 @@ public boolean shouldEnable() { return config.getBoolean(configPath + ".enable", false); } + @Override + public void disable() { + HandlerList.unregisterAll(this); + if (bukkitTask != null) bukkitTask.cancel(); + } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onItemDrop(ItemSpawnEvent event) { Chunk chunk = event.getEntity().getChunk(); diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/chunklimits/ExpBottleLimit.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/chunklimits/ExpBottleLimit.java index f698c1d6d..171d42eba 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/chunklimits/ExpBottleLimit.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/chunklimits/ExpBottleLimit.java @@ -8,14 +8,17 @@ import org.bukkit.entity.Entity; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.entity.ExpBottleEvent; +import org.bukkit.scheduler.BukkitTask; public class ExpBottleLimit extends AEFModule implements Runnable, Listener { private final long checkPeriod; private final int maxExpBottlePerChunk; private final boolean logIsEnabled; + private BukkitTask bukkitTask; public ExpBottleLimit() { super("chunk-limits.exp-bottle-limit"); @@ -34,7 +37,7 @@ public ExpBottleLimit() { @Override public void enable() { plugin.getServer().getPluginManager().registerEvents(this, plugin); - plugin.getServer().getScheduler().scheduleSyncRepeatingTask(plugin, this, 20L, checkPeriod); + bukkitTask = plugin.getServer().getScheduler().runTaskTimer(plugin, this, checkPeriod, checkPeriod); } @Override @@ -42,6 +45,12 @@ public boolean shouldEnable() { return config.getBoolean(configPath + ".enable", true); } + @Override + public void disable() { + HandlerList.unregisterAll(this); + if (bukkitTask != null) bukkitTask.cancel(); + } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onExpBottle(ExpBottleEvent event) { int expBottleCount = 0; diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/chunklimits/FallingBlockLimit.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/chunklimits/FallingBlockLimit.java old mode 100755 new mode 100644 index 292695f33..3545e6e1c --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/chunklimits/FallingBlockLimit.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/chunklimits/FallingBlockLimit.java @@ -9,6 +9,7 @@ import org.bukkit.entity.Entity; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.block.BlockPhysicsEvent; import org.bukkit.event.entity.EntityChangeBlockEvent; @@ -33,9 +34,9 @@ public FallingBlockLimit() { "falling in a chunk."); this.checkedChunks = new ExpiringSet<>(Duration.ofMillis( Math.max(1, config.getInt(configPath + ".chunk-check-delay-in-ticks", 20, - "Delay in ticks until the same chunk can be checked again.\n" + - "Prevents overchecking, because physics events can be called many\n" + - "times in a short time for the same chunk.")) * 50L + "Delay in ticks until the same chunk can be checked again.\n" + + "Prevents overchecking, because physics events can be called many\n" + + "times in a short time for the same chunk.")) * 50L )); } @@ -49,6 +50,11 @@ public boolean shouldEnable() { return config.getBoolean(configPath + ".enable", true); } + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onBlockPhysics(BlockPhysicsEvent event) { Chunk chunk = event.getBlock().getChunk(); diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/chunklimits/MinecartLimit.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/chunklimits/MinecartLimit.java index 5d3a69ba7..c6459c292 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/chunklimits/MinecartLimit.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/chunklimits/MinecartLimit.java @@ -9,14 +9,17 @@ import org.bukkit.entity.Vehicle; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.vehicle.VehicleCreateEvent; +import org.bukkit.scheduler.BukkitTask; public class MinecartLimit extends AEFModule implements Listener, Runnable { private final long checkPeriod; private final int maxMinecartsPerChunk; private final boolean logIsEnabled; + private BukkitTask bukkitTask; public MinecartLimit() { super("chunk-limits.minecart-limit"); @@ -31,7 +34,7 @@ public MinecartLimit() { @Override public void enable() { plugin.getServer().getPluginManager().registerEvents(this, plugin); - plugin.getServer().getScheduler().scheduleSyncRepeatingTask(plugin, this, checkPeriod, checkPeriod); + bukkitTask = plugin.getServer().getScheduler().runTaskTimer(plugin, this, checkPeriod, checkPeriod); } @Override @@ -39,6 +42,12 @@ public boolean shouldEnable() { return config.getBoolean(configPath + ".enable", false); } + @Override + public void disable() { + HandlerList.unregisterAll(this); + if (bukkitTask != null) bukkitTask.cancel(); + } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onCreate(VehicleCreateEvent event) { Vehicle vehicle = event.getVehicle(); diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/chunklimits/NonLivingEntityLimit.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/chunklimits/NonLivingEntityLimit.java index 90738a80d..0088beb1d 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/chunklimits/NonLivingEntityLimit.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/chunklimits/NonLivingEntityLimit.java @@ -9,14 +9,17 @@ import org.bukkit.entity.EntityType; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.entity.EntitySpawnEvent; +import org.bukkit.scheduler.BukkitTask; public class NonLivingEntityLimit extends AEFModule implements Listener, Runnable { private final long checkPeriod; private final int maxNonLivingEntities; private final boolean logIsEnabled; + private BukkitTask bukkitTask; public NonLivingEntityLimit() { super("chunk-limits.entity-limits.non-living-limit"); @@ -32,7 +35,7 @@ public NonLivingEntityLimit() { @Override public void enable() { plugin.getServer().getPluginManager().registerEvents(this, plugin); - plugin.getServer().getScheduler().scheduleSyncRepeatingTask(plugin, this, checkPeriod, checkPeriod); + bukkitTask = plugin.getServer().getScheduler().runTaskTimer(plugin, this, checkPeriod, checkPeriod); } @Override @@ -40,6 +43,12 @@ public boolean shouldEnable() { return config.getBoolean(configPath + ".enable", false); } + @Override + public void disable() { + HandlerList.unregisterAll(this); + if (bukkitTask != null) bukkitTask.cancel(); + } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onSpawn(EntitySpawnEvent event) { if (event.getEntityType().equals(EntityType.DROPPED_ITEM) || EntityUtil.isLivingEntity(event.getEntity())) return; diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/chunklimits/TileEntityLimit.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/chunklimits/TileEntityLimit.java index 08cdfe00c..f6eee68cd 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/chunklimits/TileEntityLimit.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/chunklimits/TileEntityLimit.java @@ -6,13 +6,16 @@ import org.bukkit.Material; import org.bukkit.World; import org.bukkit.block.BlockState; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; +import org.bukkit.scheduler.BukkitTask; public class TileEntityLimit extends AEFModule implements Runnable, Listener { private final long checkPeriod; private final int maxTileEntities; private final boolean logIsEnabled; + private BukkitTask bukkitTask; public TileEntityLimit() { super("chunk-limits.entity-limits.tile-entity-limit"); @@ -26,7 +29,7 @@ public TileEntityLimit() { @Override public void enable() { plugin.getServer().getPluginManager().registerEvents(this, plugin); - plugin.getServer().getScheduler().runTaskTimer(plugin, this, checkPeriod, checkPeriod); + bukkitTask = plugin.getServer().getScheduler().runTaskTimer(plugin, this, checkPeriod, checkPeriod); } @Override @@ -34,6 +37,12 @@ public boolean shouldEnable() { return config.getBoolean(configPath + ".enable", false); } + @Override + public void disable() { + HandlerList.unregisterAll(this); + if (bukkitTask != null) bukkitTask.cancel(); + } + @Override public void run() { for (World world : plugin.getServer().getWorlds()) { diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/chunklimits/VehicleLimit.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/chunklimits/VehicleLimit.java index cb2f2ca55..28322a5e9 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/chunklimits/VehicleLimit.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/chunklimits/VehicleLimit.java @@ -8,14 +8,17 @@ import org.bukkit.entity.Entity; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.vehicle.VehicleCreateEvent; +import org.bukkit.scheduler.BukkitTask; public class VehicleLimit extends AEFModule implements Listener, Runnable { private final long checkPeriod; private final int maxVehiclesPerChunk; private final boolean logIsEnabled; + private BukkitTask bukkitTask; public VehicleLimit() { super("chunk-limits.vehicle-limit"); @@ -32,7 +35,7 @@ public VehicleLimit() { @Override public void enable() { plugin.getServer().getPluginManager().registerEvents(this, plugin); - plugin.getServer().getScheduler().scheduleSyncRepeatingTask(plugin, this, 20L, checkPeriod); + bukkitTask = plugin.getServer().getScheduler().runTaskTimer(plugin, this, checkPeriod, checkPeriod); } @Override @@ -40,6 +43,12 @@ public boolean shouldEnable() { return config.getBoolean(configPath + ".enable", false); } + @Override + public void disable() { + HandlerList.unregisterAll(this); + if (bukkitTask != null) bukkitTask.cancel(); + } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onCreate(VehicleCreateEvent event) { int vehicleCount = 0; diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/chunklimits/VillagerLimit.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/chunklimits/VillagerLimit.java old mode 100755 new mode 100644 index 2141f9db4..f7c3018e4 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/chunklimits/VillagerLimit.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/chunklimits/VillagerLimit.java @@ -6,26 +6,31 @@ import org.bukkit.Chunk; import org.bukkit.World; import org.bukkit.entity.Entity; -import org.bukkit.entity.EntityType; import org.bukkit.entity.Villager; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.entity.CreatureSpawnEvent; +import org.bukkit.scheduler.BukkitTask; import java.util.ArrayList; import java.util.Comparator; +import java.util.HashSet; import java.util.List; import java.util.Objects; +import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; public class VillagerLimit extends AEFModule implements Runnable, Listener { private final List removalPriority; + private final Set professionWhitelist; private final long checkPeriod; private final int maxVillagersPerChunk; - private final boolean logIsEnabled; + private final boolean logIsEnabled, whitelistEnabled; + private BukkitTask bukkitTask; public VillagerLimit() { super("chunk-limits.entity-limits.villager-limit"); @@ -33,7 +38,7 @@ public VillagerLimit() { this.logIsEnabled = config.getBoolean(configPath + ".log-removals", false); this.checkPeriod = Math.max(1, config.getInt(configPath + ".check-period-in-ticks", 600, "check all chunks every x ticks.")); - final List universal = Stream.of("NONE", "NITWIT", "SHEPHERD", "FISHERMAN", "BUTCHER", "CARTOGRAPHER", + final List defPriority = Stream.of("NONE", "NITWIT", "SHEPHERD", "FISHERMAN", "BUTCHER", "CARTOGRAPHER", "LEATHERWORKER", "FLETCHER", "MASON", "FARMER", "ARMORER", "TOOLSMITH", "WEAPONSMITH", "CLERIC", "LIBRARIAN") .filter(prof -> { try { @@ -42,8 +47,20 @@ public VillagerLimit() { } catch (IllegalArgumentException e) { return false; } - }).collect(Collectors.toList()); - this.removalPriority = config.getList(configPath + ".removal-priority", universal, + }) + .collect(Collectors.toList()); + final List defWhitelist = Stream.of("NONE", "NITWIT", "SHEPHERD", "FISHERMAN", "BUTCHER", "CARTOGRAPHER", + "LEATHERWORKER", "FLETCHER", "MASON", "FARMER", "ARMORER", "TOOLSMITH", "WEAPONSMITH", "CLERIC", "LIBRARIAN") + .filter(prof -> { + try { + Villager.Profession.valueOf(prof); + return true; + } catch (IllegalArgumentException e) { + return false; + } + }) + .collect(Collectors.toList()); + this.removalPriority = config.getList(configPath + ".removal-priority", defPriority, "Professions that are in the top of the list are going to be scheduled\n" + "for removal first.") .stream() @@ -57,12 +74,25 @@ public VillagerLimit() { }) .filter(Objects::nonNull) .collect(Collectors.toList()); + this.whitelistEnabled = config.getBoolean(configPath + ".whitelist.enable", false); + this.professionWhitelist = config.getList(configPath + ".whitelist.professions", defWhitelist) + .stream() + .map(configuredProfession -> { + try { + return Villager.Profession.valueOf(configuredProfession); + } catch (IllegalArgumentException e) { + notRecognized(Villager.Profession.class, configuredProfession); + return null; + } + }) + .filter(Objects::nonNull) + .collect(Collectors.toCollection(HashSet::new)); } @Override public void enable() { plugin.getServer().getPluginManager().registerEvents(this, plugin); - plugin.getServer().getScheduler().scheduleSyncRepeatingTask(plugin, this, checkPeriod, checkPeriod); + bukkitTask = plugin.getServer().getScheduler().runTaskTimer(plugin, this, checkPeriod, checkPeriod); } @Override @@ -70,6 +100,12 @@ public boolean shouldEnable() { return config.getBoolean(configPath + ".enable", false); } + @Override + public void disable() { + HandlerList.unregisterAll(this); + if (bukkitTask != null) bukkitTask.cancel(); + } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onCreatureSpawn(CreatureSpawnEvent event) { if (event.getEntityType().equals(XEntityType.VILLAGER.get())) { @@ -95,8 +131,11 @@ private void checkVillagersInChunk(Chunk chunk) { // Create a list with all villagers in that chunk final List villagers_in_chunk = new ArrayList<>(); for (Entity entity : entities) { - if (entity.getType() == EntityType.VILLAGER) { - villagers_in_chunk.add((Villager) entity); + if (entity.getType() == XEntityType.VILLAGER.get()) { + Villager villager = (Villager) entity; + if (whitelistEnabled && !professionWhitelist.contains(villager.getProfession())) { + villagers_in_chunk.add(villager); + } } } diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/combat/AnchorAuraDelay.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/combat/AnchorAuraDelay.java old mode 100755 new mode 100644 index d0391a55a..0142d762d --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/combat/AnchorAuraDelay.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/combat/AnchorAuraDelay.java @@ -2,38 +2,38 @@ import com.cryptomorin.xseries.XMaterial; import me.xginko.aef.modules.AEFModule; -import me.xginko.aef.utils.models.ExpiringSet; -import org.bukkit.Material; -import org.bukkit.World; -import org.bukkit.entity.Player; +import me.xginko.aef.utils.WorldUtil; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.block.Action; import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.event.player.PlayerInteractEvent; -import org.bukkit.inventory.ItemStack; +import org.bukkit.event.player.PlayerKickEvent; +import org.bukkit.event.player.PlayerQuitEvent; -import java.time.Duration; +import java.util.Map; import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; public class AnchorAuraDelay extends AEFModule implements Listener { - private final ExpiringSet placeCooldowns, breakCooldowns; - private final Material RESPAWN_ANCHOR, GLOWSTONE; - private final long placeDelayMillis, breakDelayMillis; - private final boolean updateInv; + private final Map placeCooldowns, breakCooldowns; + private final long placeDelayNanos, breakDelayNanos; + private final boolean updateInventory; public AnchorAuraDelay() { super("combat.anchor-aura-delay"); - this.RESPAWN_ANCHOR = XMaterial.RESPAWN_ANCHOR.parseMaterial(); - this.GLOWSTONE = XMaterial.GLOWSTONE.parseMaterial(); - this.updateInv = config.getBoolean(configPath + ".update-inventory-on-cancel", false, - "Can help with desync but recommended to leave off unless needed."); - this.placeDelayMillis = config.getInt(configPath + ".place-delay-in-ticks", 8) * 50L; - this.placeCooldowns = placeDelayMillis <= 0 ? null : new ExpiringSet<>(Duration.ofMillis(placeDelayMillis)); - this.breakDelayMillis = config.getInt(configPath + ".break-delay-in-ticks", -1) * 50L; - this.breakCooldowns = breakDelayMillis <= 0 ? null : new ExpiringSet<>(Duration.ofMillis(breakDelayMillis)); + this.breakCooldowns = new ConcurrentHashMap<>(); + this.breakDelayNanos = TimeUnit.MILLISECONDS.toNanos( + config.getLong(configPath + ".break-delay-millis", 0, "1 tick = 50 ms")); + this.placeCooldowns = new ConcurrentHashMap<>(); + this.placeDelayNanos = TimeUnit.MILLISECONDS.toNanos( + config.getLong(configPath + ".place-delay-millis", 400, "1 tick = 50 ms")); + this.updateInventory = config.getBoolean(configPath + ".update-inventory-on-cancel", false, + "Can help with desync but recommended to leave off unless you have issues."); } @Override @@ -46,35 +46,56 @@ public boolean shouldEnable() { return config.getBoolean(configPath + ".enable", false); } + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - private void onAnchorBreak(PlayerInteractEvent event) { - if (breakDelayMillis <= 0) return; + private void onPlayerInteract(PlayerInteractEvent event) { + if (breakDelayNanos <= 0) return; if (event.getAction() != Action.RIGHT_CLICK_BLOCK) return; - final ItemStack interactItem = event.getItem(); - if (interactItem == null || interactItem.getType() != GLOWSTONE) return; - final Player player = event.getPlayer(); - if (player.getWorld().getEnvironment() == World.Environment.NORMAL) return; + if (event.getClickedBlock().getType() != XMaterial.RESPAWN_ANCHOR.parseMaterial()) return; + if (event.getItem() == null || event.getItem().getType() != XMaterial.GLOWSTONE.parseMaterial()) return; + if (WorldUtil.isRespawnAnchorWorks(event.getPlayer().getWorld())) return; - if (breakCooldowns.contains(player.getUniqueId())) { + if ( + breakCooldowns.containsKey(event.getPlayer().getUniqueId()) + && breakCooldowns.get(event.getPlayer().getUniqueId()) > System.nanoTime() + ) { event.setCancelled(true); - if (updateInv) event.getPlayer().updateInventory(); + if (updateInventory) event.getPlayer().updateInventory(); } else { - breakCooldowns.add(player.getUniqueId()); + breakCooldowns.put(event.getPlayer().getUniqueId(), System.nanoTime() + breakDelayNanos); } } @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - private void onAnchorPlace(BlockPlaceEvent event) { - if (placeDelayMillis <= 0) return; - if (event.getBlock().getType() != RESPAWN_ANCHOR) return; - final Player player = event.getPlayer(); - if (player.getWorld().getEnvironment() == World.Environment.NORMAL) return; + private void onBlockPlace(BlockPlaceEvent event) { + if (placeDelayNanos <= 0) return; + if (event.getBlock().getType() != XMaterial.RESPAWN_ANCHOR.parseMaterial()) return; + if (WorldUtil.isRespawnAnchorWorks(event.getPlayer().getWorld())) return; - if (placeCooldowns.contains(player.getUniqueId())) { + if ( + placeCooldowns.containsKey(event.getPlayer().getUniqueId()) + && placeCooldowns.get(event.getPlayer().getUniqueId()) > System.nanoTime() + ) { event.setCancelled(true); - if (updateInv) event.getPlayer().updateInventory(); + if (updateInventory) event.getPlayer().updateInventory(); } else { - placeCooldowns.add(player.getUniqueId()); + placeCooldowns.put(event.getPlayer().getUniqueId(), System.nanoTime() + placeDelayNanos); } } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + private void onPlayerQuit(PlayerQuitEvent event) { + placeCooldowns.remove(event.getPlayer().getUniqueId()); + breakCooldowns.remove(event.getPlayer().getUniqueId()); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + private void onPlayerKick(PlayerKickEvent event) { + placeCooldowns.remove(event.getPlayer().getUniqueId()); + breakCooldowns.remove(event.getPlayer().getUniqueId()); + } } \ No newline at end of file diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/combat/BedAuraDelay.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/combat/BedAuraDelay.java index 12e72cd56..f002632bc 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/combat/BedAuraDelay.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/combat/BedAuraDelay.java @@ -1,42 +1,39 @@ package me.xginko.aef.modules.combat; -import com.cryptomorin.xseries.XMaterial; -import com.cryptomorin.xseries.XTag; import me.xginko.aef.modules.AEFModule; -import me.xginko.aef.utils.models.ExpiringSet; -import org.bukkit.Material; -import org.bukkit.World; -import org.bukkit.block.Block; +import me.xginko.aef.utils.MaterialUtil; +import me.xginko.aef.utils.WorldUtil; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.block.Action; import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.event.player.PlayerKickEvent; +import org.bukkit.event.player.PlayerQuitEvent; -import java.time.Duration; -import java.util.EnumSet; -import java.util.Set; +import java.util.Map; import java.util.UUID; -import java.util.stream.Collectors; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; public class BedAuraDelay extends AEFModule implements Listener { - private final ExpiringSet breakCooldowns, placeCooldowns; - private final Set beds; - private final long breakDelayMillis, placeDelayMillis; + private final Map breakCooldowns, placeCooldowns; + private final long breakDelayNanos, placeDelayNanos; + private final boolean updateInventory; public BedAuraDelay() { super("combat.bed-aura-delay"); - this.beds = XTag.BEDS.getValues().stream() - .filter(XMaterial::isSupported) - .map(XMaterial::parseMaterial) - .collect(Collectors.toCollection(() -> EnumSet.noneOf(Material.class))); - this.breakDelayMillis = config.getInt(configPath + ".break-delay-in-ticks", 5, - "Set to -1 to disable.") * 50L; - this.breakCooldowns = breakDelayMillis <= 0 ? null : new ExpiringSet<>(Duration.ofMillis(breakDelayMillis)); - this.placeDelayMillis = config.getInt(configPath + ".place-delay-in-ticks", -1) * 50L; - this.placeCooldowns = placeDelayMillis <= 0 ? null : new ExpiringSet<>(Duration.ofMillis(placeDelayMillis)); + this.breakCooldowns = new ConcurrentHashMap<>(); + this.breakDelayNanos = TimeUnit.MILLISECONDS.toNanos( + config.getLong(configPath + ".break-delay-millis", 0, "1 tick = 50 ms")); + this.placeCooldowns = new ConcurrentHashMap<>(); + this.placeDelayNanos = TimeUnit.MILLISECONDS.toNanos( + config.getLong(configPath + ".place-delay-millis", 250, "1 tick = 50 ms")); + this.updateInventory = config.getBoolean(configPath + ".update-inventory-on-cancel", false, + "Can help with desync but recommended to leave off unless you have issues."); } @Override @@ -49,32 +46,55 @@ public boolean shouldEnable() { return config.getBoolean(configPath + ".enable", false); } + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - private void onBedBreak(PlayerInteractEvent event) { - if (breakDelayMillis <= 0) return; + private void onPlayerInteract(PlayerInteractEvent event) { + if (breakDelayNanos <= 0) return; if (event.getAction() != Action.RIGHT_CLICK_BLOCK) return; - final Block clicked = event.getClickedBlock(); - if (!beds.contains(clicked.getType())) return; - if (clicked.getWorld().getEnvironment() == World.Environment.NORMAL) return; + if (!MaterialUtil.BEDS.contains(event.getClickedBlock().getType())) return; + if (WorldUtil.isBedWorks(event.getClickedBlock().getWorld())) return; - if (breakCooldowns.contains(event.getPlayer().getUniqueId())) { + if ( + breakCooldowns.containsKey(event.getPlayer().getUniqueId()) + && breakCooldowns.get(event.getPlayer().getUniqueId()) > System.nanoTime() + ) { event.setCancelled(true); + if (updateInventory) event.getPlayer().updateInventory(); } else { - breakCooldowns.add(event.getPlayer().getUniqueId()); + breakCooldowns.put(event.getPlayer().getUniqueId(), System.nanoTime() + breakDelayNanos); } } @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - private void onBedPlace(BlockPlaceEvent event) { - if (placeDelayMillis <= 0) return; - final Block placed = event.getBlockPlaced(); - if (!beds.contains(placed.getType())) return; - if (placed.getWorld().getEnvironment() == World.Environment.NORMAL) return; + private void onBlockPlace(BlockPlaceEvent event) { + if (placeDelayNanos <= 0) return; + if (!MaterialUtil.BEDS.contains(event.getBlockPlaced().getType())) return; + if (WorldUtil.isBedWorks(event.getBlockPlaced().getWorld())) return; - if (placeCooldowns.contains(event.getPlayer().getUniqueId())) { + if ( + placeCooldowns.containsKey(event.getPlayer().getUniqueId()) + && placeCooldowns.get(event.getPlayer().getUniqueId()) > System.nanoTime() + ) { event.setCancelled(true); + if (updateInventory) event.getPlayer().updateInventory(); } else { - placeCooldowns.add(event.getPlayer().getUniqueId()); + placeCooldowns.put(event.getPlayer().getUniqueId(), System.nanoTime() + placeDelayNanos); } } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + private void onPlayerQuit(PlayerQuitEvent event) { + placeCooldowns.remove(event.getPlayer().getUniqueId()); + breakCooldowns.remove(event.getPlayer().getUniqueId()); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + private void onPlayerKick(PlayerKickEvent event) { + placeCooldowns.remove(event.getPlayer().getUniqueId()); + breakCooldowns.remove(event.getPlayer().getUniqueId()); + } } diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/combat/BowBomb.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/combat/BowBomb.java index aabae2564..eb6c83ef5 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/combat/BowBomb.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/combat/BowBomb.java @@ -1,9 +1,10 @@ package me.xginko.aef.modules.combat; +import com.cryptomorin.xseries.XEntityType; import me.xginko.aef.modules.AEFModule; -import org.bukkit.entity.EntityType; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.entity.EntityShootBowEvent; @@ -27,12 +28,16 @@ public boolean shouldEnable() { return config.getBoolean(configPath + ".enable", false); } + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onProjectileLaunch(EntityShootBowEvent event) { - if ( - event.getEntityType() == EntityType.PLAYER - && event.getProjectile().getVelocity().lengthSquared() > maxBowSquaredVelocity - ) { + if (event.getEntityType() != XEntityType.PLAYER.get()) return; // Skeletons also shoot bows + + if (event.getProjectile().getVelocity().lengthSquared() > maxBowSquaredVelocity) { event.setCancelled(true); } } diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/combat/Burrow.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/combat/Burrow.java index 4b311b7e6..61fe7c008 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/combat/Burrow.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/combat/Burrow.java @@ -1,9 +1,11 @@ package me.xginko.aef.modules.combat; import com.cryptomorin.xseries.XMaterial; -import io.papermc.lib.PaperLib; +import com.cryptomorin.xseries.XTag; import me.xginko.aef.modules.AEFModule; +import me.xginko.aef.utils.EntityUtil; import me.xginko.aef.utils.MaterialUtil; +import me.xginko.aef.utils.PlatformUtil; import org.bukkit.GameMode; import org.bukkit.Location; import org.bukkit.Material; @@ -12,39 +14,56 @@ import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.event.player.PlayerMoveEvent; +import java.util.EnumSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + public class Burrow extends AEFModule implements Listener { - private final Material SAND, GRAVEL, DIRT, ENCHANTING_TABLE, ENDER_CHEST, BEACON; + private final Set ignoredMaterial; private final double damageWhenMovingInBurrow; private final boolean shouldTeleportUp, preventIfBlockAboveBurrow, breakAnvilInsteadOfTP, allowSlabs; public Burrow() { super("combat.prevent-burrow"); - // Other cached parsed material - this.SAND = XMaterial.SAND.parseMaterial(); - this.GRAVEL = XMaterial.GRAVEL.parseMaterial(); - this.ENCHANTING_TABLE = XMaterial.ENCHANTING_TABLE.parseMaterial(); - this.ENDER_CHEST = XMaterial.ENDER_CHEST.parseMaterial(); - this.BEACON = XMaterial.BEACON.parseMaterial(); - this.DIRT = XMaterial.DIRT.parseMaterial(); - this.damageWhenMovingInBurrow = config.getDouble(configPath + ".damage-when-moving",1.0, "1.0 = Half a heart of damage every time you move."); this.shouldTeleportUp = config.getBoolean(configPath + ".teleport-above-block", true); this.preventIfBlockAboveBurrow = config.getBoolean(configPath + ".prevent-if-block-above-burrow", false, "Prevent burrow even if there is a block above the block they\n" + - "are burrowing in.\n" + - "Please note this may allow creating an \"elevator\", players will\n" + - "keep teleporting up until they hit air."); + "are burrowing in.\n" + + "Please note this may allow creating an 'elevator', players will\n" + + "keep teleporting up until they hit air."); this.breakAnvilInsteadOfTP = config.getBoolean(configPath + ".break-anvil-instead-of-teleport", true); - boolean slabsAreAllowed = config.getBoolean(configPath + ".allow-slabs-in-burrow", true, + this.allowSlabs = config.getBoolean(configPath + ".allow-slabs-in-burrow", PlatformUtil.getMinecraftVersion() > 12, "Needs to be enabled to prevent a bug where players are teleported\n" + - "above a slab when the slab is underwater."); - this.allowSlabs = PaperLib.getMinecraftVersion() > 12 && slabsAreAllowed; + "above a slab when the slab is underwater."); + List defaults = Stream.concat(XTag.SHULKER_BOXES.getValues().stream(), + Stream.of(XMaterial.AIR, XMaterial.DIRT, XMaterial.DIRT_PATH, XMaterial.SAND, XMaterial.GRAVEL)) + .filter(XMaterial::isSupported) + .map(XMaterial::parseMaterial) + .map(Enum::name) + .collect(Collectors.toList()); + this.ignoredMaterial = config.getList(configPath + ".ignored-materials", defaults) + .stream() + .map(ignored -> { + try { + return Material.valueOf(ignored); + } catch (IllegalArgumentException e) { + notRecognized(Material.class, ignored); + return null; + } + }) + .filter(Objects::nonNull) + .collect(Collectors.toCollection(() -> EnumSet.noneOf(Material.class))); } @Override @@ -57,8 +76,13 @@ public boolean shouldEnable() { return config.getBoolean(configPath + ".enable", false); } + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + private void teleportUpAndCenter(Player player, Location from) { - player.teleport(from.clone().add(0.5, 1, 0.5)); + player.teleport(from.clone().add(0.5, 1, 0.5)); } @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) @@ -73,63 +97,56 @@ private void onSelfPlace(BlockPlaceEvent event) { @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onPlayerMove(PlayerMoveEvent event) { Player player = event.getPlayer(); - if (!player.getGameMode().equals(GameMode.SURVIVAL)) return; - if (player.isInsideVehicle() || player.isGliding()) return; - + if (player.getGameMode() != GameMode.SURVIVAL) return; + if (player.isInsideVehicle() || player.isGliding() || EntityUtil.isSwimming(player)) return; + final Location playerLocation = player.getLocation(); final Block burrowBlock = playerLocation.getBlock(); - final Material burrowMaterial = burrowBlock.getType(); - - if ( - burrowMaterial.equals(Material.AIR) - || burrowMaterial.equals(DIRT) // Fixes false positives when trampling farmland - || burrowMaterial.equals(SAND) - || burrowMaterial.equals(GRAVEL) - || MaterialUtil.SHULKER_BOXES.contains(burrowMaterial) - ) return; - - if (preventIfBlockAboveBurrow || burrowBlock.getRelative(BlockFace.UP).getType().equals(Material.AIR)) { - - // Occluding Blocks - if (burrowMaterial.isOccluding() && !MaterialUtil.SINK_IN_BLOCKS.contains(burrowMaterial)) { - if (!allowSlabs || !MaterialUtil.SLAB_LIKE.contains(burrowMaterial)) { - player.damage(damageWhenMovingInBurrow); - if (shouldTeleportUp) teleportUpAndCenter(player, burrowBlock.getLocation()); - } - return; - } + if (ignoredMaterial.contains(burrowBlock.getType())) return; + + if (!preventIfBlockAboveBurrow && burrowBlock.getRelative(BlockFace.UP).getType() != XMaterial.AIR.parseMaterial()) { + return; + } - // Ender chests and blocks that are slightly lower in height - if (burrowMaterial.equals(ENDER_CHEST) || MaterialUtil.SINK_IN_BLOCKS.contains(burrowMaterial)) { - if (playerLocation.getY() - playerLocation.getBlockY() < 0.875) { - player.damage(damageWhenMovingInBurrow); - if (shouldTeleportUp) teleportUpAndCenter(player, burrowBlock.getLocation()); - } - return; + // Beacon and Indestructibles + if (MaterialUtil.SOLID_INDESTRUCTIBLES.contains(burrowBlock.getType()) || burrowBlock.getType() == XMaterial.BEACON.parseMaterial()) { + player.damage(damageWhenMovingInBurrow); + if (shouldTeleportUp) teleportUpAndCenter(player, burrowBlock.getLocation()); + return; + } + + // Occluding blocks that do not lower the player into themselves + if (burrowBlock.getType().isOccluding() && !MaterialUtil.SINK_IN_BLOCKS.contains(burrowBlock.getType())) { + if (!allowSlabs || !MaterialUtil.SLAB_LIKE.contains(burrowBlock.getType())) { + player.damage(damageWhenMovingInBurrow); + if (shouldTeleportUp) teleportUpAndCenter(player, burrowBlock.getLocation()); } + return; + } - // Enchantment Tables - if (burrowMaterial.equals(ENCHANTING_TABLE)) { - if (playerLocation.getY() - playerLocation.getBlockY() < 0.75) { - player.damage(damageWhenMovingInBurrow); - if (shouldTeleportUp) teleportUpAndCenter(player, burrowBlock.getLocation()); - } - return; + // Anvil + if (MaterialUtil.ANVILS.contains(burrowBlock.getType())) { + player.damage(damageWhenMovingInBurrow); + if (breakAnvilInsteadOfTP) { + burrowBlock.setType(XMaterial.AIR.parseMaterial()); + } else { + if (shouldTeleportUp) teleportUpAndCenter(player, burrowBlock.getLocation()); } + return; + } - // Anvils - if (MaterialUtil.ANVILS.contains(burrowMaterial)) { + // Ender chest & Blocks that are slightly lower in height + if (burrowBlock.getType() == XMaterial.ENDER_CHEST.parseMaterial() || MaterialUtil.SINK_IN_BLOCKS.contains(burrowBlock.getType())) { + if (playerLocation.getY() - playerLocation.getBlockY() < 0.875) { player.damage(damageWhenMovingInBurrow); - if (breakAnvilInsteadOfTP) { - burrowBlock.breakNaturally(); - } else { - if (shouldTeleportUp) teleportUpAndCenter(player, burrowBlock.getLocation()); - } - return; + if (shouldTeleportUp) teleportUpAndCenter(player, burrowBlock.getLocation()); } + return; + } - // Beacons and Indestructibles - if (burrowMaterial.equals(BEACON) || MaterialUtil.SOLID_INDESTRUCTIBLES.contains(burrowMaterial)) { + // Enchantment Table + if (burrowBlock.getType() == XMaterial.ENCHANTING_TABLE.parseMaterial()) { + if (playerLocation.getY() - playerLocation.getBlockY() < 0.75) { player.damage(damageWhenMovingInBurrow); if (shouldTeleportUp) teleportUpAndCenter(player, burrowBlock.getLocation()); } diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/combat/CrystalAuraDelay.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/combat/CrystalAuraDelay.java index a6930a2ff..20319b10a 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/combat/CrystalAuraDelay.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/combat/CrystalAuraDelay.java @@ -1,41 +1,40 @@ package me.xginko.aef.modules.combat; +import com.cryptomorin.xseries.XEntityType; import com.cryptomorin.xseries.XMaterial; -import io.papermc.lib.PaperLib; import me.xginko.aef.modules.AEFModule; -import me.xginko.aef.utils.models.ExpiringSet; -import org.bukkit.Material; -import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.block.Action; import org.bukkit.event.entity.EntityDamageByEntityEvent; import org.bukkit.event.player.PlayerInteractEvent; -import org.bukkit.inventory.ItemStack; +import org.bukkit.event.player.PlayerKickEvent; +import org.bukkit.event.player.PlayerQuitEvent; -import java.time.Duration; +import java.util.Map; import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; public class CrystalAuraDelay extends AEFModule implements Listener { - private final ExpiringSet breakCooldowns, placeCooldowns; - private final Material END_CRYSTAL; - private final long breakDelayMillis, placeDelayMillis; - private final boolean updateInv; + private final Map breakCooldowns, placeCooldowns; + private final long breakDelayNanos, placeDelayNanos; + private final boolean updateInventory; public CrystalAuraDelay() { super("combat.crystal-aura.regular-delay"); - this.END_CRYSTAL = XMaterial.END_CRYSTAL.parseMaterial(); - this.updateInv = config.getBoolean(configPath + ".update-inventory-on-cancel", false, - "Can help with desync but recommended to leave off unless needed."); - this.breakDelayMillis = config.getInt(configPath + ".break-delay-in-ticks", PaperLib.getMinecraftVersion() <= 12 ? -1 : 4, - "This will not do anything in 1.12 because the event is broken.\n" + - "Use place delay instead.") * 50L; - this.breakCooldowns = breakDelayMillis <= 0 ? null : new ExpiringSet<>(Duration.ofMillis(breakDelayMillis)); - this.placeDelayMillis = config.getInt(configPath + ".place-delay-in-ticks", PaperLib.getMinecraftVersion() <= 12 ? 4 : -1) * 50L; - this.placeCooldowns = placeDelayMillis <= 0 ? null : new ExpiringSet<>(Duration.ofMillis(placeDelayMillis)); + this.breakCooldowns = new ConcurrentHashMap<>(); + this.breakDelayNanos = TimeUnit.MILLISECONDS.toNanos( + config.getLong(configPath + ".break-delay-millis", 200, "1 tick = 50 ms")); + this.placeCooldowns = new ConcurrentHashMap<>(); + this.placeDelayNanos = TimeUnit.MILLISECONDS.toNanos( + config.getLong(configPath + ".place-delay-millis", 0, "1 tick = 50 ms")); + this.updateInventory = config.getBoolean(configPath + ".update-inventory-on-cancel", false, + "Can help with desync but recommended to leave off unless you have issues."); } @Override @@ -48,35 +47,54 @@ public boolean shouldEnable() { return config.getBoolean(configPath + ".enable", false); } + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - private void onCrystalBreak(EntityDamageByEntityEvent event) { - if (breakDelayMillis <= 0) return; + private void onEntityDamageByEntity(EntityDamageByEntityEvent event) { + if (breakDelayNanos <= 0) return; + if (event.getEntityType() != XEntityType.END_CRYSTAL.get()) return; + if (event.getDamager().getType() != XEntityType.PLAYER.get()) return; if ( - event.getEntityType() == EntityType.ENDER_CRYSTAL - && event.getDamager().getType() == EntityType.PLAYER + breakCooldowns.containsKey(event.getDamager().getUniqueId()) + && breakCooldowns.get(event.getDamager().getUniqueId()) > System.nanoTime() ) { - if (breakCooldowns.contains(event.getDamager().getUniqueId())) { - event.setCancelled(true); - if (updateInv) ((Player) event.getDamager()).updateInventory(); - } else { - breakCooldowns.add(event.getDamager().getUniqueId()); - } + event.setCancelled(true); + if (updateInventory) ((Player) event.getDamager()).updateInventory(); + } else { + breakCooldowns.put(event.getDamager().getUniqueId(), System.nanoTime() + breakDelayNanos); } } @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - private void onCrystalPlace(PlayerInteractEvent event) { - if (placeDelayMillis <= 0) return; - if (event.getAction() != Action.RIGHT_CLICK_BLOCK) return; - final ItemStack interactItem = event.getItem(); - if (interactItem == null || interactItem.getType() != END_CRYSTAL) return; + private void onPlayerInteract(PlayerInteractEvent event) { + if (placeDelayNanos <= 0) return; + if (event.getAction() != Action.RIGHT_CLICK_BLOCK) return; // Need to right-click a block to place a crystal + if (event.getItem() == null || event.getItem().getType() != XMaterial.END_CRYSTAL.parseMaterial()) return; - if (placeCooldowns.contains(event.getPlayer().getUniqueId())) { + if ( + placeCooldowns.containsKey(event.getPlayer().getUniqueId()) + && placeCooldowns.get(event.getPlayer().getUniqueId()) > System.nanoTime() + ) { event.setCancelled(true); - if (updateInv) event.getPlayer().updateInventory(); + if (updateInventory) event.getPlayer().updateInventory(); } else { - placeCooldowns.add(event.getPlayer().getUniqueId()); + placeCooldowns.put(event.getPlayer().getUniqueId(), System.nanoTime() + placeDelayNanos); } } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + private void onPlayerQuit(PlayerQuitEvent event) { + placeCooldowns.remove(event.getPlayer().getUniqueId()); + breakCooldowns.remove(event.getPlayer().getUniqueId()); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + private void onPlayerKick(PlayerKickEvent event) { + placeCooldowns.remove(event.getPlayer().getUniqueId()); + breakCooldowns.remove(event.getPlayer().getUniqueId()); + } } \ No newline at end of file diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/combat/CrystalAuraHotbarSwitchDelay.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/combat/CrystalAuraHotbarSwitchDelay.java deleted file mode 100755 index ed6f8d74d..000000000 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/combat/CrystalAuraHotbarSwitchDelay.java +++ /dev/null @@ -1,99 +0,0 @@ -package me.xginko.aef.modules.combat; - -import com.cryptomorin.xseries.XMaterial; -import com.cryptomorin.xseries.XTag; -import me.xginko.aef.modules.AEFModule; -import me.xginko.aef.utils.models.ExpiringSet; -import org.bukkit.Material; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.Listener; -import org.bukkit.event.player.PlayerItemHeldEvent; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.PlayerInventory; - -import java.time.Duration; -import java.util.Arrays; -import java.util.EnumSet; -import java.util.HashSet; -import java.util.List; -import java.util.Objects; -import java.util.Set; -import java.util.UUID; -import java.util.stream.Collectors; - -public class CrystalAuraHotbarSwitchDelay extends AEFModule implements Listener { - - private final ExpiringSet hotbarItemSwitchCooldowns; - private final Set blacklistedSwitchMaterials; - private final Material END_CRYSTAL; - private final boolean onlyForSpecificMaterials; - - public CrystalAuraHotbarSwitchDelay() { - super("combat.crystal-aura.hotbar-switch-delay"); - this.END_CRYSTAL = XMaterial.END_CRYSTAL.parseMaterial(); - final long switchAwayFromCrystalsDelayInMillis = Math.max(config.getInt(configPath + ".delay-in-ticks", 2, - "Delay between switching from an end crystal to other items in hotbar"), 1) * 50L; - this.hotbarItemSwitchCooldowns = new ExpiringSet<>(Duration.ofMillis(switchAwayFromCrystalsDelayInMillis)); - this.onlyForSpecificMaterials = config.getBoolean(configPath + ".only-delay-specific-materials", true, - "Only delay when switched to specific materials"); - - Set defaults = new HashSet<>(); - defaults.addAll(Arrays.stream(XMaterial.values()) - .filter(xMaterial -> xMaterial.name().endsWith("_AXE") || xMaterial.name().endsWith("_SWORD")) - .collect(Collectors.toSet())); - defaults.addAll(XTag.BEDS.getValues()); - defaults.add(XMaterial.BOW); - defaults.add(XMaterial.CROSSBOW); - - List versionDefaults = defaults.stream() - .filter(XMaterial::isSupported) - .map(xMaterial -> xMaterial.parseMaterial().name()) - .distinct() - .sorted() - .collect(Collectors.toList()); - - this.blacklistedSwitchMaterials = config.getList(configPath + ".delayed-specific-materials", versionDefaults) - .stream() - .map(configuredMaterial -> { - try { - return Material.valueOf(configuredMaterial); - } catch (IllegalArgumentException e) { - notRecognized(Material.class, configuredMaterial); - return null; - } - }) - .filter(Objects::nonNull) - .collect(Collectors.toCollection(() -> EnumSet.noneOf(Material.class))); - } - - @Override - public void enable() { - plugin.getServer().getPluginManager().registerEvents(this, plugin); - } - - @Override - public boolean shouldEnable() { - return config.getBoolean(configPath + ".enable", false); - } - - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - private void onHotbarSwitch(PlayerItemHeldEvent event) { - if (!hotbarItemSwitchCooldowns.contains(event.getPlayer().getUniqueId())) { - hotbarItemSwitchCooldowns.add(event.getPlayer().getUniqueId()); - return; - } - - final PlayerInventory playerInventory = event.getPlayer().getInventory(); - - final ItemStack previouslyHeldItem = playerInventory.getItem(event.getPreviousSlot()); - if (previouslyHeldItem == null || previouslyHeldItem.getType() != END_CRYSTAL) return; - - final ItemStack newHeldItem = playerInventory.getItem(event.getNewSlot()); - if (newHeldItem == null || newHeldItem.getType() == END_CRYSTAL) return; - - if (onlyForSpecificMaterials && !blacklistedSwitchMaterials.contains(newHeldItem.getType())) return; - - event.setCancelled(true); - } -} \ No newline at end of file diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/combat/MultiTask.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/combat/MultiTask.java new file mode 100755 index 000000000..3e4fa5562 --- /dev/null +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/combat/MultiTask.java @@ -0,0 +1,57 @@ +package me.xginko.aef.modules.combat; + +import com.cryptomorin.xseries.XEntityType; +import me.xginko.aef.modules.AEFModule; +import org.bukkit.entity.LivingEntity; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.player.PlayerInteractEvent; + +public class MultiTask extends AEFModule implements Listener { + + public MultiTask() { + super("combat.multi-task-patch"); + } + + @Override + public void enable() { + plugin.getServer().getPluginManager().registerEvents(this, plugin); + } + + @Override + public boolean shouldEnable() { + return config.getBoolean(configPath + ".enable", false); + } + + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onEntityDamageByEntity(EntityDamageByEntityEvent event) { + if (event.getDamager().getType() == XEntityType.PLAYER.get()) { + if (((LivingEntity) event.getDamager()).getActiveItem() != null) { + event.setCancelled(true); + } + } + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onPlayerInteract(PlayerInteractEvent event) { + if (event.getPlayer().getActiveItem() != null) { + event.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onBlockPlace(BlockPlaceEvent event) { + if (event.getPlayer().getActiveItem() != null) { + event.setCancelled(true); + } + } +} \ No newline at end of file diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/combat/PistonAuraDelay.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/combat/PistonCrystalDelay.java similarity index 59% rename from AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/combat/PistonAuraDelay.java rename to AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/combat/PistonCrystalDelay.java index e7073a507..20441f57a 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/combat/PistonAuraDelay.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/combat/PistonCrystalDelay.java @@ -1,21 +1,23 @@ package me.xginko.aef.modules.combat; +import com.cryptomorin.xseries.XEntityType; import me.xginko.aef.modules.AEFModule; import me.xginko.aef.utils.models.ExpiringSet; import org.bukkit.Location; -import org.bukkit.entity.EnderCrystal; +import org.bukkit.entity.Entity; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.block.BlockPistonExtendEvent; import java.time.Duration; -public class PistonAuraDelay extends AEFModule implements Listener { +public class PistonCrystalDelay extends AEFModule implements Listener { private final ExpiringSet pistonsPushingCrystals; - public PistonAuraDelay() { + public PistonCrystalDelay() { super("combat.crystal-aura.piston-aura-delay"); config.addComment(configPath + ".enable", "Rate-limits pistons that extend into crystals"); this.pistonsPushingCrystals = new ExpiringSet<>(Duration.ofMillis( @@ -32,16 +34,21 @@ public boolean shouldEnable() { return config.getBoolean(configPath + ".enable", false); } + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onPistonExtend(BlockPistonExtendEvent event) { - if ( - !event.getBlock().getRelative(event.getDirection()).getLocation() - .getNearbyEntitiesByType(EnderCrystal.class, 1).isEmpty() - ) { - if (pistonsPushingCrystals.contains(event.getBlock().getLocation())) { - event.setCancelled(true); - } else { - pistonsPushingCrystals.add(event.getBlock().getLocation()); + for (Entity entity : event.getBlock().getRelative(event.getDirection()).getLocation().getNearbyEntities(1, 1, 1)) { + if (entity.getType() == XEntityType.END_CRYSTAL.get()) { + if (pistonsPushingCrystals.contains(event.getBlock().getLocation())) { + event.setCancelled(true); + } else { + pistonsPushingCrystals.add(event.getBlock().getLocation()); + } + return; } } } diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/combat/PistonPush.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/combat/PistonPush.java new file mode 100755 index 000000000..b03c60595 --- /dev/null +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/combat/PistonPush.java @@ -0,0 +1,65 @@ +package me.xginko.aef.modules.combat; + +import me.xginko.aef.modules.AEFModule; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockPistonExtendEvent; + +import java.util.Collections; +import java.util.EnumSet; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +public class PistonPush extends AEFModule implements Listener { + + private final Set pushDisabledTypes; + + public PistonPush() { + super("combat.piston-push"); + config.addComment(configPath+".enable", + "Disables pistons from extending if it would push certain configured entities.\n" + + "This can be used to prevent players from pushing other players out of burrows, by\n" + + "configuring PLAYER, or to disable piston-crystal by adding ENDER_CRYSTAL to the list."); + this.pushDisabledTypes = config.getList(configPath+".piston-push-blocked-entities", Collections.singletonList("PLAYER")) + .stream() + .map(configuredType -> { + try { + return EntityType.valueOf(configuredType); + } catch (IllegalArgumentException e) { + return null; + } + }) + .filter(Objects::nonNull) + .collect(Collectors.toCollection(() -> EnumSet.noneOf(EntityType.class))); + } + + @Override + public void enable() { + plugin.getServer().getPluginManager().registerEvents(this, plugin); + } + + @Override + public boolean shouldEnable() { + return config.getBoolean(configPath+".enable", false); + } + + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onPistonExtend(BlockPistonExtendEvent event) { + for (Entity entity : event.getBlock().getRelative(event.getDirection()).getLocation().getNearbyEntities(1,1,1)) { + if (pushDisabledTypes.contains(entity.getType())) { + event.setCancelled(true); + return; + } + } + } +} diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/combat/PortalGodMode.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/combat/PortalGodMode.java new file mode 100755 index 000000000..873560cac --- /dev/null +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/combat/PortalGodMode.java @@ -0,0 +1,93 @@ +package me.xginko.aef.modules.combat; + +import com.cryptomorin.xseries.XEntityType; +import com.cryptomorin.xseries.XMaterial; +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; +import me.xginko.aef.modules.AEFModule; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityPortalEnterEvent; +import org.bukkit.event.player.PlayerMoveEvent; +import org.bukkit.event.player.PlayerTeleportEvent; +import org.bukkit.scheduler.BukkitTask; +import org.checkerframework.checker.nullness.qual.Nullable; + +import java.time.Duration; +import java.util.UUID; + +public class PortalGodMode extends AEFModule implements Listener { + + private final Cache playersWaitingForPortalTeleport; + private final long delayTicks; + + public PortalGodMode() { + super("combat.portal-god-mode-patch"); + config.addComment(configPath + ".enable", + "Prevents an exploit that allows players to stand in nether portals and not\n" + + "take damage indefinitely by just never sending a TeleportConfirm packet to\n" + + "the server.\n" + + "A similar method is used for the chorus tp exploit, which is not covered\n" + + "by this module."); + this.delayTicks = config.getInt(configPath + ".break-portal-delay-ticks", 100, + "If the player stays inside the nether portal for this time without teleporting,\n" + + "the portal will be broken, making the player inside vulnerable again.\n" + + "Nether portal teleports normally happen within ~3s after enter, so 5s (100ticks)\n" + + "should be a safe value."); + this.playersWaitingForPortalTeleport = Caffeine.newBuilder() + .expireAfterWrite(Duration.ofMillis((delayTicks * 50L) + 1000L)).build(); // Keep cached content for a second longer just in case + } + + @Override + public void enable() { + plugin.getServer().getPluginManager().registerEvents(this, plugin); + } + + @Override + public boolean shouldEnable() { + return config.getBoolean(configPath + ".enable", false); + } + + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + private void onEntityPortalEnter(EntityPortalEnterEvent event) { + if (event.getEntityType() != XEntityType.PLAYER.get()) return; + + if (playersWaitingForPortalTeleport.getIfPresent(event.getEntity().getUniqueId()) != null) return; + + playersWaitingForPortalTeleport.put(event.getEntity().getUniqueId(), + plugin.getServer().getScheduler().runTaskLater(plugin, () -> { + event.getLocation().getBlock().setType(XMaterial.AIR.parseMaterial(), true); + playersWaitingForPortalTeleport.invalidate(event.getEntity().getUniqueId()); + }, delayTicks)); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + private void onPlayerTeleport(PlayerTeleportEvent event) { + if (event.getCause() != PlayerTeleportEvent.TeleportCause.NETHER_PORTAL) return; + + @Nullable BukkitTask breakPortalTask = playersWaitingForPortalTeleport.getIfPresent(event.getPlayer().getUniqueId()); + + if (breakPortalTask != null) { + breakPortalTask.cancel(); + playersWaitingForPortalTeleport.invalidate(event.getPlayer().getUniqueId()); + } + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + private void onPlayerMove(PlayerMoveEvent event) { + @Nullable BukkitTask breakPortalTask = playersWaitingForPortalTeleport.getIfPresent(event.getPlayer().getUniqueId()); + if (breakPortalTask == null) return; + + if (event.getTo().getBlock().getType() != XMaterial.NETHER_PORTAL.parseMaterial()) { + breakPortalTask.cancel(); + playersWaitingForPortalTeleport.invalidate(event.getPlayer().getUniqueId()); + } + } +} diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/combat/SilentSwapDelay.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/combat/SilentSwapDelay.java new file mode 100755 index 000000000..4d9c5b1ca --- /dev/null +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/combat/SilentSwapDelay.java @@ -0,0 +1,86 @@ +package me.xginko.aef.modules.combat; + +import com.cryptomorin.xseries.XEntityType; +import me.xginko.aef.modules.AEFModule; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.event.player.PlayerItemHeldEvent; +import org.bukkit.event.player.PlayerKickEvent; +import org.bukkit.event.player.PlayerQuitEvent; + +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; + +public class SilentSwapDelay extends AEFModule implements Listener { + + private final Map swapItemCooldowns; + private final long cooldownNanos; + + public SilentSwapDelay() { + super("combat.silent-swap-delay"); + this.swapItemCooldowns = new ConcurrentHashMap<>(); + this.cooldownNanos = TimeUnit.MILLISECONDS.toNanos( + config.getLong(configPath + ".min-swap-delay-millis", 40L, + "The delay in millis a player cant swap hotbar items after placing\n" + + "a block, clicking a block (for example to place a crystal) or\n" + + "damaging an entity. (50 ms = 1 tick)")); + } + + @Override + public void enable() { + plugin.getServer().getPluginManager().registerEvents(this, plugin); + } + + @Override + public boolean shouldEnable() { + return config.getBoolean(configPath + ".enable", false); + } + + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onPlayerItemHeld(PlayerItemHeldEvent event) { // Fired when a hot bar item selection changes + if (!swapItemCooldowns.containsKey(event.getPlayer().getUniqueId())) return; + + if (swapItemCooldowns.get(event.getPlayer().getUniqueId()) > System.nanoTime()) { + event.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + private void onEntityDamageByEntity(EntityDamageByEntityEvent event) { + if (event.getDamager().getType() == XEntityType.PLAYER.get()) { + swapItemCooldowns.put(event.getDamager().getUniqueId(), System.nanoTime() + cooldownNanos); + } + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + private void onPlayerInteract(PlayerInteractEvent event) { + swapItemCooldowns.put(event.getPlayer().getUniqueId(), System.nanoTime() + cooldownNanos); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + private void onBlockPlace(BlockPlaceEvent event) { + swapItemCooldowns.put(event.getPlayer().getUniqueId(), System.nanoTime() + cooldownNanos); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + private void onPlayerQuit(PlayerQuitEvent event) { + swapItemCooldowns.remove(event.getPlayer().getUniqueId()); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + private void onPlayerKick(PlayerKickEvent event) { + swapItemCooldowns.remove(event.getPlayer().getUniqueId()); + } +} \ No newline at end of file diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/dupepreventions/ChestedEntitiesInPortals.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/dupepreventions/ChestedEntitiesInPortals.java index f4a55f67b..018e15f21 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/dupepreventions/ChestedEntitiesInPortals.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/dupepreventions/ChestedEntitiesInPortals.java @@ -5,6 +5,7 @@ import org.bukkit.entity.ChestedHorse; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.entity.EntityPortalEvent; @@ -29,6 +30,11 @@ public boolean shouldEnable() { return config.getBoolean(configPath, false); } + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onEntityPortalEvent(EntityPortalEvent event) { if (!EntityUtil.isChestableHorse(event.getEntity())) return; diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/dupepreventions/ChestsOnEntities.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/dupepreventions/ChestsOnEntities.java index 52f6f6b77..af853aa1e 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/dupepreventions/ChestsOnEntities.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/dupepreventions/ChestsOnEntities.java @@ -5,6 +5,7 @@ import org.bukkit.entity.ChestedHorse; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerInteractAtEntityEvent; @@ -29,6 +30,11 @@ public boolean shouldEnable() { return config.getBoolean(configPath, false); } + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onPlayerInteract(PlayerInteractAtEntityEvent event) { if (!EntityUtil.isChestableHorse(event.getRightClicked())) return; diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/dupepreventions/CloseEntityInventoriesOnLogout.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/dupepreventions/CloseEntityInventoriesOnLogout.java index 47bb75b02..bc31b6d8b 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/dupepreventions/CloseEntityInventoriesOnLogout.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/dupepreventions/CloseEntityInventoriesOnLogout.java @@ -6,6 +6,7 @@ import org.bukkit.entity.HumanEntity; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.inventory.InventoryHolder; @@ -29,6 +30,11 @@ public boolean shouldEnable() { return config.getBoolean(configPath, false); } + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + @EventHandler(priority = EventPriority.HIGHEST) private void onPlayerQuit(PlayerQuitEvent event) { final Entity vehicle = event.getPlayer().getVehicle(); diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/dupepreventions/CloseEntityInventoryOnChunkUnload.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/dupepreventions/CloseEntityInventoryOnChunkUnload.java index bb5d8c157..63890060e 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/dupepreventions/CloseEntityInventoryOnChunkUnload.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/dupepreventions/CloseEntityInventoryOnChunkUnload.java @@ -6,6 +6,7 @@ import org.bukkit.entity.HumanEntity; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.world.ChunkUnloadEvent; import org.bukkit.inventory.InventoryHolder; @@ -29,6 +30,11 @@ public boolean shouldEnable() { return config.getBoolean(configPath, false); } + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + @EventHandler(priority = EventPriority.LOW) private void onChunkUnload(ChunkUnloadEvent event) { for (Entity entity : event.getChunk().getEntities()) { diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/dupepreventions/EndPortalDupe.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/dupepreventions/EndPortalDupe.java index 0355d092b..a2f9763cd 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/dupepreventions/EndPortalDupe.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/dupepreventions/EndPortalDupe.java @@ -9,6 +9,7 @@ import org.bukkit.entity.LivingEntity; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.entity.EntityDamageEvent; @@ -32,6 +33,11 @@ public boolean shouldEnable() { return config.getBoolean(configPath, false); } + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) private void onEntityDamage(EntityDamageEvent event) { switch (event.getCause()) { diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/elytra/ElytraAtSpawn.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/elytra/ElytraAtSpawn.java index 2f590d253..3729b0dcc 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/elytra/ElytraAtSpawn.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/elytra/ElytraAtSpawn.java @@ -11,6 +11,7 @@ import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerMoveEvent; import org.bukkit.inventory.ItemStack; @@ -44,6 +45,11 @@ public boolean shouldEnable() { return config.elytra_enable_at_spawn; } + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onPlayerMove(PlayerMoveEvent event) { Player player = event.getPlayer(); diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/elytra/ElytraGlobal.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/elytra/ElytraGlobal.java index 09e41e197..1b357d97f 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/elytra/ElytraGlobal.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/elytra/ElytraGlobal.java @@ -11,6 +11,7 @@ import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerMoveEvent; import org.bukkit.inventory.ItemStack; @@ -54,6 +55,11 @@ public boolean shouldEnable() { return config.elytra_enable_global; } + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onPlayerMove(PlayerMoveEvent event) { Player player = event.getPlayer(); diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/elytra/ElytraHelper.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/elytra/ElytraHelper.java index 386f13654..18364d30b 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/elytra/ElytraHelper.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/elytra/ElytraHelper.java @@ -5,13 +5,11 @@ import com.github.retrooper.packetevents.event.PacketListenerAbstract; import com.github.retrooper.packetevents.event.PacketListenerPriority; import com.github.retrooper.packetevents.event.PacketReceiveEvent; -import com.github.retrooper.packetevents.protocol.PacketSide; import com.github.retrooper.packetevents.protocol.packettype.PacketType; import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerFlying; import me.xginko.aef.modules.AEFModule; import me.xginko.aef.utils.ChunkUtil; import me.xginko.aef.utils.LocationUtil; -import me.xginko.aef.utils.models.Disableable; import me.xginko.aef.utils.models.ExpiringSet; import org.bukkit.Location; import org.bukkit.entity.Player; @@ -35,7 +33,7 @@ import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; -public class ElytraHelper extends AEFModule implements Disableable, Runnable, PacketListener, Listener { +public class ElytraHelper extends AEFModule implements Runnable, PacketListener, Listener { private static ElytraHelper instance; private final Map playerDataMap; @@ -43,6 +41,7 @@ public class ElytraHelper extends AEFModule implements Disableable, Runnable, Pa private NewChunksListener newChunksListener; private ScheduledExecutorService executorService; private ScheduledFuture scheduledTask; + private final long speed_as_ticks = config.elytra_speed_calc_period_millis / 50L; public ElytraHelper() { super("elytra.elytra-speed"); @@ -61,7 +60,7 @@ public void enable() { PacketEvents.getAPI().getEventManager().registerListener(packetListener); executorService = Executors.newScheduledThreadPool(1); scheduledTask = executorService.scheduleAtFixedRate( - this, 50L, config.elytra_speed_calc_period * 50L, TimeUnit.MILLISECONDS); + this, 50L, config.elytra_speed_calc_period_millis, TimeUnit.MILLISECONDS); if (!ChunkUtil.canGetInhabitedTime()) newChunksListener = new NewChunksListener(); } @@ -83,23 +82,22 @@ public boolean shouldEnable() { @Override public void run() { for (Map.Entry entry : playerDataMap.entrySet()) { - entry.getValue().calcSpeedAvg(config.elytra_calculate_3D, config.elytra_speed_calc_period); + entry.getValue().calcSpeedAvg(config.elytra_calculate_3D, speed_as_ticks); } } @Override public void onPacketReceive(PacketReceiveEvent event) { - if (event.getPacketType().getSide() != PacketSide.CLIENT) return; - if (event.getUser() == null) return; - UUID player = event.getUser().getUUID(); - if (player == null || !playerDataMap.containsKey(event.getUser().getUUID())) return; + if (event.isCancelled() || event.getUser() == null) return; + if (event.getUser().getUUID() == null || !playerDataMap.containsKey(event.getUser().getUUID())) return; if ( event.getPacketType() == PacketType.Play.Client.PLAYER_FLYING || event.getPacketType() == PacketType.Play.Client.PLAYER_POSITION || event.getPacketType() == PacketType.Play.Client.PLAYER_POSITION_AND_ROTATION ) { - playerDataMap.get(player).updateLatestPosition(new WrapperPlayClientPlayerFlying(event).getLocation()); + playerDataMap.get(event.getUser().getUUID()) + .updateLatestPosition(new WrapperPlayClientPlayerFlying(event).getLocation()); } } @@ -131,7 +129,7 @@ public Location getSetbackLocation(PlayerMoveEvent event) { public boolean isInNewChunks(Player player) { if (ChunkUtil.canGetInhabitedTime()) { - return ChunkUtil.getInhabitedTime(player.getChunk()) <= 200L; + return ChunkUtil.getInhabitedTime(player.getChunk()) <= config.elytra_old_chunk_limit; } else { return newChunksListener.isInNewChunks(player.getUniqueId()); } @@ -163,8 +161,9 @@ public void updateLatestPosition(Location location) { latest.setZ((float) location.getZ()); } - public void calcSpeedAvg(boolean using3D, long period) { - speedAvg = Math.abs(using3D ? LocationUtil.getRelDistance3D(previous, latest) : LocationUtil.getRelDistance2D(previous, latest)) / period; + public void calcSpeedAvg(boolean using3D, long tickPeriod) { + // Blockdistance per tick + speedAvg = Math.abs(using3D ? LocationUtil.getRelDistance3D(previous, latest) : LocationUtil.getRelDistance2D(previous, latest)) / tickPeriod; previous = latest.clone(); } }; diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/elytra/ElytraOnCeiling.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/elytra/ElytraOnCeiling.java index df6a164ac..e91001dfc 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/elytra/ElytraOnCeiling.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/elytra/ElytraOnCeiling.java @@ -11,6 +11,7 @@ import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerMoveEvent; import org.bukkit.inventory.ItemStack; @@ -52,6 +53,11 @@ public boolean shouldEnable() { return config.elytra_enable_netherceiling; } + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onPlayerMove(PlayerMoveEvent event) { Player player = event.getPlayer(); diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/elytra/ElytraPacketFly.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/elytra/ElytraPacketFly.java index 12dd59542..fea0553f1 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/elytra/ElytraPacketFly.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/elytra/ElytraPacketFly.java @@ -9,6 +9,7 @@ import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.entity.EntityToggleGlideEvent; import org.bukkit.inventory.ItemStack; @@ -55,6 +56,11 @@ public boolean shouldEnable() { return config.getBoolean(configPath + ".patch-packet-elytra-fly", false); } + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onElytraOpen(EntityToggleGlideEvent event) { if (event.getEntityType() != EntityType.PLAYER) return; diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/items/BannedItemNames.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/items/BannedItemNames.java index 0e929b2fb..89817925e 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/items/BannedItemNames.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/items/BannedItemNames.java @@ -6,6 +6,8 @@ import org.bukkit.Material; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.jetbrains.annotations.NotNull; import java.util.Collections; import java.util.EnumSet; @@ -56,7 +58,7 @@ public boolean shouldEnable() { } @Override - public ItemLegality legalityOf(ItemStack itemStack) { + public @NotNull ItemLegality legalityOf(@Nullable ItemStack itemStack) { if ( itemStack == null || whitelistedTypes.contains(itemStack.getType()) diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/items/BannedMaterials.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/items/BannedMaterials.java index df1627637..838dfd9d0 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/items/BannedMaterials.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/items/BannedMaterials.java @@ -7,6 +7,8 @@ import me.xginko.aef.utils.ItemUtil; import org.bukkit.Material; import org.bukkit.inventory.ItemStack; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.jetbrains.annotations.NotNull; import java.util.EnumSet; import java.util.List; @@ -65,7 +67,7 @@ public boolean shouldEnable() { } @Override - public ItemLegality legalityOf(ItemStack itemStack) { + public @NotNull ItemLegality legalityOf(@Nullable ItemStack itemStack) { if (itemStack == null) { return ItemLegality.LEGAL; } diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/items/IllegalItemModule.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/items/IllegalItemModule.java index d649645fc..19549a31f 100644 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/items/IllegalItemModule.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/items/IllegalItemModule.java @@ -8,16 +8,16 @@ import me.xginko.aef.enums.ItemLegality; import me.xginko.aef.modules.AEFModule; import me.xginko.aef.utils.CachingPermTool; -import me.xginko.aef.utils.WorldUtil; import me.xginko.aef.utils.EntityUtil; import me.xginko.aef.utils.MaterialUtil; -import me.xginko.aef.utils.models.Disableable; +import me.xginko.aef.utils.WorldUtil; import me.xginko.aef.utils.models.ExpiringSet; import org.bukkit.Chunk; import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.block.BlockState; import org.bukkit.entity.EntityType; +import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; import org.bukkit.event.Event; import org.bukkit.event.EventHandler; @@ -38,7 +38,9 @@ import org.bukkit.event.world.ChunkLoadEvent; import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.ItemStack; +import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.PolyNull; +import org.jetbrains.annotations.NotNull; import java.time.Duration; import java.util.Arrays; @@ -47,10 +49,7 @@ import java.util.function.Function; import java.util.stream.Collectors; -public abstract class IllegalItemModule extends AEFModule implements Disableable, Listener { - - public abstract ItemLegality legalityOf(ItemStack itemStack); - public abstract void handleItem(ItemStack itemStack, ItemLegality legality); +public abstract class IllegalItemModule extends AEFModule implements Listener { protected final AEFPermission bypassPermission; protected final IllegalHandling handling; @@ -62,6 +61,7 @@ public IllegalItemModule(String configPath, AEFPermission bypassPermission) { super(configPath); this.bypassPermission = bypassPermission; this.optionalListeners = new HashSet<>(); + String configuredHandling = config.getString(configPath + ".handling", IllegalHandling.PREVENT_USE_ONLY.name(), "Available options:\n" + Arrays.stream(IllegalHandling.values()) .map(option -> option.name() + " - " + option.description()) @@ -76,16 +76,14 @@ public IllegalItemModule(String configPath, AEFPermission bypassPermission) { this.handling = handling; final boolean guiPluginsSupported = config.getBoolean(configPath + ".gui-plugins-supported", false, - "Enable this if you have problems with the plugin removing items from chest guis\n."+ - "Check if the inventory is connected to a location in the game\n."+ - "If it is not, its very likely created by custom gui plugin."); + "Enable this if you have problems with the plugin removing items from chest guis."); if (this.handling == IllegalHandling.STRICT) { optionalListeners.add(new Listener() { @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onInventoryOpen(InventoryOpenEvent event) { if (CachingPermTool.hasPermission(bypassPermission, event.getPlayer())) return; // Check if the inventory is connected to a location in the game. If it is not, - // its very likely created by custom gui plugin + // it was very likely created by a plugin if (!guiPluginsSupported || event.getInventory().getLocation() != null) { for (ItemStack invItem : event.getInventory()) { handleItem(invItem, legalityOf(invItem)); @@ -96,11 +94,11 @@ private void onInventoryOpen(InventoryOpenEvent event) { } if (config.getBoolean(configPath + ".prevent-hopper32k-mechanic", false, - "Prevents Hopper32k mechanic of placing a shulker containing illegals\n" + - "on top of a hopper, then using the illegal out of the hoppers inventory.\n" + - "WARNING: Hooks into InventoryMoveItemEvent, which can become VERY resource\n" + - "intense as the event fires for every single item getting moved by the\n" + - "hopper. Enable only if you are very sure you need this.")) { + "Prevents Hopper32k mechanic of placing a shulker containing illegals on top\n" + + "of a hopper, then using the illegal out of the hopper's inventory.\n" + + "WARNING: Hooks into InventoryMoveItemEvent, which can become VERY resource\n" + + "intense as the event fires in high frequencies as soon as players start using\n" + + "farms or item sorters. Recommended to leave off if not necessary.")) { optionalListeners.add(new Listener() { @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onItemGoesThroughHopper(InventoryMoveItemEvent event) { @@ -115,7 +113,7 @@ private void onItemGoesThroughHopper(InventoryMoveItemEvent event) { "WARNING: CHECKING ON CHUNKLOAD IS NOT RECOMMENDED AS IT IS VERY RESOURCE INTENSE.\n" + "BE VERY SURE YOU ACTUALLY NEED THIS.\n" + "Iterates over all blocks in a chunk when it is loaded and checks any inventories\n" + - "for illegals. If a container with illegals is found, it will be REMOVED."); + "for illegals. If a container with illegals is found, the container will be REMOVED."); final boolean removeContainers = config.getBoolean(configPath + ".check-on-chunkload.remove-container", false, "If set to true, immediately replaces the container with air. Otherwise, will try\n" + "to handle items separately."); @@ -133,7 +131,7 @@ private void onChunkLoad(ChunkLoadEvent event) { for (int z = 0; z < 16; z++) { for (int y = minY; y < maxY; y++) { Block block = chunk.getBlock(x, y, z); - if (!MaterialUtil.INVENTORY_HOLDER_ITEMS.contains(block.getType())) continue; + if (!MaterialUtil.INVENTORY_HOLDERS.contains(block.getType())) continue; if (removeContainers) { if (legalityOf(((InventoryHolder) block.getState(false)).getInventory()) != ItemLegality.LEGAL) @@ -152,14 +150,17 @@ private void onChunkLoad(ChunkLoadEvent event) { } this.listenerCooldowns = Caffeine.newBuilder().expireAfterWrite(Duration.ofMinutes(5)).build(); - this.createIfAbsent = k -> new ExpiringSet<>(Duration.ofMillis( - config.getInt(configPath+".check-rate-limit-millis", 3000, - "The time in milliseconds to wait before performing another check,\n" + - "if a check was positive. Helps with lag resulting from repeatedly\n" + - "checking illegals.") - )); + final Duration delay = Duration.ofMillis(config.getInt(configPath+".check-rate-limit-millis", 3000, + "The time in milliseconds to wait before performing another check,\n" + + "if a check was positive. Helps with lag resulting from repeatedly\n" + + "checking illegals.")); + this.createIfAbsent = eventClass -> new ExpiringSet<>(delay); } + public abstract @NotNull ItemLegality legalityOf(@Nullable ItemStack itemStack); + + public abstract void handleItem(ItemStack itemStack, ItemLegality legality); + @Override public void enable() { plugin.getServer().getPluginManager().registerEvents(this, plugin); @@ -215,6 +216,8 @@ public void onBlockDispense(BlockDispenseEvent event) { @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void onPlayerArmorChange(PlayerArmorChangeEvent event) { + if (handling == IllegalHandling.PREVENT_USE_ONLY) return; // Cant cancel this event + if (!CachingPermTool.hasPermission(bypassPermission, event.getPlayer())) { handleItem(event.getNewItem(), legalityOf(event.getNewItem())); handleItem(event.getOldItem(), legalityOf(event.getOldItem())); @@ -292,10 +295,20 @@ public void onEntityDamageByEntity(EntityDamageByEntityEvent event) { return; } + if (EntityUtil.isLivingEntity(event.getDamager())) { + if (legalityOf(((LivingEntity) event.getDamager()).getActiveItem()) != ItemLegality.LEGAL) { + event.setCancelled(true); + if (handling != IllegalHandling.PREVENT_USE_ONLY) + event.getDamager().remove(); + return; + } + } + if (EntityUtil.isInventoryHolder(event.getDamager())) { if (legalityOf(((InventoryHolder) event.getDamager()).getInventory()) != ItemLegality.LEGAL) { event.setCancelled(true); - event.getDamager().remove(); + if (handling != IllegalHandling.PREVENT_USE_ONLY) + event.getDamager().remove(); } } } diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/items/IllegalPotions.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/items/IllegalPotions.java new file mode 100755 index 000000000..8ebebaad4 --- /dev/null +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/items/IllegalPotions.java @@ -0,0 +1,63 @@ +package me.xginko.aef.modules.illegals.items; + +import me.xginko.aef.enums.AEFPermission; +import me.xginko.aef.enums.IllegalHandling; +import me.xginko.aef.enums.ItemLegality; +import me.xginko.aef.utils.ItemUtil; +import me.xginko.aef.utils.MaterialUtil; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.PotionMeta; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.jetbrains.annotations.NotNull; + +public class IllegalPotions extends IllegalItemModule { + + private final boolean checkStored; + + public IllegalPotions() { + super("illegals.potions", AEFPermission.BYPASS_ILLEGAL_POTIONS); + config.addComment(configPath + ".enable", + "Bypass permission: " + bypassPermission.string() + "\n" + + "Prevents usage of or reverts items with any attribute modifiers\n" + + "or item flags."); + this.checkStored = config.getBoolean(configPath + ".check-stored-items", false); + } + + @Override + public boolean shouldEnable() { + return config.getBoolean(configPath + ".enable", false); + } + + @Override + public @NotNull ItemLegality legalityOf(@Nullable ItemStack itemStack) { + if (itemStack == null || !MaterialUtil.POTIONS.contains(itemStack.getType()) || !itemStack.hasItemMeta()) { + return ItemLegality.LEGAL; + } + + PotionMeta potionMeta = (PotionMeta) itemStack.getItemMeta(); + if (potionMeta.hasCustomEffects() || potionMeta.hasColor()) { + return ItemLegality.ILLEGAL; + } + + if (checkStored) { + return legalityOf(ItemUtil.getStoredItems(itemStack)); + } + + return ItemLegality.LEGAL; + } + + @Override + public void handleItem(ItemStack itemStack, ItemLegality legality) { + if (handling == IllegalHandling.PREVENT_USE_ONLY) return; // We are cancelling the action in the super class + + switch (legality) { + case CONTAINS_ILLEGAL: + itemStack.setAmount(0); + case ILLEGAL: + PotionMeta potionMeta = (PotionMeta) itemStack.getItemMeta(); + potionMeta.setColor(null); + potionMeta.clearCustomEffects(); + itemStack.setItemMeta(potionMeta); + } + } +} diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/items/InvalidStackSize.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/items/InvalidStackSize.java index e14f5168d..ca5fe7503 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/items/InvalidStackSize.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/items/InvalidStackSize.java @@ -7,6 +7,8 @@ import me.xginko.aef.utils.ItemUtil; import org.bukkit.Material; import org.bukkit.inventory.ItemStack; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.jetbrains.annotations.NotNull; import java.util.Collections; import java.util.EnumSet; @@ -49,7 +51,7 @@ public boolean shouldEnable() { } @Override - public ItemLegality legalityOf(ItemStack itemStack) { + public @NotNull ItemLegality legalityOf(@Nullable ItemStack itemStack) { if (itemStack == null || itemStack.getType() == Material.AIR) { return ItemLegality.LEGAL; } @@ -73,8 +75,15 @@ public void handleItem(ItemStack itemStack, ItemLegality legality) { switch (legality) { case ILLEGAL: - itemStack.setAmount(itemStack.getMaxStackSize()); - break; + if (itemStack.getAmount() < 1) { + itemStack.setAmount(1); + return; + } + + if (itemStack.getAmount() > itemStack.getMaxStackSize()) { + itemStack.setAmount(itemStack.getMaxStackSize()); + } + return; case CONTAINS_ILLEGAL: itemStack.setAmount(0); } diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/items/PlayerHeads.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/items/PlayerHeads.java index 364b1b1df..1fd8040af 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/items/PlayerHeads.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/items/PlayerHeads.java @@ -6,6 +6,8 @@ import me.xginko.aef.utils.ItemUtil; import me.xginko.aef.utils.MaterialUtil; import org.bukkit.inventory.ItemStack; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.jetbrains.annotations.NotNull; public class PlayerHeads extends IllegalItemModule { @@ -26,7 +28,7 @@ public boolean shouldEnable() { } @Override - public ItemLegality legalityOf(ItemStack itemStack) { + public @NotNull ItemLegality legalityOf(@Nullable ItemStack itemStack) { if (itemStack == null) { return ItemLegality.LEGAL; } diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/items/SpawnEggs.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/items/SpawnEggs.java index fdc6b846e..ed2d6b419 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/items/SpawnEggs.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/items/SpawnEggs.java @@ -8,6 +8,8 @@ import me.xginko.aef.utils.MaterialUtil; import org.bukkit.Material; import org.bukkit.inventory.ItemStack; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.jetbrains.annotations.NotNull; import java.util.Collections; import java.util.EnumSet; @@ -49,7 +51,7 @@ public boolean shouldEnable() { } @Override - public ItemLegality legalityOf(ItemStack itemStack) { + public @NotNull ItemLegality legalityOf(@Nullable ItemStack itemStack) { if (itemStack == null || whitelistedTypes.contains(itemStack.getType())) { return ItemLegality.LEGAL; } diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/items/Unbreakables.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/items/Unbreakables.java index 949444f29..787e53276 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/items/Unbreakables.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/items/Unbreakables.java @@ -1,12 +1,16 @@ package me.xginko.aef.modules.illegals.items; import com.cryptomorin.xseries.XMaterial; -import io.papermc.lib.PaperLib; import me.xginko.aef.enums.AEFPermission; +import me.xginko.aef.enums.IllegalHandling; import me.xginko.aef.enums.ItemLegality; import me.xginko.aef.utils.ItemUtil; +import me.xginko.aef.utils.PlatformUtil; import org.bukkit.Material; import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.jetbrains.annotations.NotNull; import java.util.Collections; import java.util.EnumSet; @@ -32,7 +36,7 @@ public Unbreakables() { this.blacklistMode = config.getBoolean(configPath + ".use-as-blacklist-instead", false); this.checkStored = config.getBoolean(configPath + ".check-stored-items", false, "Will delete shulkers and bundles if they contain unbreakables."); - this.skipZeroDurability = config.getBoolean(configPath + ".skip-zero-durability", PaperLib.getMinecraftVersion() < 16, + this.skipZeroDurability = config.getBoolean(configPath + ".skip-zero-durability", PlatformUtil.getMinecraftVersion() >= 16, "Make sure to keep enabled on 1.16+, otherwise netherite tools\n" + "will mistakenly be set to max durability, due to some bug in paper."); this.whitelistedTypes = config.getList(configPath + ".whitelisted-items", @@ -56,13 +60,21 @@ public boolean shouldEnable() { } @Override - public ItemLegality legalityOf(ItemStack itemStack) { + public @NotNull ItemLegality legalityOf(@Nullable ItemStack itemStack) { if (itemStack == null || itemStack.getType() == Material.AIR) { return ItemLegality.LEGAL; } if (!useWhitelist || blacklistMode == whitelistedTypes.contains(itemStack.getType())) { - if (isUnbreakable(itemStack)) { + if (skipZeroDurability && itemStack.getType().getMaxDurability() == 0) { + return ItemLegality.LEGAL; + } + + if (itemStack.getDurability() > itemStack.getType().getMaxDurability() || itemStack.getDurability() < 0) { + return ItemLegality.ILLEGAL; + } + + if (itemStack.hasItemMeta() && itemStack.getItemMeta().isUnbreakable()) { return ItemLegality.ILLEGAL; } } @@ -76,22 +88,28 @@ public ItemLegality legalityOf(ItemStack itemStack) { @Override public void handleItem(ItemStack itemStack, ItemLegality legality) { - // We need to always take action here as we cant reliably prevent usage otherwise - if (legality != ItemLegality.LEGAL) { - itemStack.setAmount(0); - } - } + if (handling == IllegalHandling.PREVENT_USE_ONLY) return; - private boolean isUnbreakable(ItemStack itemStack) { - final int durability = itemStack.getDurability(); - final short maxDurability = itemStack.getType().getMaxDurability(); - if (!skipZeroDurability || maxDurability != 0) { - if (durability > maxDurability) { - return true; - } - return durability < 0; - } else { - return durability > 2031; + switch (legality) { + case CONTAINS_ILLEGAL: + itemStack.setAmount(0); + return; + case ILLEGAL: + ItemMeta itemMeta = itemStack.getItemMeta(); + + if (itemMeta.isUnbreakable()) { + itemMeta.setUnbreakable(false); + itemStack.setItemMeta(itemMeta); + } + + if (skipZeroDurability && itemStack.getType().getMaxDurability() == 0) { + return; + } + + if (itemStack.getDurability() > itemStack.getType().getMaxDurability() || itemStack.getDurability() < 0) { + itemStack.setDurability(itemStack.getType().getMaxDurability()); + itemStack.setItemMeta(itemMeta); + } } } } diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/items/datavalues/CustomDataValues.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/items/datavalues/CustomDataValues.java index 4403d227c..66513f506 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/items/datavalues/CustomDataValues.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/items/datavalues/CustomDataValues.java @@ -1,13 +1,15 @@ package me.xginko.aef.modules.illegals.items.datavalues; -import io.papermc.lib.PaperLib; import me.xginko.aef.enums.AEFPermission; import me.xginko.aef.enums.IllegalHandling; import me.xginko.aef.enums.ItemLegality; import me.xginko.aef.modules.illegals.items.IllegalItemModule; import me.xginko.aef.utils.ItemUtil; +import me.xginko.aef.utils.PlatformUtil; import org.bukkit.Material; import org.bukkit.inventory.ItemStack; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.jetbrains.annotations.NotNull; import java.util.Collections; import java.util.EnumSet; @@ -51,11 +53,11 @@ public CustomDataValues() { @Override public boolean shouldEnable() { - return config.getBoolean(configPath + ".enable", false) && PaperLib.getMinecraftVersion() <= 12; + return config.getBoolean(configPath + ".enable", false) && PlatformUtil.getMinecraftVersion() <= 12; } @Override - public ItemLegality legalityOf(ItemStack itemStack) { + public @NotNull ItemLegality legalityOf(@Nullable ItemStack itemStack) { if (itemStack == null || itemStack.getType() == Material.AIR) { return ItemLegality.LEGAL; } diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/items/datavalues/IllegalGoldenApples.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/items/datavalues/IllegalGoldenApples.java index 63afae13c..7c163c1ff 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/items/datavalues/IllegalGoldenApples.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/items/datavalues/IllegalGoldenApples.java @@ -1,15 +1,17 @@ package me.xginko.aef.modules.illegals.items.datavalues; import com.cryptomorin.xseries.XMaterial; -import io.papermc.lib.PaperLib; import me.xginko.aef.enums.AEFPermission; import me.xginko.aef.enums.IllegalHandling; import me.xginko.aef.enums.ItemLegality; import me.xginko.aef.modules.illegals.items.IllegalItemModule; import me.xginko.aef.utils.ItemUtil; +import me.xginko.aef.utils.PlatformUtil; import org.bukkit.Material; import org.bukkit.inventory.ItemStack; import org.bukkit.material.MaterialData; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.jetbrains.annotations.NotNull; import java.util.EnumMap; import java.util.Map; @@ -40,11 +42,11 @@ public IllegalGoldenApples() { @Override public boolean shouldEnable() { - return config.getBoolean(configPath + ".enable", false) && PaperLib.getMinecraftVersion() <= 12; + return config.getBoolean(configPath + ".enable", false) && PlatformUtil.getMinecraftVersion() <= 12; } @Override - public ItemLegality legalityOf(ItemStack itemStack) { + public @NotNull ItemLegality legalityOf(@Nullable ItemStack itemStack) { if (itemStack == null) { return ItemLegality.LEGAL; } diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/items/enchantments/HigherEnchants.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/items/enchantments/HigherEnchants.java index c2a8002bc..572bc019f 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/items/enchantments/HigherEnchants.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/items/enchantments/HigherEnchants.java @@ -8,6 +8,8 @@ import org.bukkit.Material; import org.bukkit.enchantments.Enchantment; import org.bukkit.inventory.ItemStack; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.jetbrains.annotations.NotNull; import java.util.Collections; import java.util.EnumSet; @@ -62,7 +64,7 @@ public boolean shouldEnable() { } @Override - public ItemLegality legalityOf(ItemStack itemStack) { + public @NotNull ItemLegality legalityOf(@Nullable ItemStack itemStack) { if (itemStack == null || itemStack.getType() == Material.AIR) { return ItemLegality.LEGAL; } diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/items/enchantments/InapplicableEnchants.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/items/enchantments/InapplicableEnchants.java index 268cd7eb9..8509d6bde 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/items/enchantments/InapplicableEnchants.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/items/enchantments/InapplicableEnchants.java @@ -9,6 +9,8 @@ import org.bukkit.Material; import org.bukkit.enchantments.Enchantment; import org.bukkit.inventory.ItemStack; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.jetbrains.annotations.NotNull; import java.util.Collections; import java.util.EnumSet; @@ -53,7 +55,7 @@ public boolean shouldEnable() { } @Override - public ItemLegality legalityOf(ItemStack itemStack) { + public @NotNull ItemLegality legalityOf(@Nullable ItemStack itemStack) { if (itemStack == null || itemStack.getType() == Material.AIR) { return ItemLegality.LEGAL; } diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/items/enchantments/IncompatibleEnchants.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/items/enchantments/IncompatibleEnchants.java index f5fff5fa7..947aa3501 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/items/enchantments/IncompatibleEnchants.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/items/enchantments/IncompatibleEnchants.java @@ -1,6 +1,5 @@ package me.xginko.aef.modules.illegals.items.enchantments; -import com.cryptomorin.xseries.XEnchantment; import me.xginko.aef.enums.AEFPermission; import me.xginko.aef.enums.IllegalHandling; import me.xginko.aef.enums.ItemLegality; @@ -9,6 +8,8 @@ import org.bukkit.Material; import org.bukkit.enchantments.Enchantment; import org.bukkit.inventory.ItemStack; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.jetbrains.annotations.NotNull; import java.util.Collections; import java.util.EnumSet; @@ -16,42 +17,38 @@ import java.util.Set; import java.util.stream.Collectors; -public class IncompatibleEnchants extends IllegalItemModule { +import static com.cryptomorin.xseries.XEnchantment.BANE_OF_ARTHROPODS; +import static com.cryptomorin.xseries.XEnchantment.BINDING_CURSE; +import static com.cryptomorin.xseries.XEnchantment.BLAST_PROTECTION; +import static com.cryptomorin.xseries.XEnchantment.CHANNELING; +import static com.cryptomorin.xseries.XEnchantment.DEPTH_STRIDER; +import static com.cryptomorin.xseries.XEnchantment.FIRE_PROTECTION; +import static com.cryptomorin.xseries.XEnchantment.FORTUNE; +import static com.cryptomorin.xseries.XEnchantment.FROST_WALKER; +import static com.cryptomorin.xseries.XEnchantment.INFINITY; +import static com.cryptomorin.xseries.XEnchantment.LOYALTY; +import static com.cryptomorin.xseries.XEnchantment.MENDING; +import static com.cryptomorin.xseries.XEnchantment.MULTISHOT; +import static com.cryptomorin.xseries.XEnchantment.PIERCING; +import static com.cryptomorin.xseries.XEnchantment.PROJECTILE_PROTECTION; +import static com.cryptomorin.xseries.XEnchantment.PROTECTION; +import static com.cryptomorin.xseries.XEnchantment.RIPTIDE; +import static com.cryptomorin.xseries.XEnchantment.SHARPNESS; +import static com.cryptomorin.xseries.XEnchantment.SILK_TOUCH; +import static com.cryptomorin.xseries.XEnchantment.SMITE; +import static com.cryptomorin.xseries.XEnchantment.VANISHING_CURSE; - private static final Enchantment BINDING_CURSE = XEnchantment.BINDING_CURSE.getEnchant(); - private static final Enchantment VANISHING_CURSE = XEnchantment.VANISHING_CURSE.getEnchant(); - private static final Enchantment MULTISHOT = XEnchantment.MULTISHOT.getEnchant(); - private static final Enchantment PIERCING = XEnchantment.PIERCING.getEnchant(); - private static final Enchantment RIPTIDE = XEnchantment.RIPTIDE.getEnchant(); - private static final Enchantment LOYALTY = XEnchantment.LOYALTY.getEnchant(); - private static final Enchantment CHANNELING = XEnchantment.CHANNELING.getEnchant(); - private static final Enchantment MENDING = XEnchantment.MENDING.getEnchant(); - private static final Enchantment INFINITY = XEnchantment.INFINITY.getEnchant(); - private static final Enchantment SILK_TOUCH = XEnchantment.SILK_TOUCH.getEnchant(); - private static final Enchantment FORTUNE = XEnchantment.FORTUNE.getEnchant(); - private static final Enchantment DEPTH_STRIDER = XEnchantment.DEPTH_STRIDER.getEnchant(); - private static final Enchantment FROST_WALKER = XEnchantment.FROST_WALKER.getEnchant(); - private static final Enchantment SHARPNESS = XEnchantment.SHARPNESS.getEnchant(); - private static final Enchantment BANE_OF_ARTHROPODS = XEnchantment.BANE_OF_ARTHROPODS.getEnchant(); - private static final Enchantment SMITE = XEnchantment.SMITE.getEnchant(); - private static final Enchantment PROTECTION = XEnchantment.PROTECTION.getEnchant(); - private static final Enchantment BLAST_PROTECTION = XEnchantment.BLAST_PROTECTION.getEnchant(); - private static final Enchantment FIRE_PROTECTION = XEnchantment.FIRE_PROTECTION.getEnchant(); - private static final Enchantment PROJECTILE_PROTECTION = XEnchantment.PROJECTILE_PROTECTION.getEnchant(); - - private static final Enchantment[] PROTECT_ENCHANTS = { - PROTECTION, BLAST_PROTECTION, FIRE_PROTECTION, PROJECTILE_PROTECTION - }; - - private static final Enchantment[] DAMAGE_ENCHANTS = { - SHARPNESS, SMITE, BANE_OF_ARTHROPODS - }; +public class IncompatibleEnchants extends IllegalItemModule { private final Set whitelistedTypes; + private final Enchantment[] damageEnchants, protectionEnchants; private final boolean useWhitelist, blacklistMode, checkStored; public IncompatibleEnchants() { super("illegals.enchantments.incompatible-enchants", AEFPermission.BYPASS_ILLEGAL_ENCHANT_INCOMPATIBLE); + this.damageEnchants = new Enchantment[]{SHARPNESS.getEnchant(), SMITE.getEnchant(), BANE_OF_ARTHROPODS.getEnchant()}; + this.protectionEnchants = new Enchantment[]{PROTECTION.getEnchant(), BLAST_PROTECTION.getEnchant(), + FIRE_PROTECTION.getEnchant(), PROJECTILE_PROTECTION.getEnchant()}; config.addComment(configPath + ".enable", "Bypass permission: " + bypassPermission.string() + "\n" + "Reverts or prevents usage of ItemStacks with Enchantments that\n" + @@ -81,7 +78,7 @@ public boolean shouldEnable() { } @Override - public ItemLegality legalityOf(ItemStack itemStack) { + public @NotNull ItemLegality legalityOf(@Nullable ItemStack itemStack) { if (itemStack == null || itemStack.getType() == Material.AIR) { return ItemLegality.LEGAL; } @@ -90,21 +87,26 @@ public ItemLegality legalityOf(ItemStack itemStack) { final Set enchantments = itemStack.getEnchantments().keySet(); if (!enchantments.isEmpty()) { - if (enchantments.contains(BINDING_CURSE) && enchantments.contains(VANISHING_CURSE)) - return ItemLegality.ILLEGAL; - if (enchantments.contains(INFINITY) && enchantments.contains(MENDING)) + if (enchantments.contains(SILK_TOUCH.getEnchant()) && enchantments.contains(FORTUNE.getEnchant())) return ItemLegality.ILLEGAL; - if (enchantments.contains(SILK_TOUCH) && enchantments.contains(FORTUNE)) + if (enchantments.contains(DEPTH_STRIDER.getEnchant()) && enchantments.contains(FROST_WALKER.getEnchant())) return ItemLegality.ILLEGAL; - if (enchantments.contains(DEPTH_STRIDER) && enchantments.contains(FROST_WALKER)) + if (enchantments.contains(INFINITY.getEnchant()) && enchantments.contains(MENDING.getEnchant())) return ItemLegality.ILLEGAL; - if (enchantments.contains(MULTISHOT) && enchantments.contains(PIERCING)) - return ItemLegality.ILLEGAL; - if (enchantments.contains(RIPTIDE) && (enchantments.contains(LOYALTY) || enchantments.contains(CHANNELING))) + if (enchantments.contains(BINDING_CURSE.getEnchant()) && enchantments.contains(VANISHING_CURSE.getEnchant())) return ItemLegality.ILLEGAL; + if (RIPTIDE.isSupported()) { + if (enchantments.contains(RIPTIDE.getEnchant()) + && (enchantments.contains(LOYALTY.getEnchant()) || enchantments.contains(CHANNELING.getEnchant()))) + return ItemLegality.ILLEGAL; + } + if (MULTISHOT.isSupported()) { + if (enchantments.contains(MULTISHOT.getEnchant()) && enchantments.contains(PIERCING.getEnchant())) + return ItemLegality.ILLEGAL; + } int dmgEnchCount = 0; - for (Enchantment damageEnchant : DAMAGE_ENCHANTS) { + for (Enchantment damageEnchant : damageEnchants) { if (enchantments.contains(damageEnchant)) { dmgEnchCount++; if (dmgEnchCount > 1) { @@ -114,7 +116,7 @@ public ItemLegality legalityOf(ItemStack itemStack) { } int protEnchCount = 0; - for (Enchantment protectEnchant : PROTECT_ENCHANTS) { + for (Enchantment protectEnchant : protectionEnchants) { if (enchantments.contains(protectEnchant)) { protEnchCount++; if (protEnchCount > 1) { @@ -144,43 +146,48 @@ public void handleItem(ItemStack itemStack, ItemLegality legality) { final Set enchantments = itemStack.getEnchantments().keySet(); - if (enchantments.contains(BINDING_CURSE) && enchantments.contains(VANISHING_CURSE)) - itemStack.removeEnchantment(BINDING_CURSE); - if (enchantments.contains(MULTISHOT) && enchantments.contains(PIERCING)) - itemStack.removeEnchantment(MULTISHOT); - if (enchantments.contains(RIPTIDE) && (enchantments.contains(LOYALTY) || enchantments.contains(CHANNELING))) - itemStack.removeEnchantment(RIPTIDE); - if (enchantments.contains(INFINITY) && enchantments.contains(MENDING)) - itemStack.removeEnchantment(INFINITY); - if (enchantments.contains(SILK_TOUCH) && enchantments.contains(FORTUNE)) - itemStack.removeEnchantment(FORTUNE); - if (enchantments.contains(DEPTH_STRIDER) && enchantments.contains(FROST_WALKER)) - itemStack.removeEnchantment(FROST_WALKER); - - if (enchantments.contains(SHARPNESS)) { // Prefer keeping Sharpness enchantment if present - for (Enchantment dmgEnchant : DAMAGE_ENCHANTS) { - if (dmgEnchant != SHARPNESS) { + if (enchantments.contains(SILK_TOUCH.getEnchant()) && enchantments.contains(FORTUNE.getEnchant())) + itemStack.removeEnchantment(FORTUNE.getEnchant()); + if (enchantments.contains(DEPTH_STRIDER.getEnchant()) && enchantments.contains(FROST_WALKER.getEnchant())) + itemStack.removeEnchantment(FROST_WALKER.getEnchant()); + if (enchantments.contains(INFINITY.getEnchant()) && enchantments.contains(MENDING.getEnchant())) + itemStack.removeEnchantment(INFINITY.getEnchant()); + if (enchantments.contains(BINDING_CURSE.getEnchant()) && enchantments.contains(VANISHING_CURSE.getEnchant())) + itemStack.removeEnchantment(BINDING_CURSE.getEnchant()); + if (RIPTIDE.isSupported()) { // 1.12 doesn't have tridents + if (enchantments.contains(RIPTIDE.getEnchant()) + && (enchantments.contains(LOYALTY.getEnchant()) || enchantments.contains(CHANNELING.getEnchant()))) + itemStack.removeEnchantment(RIPTIDE.getEnchant()); + } + if (MULTISHOT.isSupported()) { // 1.12 doesn't have crossbows + if (enchantments.contains(MULTISHOT.getEnchant()) && enchantments.contains(PIERCING.getEnchant())) + itemStack.removeEnchantment(MULTISHOT.getEnchant()); + } + + if (enchantments.contains(SHARPNESS.getEnchant())) { // Prefer keeping sharpness enchantment if present + for (Enchantment dmgEnchant : damageEnchants) { + if (dmgEnchant != SHARPNESS.getEnchant()) { itemStack.removeEnchantment(dmgEnchant); } } - } else if (enchantments.contains(BANE_OF_ARTHROPODS) && enchantments.contains(SMITE)) { - itemStack.removeEnchantment(BANE_OF_ARTHROPODS); + } else if (enchantments.contains(BANE_OF_ARTHROPODS.getEnchant()) && enchantments.contains(SMITE.getEnchant())) { + itemStack.removeEnchantment(BANE_OF_ARTHROPODS.getEnchant()); } - if (enchantments.contains(PROTECTION)) { // Prefer keeping Protection enchantment if present - for (Enchantment protEnchant : PROTECT_ENCHANTS) { - if (protEnchant != PROTECTION) { + if (enchantments.contains(PROTECTION.getEnchant())) { // Prefer keeping protection enchantment if present + for (Enchantment protEnchant : protectionEnchants) { + if (protEnchant != PROTECTION.getEnchant()) { itemStack.removeEnchantment(protEnchant); } } - } else if (enchantments.contains(BLAST_PROTECTION)) { // If protection is present, prefer blast protection - for (Enchantment protEnchant : PROTECT_ENCHANTS) { - if (protEnchant != BLAST_PROTECTION) { + } else if (enchantments.contains(BLAST_PROTECTION.getEnchant())) { // If protection isn't present, prefer blast protection + for (Enchantment protEnchant : protectionEnchants) { + if (protEnchant != BLAST_PROTECTION.getEnchant()) { itemStack.removeEnchantment(protEnchant); } } - } else if (enchantments.contains(PROJECTILE_PROTECTION) && enchantments.contains(FIRE_PROTECTION)) { - itemStack.removeEnchantment(FIRE_PROTECTION); // If protection and blast protection is not present, prefer projectile protection + } else if (enchantments.contains(PROJECTILE_PROTECTION.getEnchant()) && enchantments.contains(FIRE_PROTECTION.getEnchant())) { + itemStack.removeEnchantment(FIRE_PROTECTION.getEnchant()); // If protection and blast protection is not present, prefer projectile protection } } -} +} \ No newline at end of file diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/items/nbt/CommandItems.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/items/nbt/CommandItems.java index cba4f2cea..11ac2ec1c 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/items/nbt/CommandItems.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/items/nbt/CommandItems.java @@ -1,12 +1,14 @@ package me.xginko.aef.modules.illegals.items.nbt; -import de.tr7zw.changeme.nbtapi.NBTItem; import me.xginko.aef.enums.AEFPermission; import me.xginko.aef.enums.ItemLegality; import me.xginko.aef.modules.illegals.items.IllegalItemModule; +import me.xginko.aef.utils.ItemUtil; import me.xginko.aef.utils.MaterialUtil; import org.bukkit.Material; import org.bukkit.inventory.ItemStack; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.jetbrains.annotations.NotNull; public class CommandItems extends IllegalItemModule { @@ -28,16 +30,16 @@ public boolean shouldEnable() { } @Override - public ItemLegality legalityOf(ItemStack itemStack) { + public @NotNull ItemLegality legalityOf(@Nullable ItemStack itemStack) { if (itemStack == null || itemStack.getType() == Material.AIR) { return ItemLegality.LEGAL; } - if (!checkStored && MaterialUtil.INVENTORY_HOLDER_ITEMS.contains(itemStack.getType())) { + if (!checkStored && MaterialUtil.INVENTORY_HOLDERS.contains(itemStack.getType())) { return ItemLegality.LEGAL; } - if (new NBTItem(itemStack).toString().contains("run_command")) { + if (ItemUtil.getNBTString(itemStack).contains("run_command")) { return ItemLegality.ILLEGAL; } diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/items/nbt/CustomNBTFilter.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/items/nbt/CustomNBTFilter.java index d56339b3c..59dde091c 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/items/nbt/CustomNBTFilter.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/items/nbt/CustomNBTFilter.java @@ -1,6 +1,5 @@ package me.xginko.aef.modules.illegals.items.nbt; -import de.tr7zw.changeme.nbtapi.NBTItem; import me.xginko.aef.enums.AEFPermission; import me.xginko.aef.enums.IllegalHandling; import me.xginko.aef.enums.ItemLegality; @@ -8,6 +7,8 @@ import me.xginko.aef.utils.ItemUtil; import org.bukkit.Material; import org.bukkit.inventory.ItemStack; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.jetbrains.annotations.NotNull; import java.util.Collections; import java.util.EnumSet; @@ -52,13 +53,13 @@ public boolean shouldEnable() { } @Override - public ItemLegality legalityOf(ItemStack itemStack) { + public @NotNull ItemLegality legalityOf(@Nullable ItemStack itemStack) { if (itemStack == null || itemStack.getType() == Material.AIR) { return ItemLegality.LEGAL; } if (!useWhitelist || blacklistMode == whitelistedTypes.contains(itemStack.getType())) { - String nbtItemToString = new NBTItem(itemStack).toString(); + String nbtItemToString = ItemUtil.getNBTString(itemStack); for (String tag : illegalTags) { if (nbtItemToString.contains(tag)) { return ItemLegality.ILLEGAL; diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/items/nbt/NBTFilledStorageItem.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/items/nbt/NBTFilledStorageItem.java index 7bd4f2180..bbec50758 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/items/nbt/NBTFilledStorageItem.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/items/nbt/NBTFilledStorageItem.java @@ -1,6 +1,6 @@ package me.xginko.aef.modules.illegals.items.nbt; -import de.tr7zw.changeme.nbtapi.NBTItem; +import de.tr7zw.changeme.nbtapi.NBT; import me.xginko.aef.enums.AEFPermission; import me.xginko.aef.enums.IllegalHandling; import me.xginko.aef.enums.ItemLegality; @@ -9,6 +9,8 @@ import me.xginko.aef.utils.MaterialUtil; import org.bukkit.Material; import org.bukkit.inventory.ItemStack; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.jetbrains.annotations.NotNull; import java.util.EnumSet; import java.util.Objects; @@ -33,7 +35,7 @@ public NBTFilledStorageItem() { this.stored_items_tag = config.getString(configPath + ".tag", "BlockEntityTag", "The exact name of the nbt tag that signals items are stored inside."); this.checkStored = config.getBoolean(configPath + ".check-stored-items", false); - this.storageTypes = config.getList(configPath + ".storage-types", MaterialUtil.INVENTORY_HOLDER_ITEMS.stream() + this.storageTypes = config.getList(configPath + ".storage-types", MaterialUtil.INVENTORY_HOLDERS.stream() .filter(material -> !MaterialUtil.SHULKER_BOXES.contains(material)).map(Enum::name).sorted().collect(Collectors.toList())) .stream() .map(configuredType -> { @@ -54,12 +56,12 @@ public boolean shouldEnable() { } @Override - public ItemLegality legalityOf(ItemStack itemStack) { + public @NotNull ItemLegality legalityOf(@Nullable ItemStack itemStack) { if (itemStack == null || itemStack.getType() == Material.AIR) { return ItemLegality.LEGAL; } - if (storageTypes.contains(itemStack.getType()) && new NBTItem(itemStack).getKeys().contains(stored_items_tag)) { + if (storageTypes.contains(itemStack.getType()) && NBT.readNbt(itemStack).getKeys().contains(stored_items_tag)) { return ItemLegality.ILLEGAL; } diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/placedblocks/PeriodicallyRemoveIllegalBlocks.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/placedblocks/PeriodicallyRemoveIllegalBlocks.java index 1dd77e516..e466ca115 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/placedblocks/PeriodicallyRemoveIllegalBlocks.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/placedblocks/PeriodicallyRemoveIllegalBlocks.java @@ -3,12 +3,13 @@ import com.cryptomorin.xseries.XMaterial; import me.xginko.aef.AnarchyExploitFixes; import me.xginko.aef.modules.AEFModule; -import me.xginko.aef.utils.WorldUtil; import me.xginko.aef.utils.MaterialUtil; +import me.xginko.aef.utils.WorldUtil; import org.bukkit.Chunk; import org.bukkit.Material; import org.bukkit.World; import org.bukkit.block.Block; +import org.bukkit.scheduler.BukkitTask; import java.util.Arrays; import java.util.EnumSet; @@ -23,10 +24,10 @@ public class PeriodicallyRemoveIllegalBlocks extends AEFModule implements Runnab private final Set blocksToRemove; private final Set exemptedWorlds; - private final Material BEDROCK; private final long checkPeriod; private final double pauseTPS; private final boolean checkShouldPauseOnLowTPS; + private BukkitTask bukkitTask; public PeriodicallyRemoveIllegalBlocks() { super("illegals.remove-placed-blocks.periodically"); @@ -59,12 +60,11 @@ public PeriodicallyRemoveIllegalBlocks() { this.checkPeriod = config.getInt(configPath + ".check-period-in-seconds", 10) * 20L; this.checkShouldPauseOnLowTPS = config.getBoolean(configPath + ".pause-on-low-TPS", true); this.pauseTPS = config.getDouble(configPath + ".pause-TPS", 14.0); - this.BEDROCK = XMaterial.BEDROCK.parseMaterial(); } @Override public void enable() { - plugin.getServer().getScheduler().scheduleSyncRepeatingTask(plugin, this, checkPeriod, checkPeriod); + bukkitTask = plugin.getServer().getScheduler().runTaskTimer(plugin, this, checkPeriod, checkPeriod); } @Override @@ -72,6 +72,11 @@ public boolean shouldEnable() { return config.getBoolean(configPath + ".enable", false); } + @Override + public void disable() { + if (bukkitTask != null) bukkitTask.cancel(); + } + @Override public void run() { if (checkShouldPauseOnLowTPS && AnarchyExploitFixes.getTickReporter().getTPS() <= pauseTPS) return; @@ -95,26 +100,27 @@ public void run() { // If is skull, check if it is player head if (MaterialUtil.PLAYER_HEADS.contains(block.getType())) { if (MaterialUtil.isPlayerHead(block.getState())) { - block.setType(Material.AIR, false); + block.setType(XMaterial.AIR.parseMaterial(), false); } continue; } // If is bedrock, make sure not to delete naturally generated - if (block.getType() == BEDROCK) { + if (block.getType() == XMaterial.BEDROCK.parseMaterial()) { if (y > minY + 4) { // offset to not delete natural bedrock floor if (inNether) { // offset to not delete bedrock ceiling - if (y < config.nether_ceiling_max_y - 5) block.setType(Material.AIR, false); + if (y < config.nether_ceiling_max_y - 5) + block.setType(XMaterial.AIR.parseMaterial(), false); } else { - block.setType(Material.AIR, false); + block.setType(XMaterial.AIR.parseMaterial(), false); } } continue; } // Everything else may be removed - block.setType(Material.AIR, false); + block.setType(XMaterial.AIR.parseMaterial(), false); } } } diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/placedblocks/RemoveIllegalBlocksOnChunkload.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/placedblocks/RemoveIllegalBlocksOnChunkload.java index 0f403f5ed..e6329c72c 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/placedblocks/RemoveIllegalBlocksOnChunkload.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/placedblocks/RemoveIllegalBlocksOnChunkload.java @@ -11,6 +11,7 @@ import org.bukkit.block.Block; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.world.ChunkLoadEvent; @@ -27,7 +28,6 @@ public class RemoveIllegalBlocksOnChunkload extends AEFModule implements Listene private final Set blocksToRemove; private final Set exemptedWorlds; - private final Material BEDROCK; private final double pauseTPS; private final boolean checkShouldPauseOnLowTPS; @@ -62,7 +62,6 @@ public RemoveIllegalBlocksOnChunkload() { Arrays.asList("exampleworld1", "exampleworld2"))); this.checkShouldPauseOnLowTPS = config.getBoolean(configPath + ".pause-on-low-TPS", true); this.pauseTPS = config.getDouble(configPath + ".pause-TPS", 14.0); - this.BEDROCK = XMaterial.BEDROCK.parseMaterial(); } @Override @@ -75,6 +74,11 @@ public boolean shouldEnable() { return config.getBoolean(configPath + ".enable", false); } + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) private void onChunkLoad(ChunkLoadEvent event) { if (event.isNewChunk() || (checkShouldPauseOnLowTPS && AnarchyExploitFixes.getTickReporter().getTPS() <= pauseTPS)) return; @@ -96,25 +100,26 @@ private void onChunkLoad(ChunkLoadEvent event) { // If is skull, check if it is player head if (MaterialUtil.PLAYER_HEADS.contains(block.getType())) { if (MaterialUtil.isPlayerHead(block.getState())) { - block.setType(Material.AIR, false); + block.setType(XMaterial.AIR.parseMaterial(), false); } continue; } // If is bedrock, make sure not to delete naturally generated - if (block.getType() == BEDROCK) { + if (block.getType() == XMaterial.BEDROCK.parseMaterial()) { if (y > minY + 4) { // offset to not delete natural bedrock floor if (inNether) { // offset to not delete bedrock ceiling - if (y < config.nether_ceiling_max_y - 5) block.setType(Material.AIR, false); + if (y < config.nether_ceiling_max_y - 5) + block.setType(XMaterial.AIR.parseMaterial(), false); } else { - block.setType(Material.AIR, false); + block.setType(XMaterial.AIR.parseMaterial(), false); } } continue; } - block.setType(Material.AIR, false); + block.setType(XMaterial.AIR.parseMaterial(), false); } } } diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/placedblocks/RemoveUnnaturalSpawners.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/placedblocks/RemoveUnnaturalSpawners.java index bca68c6b1..0beb29a54 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/placedblocks/RemoveUnnaturalSpawners.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/illegals/placedblocks/RemoveUnnaturalSpawners.java @@ -7,13 +7,13 @@ import me.xginko.aef.modules.AEFModule; import me.xginko.aef.utils.WorldUtil; import org.bukkit.Chunk; -import org.bukkit.Material; import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.block.CreatureSpawner; import org.bukkit.entity.EntityType; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.world.ChunkLoadEvent; @@ -52,23 +52,21 @@ public RemoveUnnaturalSpawners() { ConfigSection section = config.getConfigSection(configPath + ".natural-spawner-types-per-world", defaults, "You can add or remove as much world names here as you want."); - if (section != null) { - for (String configuredWorlds : section.getKeys(false)) { - naturalSpawners.put(configuredWorlds, section.getList(configuredWorlds) - .stream() - .map(String::valueOf) - .map(configuredType -> { - try { - return EntityType.valueOf(configuredType); - } catch (IllegalArgumentException e) { - notRecognized(EntityType.class, configuredType); - return null; - } - }) - .filter(Objects::nonNull) - .collect(Collectors.toCollection(() -> EnumSet.noneOf(EntityType.class))) - ); - } + for (String configuredWorlds : section.getKeys(false)) { + naturalSpawners.put(configuredWorlds, section.getList(configuredWorlds) + .stream() + .map(String::valueOf) + .map(configuredType -> { + try { + return EntityType.valueOf(configuredType); + } catch (IllegalArgumentException e) { + notRecognized(EntityType.class, configuredType); + return null; + } + }) + .filter(Objects::nonNull) + .collect(Collectors.toCollection(() -> EnumSet.noneOf(EntityType.class))) + ); } } @@ -79,12 +77,17 @@ public void enable() { @Override public boolean shouldEnable() { - return config.getBoolean(configPath + ".enable", false) && !naturalSpawners.isEmpty(); + return config.getBoolean(configPath + ".enable", false); + } + + @Override + public void disable() { + HandlerList.unregisterAll(this); } @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) private void onChunkLoad(ChunkLoadEvent event) { - if (event.isNewChunk() || (checkShouldPauseOnLowTPS && AnarchyExploitFixes.getTickReporter().getTPS() <= pauseTPS)) return; + if (event.isNewChunk() || checkShouldPauseOnLowTPS && AnarchyExploitFixes.getTickReporter().getTPS() <= pauseTPS) return; Chunk chunk = event.getChunk(); World world = chunk.getWorld(); @@ -100,7 +103,7 @@ private void onChunkLoad(ChunkLoadEvent event) { if (block.getType() == XMaterial.SPAWNER.parseMaterial() && !naturalSpawners.get(world.getName()).contains(((CreatureSpawner) block.getState()).getSpawnedType()) ) { - block.setType(Material.AIR, false); + block.setType(XMaterial.AIR.parseMaterial(), false); } } } diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/FloodingMachines.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/FloodingMachines.java index 96b2dfa81..e04eacad1 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/FloodingMachines.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/FloodingMachines.java @@ -1,12 +1,12 @@ package me.xginko.aef.modules.lagpreventions; -import io.papermc.lib.PaperLib; import me.xginko.aef.modules.AEFModule; import me.xginko.aef.utils.BlockUtil; import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.block.BlockPistonExtendEvent; @@ -27,7 +27,12 @@ public void enable() { @Override public boolean shouldEnable() { - return config.getBoolean(configPath + ".enable", false) && PaperLib.getMinecraftVersion() > 12; + return config.getBoolean(configPath + ".enable", false) && BlockUtil.isWaterloggedAvailable(); + } + + @Override + public void disable() { + HandlerList.unregisterAll(this); } @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/InventoryActionLag.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/InventoryActionLag.java deleted file mode 100755 index d90eb9dbf..000000000 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/InventoryActionLag.java +++ /dev/null @@ -1,116 +0,0 @@ -package me.xginko.aef.modules.lagpreventions; - -import com.github.benmanes.caffeine.cache.Cache; -import com.github.benmanes.caffeine.cache.Caffeine; -import io.github.thatsmusic99.configurationmaster.api.ConfigSection; -import me.xginko.aef.modules.AEFModule; -import me.xginko.aef.utils.LocationUtil; -import org.bukkit.Location; -import org.bukkit.block.Block; -import org.bukkit.entity.Entity; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.Listener; -import org.bukkit.event.inventory.InventoryAction; -import org.bukkit.event.inventory.InventoryClickEvent; - -import java.time.Duration; -import java.util.EnumMap; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; - -public class InventoryActionLag extends AEFModule implements Listener { - - private final Map clickActionLimits = new EnumMap<>(InventoryAction.class); - private final Cache> entityInventoryClicks; - private final Cache> blockInventoryClicks; - private final boolean logIsEnabled; - - public InventoryActionLag() { - super("lag-preventions.prevent-inventory-action-lag"); - config.addComment(configPath + ".enable", - "WARNING: VERY EXPERIMENTAL!\n" + - "Prevent lag generated by players quickly moving big items back and\n" + - "forth between inventories. Uses cached counters that auto-reset after\n" + - "the configurable time in milliseconds."); - this.logIsEnabled = config.getBoolean(configPath + ".log", true); - Duration cacheTime = Duration.ofMillis(Math.max(config.getInt(configPath + ".cache-time-millis", 2000, - "The amount of time in milliseconds an entry is kept after writing."), 1)); - this.blockInventoryClicks = Caffeine.newBuilder().expireAfterWrite(cacheTime).build(); - this.entityInventoryClicks = Caffeine.newBuilder().expireAfterWrite(cacheTime).build(); - Map defaults = new HashMap<>(); - defaults.put("COLLECT_TO_CURSOR", 15); - defaults.put("MOVE_TO_OTHER_INVENTORY", 8); - defaults.put("HOTBAR_SWAP", 30); - ConfigSection section = config.getConfigSection(configPath + ".click-action-limits", defaults, - "Use correct enums from:\n" + - "https://jd.papermc.io/paper/1.20/org/bukkit/event/inventory/InventoryAction.html.\n" + - "Format is: InventoryClickAction: AllowedClicksPerTime"); - for (String configuredAction : section.getKeys(false)) { - try { - InventoryAction action = InventoryAction.valueOf(configuredAction); - Integer maxClicksPerTime = Integer.valueOf(section.getString(configuredAction)); - clickActionLimits.put(action, maxClicksPerTime); - } catch (NumberFormatException e) { - notRecognized(Integer.class, configuredAction); - } catch (IllegalArgumentException e) { - notRecognized(InventoryAction.class, configuredAction); - } - } - } - - @Override - public void enable() { - plugin.getServer().getPluginManager().registerEvents(this, plugin); - } - - @Override - public boolean shouldEnable() { - return config.getBoolean(configPath + ".enable", false); - } - - @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) - private void onInventoryClick(InventoryClickEvent event) { - if (!clickActionLimits.containsKey(event.getAction())) return; - if (event.getInventory().getHolder() == null) return; - - if (event.getInventory().getHolder() instanceof Block) { - Block block = (Block) event.getInventory().getHolder(); - Map recordedClicks = blockInventoryClicks.get(block.getLocation(), - k -> new EnumMap<>(InventoryAction.class)); - Integer clickActionCounter = recordedClicks.getOrDefault(event.getAction(), 0); - - clickActionCounter++; - - if (clickActionCounter > clickActionLimits.get(event.getAction())) { - event.setCancelled(true); - if (logIsEnabled) info("Cancelled spammy inventory click of type " + event.getAction().name() + " at " + - LocationUtil.toString(block.getLocation())); - } - - recordedClicks.put(event.getAction(), clickActionCounter); - blockInventoryClicks.put(block.getLocation(), recordedClicks); - - return; - } - - if (event.getInventory().getHolder() instanceof Entity) { - Entity entity = (Entity) event.getInventory().getHolder(); - Map recordedClicks = entityInventoryClicks.get(entity.getUniqueId(), - k -> new EnumMap<>(InventoryAction.class)); - Integer clickActionCounter = recordedClicks.getOrDefault(event.getAction(), 0); - - clickActionCounter++; - - if (clickActionCounter > clickActionLimits.get(event.getAction())) { - event.setCancelled(true); - if (logIsEnabled) info("Cancelled spammy inventory click of type " + event.getAction().name() - + " at " + LocationUtil.toString(entity.getLocation())); - } - - recordedClicks.put(event.getAction(), clickActionCounter); - entityInventoryClicks.put(entity.getUniqueId(), recordedClicks); - } - } -} \ No newline at end of file diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/InventoryOpenSpam.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/InventoryOpenSpam.java deleted file mode 100755 index 9aa272390..000000000 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/InventoryOpenSpam.java +++ /dev/null @@ -1,79 +0,0 @@ -package me.xginko.aef.modules.lagpreventions; - -import com.github.benmanes.caffeine.cache.Cache; -import com.github.benmanes.caffeine.cache.Caffeine; -import me.xginko.aef.modules.AEFModule; -import me.xginko.aef.utils.EntityUtil; -import me.xginko.aef.utils.MaterialUtil; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.Listener; -import org.bukkit.event.block.Action; -import org.bukkit.event.player.PlayerInteractEntityEvent; -import org.bukkit.event.player.PlayerInteractEvent; - -import java.time.Duration; -import java.util.UUID; - -public class InventoryOpenSpam extends AEFModule implements Listener { - - /** - * This is pretty much an ugly bandage to the actual problem and will need more work. - * For now this is just barely enough to make a meaningful difference on some servers but - * this needs a better implementation - */ - - private final Cache playerInvOpenCooldowns; - private final int interactLimit; - - public InventoryOpenSpam() { - super("lag-preventions.prevent-inventory-open-spam"); - config.addComment(configPath + ".enable", - "Rate-limit interactions with inventory holders to prevent a lag exploit."); - this.interactLimit = config.getInt(configPath + ".max-interacts-per-time", 2); - this.playerInvOpenCooldowns = Caffeine.newBuilder().expireAfterWrite(Duration.ofMillis( - config.getInt(configPath + ".time-in-ticks", 20) * 50L - )).build(); - } - - @Override - public void enable() { - plugin.getServer().getPluginManager().registerEvents(this, plugin); - } - - @Override - public boolean shouldEnable() { - return config.getBoolean(configPath + ".enable", false); - } - - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - private void onPlayerInteractEntity(PlayerInteractEntityEvent event) { - if (!EntityUtil.isInventoryHolder(event.getRightClicked())) return; - - Player player = event.getPlayer(); - int interactCount = playerInvOpenCooldowns.get(player.getUniqueId(), k -> 0); - interactCount++; - playerInvOpenCooldowns.put(player.getUniqueId(), interactCount); - - if (interactCount > interactLimit) { - event.setCancelled(true); - } - } - - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - private void onPlayerInteract(PlayerInteractEvent event) { - if (event.getAction() != Action.RIGHT_CLICK_BLOCK) return; - if (!MaterialUtil.INVENTORY_HOLDER_ITEMS.contains(event.getClickedBlock().getType())) return; - Player player = event.getPlayer(); - if (player.isSneaking()) return; // Try not to interfere with building - - int interactCount = playerInvOpenCooldowns.get(player.getUniqueId(), k -> 0); - interactCount++; - playerInvOpenCooldowns.put(player.getUniqueId(), interactCount); - - if (interactCount > interactLimit) { - event.setCancelled(true); - } - } -} \ No newline at end of file diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/KeepStashLoaded.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/KeepStashLoaded.java index 7b3659c5d..7d770ac13 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/KeepStashLoaded.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/KeepStashLoaded.java @@ -6,7 +6,6 @@ import me.xginko.aef.utils.MaterialUtil; import me.xginko.aef.utils.WorldUtil; import me.xginko.aef.utils.models.ChunkUID; -import me.xginko.aef.utils.models.Disableable; import org.bukkit.Chunk; import org.bukkit.Material; import org.bukkit.block.BlockState; @@ -26,7 +25,11 @@ import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; -public class KeepStashLoaded extends AEFModule implements Disableable, Runnable, Listener { +/** + * Credits to the initial idea of just keeping big chunks loaded to reduce lag + * created from loading them go to kumori (Soft1k) of 3b3t.org. + */ +public class KeepStashLoaded extends AEFModule implements Runnable, Listener { private final Map forceLoadedChunks; private final Map worldsAndTheirRadiuses = new HashMap<>(); @@ -39,7 +42,7 @@ public KeepStashLoaded() { super("lag-preventions.keep-stash-chunks-loaded"); this.forceLoadedChunks = new ConcurrentHashMap<>(); config.addComment(configPath + ".enable", - "Idea by 6g6s admin kumori:\n"+ + "Idea by 3b3t admin kumori (Soft1k)\n"+ "Improves lag generated by large stash chunks constantly loading and\n"+ "unloading by setting them force loaded. This might cause increased ram\n"+ "usage, so keep an eye out for that.\n" + @@ -48,13 +51,13 @@ public KeepStashLoaded() { this.stashCount = config.getInt(configPath + ".container-block-threshold", 50, "How many container blocks have to be in a chunk for it to be seen\n"+ "as a stash chunk to keep force loaded."); - this.keepLoadedMillis = TimeUnit.MINUTES.toMillis(config.getInt(configPath + ".keep-loaded-minutes", 60, + this.keepLoadedMillis = TimeUnit.MINUTES.toMillis(config.getInt(configPath + ".keep-loaded-minutes", 120, "The time in minutes a stash chunks will be kept force loaded before\n"+ "setting it back to normal.")); this.onlyTileEntities = config.getBoolean(configPath + ".only-check-tile-entities", true, "Set to false if you want to check more blocks than just tile entities.\n" + "Makes the overall speed of the module faster if set to true."); - this.storageTypes = config.getList(configPath + ".container-types", MaterialUtil.INVENTORY_HOLDER_ITEMS + this.storageTypes = config.getList(configPath + ".container-types", MaterialUtil.INVENTORY_HOLDERS .stream() .map(Enum::name) .collect(Collectors.toList())) @@ -69,6 +72,7 @@ public KeepStashLoaded() { }) .filter(Objects::nonNull) .collect(Collectors.toCollection(() -> EnumSet.noneOf(Material.class))); + Map defaults = new HashMap<>(); defaults.put("world", 100); defaults.put("world_nether", 100); @@ -96,10 +100,11 @@ public void enable() { public void disable() { HandlerList.unregisterAll(this); for (Map.Entry entry : forceLoadedChunks.entrySet()) { - Chunk chunk = entry.getKey().getChunk(); - if (chunk != null) - ChunkUtil.setForceLoaded(chunk, false); - forceLoadedChunks.remove(entry.getKey()); + entry.getKey().getChunkAsync(false).thenAccept(chunk -> { + if (chunk != null) + ChunkUtil.setForceLoaded(chunk, false); + forceLoadedChunks.remove(entry.getKey()); + }); } } @@ -115,18 +120,19 @@ public void run() { continue; } - Chunk chunk = entry.getKey().getChunk(); - if (chunk == null) { + entry.getKey().getChunkAsync(false).thenAccept(chunk -> { + if (chunk == null) { + forceLoadedChunks.remove(entry.getKey()); + if (logIsEnabled) + info("Removing key that returns a null chunk: "+entry.getKey()+"."); + return; + } + + ChunkUtil.setForceLoaded(chunk, false); forceLoadedChunks.remove(entry.getKey()); if (logIsEnabled) - info("Removing key that returns a null chunk: "+entry.getKey()+"."); - continue; - } - - ChunkUtil.setForceLoaded(chunk, false); - forceLoadedChunks.remove(entry.getKey()); - if (logIsEnabled) - info("Set chunk "+entry.getKey()+" to no longer force loaded."); + info("Set chunk "+entry.getKey()+" to no longer force loaded."); + }); } } diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/LeverSpam.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/LeverSpam.java index 23d3879db..bd987b781 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/LeverSpam.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/LeverSpam.java @@ -11,6 +11,7 @@ import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.block.Action; import org.bukkit.event.player.PlayerInteractEvent; @@ -47,6 +48,11 @@ public boolean shouldEnable() { return config.getBoolean(configPath + ".enable", false); } + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onInteract(PlayerInteractEvent event) { if (event.getAction() != Action.RIGHT_CLICK_BLOCK) return; diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/LiquidUpdateLag.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/LiquidUpdateLag.java deleted file mode 100755 index e56d6e2f5..000000000 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/LiquidUpdateLag.java +++ /dev/null @@ -1,72 +0,0 @@ -package me.xginko.aef.modules.lagpreventions; - -import com.cryptomorin.xseries.XMaterial; -import com.github.benmanes.caffeine.cache.Cache; -import com.github.benmanes.caffeine.cache.Caffeine; -import me.xginko.aef.modules.AEFModule; -import me.xginko.aef.utils.models.ChunkUID; -import org.bukkit.Chunk; -import org.bukkit.block.Block; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.Listener; -import org.bukkit.event.block.BlockFromToEvent; - -import java.time.Duration; - -public class LiquidUpdateLag extends AEFModule implements Listener { - - private final Cache liquidSpreadEventCountCache; - private final int maxLiquidSpreadEventsPerChunk; - private final boolean logIsEnabled; - - public LiquidUpdateLag() { - super("lag-preventions.prevent-liquid-update-lag"); - this.maxLiquidSpreadEventsPerChunk = config.getInt(configPath + ".max-liquid-events-in-same-chunk-per-time", 1200, - "WARNING: DEFAULTS ARE VERY ROUGH, DEFINITELY TWEAK THIS!\n" + - "Number is the result of:\n" + - " Amount of liquid source blocks\n" + - " multiplied by sides it can spread to\n" + - " multiplied by block spread length."); - this.liquidSpreadEventCountCache = Caffeine.newBuilder().expireAfterWrite(Duration.ofMillis( - Math.max(config.getInt(configPath + ".time-in-ticks", 100, - "Record time after first liquid spread.\n" + - "When this time runs out, the spread counter resets"), 1) * 50L)).build(); - this.logIsEnabled = config.getBoolean(configPath + ".log", false, - "Very spammy, use for testing/debugging only"); - } - - @Override - public void enable() { - plugin.getServer().getPluginManager().registerEvents(this, plugin); - } - - @Override - public boolean shouldEnable() { - return config.getBoolean(configPath + ".enable", false); - } - - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - private void onLiquidSpread(BlockFromToEvent event) { - final Block sourceBlock = event.getBlock(); - if (sourceBlock.getType() == XMaterial.DRAGON_EGG.parseMaterial()) return; // Event fires only for liquids and the dragon egg - - final Chunk chunk = sourceBlock.getChunk(); - final ChunkUID chunkUID = ChunkUID.of(chunk); - Integer liquidSpreadCount = liquidSpreadEventCountCache.getIfPresent(chunkUID); - - if (liquidSpreadCount == null) liquidSpreadCount = 0; - liquidSpreadCount++; - liquidSpreadEventCountCache.put(chunkUID, liquidSpreadCount); - - if (liquidSpreadCount > maxLiquidSpreadEventsPerChunk) { - event.setCancelled(true); - if (logIsEnabled) warn("Cancelled liquid events for chunk x=" + chunk.getX() + ", z=" + chunk.getZ() + - " in world: " + chunk.getWorld().getName()); - return; - } - - if (logIsEnabled) info("Recorded " + liquidSpreadCount + " liquid updates in chunk x=" + chunk.getX() + - ", z=" + chunk.getZ() + " in world: " + chunk.getWorld().getName()); - } -} \ No newline at end of file diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/NoShulkerDrops.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/NoShulkerDrops.java index 85ee23c27..0615eebf7 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/NoShulkerDrops.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/NoShulkerDrops.java @@ -6,6 +6,7 @@ import org.bukkit.entity.Item; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.entity.EntityDamageEvent; @@ -28,6 +29,11 @@ public boolean shouldEnable() { return config.getBoolean(configPath + ".enable", false); } + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onEntityDamage(EntityDamageEvent event) { if (event.getEntityType() != XEntityType.ITEM.get()) return; diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/StashExplosions.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/StashExplosions.java index 7613c8603..db897e6a1 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/StashExplosions.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/StashExplosions.java @@ -10,6 +10,7 @@ import org.bukkit.block.Block; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.block.BlockExplodeEvent; import org.bukkit.event.entity.EntityExplodeEvent; @@ -43,7 +44,7 @@ public StashExplosions() { "If no explosion happens within x seconds after the first one, the count\n" + "resets to 0.")) )).build(); - this.storageTypes = config.getList(configPath + ".container-types", MaterialUtil.INVENTORY_HOLDER_ITEMS + this.storageTypes = config.getList(configPath + ".container-types", MaterialUtil.INVENTORY_HOLDERS .stream() .filter(material -> !MaterialUtil.SHULKER_BOXES.contains(material)) .map(Enum::name) @@ -70,6 +71,12 @@ public boolean shouldEnable() { return config.getBoolean(configPath + ".enable", false); } + @Override + public void disable() { + HandlerList.unregisterAll(this); + containerExplosions.cleanUp(); + } + private void handleExplosion(Chunk chunk, List affectedBlocks) { ChunkUID chunkUID = ChunkUID.of(chunk); Integer containerExplosionsInChunk = containerExplosions.getIfPresent(chunkUID); diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/agelimits/CustomAgeLimits.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/agelimits/CustomAgeLimits.java index b2adcb889..ab16fbfe9 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/agelimits/CustomAgeLimits.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/agelimits/CustomAgeLimits.java @@ -9,8 +9,10 @@ import org.bukkit.entity.EntityType; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.world.ChunkLoadEvent; +import org.bukkit.scheduler.BukkitTask; import java.util.EnumMap; import java.util.Map; @@ -21,6 +23,7 @@ public class CustomAgeLimits extends AEFModule implements Runnable, Listener { private final Map entityLimits = new EnumMap<>(EntityType.class); private final long checkPeriod; private final boolean logIsEnabled; + private BukkitTask bukkitTask; public CustomAgeLimits() { super("lag-preventions.entity-age-limits.custom-limits"); @@ -51,7 +54,7 @@ public CustomAgeLimits() { for (String configuredEntity : section.getKeys(false)) { try { EntityType limitedEntity = EntityType.valueOf(configuredEntity); - Integer maxAmountPerChunk = Integer.valueOf(section.getString(configuredEntity)); + Integer maxAmountPerChunk = Integer.parseInt(section.getString(configuredEntity)); entityLimits.put(limitedEntity, maxAmountPerChunk); } catch (NumberFormatException e) { notRecognized(Integer.class, configuredEntity); @@ -63,7 +66,8 @@ public CustomAgeLimits() { @Override public void enable() { - plugin.getServer().getScheduler().scheduleSyncRepeatingTask(plugin, this, 20L, checkPeriod); + plugin.getServer().getPluginManager().registerEvents(this, plugin); + bukkitTask = plugin.getServer().getScheduler().runTaskTimer(plugin, this, checkPeriod, checkPeriod); } @Override @@ -71,6 +75,12 @@ public boolean shouldEnable() { return config.getBoolean(configPath + ".enable", false); } + @Override + public void disable() { + HandlerList.unregisterAll(this); + if (bukkitTask != null) bukkitTask.cancel(); + } + @Override public void run() { for (World world : plugin.getServer().getWorlds()) { diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/agelimits/ProjectileAgeLimit.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/agelimits/ProjectileAgeLimit.java index d875f65a7..82c9868f4 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/agelimits/ProjectileAgeLimit.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/agelimits/ProjectileAgeLimit.java @@ -4,11 +4,13 @@ import me.xginko.aef.utils.EntityUtil; import org.bukkit.World; import org.bukkit.entity.Entity; +import org.bukkit.scheduler.BukkitTask; public class ProjectileAgeLimit extends AEFModule implements Runnable { - private final long check_period_in_ticks; + private final long checkPeriod; private final int max_alive_time; + private BukkitTask bukkitTask; public ProjectileAgeLimit() { super("lag-preventions.entity-age-limits.projectile-limit"); @@ -20,13 +22,13 @@ public ProjectileAgeLimit() { "limit section."); this.max_alive_time = config.getInt(configPath + ".max-alive-time-ticks", 300, "(20 ticks = 1 second) Will not touch Ender Pearls"); - this.check_period_in_ticks = config.getInt(configPath + ".check-period-seconds", 20, + this.checkPeriod = config.getInt(configPath + ".check-period-seconds", 20, "How frequently we should check all projectiles for their alive time") * 20L; } @Override public void enable() { - plugin.getServer().getScheduler().scheduleSyncRepeatingTask(plugin, this, 20L, check_period_in_ticks); + bukkitTask = plugin.getServer().getScheduler().runTaskTimer(plugin, this, checkPeriod, checkPeriod); } @Override @@ -34,6 +36,11 @@ public boolean shouldEnable() { return config.getBoolean(configPath + ".enable", false); } + @Override + public void disable() { + if (bukkitTask != null) bukkitTask.cancel(); + } + @Override public void run() { for (World world : plugin.getServer().getWorlds()) { diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/lowtpsphysics/BlockMelting.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/lowtpsphysics/BlockMelting.java deleted file mode 100755 index 1c53d6cf0..000000000 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/lowtpsphysics/BlockMelting.java +++ /dev/null @@ -1,38 +0,0 @@ -package me.xginko.aef.modules.lagpreventions.lowtpsphysics; - -import me.xginko.aef.AnarchyExploitFixes; -import me.xginko.aef.modules.AEFModule; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.Listener; -import org.bukkit.event.block.BlockFadeEvent; - -public class BlockMelting extends AEFModule implements Listener { - - private final double disableMeltingTPS; - private final boolean logIsEnabled; - - public BlockMelting() { - super("lag-preventions.disable-physics-during-low-tps.melting-blocks"); - this.disableMeltingTPS = config.getDouble(configPath + ".disable-TPS", 16.0); - this.logIsEnabled = config.getBoolean(configPath + ".log", false); - } - - @Override - public void enable() { - plugin.getServer().getPluginManager().registerEvents(this, plugin); - } - - @Override - public boolean shouldEnable() { - return config.getBoolean(configPath + ".enable", false); - } - - @EventHandler(priority = EventPriority.HIGHEST) - private void onBlockFade(BlockFadeEvent event) { - if (AnarchyExploitFixes.getTickReporter().getTPS() <= disableMeltingTPS) { - event.setCancelled(true); - if (logIsEnabled) info("Stopped block melting because tps is lower than " + disableMeltingTPS); - } - } -} diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/lowtpsphysics/BlockPhysics.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/lowtpsphysics/BlockPhysics.java deleted file mode 100755 index a27f0dd25..000000000 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/lowtpsphysics/BlockPhysics.java +++ /dev/null @@ -1,38 +0,0 @@ -package me.xginko.aef.modules.lagpreventions.lowtpsphysics; - -import me.xginko.aef.AnarchyExploitFixes; -import me.xginko.aef.modules.AEFModule; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.Listener; -import org.bukkit.event.block.BlockPhysicsEvent; - -public class BlockPhysics extends AEFModule implements Listener { - - private final double disablePhysicsTPS; - private final boolean logIsEnabled; - - public BlockPhysics() { - super("lag-preventions.disable-physics-during-low-tps.block-physics"); - this.disablePhysicsTPS = config.getDouble(configPath + ".disable-TPS", 16.0); - this.logIsEnabled = config.getBoolean(configPath + ".log", false); - } - - @Override - public void enable() { - plugin.getServer().getPluginManager().registerEvents(this, plugin); - } - - @Override - public boolean shouldEnable() { - return config.getBoolean(configPath + ".enable", false); - } - - @EventHandler(priority = EventPriority.LOWEST) - private void onEntityChange(BlockPhysicsEvent event) { - if (AnarchyExploitFixes.getTickReporter().getTPS() <= disablePhysicsTPS) { - event.setCancelled(true); - if (logIsEnabled) info("Cancelled block physics because TPS is lower than " + disablePhysicsTPS); - } - } -} diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/lowtpsphysics/BlockSpread.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/lowtpsphysics/BlockSpread.java deleted file mode 100755 index 89376bee2..000000000 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/lowtpsphysics/BlockSpread.java +++ /dev/null @@ -1,47 +0,0 @@ -package me.xginko.aef.modules.lagpreventions.lowtpsphysics; - -import me.xginko.aef.AnarchyExploitFixes; -import me.xginko.aef.modules.AEFModule; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.Listener; -import org.bukkit.event.block.BlockFormEvent; -import org.bukkit.event.block.BlockSpreadEvent; - -public class BlockSpread extends AEFModule implements Listener { - - private final double disableGrassTPS; - private final boolean logIsEnabled; - - public BlockSpread() { - super("lag-preventions.disable-physics-during-low-tps.block-spread"); - this.disableGrassTPS = config.getDouble(configPath + ".disable-TPS", 14.0); - this.logIsEnabled = config.getBoolean(configPath + ".log", false); - } - - @Override - public void enable() { - plugin.getServer().getPluginManager().registerEvents(this, plugin); - } - - @Override - public boolean shouldEnable() { - return config.getBoolean(configPath + ".enable", false); - } - - @EventHandler(priority = EventPriority.LOWEST) - private void onBlockForm(BlockFormEvent event) { - if (AnarchyExploitFixes.getTickReporter().getTPS() <= disableGrassTPS) { - event.setCancelled(true); - if (logIsEnabled) info("Stopped block spread because tps is lower than " + disableGrassTPS); - } - } - - @EventHandler(priority = EventPriority.LOWEST) - private void onBlockForm(BlockSpreadEvent event) { - if (AnarchyExploitFixes.getTickReporter().getTPS() <= disableGrassTPS) { - event.setCancelled(true); - if (logIsEnabled) info("Stopped block spread because tps is lower than " + disableGrassTPS); - } - } -} diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/lowtpsphysics/Explosions.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/lowtpsphysics/Explosions.java deleted file mode 100755 index 0aef9e123..000000000 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/lowtpsphysics/Explosions.java +++ /dev/null @@ -1,58 +0,0 @@ -package me.xginko.aef.modules.lagpreventions.lowtpsphysics; - -import me.xginko.aef.AnarchyExploitFixes; -import me.xginko.aef.modules.AEFModule; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.Listener; -import org.bukkit.event.block.BlockExplodeEvent; -import org.bukkit.event.entity.EntityExplodeEvent; -import org.bukkit.event.entity.ExplosionPrimeEvent; - -public class Explosions extends AEFModule implements Listener { - - private final double disableExplosionsTPS; - private final boolean logIsEnabled; - - public Explosions() { - super("lag-preventions.disable-physics-during-low-tps.explosions"); - config.addComment(configPath + ".enable", - "Disable explosions during low tps to combat lag."); - this.disableExplosionsTPS = config.getDouble(configPath + ".disable-TPS", 14.0); - this.logIsEnabled = config.getBoolean(configPath + ".log", false); - } - - @Override - public void enable() { - plugin.getServer().getPluginManager().registerEvents(this, plugin); - } - - @Override - public boolean shouldEnable() { - return config.getBoolean(configPath + ".enable", false); - } - - @EventHandler(priority = EventPriority.LOWEST) - private void onEntityExplode(EntityExplodeEvent event) { - if (AnarchyExploitFixes.getTickReporter().getTPS() <= disableExplosionsTPS) { - event.setCancelled(true); - if (logIsEnabled) info("Cancelled explosion because tps is lower than " + disableExplosionsTPS); - } - } - - @EventHandler(priority = EventPriority.LOWEST) - private void onExplodePrime(ExplosionPrimeEvent event) { - if (AnarchyExploitFixes.getTickReporter().getTPS() <= disableExplosionsTPS) { - event.setCancelled(true); - if (logIsEnabled) info("Cancelled explosion because tps is lower than " + disableExplosionsTPS); - } - } - - @EventHandler(priority = EventPriority.LOWEST) - private void onBlockExplode(BlockExplodeEvent event) { - if (AnarchyExploitFixes.getTickReporter().getTPS() <= disableExplosionsTPS) { - event.setCancelled(true); - if (logIsEnabled) info("Cancelled explosion because tps is lower than " + disableExplosionsTPS); - } - } -} diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/lowtpsphysics/FireSpread.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/lowtpsphysics/FireSpread.java deleted file mode 100755 index f6e944aba..000000000 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/lowtpsphysics/FireSpread.java +++ /dev/null @@ -1,38 +0,0 @@ -package me.xginko.aef.modules.lagpreventions.lowtpsphysics; - -import me.xginko.aef.AnarchyExploitFixes; -import me.xginko.aef.modules.AEFModule; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.Listener; -import org.bukkit.event.block.BlockIgniteEvent; - -public class FireSpread extends AEFModule implements Listener { - - private final double disableFireTPS; - private final boolean logIsEnabled; - - public FireSpread() { - super("lag-preventions.disable-physics-during-low-tps.fire-spread"); - this.disableFireTPS = config.getDouble(configPath + ".disable-TPS", 14.0); - this.logIsEnabled = config.getBoolean(configPath + ".log", false); - } - - @Override - public void enable() { - plugin.getServer().getPluginManager().registerEvents(this, plugin); - } - - @Override - public boolean shouldEnable() { - return config.getBoolean(configPath + ".enable", false); - } - - @EventHandler(priority = EventPriority.LOWEST) - private void onLiquidSpread(BlockIgniteEvent event) { - if (AnarchyExploitFixes.getTickReporter().getTPS() <= disableFireTPS) { - event.setCancelled(true); - if (logIsEnabled) info("Stopped fire spread because tps is lower than " + disableFireTPS); - } - } -} diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/lowtpsphysics/LeaveDecay.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/lowtpsphysics/LeaveDecay.java deleted file mode 100755 index 69e24f07f..000000000 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/lowtpsphysics/LeaveDecay.java +++ /dev/null @@ -1,38 +0,0 @@ -package me.xginko.aef.modules.lagpreventions.lowtpsphysics; - -import me.xginko.aef.AnarchyExploitFixes; -import me.xginko.aef.modules.AEFModule; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.Listener; -import org.bukkit.event.block.LeavesDecayEvent; - -public class LeaveDecay extends AEFModule implements Listener { - - private final double disableLeaveDecayTPS; - private final boolean logIsEnabled; - - public LeaveDecay() { - super("lag-preventions.disable-physics-during-low-tps.leave-decay"); - this.disableLeaveDecayTPS = config.getDouble(configPath + ".disable-TPS", 14.0); - this.logIsEnabled = config.getBoolean(configPath + ".log", false); - } - - @Override - public void enable() { - plugin.getServer().getPluginManager().registerEvents(this, plugin); - } - - @Override - public boolean shouldEnable() { - return config.getBoolean(configPath + ".enable", false); - } - - @EventHandler(priority = EventPriority.LOWEST) - private void onLeaveDecay(LeavesDecayEvent event) { - if (AnarchyExploitFixes.getTickReporter().getTPS() <= disableLeaveDecayTPS) { - event.setCancelled(true); - if (logIsEnabled) info("Cancelled leave decay because tps is lower than " + disableLeaveDecayTPS); - } - } -} diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/lowtpsphysics/LiquidSpread.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/lowtpsphysics/LiquidSpread.java deleted file mode 100755 index 28a1a169f..000000000 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/lowtpsphysics/LiquidSpread.java +++ /dev/null @@ -1,38 +0,0 @@ -package me.xginko.aef.modules.lagpreventions.lowtpsphysics; - -import me.xginko.aef.AnarchyExploitFixes; -import me.xginko.aef.modules.AEFModule; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.Listener; -import org.bukkit.event.block.BlockFromToEvent; - -public class LiquidSpread extends AEFModule implements Listener { - - private final double disableLiquidsTPS; - private final boolean logIsEnabled; - - public LiquidSpread() { - super("lag-preventions.disable-physics-during-low-tps.liquid-spread"); - this.disableLiquidsTPS = config.getDouble(configPath + ".disable-TPS", 16.0); - this.logIsEnabled = config.getBoolean(configPath + ".log", false); - } - - @Override - public void enable() { - plugin.getServer().getPluginManager().registerEvents(this, plugin); - } - - @Override - public boolean shouldEnable() { - return config.getBoolean(configPath + ".enable", false); - } - - @EventHandler(priority = EventPriority.LOWEST) - private void onLiquidSpread(BlockFromToEvent event) { - if (AnarchyExploitFixes.getTickReporter().getTPS() <= disableLiquidsTPS) { - event.setCancelled(true); - if (logIsEnabled) info("Stopped liquid spread because tps is lower than " + disableLiquidsTPS); - } - } -} diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/lowtpsphysics/Noteblocks.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/lowtpsphysics/Noteblocks.java deleted file mode 100755 index 2dd189f61..000000000 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/lowtpsphysics/Noteblocks.java +++ /dev/null @@ -1,40 +0,0 @@ -package me.xginko.aef.modules.lagpreventions.lowtpsphysics; - -import me.xginko.aef.AnarchyExploitFixes; -import me.xginko.aef.modules.AEFModule; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.Listener; -import org.bukkit.event.block.NotePlayEvent; - -public class Noteblocks extends AEFModule implements Listener { - - private final double disableNoteblockTPS; - private final boolean logIsEnabled; - - public Noteblocks() { - super("lag-preventions.disable-physics-during-low-tps.noteblocks"); - config.addComment(configPath + ".enable", - "Some lag machines use noteblocks to work around redstone limitations."); - this.disableNoteblockTPS = config.getDouble(configPath + ".disable-TPS", 16.0); - this.logIsEnabled = config.getBoolean(configPath + ".log", false); - } - - @Override - public void enable() { - plugin.getServer().getPluginManager().registerEvents(this, plugin); - } - - @Override - public boolean shouldEnable() { - return config.getBoolean(configPath + ".enable", false); - } - - @EventHandler(priority = EventPriority.LOWEST) - private void onNoteblockGetsPlayed(NotePlayEvent event) { - if (AnarchyExploitFixes.getTickReporter().getTPS() <= disableNoteblockTPS) { - event.setCancelled(true); - if (logIsEnabled) info("Cancelled noteblocks playing because tps is lower than " + disableNoteblockTPS); - } - } -} diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/lowtpsphysics/Redstone.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/lowtpsphysics/Redstone.java deleted file mode 100755 index b1a27e447..000000000 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/lowtpsphysics/Redstone.java +++ /dev/null @@ -1,58 +0,0 @@ -package me.xginko.aef.modules.lagpreventions.lowtpsphysics; - -import me.xginko.aef.AnarchyExploitFixes; -import me.xginko.aef.modules.AEFModule; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.Listener; -import org.bukkit.event.block.BlockPistonExtendEvent; -import org.bukkit.event.block.BlockPistonRetractEvent; -import org.bukkit.event.block.BlockRedstoneEvent; - -public class Redstone extends AEFModule implements Listener { - - private final double disableRedstoneTPS; - private final boolean logIsEnabled; - - public Redstone() { - super("lag-preventions.disable-physics-during-low-tps.redstone"); - config.addComment(configPath + ".enable", - "Disable redstone during low TPS to prevent some lag machines."); - this.disableRedstoneTPS = config.getDouble(configPath + ".disable-TPS", 16.0); - this.logIsEnabled = config.getBoolean(configPath + ".log", false); - } - - @Override - public void enable() { - plugin.getServer().getPluginManager().registerEvents(this, plugin); - } - - @Override - public boolean shouldEnable() { - return config.getBoolean(configPath + ".enable", false); - } - - @EventHandler(priority = EventPriority.LOWEST) - private void onRedstoneEvent(BlockRedstoneEvent event) { - if (AnarchyExploitFixes.getTickReporter().getTPS() <= disableRedstoneTPS) { - event.setNewCurrent(0); - if (logIsEnabled) info("Disabled redstone because tps is lower than " + disableRedstoneTPS); - } - } - - @EventHandler(priority = EventPriority.LOWEST) - private void onPistonExtendEvent(BlockPistonExtendEvent event) { - if (AnarchyExploitFixes.getTickReporter().getTPS() <= disableRedstoneTPS) { - event.setCancelled(true); - if (logIsEnabled) info("Cancelled piston event because tps is lower than " + disableRedstoneTPS); - } - } - - @EventHandler(priority = EventPriority.LOWEST) - private void onPistonRetractEvent(BlockPistonRetractEvent event) { - if (AnarchyExploitFixes.getTickReporter().getTPS() <= disableRedstoneTPS) { - event.setCancelled(true); - if (logIsEnabled) info("Cancelled piston event because tps is lower than " + disableRedstoneTPS); - } - } -} diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/BlockFormOrGrow.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/BlockFormOrGrow.java new file mode 100755 index 000000000..9347d3a20 --- /dev/null +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/BlockFormOrGrow.java @@ -0,0 +1,53 @@ +package me.xginko.aef.modules.lagpreventions.regionalactivity; + +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.BlockFormEvent; +import org.bukkit.event.block.BlockSpreadEvent; + +public class BlockFormOrGrow extends RegionalActivityModule { + + private final int limit; + + public BlockFormOrGrow() { + super( + "block-spread", + false, + 1500.0, + 6000, + 10000, + 10.0, + 120.0 + ); + config.addComment(configPath+".enable", + "Limits blocks spreading or forming based on world conditions within a\n" + + "configurable radius and timeframe to help reduce lag by cancelling burst\n" + + "activity hotspots.\n" + + "\n" + + "Examples:\n" + + "\n" + + "- Snow forming due to a snow storm.\n" + + "- Ice forming in a snowy Biome like Taiga or Tundra.\n" + + "- Obsidian / Cobblestone forming due to contact with water.\n" + + "- Concrete forming due to mixing of concrete powder and water.\n" + + "- Mushrooms spreading.\n" + + "- Fire spreading."); + this.limit = config.getInt(configPath + ".block-form-event-limit", 800, + "Maximum number of times a block can form or spread within the configured\n" + + "timeframe before activity will be put on cooldown."); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onBlockForm(BlockFormEvent event) { + if (shouldCancelActivity(event, event.getBlock().getLocation(), limit)) { + event.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onBlockSpread(BlockSpreadEvent event) { + if (shouldCancelActivity(event, event.getBlock().getLocation(), limit)) { + event.setCancelled(true); + } + } +} diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/BlockPhysics.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/BlockPhysics.java new file mode 100755 index 000000000..2fa898a08 --- /dev/null +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/BlockPhysics.java @@ -0,0 +1,44 @@ +package me.xginko.aef.modules.lagpreventions.regionalactivity; + +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.BlockPhysicsEvent; + +public class BlockPhysics extends RegionalActivityModule { + + private final int limit; + + public BlockPhysics() { + super( + "block-physics", + true, + 1500.0, + 18000, + 20000, + 10.0, + 120.0 + ); + this.config.addComment(configPath+".enable", + "Limits block physics within a configurable radius and timeframe\n" + + "to help reduce lag by cancelling burst activity hotspots.\n" + + "\n" + + "Note:\n" + + "\n" + + "The event used for this check (BlockPhysicsEvent) is a high frequency event,\n" + + "it may be called thousands of times per a second on a busy server.\n" + + "Where possible the event may also only be called for the \"root\" block of\n" + + "physics updates in order to limit event spam.\n" + + "Physics updates that cause other blocks to change their state may not result\n" + + "in an event for each of those blocks (usually adjacent)."); + this.limit = config.getInt(configPath + ".block-physics-event-limit", 256000, + "Maximum number of times a physics check can be performed within the configured\n" + + "timeframe before they will be put on cooldown."); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onBlockPhysics(BlockPhysicsEvent event) { + if (shouldCancelActivity(event, event.getBlock().getLocation(), limit)) { + event.setCancelled(true); + } + } +} diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/EntitySpawns.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/EntitySpawns.java new file mode 100644 index 000000000..ea98637a3 --- /dev/null +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/EntitySpawns.java @@ -0,0 +1,40 @@ +package me.xginko.aef.modules.lagpreventions.regionalactivity; + +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.entity.EntitySpawnEvent; + +public class EntitySpawns extends RegionalActivityModule { + + private final int limit; + + public EntitySpawns() { + super( + "creature-spawn", + true, + 1500.0, + 18000, + 20000, + 10.0, + 120.0 + ); + this.config.addComment(configPath + ".enable", + "Limits entity spawning activity within a configurable radius and timeframe\n" + + "to help reduce lag by cancelling high activity hotspots.\n" + + "\n" + + "Examples:\n" + + "\n" + + "- A creature gets spawned naturally, by spawner or other reasons.\n" + + "- An entity gets spawned naturally, by spawner or other reasons.\n" + + "This does not include tile entities."); + this.limit = config.getInt(configPath + ".spawn-event-limit", 6000, + "Maximum number of entity spawns within configured timeframe."); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onEntitySpawn(EntitySpawnEvent event) { + if (shouldCancelActivity(event, event.getLocation(), limit)) { + event.setCancelled(true); + } + } +} diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/TargetDistanceLimit.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/EntityTargeting.java similarity index 59% rename from AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/TargetDistanceLimit.java rename to AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/EntityTargeting.java index 9a7f01004..7e15a79f1 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/TargetDistanceLimit.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/EntityTargeting.java @@ -1,9 +1,9 @@ -package me.xginko.aef.modules.lagpreventions; +package me.xginko.aef.modules.lagpreventions.regionalactivity; import com.cryptomorin.xseries.XEntityType; import io.github.thatsmusic99.configurationmaster.api.ConfigSection; -import me.xginko.aef.modules.AEFModule; import me.xginko.aef.utils.LocationUtil; +import org.bukkit.Location; import org.bukkit.entity.Entity; import org.bukkit.entity.EntityType; import org.bukkit.event.EventHandler; @@ -16,23 +16,37 @@ import java.util.Map; import java.util.TreeMap; -public class TargetDistanceLimit extends AEFModule implements Listener { +public class EntityTargeting extends RegionalActivityModule implements Listener { private final Map limitedTypes = new EnumMap<>(EntityType.class); private final double globalMaxDistanceSquared; - private final boolean logIsEnabled, globalDistanceEnabled, perTypeDistanceEnabled; + private final int limit; + private final boolean globalDistanceEnabled, perTypeDistanceEnabled; - public TargetDistanceLimit() { - super("lag-preventions.target-distance-limits"); - this.logIsEnabled = config.getBoolean(configPath + ".log", false); - this.globalDistanceEnabled = config.getBoolean(configPath + ".global-limit.enable", false); + public EntityTargeting() { + super("entity-targeting", + true, + 1500.0, + 18000, + 20000, + 14.0, + 120.0 + ); + this.config.addComment(configPath+".enable", + "Limits entities targeting other entities within a configurable radius\n" + + "and timeframe to help reduce lag by cancelling burst activity hotspots."); + this.limit = config.getInt(configPath + ".entity-target-event-limit", 8000, + "Maximum number of times an entity can target another entity within the\n" + + "configured timeframe before the area will be put on cooldown."); + + this.globalDistanceEnabled = config.getBoolean(configPath + ".distance-limit.global-limit.enable", false); this.globalMaxDistanceSquared = NumberConversions.square( - config.getDouble(configPath + ".global-limit.max-target-distance", 20.0, + config.getDouble(configPath + ".distance-limit.global-limit.max-target-distance", 20.0, "The max distance no target should exceed.\n" + "You want this to be higher than your highest max distance\n" + "for a specific mob.")); - this.perTypeDistanceEnabled = config.getBoolean(configPath + ".custom-limits.enable", true); + this.perTypeDistanceEnabled = config.getBoolean(configPath + ".distance-limit.custom-limits.enable", true); Map defaults = new EnumMap<>(XEntityType.class); defaults.put(XEntityType.ZOMBIE, 6.0); defaults.put(XEntityType.SKELETON, 6.0); @@ -40,15 +54,13 @@ public TargetDistanceLimit() { defaults.put(XEntityType.ZOMBIE_VILLAGER, 10.0); defaults.put(XEntityType.ZOMBIFIED_PIGLIN, 8.0); defaults.put(XEntityType.WITHER, 8.0); - Map versionDefaults = new TreeMap<>(); for (Map.Entry entry : defaults.entrySet()) { if (entry.getKey().isSupported()) { versionDefaults.put(entry.getKey().get().name(), entry.getValue()); } } - - ConfigSection section = config.getConfigSection(configPath + ".custom-limits.entities", versionDefaults); + ConfigSection section = config.getConfigSection(configPath + ".distance-limit.custom-limits.entities", versionDefaults); for (String configuredEntity : section.getKeys(false)) { try { Double maxDistanceSquared = NumberConversions.square(Double.parseDouble(section.getString(configuredEntity))); @@ -62,29 +74,35 @@ public TargetDistanceLimit() { } } - @Override - public void enable() { - plugin.getServer().getPluginManager().registerEvents(this, plugin); - } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onEntityTarget(EntityTargetEvent event) { + Entity targetEntity = event.getTarget(); + if (targetEntity == null) return; // If entity un-targets an entity, it's good for us. - @Override - public boolean shouldEnable() { - return config.getBoolean(configPath + ".enable", false); - } + Location location = event.getEntity().getLocation(); + if (shouldCancelActivity(event, location, limit)) { + event.setCancelled(true); + return; + } - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - private void onTargetAcquire(EntityTargetEvent event) { - final Entity targetEntity = event.getTarget(); - if (targetEntity == null) return; + if (!globalDistanceEnabled && !perTypeDistanceEnabled) { + return; + } - final double targetDistanceSquared = event.getEntity().getLocation().distanceSquared(targetEntity.getLocation()); + double targetDistanceSquared; + try { + targetDistanceSquared = event.getEntity().getLocation().distanceSquared(targetEntity.getLocation()); + } catch (IllegalArgumentException e) { + if (logIsEnabled) error("Unable to measure distance between entity and target.", e); + return; + } if (globalDistanceEnabled) { if (targetDistanceSquared > globalMaxDistanceSquared) { event.setCancelled(true); event.setTarget(null); - if (logIsEnabled) info("Cancelled target acquire for entity " + event.getEntityType() + " at " + - LocationUtil.toString(event.getEntity().getLocation()) + + if (logIsEnabled) info("Cancelled target acquire for entity " + event.getEntityType().name() + " at " + + LocationUtil.toString(location) + " because target is further than the global limit. Distance: " + Math.sqrt(targetDistanceSquared)); return; } @@ -94,8 +112,8 @@ private void onTargetAcquire(EntityTargetEvent event) { if (limitedTypes.containsKey(event.getEntityType()) && targetDistanceSquared > limitedTypes.get(event.getEntityType())) { event.setCancelled(true); event.setTarget(null); - if (logIsEnabled) info("Cancelled target acquire for entity " + event.getEntityType() + " at " + - LocationUtil.toString(event.getEntity().getLocation()) + + if (logIsEnabled) info("Cancelled target acquire for entity " + event.getEntityType().name() + " at " + + LocationUtil.toString(location) + " because target further than its configured limit. Distance: " + Math.sqrt(targetDistanceSquared)); } } diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/Explosions.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/Explosions.java new file mode 100644 index 000000000..d9a232bdd --- /dev/null +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/Explosions.java @@ -0,0 +1,57 @@ +package me.xginko.aef.modules.lagpreventions.regionalactivity; + +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.BlockExplodeEvent; +import org.bukkit.event.entity.EntityExplodeEvent; +import org.bukkit.event.entity.ExplosionPrimeEvent; + +public class Explosions extends RegionalActivityModule { + + private final int limit; + + public Explosions() { + super( + "explosions", + false, + 1500.0, + 6000, + 10000, + 10.0, + 120.0 + ); + this.config.addComment(configPath + ".enable", + "Limits explosions within a configurable radius and timeframe\n" + + "to help reduce lag by cancelling high activity hotspots.\n" + + "\n" + + "Examples:\n" + + "\n" + + "- A block exploding.\n" + + "- An entity exploding.\n" + + "- An entity making the decision to explode.\n"); + this.limit = config.getInt(configPath + ".explode-event-limit", 500, + "Maximum number of explode events within the configured timeframe\n" + + "before the region will be put on cooldown."); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onExplosionPrime(ExplosionPrimeEvent event) { + if (shouldCancelActivity(event, event.getEntity().getLocation(), limit)) { + event.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onEntityExplode(EntityExplodeEvent event) { + if (shouldCancelActivity(event, event.getLocation(), limit)) { + event.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onBlockExplode(BlockExplodeEvent event) { + if (shouldCancelActivity(event, event.getBlock().getLocation(), limit)) { + event.setCancelled(true); + } + } +} diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/LiquidSpread.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/LiquidSpread.java new file mode 100755 index 000000000..37d94c326 --- /dev/null +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/LiquidSpread.java @@ -0,0 +1,46 @@ +package me.xginko.aef.modules.lagpreventions.regionalactivity; + +import com.cryptomorin.xseries.XMaterial; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.BlockFromToEvent; + +public class LiquidSpread extends RegionalActivityModule { + + private final int limit; + private final boolean ignoreDragonEgg; + + public LiquidSpread() { + super( + "liquid-spread", + true, + 1500.0, + 18000, + 20000, + 12.0, + 100.0 + ); + this.config.addComment(configPath+".enable", + "Limits liquid spreading within a configurable radius and timeframe\n" + + "to help reduce lag by cancelling high activity hotspots.\n" + + "\n" + + "Examples:\n" + + "\n" + + "- A lava block spreading by flowing.\n" + + "- A water block spreading by flowing.\n" + + "- (optional) A dragon egg is teleporting from one position to another."); + this.limit = config.getInt(configPath + ".liquid-spread-event-limit", 2400, + "Maximum number of times liquids are allowed to spread within the configured\n" + + "timeframe before they will be put on cooldown."); + this.ignoreDragonEgg = config.getBoolean(configPath + ".ignore-dragon-egg", true); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onBlockFromTo(BlockFromToEvent event) { + if (ignoreDragonEgg && event.getBlock().getType() == XMaterial.DRAGON_EGG.parseMaterial()) return; + + if (shouldCancelActivity(event, event.getBlock().getLocation(), limit)) { + event.setCancelled(true); + } + } +} diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/Noteblocks.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/Noteblocks.java new file mode 100755 index 000000000..22c976582 --- /dev/null +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/Noteblocks.java @@ -0,0 +1,40 @@ +package me.xginko.aef.modules.lagpreventions.regionalactivity; + +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.NotePlayEvent; + +public class Noteblocks extends RegionalActivityModule { + + private final int limit; + + public Noteblocks() { + super( + "noteblocks", + false, + 1500.0, + 6000, + 10000, + 10.0, + 120.0 + ); + this.config.addComment(configPath+".enable", + "Limits noteblocks being played within a configurable radius and timeframe\n" + + "to help reduce lag by cancelling high activity hotspots.\n" + + "\n" + + "Examples:\n" + + "\n" + + "- A noteblock is being played through player interaction.\n" + + "- A noteblock is being played through a redstone current.\n"); + this.limit = config.getInt(configPath + ".noteblock-play-limit", 2800, + "Maximum number of times a noteblock can be played within the configured\n" + + "timeframe before they will be put on cooldown."); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onNotePlay(NotePlayEvent event) { + if (shouldCancelActivity(event, event.getBlock().getLocation(), limit)) { + event.setCancelled(true); + } + } +} diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/PathfindingLimits.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/Pathfinding.java similarity index 63% rename from AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/PathfindingLimits.java rename to AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/Pathfinding.java index e01f4df75..5e96bb85e 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/PathfindingLimits.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/Pathfinding.java @@ -1,9 +1,8 @@ -package me.xginko.aef.modules.lagpreventions; +package me.xginko.aef.modules.lagpreventions.regionalactivity; import com.cryptomorin.xseries.XEntityType; import com.destroystokyo.paper.event.entity.EntityPathfindEvent; import io.github.thatsmusic99.configurationmaster.api.ConfigSection; -import me.xginko.aef.modules.AEFModule; import me.xginko.aef.utils.LocationUtil; import org.bukkit.entity.EntityType; import org.bukkit.event.EventHandler; @@ -15,23 +14,40 @@ import java.util.Map; import java.util.TreeMap; -public class PathfindingLimits extends AEFModule implements Listener { +public class Pathfinding extends RegionalActivityModule implements Listener { private final Map limitedTypes = new EnumMap<>(EntityType.class); - private final boolean logIsEnabled, globalDistanceEnabled, perTypeDistanceEnabled; private final double globalMaxDistanceSquared; + private final int limit; + private final boolean globalDistanceEnabled, perTypeDistanceEnabled; - public PathfindingLimits() { - super("lag-preventions.pathfinding-limits"); - this.logIsEnabled = config.getBoolean(configPath + ".log", false, "Only meant for debug."); - this.globalDistanceEnabled = config.getBoolean(configPath + ".global-limit.enable", false); + public Pathfinding() { + super( + "entity-pathfinding", + false, + 1500.0, + 6000, + 10000, + 14.0, + 120.0 + ); + this.config.addComment(configPath+".enable", + "Limits entities deciding to pathfind to a specific location\n" + + "within a configurable radius and timeframe to help reduce lag\n" + + "by cancelling burst activity hotspots."); + this.limit = config.getInt(configPath + ".entity-pathfind-event-limit", 4000, + "Maximum number of times an entity can decide to start moving\n" + + "towards a location within the configured timeframe before the\n" + + "area will be put on cooldown."); + + this.globalDistanceEnabled = config.getBoolean(configPath + ".distance-limit.global-limit.enable", false); this.globalMaxDistanceSquared = NumberConversions.square( - config.getDouble(configPath + ".global-limit.max-target-distance", 20.0, + config.getDouble(configPath + ".distance-limit.global-limit.max-target-distance", 20.0, "The max distance no mob pathfinding should exceed.\n" + "You always want this to be higher than your highest max distance\n" + "for a specific mob.")); - this.perTypeDistanceEnabled = config.getBoolean(configPath + ".custom-limits.enable", true); + this.perTypeDistanceEnabled = config.getBoolean(configPath + ".distance-limit.custom-limits.enable", true); Map defaults = new EnumMap<>(XEntityType.class); defaults.put(XEntityType.ZOMBIE, 6.0); defaults.put(XEntityType.SKELETON, 6.0); @@ -39,18 +55,16 @@ public PathfindingLimits() { defaults.put(XEntityType.ZOMBIE_VILLAGER, 10.0); defaults.put(XEntityType.ZOMBIFIED_PIGLIN, 8.0); defaults.put(XEntityType.WITHER, 8.0); - Map versionDefaults = new TreeMap<>(); for (Map.Entry entry : defaults.entrySet()) { if (entry.getKey().isSupported()) { versionDefaults.put(entry.getKey().get().name(), entry.getValue()); } } - - ConfigSection section = config.getConfigSection(configPath + ".custom-limits.entities", versionDefaults); + ConfigSection section = config.getConfigSection(configPath + ".distance-limit.custom-limits.entities", versionDefaults); for (String configuredEntity : section.getKeys(false)) { try { - Double maxDistanceSquared = NumberConversions.square(Double.valueOf(section.getString(configuredEntity))); + Double maxDistanceSquared = NumberConversions.square(Double.parseDouble(section.getString(configuredEntity))); EntityType limitedEntity = EntityType.valueOf(configuredEntity); this.limitedTypes.put(limitedEntity, maxDistanceSquared); } catch (NumberFormatException e) { @@ -61,19 +75,24 @@ public PathfindingLimits() { } } - @Override - public void enable() { - plugin.getServer().getPluginManager().registerEvents(this, plugin); - } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onEntityPathfind(EntityPathfindEvent event) { + if (shouldCancelActivity(event, event.getEntity().getLocation(), limit)) { + event.setCancelled(true); + return; + } - @Override - public boolean shouldEnable() { - return config.getBoolean(configPath + ".enable", false); - } + if (!globalDistanceEnabled && !perTypeDistanceEnabled) { + return; + } - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - private void onPathfind(EntityPathfindEvent event) { - final double targetDistanceSquared = event.getEntity().getLocation().distanceSquared(event.getLoc()); + double targetDistanceSquared; + try { + targetDistanceSquared = event.getEntity().getLocation().distanceSquared(event.getLoc()); + } catch (IllegalArgumentException e) { + if (logIsEnabled) error("Unable to measure distance between entity and target.", e); + return; + } if (globalDistanceEnabled) { if (targetDistanceSquared > globalMaxDistanceSquared) { diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/Pistons.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/Pistons.java new file mode 100644 index 000000000..7d6277102 --- /dev/null +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/Pistons.java @@ -0,0 +1,48 @@ +package me.xginko.aef.modules.lagpreventions.regionalactivity; + +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.BlockPistonExtendEvent; +import org.bukkit.event.block.BlockPistonRetractEvent; + +public class Pistons extends RegionalActivityModule { + + private final int limit; + + public Pistons() { + super( + "pistons", + false, + 1500.0, + 6000, + 10000, + 10.0, + 120.0 + ); + this.config.addComment(configPath+".enable", + "Limits piston movement within a configurable radius and timeframe\n" + + "to help reduce lag by cancelling high activity hotspots.\n" + + "\n" + + "Examples:\n" + + "\n" + + "- A piston extends.\n" + + "- A piston retracts.\n"); + this.limit = config.getInt(configPath + ".piston-movement-limit", 1000, + "Maximum number of piston extend and/or retracts within the\n" + + "configured timeframe."); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onPistonExtend(BlockPistonExtendEvent event) { + if (shouldCancelActivity(event, event.getBlock().getLocation(), limit)) { + event.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onPistonRetract(BlockPistonRetractEvent event) { + if (shouldCancelActivity(event, event.getBlock().getLocation(), limit)) { + event.setCancelled(true); + } + } +} diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/Redstone.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/Redstone.java new file mode 100644 index 000000000..e694414ec --- /dev/null +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/Redstone.java @@ -0,0 +1,40 @@ +package me.xginko.aef.modules.lagpreventions.regionalactivity; + +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.BlockRedstoneEvent; + +public class Redstone extends RegionalActivityModule { + + private final int limit; + + public Redstone() { + super( + "redstone", + true, + 1500.0, + 18000, + 20000, + 10.0, + 120.0 + ); + this.config.addComment(configPath+".enable", + "Limits redstone activity within a configurable radius and timeframe\n" + + "to help reduce lag by cancelling high activity hotspots.\n" + + "\n" + + "Examples:\n" + + "\n" + + "- A redstone current changes.\n" + + "- A redstone block gets powered on.\n" + + "- A redstone block gets powered off.\n"); + this.limit = config.getInt(configPath + ".redstone-event-limit", 6000, + "Maximum number of redstone events within configured timeframe."); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onBlockRedstone(BlockRedstoneEvent event) { + if (shouldCancelActivity(event, event.getBlock().getLocation(), limit)) { + event.setNewCurrent(0); + } + } +} diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/RegionalActivityModule.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/RegionalActivityModule.java new file mode 100644 index 000000000..35c48e74d --- /dev/null +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/RegionalActivityModule.java @@ -0,0 +1,131 @@ +package me.xginko.aef.modules.lagpreventions.regionalactivity; + +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; +import me.xginko.aef.AnarchyExploitFixes; +import me.xginko.aef.modules.AEFModule; +import me.xginko.aef.utils.models.BlockRegion2D; +import org.bukkit.Location; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.jetbrains.annotations.NotNull; + +import java.time.Duration; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; + +/** + * Credits to the initial idea of measuring burst activity within a certain region + * of the world go to kumori (Soft1k) of 3b3t.org. + */ +public abstract class RegionalActivityModule extends AEFModule implements Listener { + + protected final Cache regionDataCache; + protected final long pauseTimeMillis; + protected final double checkRadius, pauseTPS, pauseMSPT; + protected final boolean logIsEnabled; + + public RegionalActivityModule( + String subConfigPath, boolean deflogEnabled, double defCheckRadius, + int defPauseMillis, int defCacheMillis, double defPauseTPS, double defPauseMSPT + ) { + super("lag-preventions.regional-activity."+subConfigPath); + String configPath = "lag-preventions.regional-activity."+subConfigPath; + this.logIsEnabled = config.getBoolean(configPath + ".log", deflogEnabled); + this.checkRadius = config.getDouble(configPath + ".check-radius-blocks", defCheckRadius, + "The radius in blocks in which activity will be grouped together and measured."); + this.pauseTimeMillis = config.getInt(configPath + ".pause-time-millis", defPauseMillis, + "The time in milliseconds all related activity will be blocked if it exceeded\n" + + "the configured limit."); + this.regionDataCache = Caffeine.newBuilder().expireAfterWrite(Duration.ofMillis( + config.getInt(configPath + ".data-keep-time-millis", defCacheMillis, + "The time in milliseconds before a region and its data will be expired\n" + + "if no activity has been detected.\n" + + "For proper functionality, needs to be at least as long as your pause time."))).build(); + this.pauseTPS = config.getDouble(configPath + ".pause-TPS", defPauseTPS, + "The TPS at which to cancel the physics entirely."); + this.pauseMSPT = config.getDouble(configPath + ".pause-MSPT", defPauseMSPT, + "The MSPT at which to cancel the physics entirely."); + } + + @Override + public boolean shouldEnable() { + return config.getBoolean(configPath + ".enable", false); + } + + @Override + public void enable() { + plugin.getServer().getPluginManager().registerEvents(this, plugin); + } + + @Override + public void disable() { + HandlerList.unregisterAll(this); + regionDataCache.invalidateAll(); + } + + @SuppressWarnings("DataFlowIssue") + protected @NotNull RegionalActivityModule.RegionData getRegionData(Location location) { + return regionDataCache.get(getRegion(location), RegionData::new); + } + + public boolean shouldCancelActivity(Event event, Location location, int limit) { + double tps = AnarchyExploitFixes.getTickReporter().getTPS(); + double mspt = AnarchyExploitFixes.getTickReporter().getMSPT(); + + if (tps <= pauseTPS || mspt >= pauseMSPT) { + if (logIsEnabled) info("Cancelling " + event.getClass().getSimpleName() + " because server is lagging." + + " (tps="+String.format("%.4f", tps)+" | mspt="+String.format("%.4f", mspt)+")"); + return true; + } + + RegionData regionData = getRegionData(location); + + if (regionData.resumeTime.get() > System.currentTimeMillis()) { + return true; + } + + if (regionData.activityCount.incrementAndGet() > limit) { + if (logIsEnabled) { + info( "Disabling in a radius of " + checkRadius + " blocks from center at " + + "x=" + regionData.region.getCenterX() + ", z=" + regionData.region.getCenterZ() + + " in world " + location.getWorld().getName() + " for " + pauseTimeMillis + "ms, " + + "because of too high activity within the configured timeframe: " + + regionData.activityCount + " (limit: " + limit + ")"); + } + regionData.resumeTime.set(System.currentTimeMillis() + pauseTimeMillis); + regionData.activityCount.set(0); // Reset count when region is cooling down + return true; + } + + return false; + } + + public @NotNull BlockRegion2D getRegion(Location location) { + // Find and return region containing this location + for (Map.Entry regionDataEntry : regionDataCache.asMap().entrySet()) { + if (regionDataEntry.getKey().contains(location)) { + return regionDataEntry.getKey(); + } + } + // Create and cache region if none exists + BlockRegion2D region = BlockRegion2D.of(location.getWorld(), location.getX(), location.getZ(), checkRadius); + regionDataCache.put(region, new RegionData(region)); + return region; + } + + protected static class RegionData { + + public final BlockRegion2D region; + public final AtomicLong resumeTime; + public final AtomicInteger activityCount; + + public RegionData(BlockRegion2D region) { + this.region = region; + this.activityCount = new AtomicInteger(0); + this.resumeTime = new AtomicLong(0L); + } + } +} diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/SculkActivity.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/SculkActivity.java new file mode 100644 index 000000000..0d4896f80 --- /dev/null +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/regionalactivity/SculkActivity.java @@ -0,0 +1,79 @@ +package me.xginko.aef.modules.lagpreventions.regionalactivity; + +import com.cryptomorin.xseries.XMaterial; +import org.bukkit.Material; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.BlockPhysicsEvent; +import org.bukkit.event.block.BlockRedstoneEvent; + +import java.util.EnumSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class SculkActivity extends RegionalActivityModule { + + private final Set sculkBlocks; + private final int limit; + + public SculkActivity() { + super( + "sculk-sensor", + true, + 1500.0, + 18000, + 20000, + 10.0, + 120.0 + ); + this.config.addComment(configPath+".enable", + "Limits sculk activity within a configurable radius and timeframe\n" + + "to help reduce lag by cancelling high activity hotspots.\n" + + "\n" + + "Examples:\n" + + "\n" + + "- A redstone current changes for a sculk sensor.\n" + + "- A physics check is being performed for a sculk sensor."); + this.limit = config.getInt(configPath + ".sculk-event-limit", 800, + "Maximum number of sculk events within configured timeframe."); + List defaults = Stream.of( + XMaterial.SCULK_SENSOR, + XMaterial.SCULK_SHRIEKER, + XMaterial.CALIBRATED_SCULK_SENSOR) + .filter(XMaterial::isSupported) + .map(XMaterial::parseMaterial) + .map(Enum::name) + .collect(Collectors.toList()); + this.sculkBlocks = config.getList(configPath + ".sculk-blocks", defaults) + .stream() + .map(configuredType -> { + try { + return Material.valueOf(configuredType); + } catch (IllegalArgumentException e) { + notRecognized(Material.class, configuredType); + return null; + } + }) + .collect(Collectors.toCollection(() -> EnumSet.noneOf(Material.class))); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onBlockRedstone(BlockRedstoneEvent event) { + if (!sculkBlocks.contains(event.getBlock().getType())) return; + + if (shouldCancelActivity(event, event.getBlock().getLocation(), limit)) { + event.setNewCurrent(0); + } + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onBlockPhysics(BlockPhysicsEvent event) { + if (!sculkBlocks.contains(event.getBlock().getType())) return; + + if (shouldCancelActivity(event, event.getBlock().getLocation(), limit)) { + event.setCancelled(true); + } + } +} diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/misc/FirstJoinMessages.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/misc/FirstJoinMessages.java index d9e9864dd..4cbd07fe5 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/misc/FirstJoinMessages.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/misc/FirstJoinMessages.java @@ -6,6 +6,7 @@ import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerJoinEvent; @@ -41,6 +42,11 @@ public boolean shouldEnable() { return config.getBoolean(configPath + ".enable", false); } + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onPlayerJoin(PlayerJoinEvent event) { final Player joiningPlayer = event.getPlayer(); diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/misc/JoinLeaveMessages.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/misc/JoinLeaveMessages.java index 04d2febde..4d92756ff 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/misc/JoinLeaveMessages.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/misc/JoinLeaveMessages.java @@ -6,6 +6,7 @@ import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerQuitEvent; @@ -34,6 +35,11 @@ public boolean shouldEnable() { return config.getBoolean(configPath + ".enable", true); } + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onPlayerJoinEvent(PlayerJoinEvent event) { event.setJoinMessage(null); diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/misc/MaskKickMessages.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/misc/MaskKickMessages.java index c176bb786..93262194e 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/misc/MaskKickMessages.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/misc/MaskKickMessages.java @@ -4,6 +4,7 @@ import me.xginko.aef.modules.AEFModule; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerKickEvent; @@ -24,6 +25,11 @@ public boolean shouldEnable() { return config.getBoolean(configPath, false); } + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onPlayerKick(PlayerKickEvent event) { event.setReason(AnarchyExploitFixes.getLang(event.getPlayer().getLocale()).misc_MaskedKickMessage); diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/misc/PreventMessageKick.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/misc/PreventMessageKick.java index 4a41067be..24aa41e3e 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/misc/PreventMessageKick.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/misc/PreventMessageKick.java @@ -3,6 +3,7 @@ import me.xginko.aef.modules.AEFModule; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerKickEvent; @@ -35,6 +36,11 @@ public boolean shouldEnable() { return config.getBoolean(configPath + ".enable", false); } + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onPlayerKick(PlayerKickEvent event) { if (kickMessagesToListenTo.contains(event.getReason().toLowerCase())) { diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/packets/BeehiveCoordinates.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/packets/BeehiveCoordinates.java index a2ac420bc..2f8cfd8be 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/packets/BeehiveCoordinates.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/packets/BeehiveCoordinates.java @@ -7,6 +7,7 @@ import com.github.retrooper.packetevents.protocol.nbt.NBTList; import com.github.retrooper.packetevents.protocol.packettype.PacketType; import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerSetSlot; +import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerWindowItems; import java.util.Arrays; @@ -41,23 +42,40 @@ public boolean shouldEnable() { @Override public void onPacketSend(PacketSendEvent event) { - if (event.getPacketType() != PacketType.Play.Server.SET_SLOT) return; - WrapperPlayServerSetSlot packet = new WrapperPlayServerSetSlot(event); - ItemStack filtered = filterItemStack(packet.getItem()); - if (filtered == null) return; - packet.setItem(filtered); - event.markForReEncode(true); - } + if (event.isCancelled()) return; + + if (event.getPacketType() == PacketType.Play.Server.SET_SLOT) { + WrapperPlayServerSetSlot packet = new WrapperPlayServerSetSlot(event); + + ItemStack itemStack = packet.getItem(); + if (itemStack == null || itemStack.isEmpty()) return; + + NBTCompound nbt = itemStack.getNBT(); + if (fixCompound(nbt)) { + itemStack.setNBT(nbt); + packet.setItem(itemStack); + event.markForReEncode(true); + } + } - private ItemStack filterItemStack(ItemStack itemStack) { - if (itemStack == null || itemStack.isEmpty()) return null; - final NBTCompound rootCompound = itemStack.getNBT(); - if (!filterCompound(rootCompound)) return null; - itemStack.setNBT(rootCompound); - return itemStack; + if (event.getPacketType() == PacketType.Play.Server.WINDOW_ITEMS) { + WrapperPlayServerWindowItems packet = new WrapperPlayServerWindowItems(event); + + for (int i = 0; i < packet.getItems().size(); i++) { + ItemStack itemStack = packet.getItems().get(i); + if (itemStack == null || itemStack.isEmpty()) return; + + NBTCompound nbt = itemStack.getNBT(); + if (fixCompound(nbt)) { + itemStack.setNBT(nbt); + packet.getItems().set(i, itemStack); + event.markForReEncode(true); + } + } + } } - private boolean filterCompound(NBTCompound compound) { + private boolean fixCompound(NBTCompound compound) { if (compound == null || compound.isEmpty()) return false; boolean needsReEncode = false; @@ -93,7 +111,8 @@ private boolean filterCompound(NBTCompound compound) { for (int i = 0; i < items.size(); i++) { NBTCompound item = items.getTag(i); NBTCompound itemRootCompound = item.getCompoundTagOrNull("tag"); - if (filterCompound(itemRootCompound)) { + if (fixCompound(itemRootCompound)) { + item.setTag("tag", itemRootCompound); items.setTag(i, item); needsReEncode = true; } @@ -105,7 +124,7 @@ private boolean filterCompound(NBTCompound compound) { if (compound.getTags().containsKey("BlockEntityTag")) { NBTCompound blockEntityTag = compound.getCompoundTagOrNull("BlockEntityTag"); - if (filterCompound(blockEntityTag)) { + if (fixCompound(blockEntityTag)) { compound.setTag("BlockEntityTag", blockEntityTag); needsReEncode = true; } diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/packets/CraftingRecipeLag.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/packets/CraftingRecipeLag.java index c06696780..ef1e3c283 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/packets/CraftingRecipeLag.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/packets/CraftingRecipeLag.java @@ -35,6 +35,7 @@ public boolean shouldEnable() { @Override public void onPacketReceive(PacketReceiveEvent event) { + if (event.isCancelled()) return; if (event.getPacketType() != PacketType.Play.Client.CRAFT_RECIPE_REQUEST) return; if (recipeCooldowns.contains(event.getUser().getUUID())) { diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/packets/InventoryLag.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/packets/InventoryLag.java new file mode 100644 index 000000000..a25bc63b5 --- /dev/null +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/packets/InventoryLag.java @@ -0,0 +1,194 @@ +package me.xginko.aef.modules.packets; + +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; +import com.github.retrooper.packetevents.PacketEvents; +import com.github.retrooper.packetevents.event.PacketListenerPriority; +import com.github.retrooper.packetevents.event.PacketSendEvent; +import com.github.retrooper.packetevents.netty.buffer.ByteBufHelper; +import com.github.retrooper.packetevents.protocol.packettype.PacketType; +import com.github.retrooper.packetevents.protocol.packettype.PacketTypeCommon; +import me.xginko.aef.utils.EntityUtil; +import me.xginko.aef.utils.MaterialUtil; +import org.bukkit.entity.HumanEntity; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.event.block.Action; +import org.bukkit.event.inventory.InventoryOpenEvent; +import org.bukkit.event.player.PlayerInteractEntityEvent; +import org.bukkit.event.player.PlayerInteractEvent; + +import java.time.Duration; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; +import java.util.stream.Collectors; + +public class InventoryLag extends PacketModule implements Listener { + + private final Cache playerDataCache; + private final Set measuredPacketTypes; + private final long rateLimitBytes, lockoutBytes, lockoutMillis; + private final int screenOpenLimit, screenOpenDelay; + private final boolean closeInventory, log; + + public InventoryLag() { + super("patches.inventory-lag", PacketListenerPriority.HIGHEST); + config.addComment(configPath + ".enable", + "Checks if a player is requesting unusual amounts of traffic from the server\n" + + "using ItemStacks.\n" + + "If a player exceeds the limit, they will be put on a cooldown, during which\n" + + "they will be very limited in terms of ItemStack or Inventory interactions."); + this.log = config.getBoolean(configPath + ".log", false, + "For debug purposes. Don't leave enabled for too long as it is very spammy."); + this.closeInventory = config.getBoolean(configPath + ".close-open-inventory", true, + "Whether to immediately close any open inventory of the player on limit exceed\n" + + "Note: Closing has to be scheduled so it will take a bit if the server is heavily\n" + + "lagging."); + this.playerDataCache = Caffeine.newBuilder().expireAfterWrite(Duration.ofMillis(Math.max(1L, + config.getLong(configPath + ".byte-data-keep-time-millis", 30_000, + "The time in millis in which to check if the player exceeded the limit.\n" + + "Needs to be at least as long as your lockout duration millis.")))).build(); + this.rateLimitBytes = config.getLong(configPath + ".rate-limit.bytesize-limit", 8_000_000, + "The limit in bytes the server has sent the server in the form of ItemStacks,\n" + + "before the player will be put on a rate-limit.\n" + + "Should always be lower than your lockout bytesize limit."); + this.screenOpenDelay = config.getInt(configPath + ".rate-limit.timeframe-millis", 2500, + "The time in millis in which a player is allowed to open x amounts of windows\n" + + "but not more."); + this.screenOpenLimit = config.getInt(configPath + ".rate-limit.max-window-opens-per-timeframe", 2, + "The amount of windows that can be opened during the timeframe-millis."); + this.lockoutBytes = config.getLong(configPath + ".lockout.bytesize-limit", 24_000_000, + "The upper limit in bytes a player is allowed to request from the server\n" + + "within the configured timeframe before he will be put on cooldown.\n" + + "During the cooldown, he will not be able to open any inventory screens\n" + + "or interact with items."); + this.lockoutMillis = config.getLong(configPath + ".lockout.duration-millis", 15_000, + "The time in milliseconds the player will have to wait before\n" + + "being able to open an inventory again after he exceeded the limit."); + this.measuredPacketTypes = config.getList(configPath + ".check-packets", Arrays.asList("SET_SLOT", "WINDOW_ITEMS")) + .stream() + .map(configuredPacketType -> { + try { + return PacketType.Play.Server.valueOf(configuredPacketType); + } catch (IllegalArgumentException e) { + notRecognized(PacketType.Play.Server.class, configuredPacketType); + return null; + } + }) + .filter(Objects::nonNull) + .collect(Collectors.toCollection(HashSet::new)); + } + + @Override + public boolean shouldEnable() { + return config.getBoolean(configPath + ".enable", false); + } + + @Override + public void enable() { + plugin.getServer().getPluginManager().registerEvents(this, plugin); + PacketEvents.getAPI().getEventManager().registerListener(asAbstract); + } + + @Override + public void disable() { + HandlerList.unregisterAll(this); + PacketEvents.getAPI().getEventManager().unregisterListener(asAbstract); + } + + @Override + public void onPacketSend(PacketSendEvent event) { + if (event.isCancelled() || !measuredPacketTypes.contains(event.getPacketType())) return; + + PlayerData data = playerDataCache.get(event.getUser().getUUID(), PlayerData::new); + + long servedBytes = data.servedSetSlotBytes.addAndGet(ByteBufHelper.readableBytes(event.getByteBuf())); + if (log) info("Player '" + event.getUser().getName() + "' requested " + servedBytes + " bytes in ItemStacks. " + + "(PacketType: " + event.getPacketType() + ")"); + + if (servedBytes <= lockoutBytes) { + return; + } + + if (data.cooldownResumeTime.get() > System.currentTimeMillis()) { + event.setCancelled(true); + return; + } + + data.cooldownResumeTime.set(System.currentTimeMillis() + lockoutMillis); + + if (log) warn("Player '" + event.getUser().getName() + "' is now on LOCKOUT as they exceeded" + + " the set limit (now " + servedBytes + " bytes)."); + + if (closeInventory && event.getPlayer() != null) { + plugin.getServer().getScheduler().runTask(plugin, ((Player) event.getPlayer())::closeInventory); + } + } + + private void onInventoryOpen(Cancellable event, HumanEntity player) { + PlayerData data = playerDataCache.get(player.getUniqueId(), PlayerData::new); + + if (data.cooldownResumeTime.get() > System.currentTimeMillis()) { + event.setCancelled(true); + if (log) info("Player '" + player.getName() + "' could not open screen because they are on cooldown."); + return; + } + + long servedBytes = data.servedSetSlotBytes.get(); + + if ( + servedBytes > rateLimitBytes && servedBytes < lockoutBytes + && data.screenOpenCount.getAndIncrement() > screenOpenLimit + ) { + event.setCancelled(true); + data.cooldownResumeTime.set(System.currentTimeMillis() + screenOpenDelay); + data.screenOpenCount.set(0); + if (log) warn("Player '" + player.getName() + "' is now on RATE-LIMIT as they exceeded" + + " the set limit (now " + servedBytes + " bytes)."); + } + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onPlayerInteract(PlayerInteractEvent event) { + if (event.getAction() != Action.RIGHT_CLICK_BLOCK) return; + + if (MaterialUtil.INVENTORY_HOLDERS.contains(event.getClickedBlock().getType())) { + onInventoryOpen(event, event.getPlayer()); + } + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onPlayerInteract(PlayerInteractEntityEvent event) { + if (EntityUtil.isInventoryHolder(event.getRightClicked())) { + onInventoryOpen(event, event.getPlayer()); + } + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onInventoryOpen(InventoryOpenEvent event) { + onInventoryOpen(event, event.getPlayer()); + } + + private static class PlayerData { + + public final UUID uuid; + public final AtomicLong servedSetSlotBytes, cooldownResumeTime; + public final AtomicInteger screenOpenCount; + + public PlayerData(UUID uuid) { + this.uuid = uuid; + this.servedSetSlotBytes = new AtomicLong(0L); + this.cooldownResumeTime = new AtomicLong(0L); + this.screenOpenCount = new AtomicInteger(0); + } + } +} diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/packets/LecternCrash.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/packets/LecternCrash.java index fb6c842bf..5cc6d6b00 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/packets/LecternCrash.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/packets/LecternCrash.java @@ -4,7 +4,7 @@ import com.github.retrooper.packetevents.event.PacketReceiveEvent; import com.github.retrooper.packetevents.protocol.packettype.PacketType; import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientClickWindow; -import org.bukkit.entity.Player; +import org.bukkit.entity.HumanEntity; import org.bukkit.event.inventory.InventoryType; public class LecternCrash extends PacketModule { @@ -29,12 +29,11 @@ public boolean shouldEnable() { @Override public void onPacketReceive(PacketReceiveEvent event) { + if (event.isCancelled() || event.getPlayer() == null) return; if (event.getPacketType() != PacketType.Play.Client.CLICK_WINDOW) return; if (new WrapperPlayClientClickWindow(event).getWindowClickType() != WrapperPlayClientClickWindow.WindowClickType.QUICK_MOVE) return; - final Player player = (Player) event.getPlayer(); - if (player == null) return; - if (player.getOpenInventory().getType() == LECTERN) { + if (((HumanEntity) event.getPlayer()).getOpenInventory().getType() == LECTERN) { event.setCancelled(true); onCancel(log, kick, event.getUser()); } diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/packets/MapCursorLag.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/packets/MapCursorLag.java new file mode 100644 index 000000000..9be90524c --- /dev/null +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/packets/MapCursorLag.java @@ -0,0 +1,76 @@ +package me.xginko.aef.modules.packets; + +import com.github.retrooper.packetevents.PacketEvents; +import com.github.retrooper.packetevents.event.PacketListenerPriority; +import com.github.retrooper.packetevents.event.PacketSendEvent; +import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes; +import com.github.retrooper.packetevents.protocol.packettype.PacketType; +import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerSpawnEntity; +import me.xginko.aef.utils.EntityUtil; +import org.bukkit.entity.Entity; +import org.bukkit.entity.ItemFrame; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerInteractEntityEvent; +import org.bukkit.event.world.ChunkLoadEvent; + +public class MapCursorLag extends PacketModule implements Listener { + + public MapCursorLag() { + super("patches.map-cursor-lag-patch", PacketListenerPriority.HIGHEST); + config.addComment( configPath + ".enable", + "Patches the famous stacked map cursor lag that causes both \n" + + "client and server crashes."); + } + + @Override + public void enable() { + if (EntityUtil.canDisableMapPositionCursor()) + plugin.getServer().getPluginManager().registerEvents(this, plugin); + PacketEvents.getAPI().getEventManager().registerListener(asAbstract); + } + + @Override + public boolean shouldEnable() { + return config.getBoolean(configPath + ".enable", true); + } + + @Override + public void disable() { + HandlerList.unregisterAll(this); + PacketEvents.getAPI().getEventManager().unregisterListener(asAbstract); + } + + @Override + public void onPacketSend(PacketSendEvent event) { + if (event.getPacketType() != PacketType.Play.Server.SPAWN_ENTITY) return; + + if (new WrapperPlayServerSpawnEntity(event).getEntityType() == EntityTypes.MARKER) { + event.setCancelled(true); + } + } + + /* + For 1.16 and up + */ + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onChunkLoad(ChunkLoadEvent event) { + if (event.isNewChunk()) return; + + for (Entity entity : event.getChunk().getEntities()) { + if (EntityUtil.ITEM_FRAMES.contains(entity.getType())) { + EntityUtil.disableMapPositionCursor((ItemFrame) entity); + } + } + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onInteract(PlayerInteractEntityEvent event) { + Entity rightClicked = event.getRightClicked(); + if (EntityUtil.ITEM_FRAMES.contains(rightClicked.getType())) { + EntityUtil.disableMapPositionCursor((ItemFrame) rightClicked); + } + } +} diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/packets/NoComExploit.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/packets/NoComExploit.java index e4fc1b2f9..6ec63286c 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/packets/NoComExploit.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/packets/NoComExploit.java @@ -11,7 +11,7 @@ import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientUpdateJigsawBlock; import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientUpdateSign; import org.bukkit.Location; -import org.bukkit.entity.Player; +import org.bukkit.entity.Entity; import org.bukkit.util.NumberConversions; public class NoComExploit extends PacketModule { @@ -42,6 +42,8 @@ public boolean shouldEnable() { @Override public void onPacketReceive(PacketReceiveEvent event) { + if (event.isCancelled()) return; + Vector3i blockPos; if (event.getPacketType() == PacketType.Play.Client.PLAYER_DIGGING) { blockPos = new WrapperPlayClientPlayerDigging(event).getBlockPosition(); @@ -63,10 +65,11 @@ public void onPacketReceive(PacketReceiveEvent event) { return; // PacketTypes like PlayerDigging are also used for dropping and swapping items. This prevents false positives. } - final Player player = (Player) event.getPlayer(); - if (player == null) return; + if (event.getPlayer() == null) { + return; + } - if (distanceSquared(blockPos, player.getLocation()) > maxDistanceSquared) { + if (distanceSquared(blockPos, ((Entity) event.getPlayer()).getLocation()) > maxDistanceSquared) { event.setCancelled(true); onCancel(log, kick, event.getUser()); } diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/packets/PacketModule.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/packets/PacketModule.java index f031ec433..cf4275353 100644 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/packets/PacketModule.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/packets/PacketModule.java @@ -6,14 +6,13 @@ import com.github.retrooper.packetevents.event.PacketListenerPriority; import com.github.retrooper.packetevents.protocol.player.User; import me.xginko.aef.modules.AEFModule; -import me.xginko.aef.utils.models.Disableable; import me.xginko.aef.utils.models.ExpiringSet; import java.time.Duration; import java.util.Set; import java.util.UUID; -public abstract class PacketModule extends AEFModule implements Disableable, PacketListener { +public abstract class PacketModule extends AEFModule implements PacketListener { protected final PacketListenerAbstract asAbstract; private final Set loggingCooldown; diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/packets/PurpurBeehiveCrash.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/packets/PurpurBeehiveCrash.java index 502d82f7b..bb1394e88 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/packets/PurpurBeehiveCrash.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/packets/PurpurBeehiveCrash.java @@ -5,19 +5,20 @@ import com.github.retrooper.packetevents.protocol.packettype.PacketType; import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPluginMessage; import com.google.common.io.ByteStreams; +import me.xginko.aef.utils.PlatformUtil; import org.bukkit.Location; -import org.bukkit.entity.Player; +import org.bukkit.entity.Entity; import org.bukkit.util.NumberConversions; public class PurpurBeehiveCrash extends PacketModule { - private static final String BEEHIVE_C2S_CHANNEL = "purpur:beehive_c2s"; private static final int SIZE_BITS_X = 26; private static final int SIZE_BITS_Z = SIZE_BITS_X; private static final int SIZE_BITS_Y = 64 - SIZE_BITS_X - SIZE_BITS_Z; private static final int BIT_SHIFT_Z = SIZE_BITS_Y; private static final int BIT_SHIFT_X = SIZE_BITS_Y + SIZE_BITS_Z; + private final String beehive_c2s_channel; private final double maxDistanceSquared; private final boolean log, kick; @@ -31,6 +32,7 @@ public PurpurBeehiveCrash() { "beehive is away from the client enabling a malicious sender\n" + "to load chunks very fast at far away locations by telling\n" + "the server it clicked a beehive there."); + this.beehive_c2s_channel = config.getString(configPath + ".channel", "purpur:beehive_c2s"); this.maxDistanceSquared = NumberConversions.square(config.getInt(configPath + ".max-distance", 24)); this.log = config.getBoolean(configPath + ".log", false); this.kick = config.getBoolean(configPath + ".kick-player", false); @@ -38,20 +40,20 @@ public PurpurBeehiveCrash() { @Override public boolean shouldEnable() { - return config.getBoolean(configPath + ".enable", true); + return config.getBoolean(configPath + ".enable", PlatformUtil.isPurpur()); } @Override @SuppressWarnings("UnstableApiUsage") public void onPacketReceive(PacketReceiveEvent event) { + if (event.isCancelled()) return; if (event.getPacketType() != PacketType.Play.Client.PLUGIN_MESSAGE) return; - WrapperPlayClientPluginMessage packet = new WrapperPlayClientPluginMessage(event); - if (!packet.getChannelName().equalsIgnoreCase(BEEHIVE_C2S_CHANNEL)) return; + if (event.getPlayer() == null) return; - Player player = (Player) event.getPlayer(); - if (player == null) return; + WrapperPlayClientPluginMessage packet = new WrapperPlayClientPluginMessage(event); + if (!packet.getChannelName().equalsIgnoreCase(beehive_c2s_channel)) return; - if (distanceSquared(ByteStreams.newDataInput(packet.getData()).readLong(), player.getLocation()) > maxDistanceSquared) { + if (distanceSquared(ByteStreams.newDataInput(packet.getData()).readLong(), ((Entity) event.getPlayer()).getLocation()) > maxDistanceSquared) { event.setCancelled(true); onCancel(log, kick, event.getUser()); } diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/packets/SequenceCrash.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/packets/SequenceCrash.java index c91d2b94e..0a4556b97 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/packets/SequenceCrash.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/packets/SequenceCrash.java @@ -6,7 +6,7 @@ import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerBlockPlacement; import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerDigging; import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientUseItem; -import io.papermc.lib.PaperLib; +import me.xginko.aef.utils.PlatformUtil; public class SequenceCrash extends PacketModule { @@ -23,11 +23,13 @@ public SequenceCrash() { @Override public boolean shouldEnable() { - return config.getBoolean(configPath + ".enable", true) && PaperLib.getMinecraftVersion() >= 19; + return config.getBoolean(configPath + ".enable", true) && PlatformUtil.getMinecraftVersion() >= 19; } @Override public void onPacketReceive(PacketReceiveEvent event) { + if (event.isCancelled()) return; + int sequence; if (event.getPacketType() == PacketType.Play.Client.PLAYER_BLOCK_PLACEMENT) { sequence = new WrapperPlayClientPlayerBlockPlacement(event).getSequence(); diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/packets/SignLag.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/packets/SignLag.java index 6afd5d2bf..33050debe 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/packets/SignLag.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/packets/SignLag.java @@ -4,9 +4,14 @@ import com.github.retrooper.packetevents.event.PacketReceiveEvent; import com.github.retrooper.packetevents.protocol.packettype.PacketType; import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientUpdateSign; +import me.xginko.aef.utils.models.ExpiringSet; + +import java.time.Duration; +import java.util.UUID; public class SignLag extends PacketModule { + private final ExpiringSet cooldowns; private final int line_char_limit, total_char_limit; private final boolean log, kick; @@ -15,6 +20,10 @@ public SignLag() { config.addComment(configPath + ".enable", "Patches a lag exploit that involves sending specific oversized \n" + "sign edit packets."); + this.cooldowns = new ExpiringSet<>(Duration.ofMillis(Math.max(1, + config.getInt(configPath + ".packet-delay-in-ticks", 10, + "How many ticks a player needs to wait to be able to send\n" + + "another sign update packet (renaming or writing).")) * 50L)); this.line_char_limit = config.getInt(configPath + ".line-character-limit", 80, "Vanilla limit is 384 characters per line, which is too much."); this.total_char_limit = config.getInt(configPath + ".total-char-limit", 384, @@ -30,8 +39,12 @@ public boolean shouldEnable() { @Override public void onPacketReceive(PacketReceiveEvent event) { + if (event.isCancelled()) return; if (event.getPacketType() != PacketType.Play.Client.UPDATE_SIGN) return; + if (cooldowns.contains(event.getUser().getUUID())) return; + cooldowns.add(event.getUser().getUUID()); + int sum = 0; for (String line : new WrapperPlayClientUpdateSign(event).getTextLines()) { diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/packets/TabCompleteCrash.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/packets/TabCompleteCrash.java index 741be1aef..e6227dd26 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/packets/TabCompleteCrash.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/packets/TabCompleteCrash.java @@ -4,7 +4,7 @@ import com.github.retrooper.packetevents.event.PacketReceiveEvent; import com.github.retrooper.packetevents.protocol.packettype.PacketType; import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientTabComplete; -import org.bukkit.entity.Player; +import org.bukkit.permissions.ServerOperator; public class TabCompleteCrash extends PacketModule { @@ -28,6 +28,7 @@ public boolean shouldEnable() { @Override public void onPacketReceive(PacketReceiveEvent event) { + if (event.isCancelled()) return; if (event.getPacketType() != PacketType.Play.Client.TAB_COMPLETE) return; final String text = new WrapperPlayClientTabComplete(event).getText(); @@ -48,8 +49,7 @@ public void onPacketReceive(PacketReceiveEvent event) { } } - final Player player = (Player) event.getPlayer(); - if (player != null && player.isOp()) return; + if (event.getPlayer() == null || ((ServerOperator) event.getPlayer()).isOp()) return; for (String sequence : ABUSABLE_SEQUENCES) { if (text.indexOf(sequence) != -1) { diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/packets/WindowClickCrash.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/packets/WindowClickCrash.java index 62adb35aa..90b2429b2 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/packets/WindowClickCrash.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/packets/WindowClickCrash.java @@ -4,7 +4,7 @@ import com.github.retrooper.packetevents.event.PacketReceiveEvent; import com.github.retrooper.packetevents.protocol.packettype.PacketType; import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientClickWindow; -import org.bukkit.entity.Player; +import org.bukkit.entity.HumanEntity; public class WindowClickCrash extends PacketModule { @@ -27,10 +27,11 @@ public boolean shouldEnable() { @Override public void onPacketReceive(PacketReceiveEvent event) { + if (event.isCancelled()) return; if (event.getPacketType() != PacketType.Play.Client.CLICK_WINDOW) return; - final WrapperPlayClientClickWindow packet = new WrapperPlayClientClickWindow(event); + WrapperPlayClientClickWindow packet = new WrapperPlayClientClickWindow(event); - final int button = packet.getButton(); + int button = packet.getButton(); if (button < 0 || button > 10 && button != 40 && button != 99) { event.setCancelled(true); @@ -38,12 +39,14 @@ public void onPacketReceive(PacketReceiveEvent event) { return; } - final int slot = packet.getSlot(); + int slot = packet.getSlot(); - final Player player = (Player) event.getPlayer(); - if (player != null && slot >= player.getOpenInventory().countSlots()) { - event.setCancelled(true); - onCancel(log, kick, event.getUser()); + if (event.getPlayer() != null) { + if (((HumanEntity) event.getPlayer()).getOpenInventory().countSlots() < slot) { + event.setCancelled(true); + onCancel(log, kick, event.getUser()); + return; + } } if (slot != -999 && slot != -1) { diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/patches/BookBan.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/patches/BookBan.java index 8394cb5ff..708efe73d 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/patches/BookBan.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/patches/BookBan.java @@ -9,6 +9,7 @@ import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.entity.ItemSpawnEvent; import org.bukkit.event.player.PlayerAttemptPickupItemEvent; @@ -66,6 +67,13 @@ public boolean shouldEnable() { return config.getBoolean(configPath + ".enable", false); } + @Override + public void disable() { + HandlerList.unregisterAll(this); + cachedInventorySizes.cleanUp(); + cachedItemSizes.cleanUp(); + } + // Prevent players from creating big books @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onBookEdit(PlayerEditBookEvent event) { diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/patches/CowDupe.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/patches/CowDupe.java index 697691de9..74257ae56 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/patches/CowDupe.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/patches/CowDupe.java @@ -5,6 +5,7 @@ import me.xginko.aef.modules.AEFModule; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerShearEntityEvent; @@ -30,6 +31,11 @@ public boolean shouldEnable() { return config.getBoolean(configPath, false); } + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onPlayerShearEntity(PlayerShearEntityEvent event) { if (event.getEntity().getType() != XEntityType.MOOSHROOM.get()) return; diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/patches/GodMode.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/patches/GodMode.java index 70d57f4b8..6984ada84 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/patches/GodMode.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/patches/GodMode.java @@ -7,6 +7,7 @@ import org.bukkit.entity.Vehicle; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerMoveEvent; import org.bukkit.event.vehicle.VehicleEnterEvent; @@ -30,6 +31,11 @@ public boolean shouldEnable() { return config.getBoolean(configPath, false); } + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onVehicleEnter(VehicleEnterEvent event) { final Vehicle vehicle = event.getVehicle(); diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/patches/MapCursorLag.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/patches/MapCursorLag.java deleted file mode 100644 index ce785f090..000000000 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/patches/MapCursorLag.java +++ /dev/null @@ -1,51 +0,0 @@ -package me.xginko.aef.modules.patches; - -import me.xginko.aef.modules.AEFModule; -import me.xginko.aef.utils.EntityUtil; -import me.xginko.aef.utils.FramedMapUtil; -import org.bukkit.entity.Entity; -import org.bukkit.entity.ItemFrame; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.Listener; -import org.bukkit.event.player.PlayerInteractEntityEvent; -import org.bukkit.event.world.ChunkLoadEvent; - -public class MapCursorLag extends AEFModule implements Listener { - - public MapCursorLag() { - super("patches.map-cursor-lag-patch"); - config.addComment( configPath + ".enable", - "Patches the famous stacked map cursor lag that causes both \n" + - "client and server crashes."); - } - - @Override - public void enable() { - plugin.getServer().getPluginManager().registerEvents(this, plugin); - } - - @Override - public boolean shouldEnable() { - return config.getBoolean(configPath + ".enable", true) && FramedMapUtil.canDisableTracker(); - } - - @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) - private void onChunkLoad(ChunkLoadEvent event) { - if (event.isNewChunk()) return; - - for (Entity entity : event.getChunk().getEntities()) { - if (EntityUtil.ITEM_FRAMES.contains(entity.getType())) { - FramedMapUtil.disableTracker((ItemFrame) entity); - } - } - } - - @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) - private void onInteract(PlayerInteractEntityEvent event) { - Entity rightClicked = event.getRightClicked(); - if (EntityUtil.ITEM_FRAMES.contains(rightClicked.getType())) { - FramedMapUtil.disableTracker((ItemFrame) rightClicked); - } - } -} diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/patches/PearlPhase.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/patches/PearlPhase.java index e07c400f5..18f0d9372 100644 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/patches/PearlPhase.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/patches/PearlPhase.java @@ -2,6 +2,7 @@ import com.cryptomorin.xseries.XMaterial; import me.xginko.aef.modules.AEFModule; +import me.xginko.aef.utils.LocationUtil; import me.xginko.aef.utils.MaterialUtil; import me.xginko.aef.utils.WorldUtil; import org.bukkit.Location; @@ -10,6 +11,7 @@ import org.bukkit.block.Block; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerTeleportEvent; @@ -23,16 +25,19 @@ public class PearlPhase extends AEFModule implements Listener { private final Set glitchyMaterial; + private final double maxDistance; private final int radius; public PearlPhase() { super("patches.pearl-phase"); config.addComment(configPath+ ".enable", "Attempts to patch a pearl phasing exploit by cancelling the teleport\n" + - "if the pearl is thrown at or near a specific block."); + "if the pearl is thrown at or near a specific block.\n" + + "At the time of the creation of this module, this is an issue with NoCheatPlus."); this.radius = Math.min(1, config.getInt(configPath + ".search-radius", 2, "How many blocks around the teleport location should be searched\n" + "for potential glitch blocks if the teleport location isn't one itself.")); + this.maxDistance = config.getDouble(configPath + ".maximum-distance-to-cancel-teleport", 3.0); Stream concatA = Stream.concat(MaterialUtil.SLAB_LIKE.stream(), Stream.of(XMaterial.COBWEB, XMaterial.POWDER_SNOW).filter(XMaterial::isSupported).map(XMaterial::parseMaterial)); Stream concatB = Stream.concat(MaterialUtil.PRESSURE_PLATES.stream(), MaterialUtil.TRAPDOORS.stream()); @@ -64,11 +69,18 @@ public void enable() { plugin.getServer().getPluginManager().registerEvents(this, plugin); } + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onPlayerTeleport(PlayerTeleportEvent event) { if (event.getCause() != PlayerTeleportEvent.TeleportCause.ENDER_PEARL) return; Location destination = event.getTo(); + if (LocationUtil.getRelDistance2D(event.getFrom(), destination) >= maxDistance) return; + Block destBlock = destination.getBlock(); if (glitchyMaterial.contains(destBlock.getType())) { diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/patches/TeleportCoordExploit.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/patches/TeleportCoordExploit.java index 08c8102cb..17aaa72c1 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/patches/TeleportCoordExploit.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/patches/TeleportCoordExploit.java @@ -5,6 +5,7 @@ import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerRespawnEvent; import org.bukkit.event.player.PlayerTeleportEvent; @@ -34,6 +35,11 @@ public boolean shouldEnable() { return config.getBoolean(configPath + ".enable", true); } + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + private void vanish(Player player) { for (Player onlinePlayer : plugin.getServer().getOnlinePlayers()) { if (onlinePlayer.getUniqueId() != player.getUniqueId()) { diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/patches/commandsign/CommandSign.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/patches/commandsign/CommandSign.java index f7c7b32f2..8868c5bb7 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/patches/commandsign/CommandSign.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/patches/commandsign/CommandSign.java @@ -6,11 +6,12 @@ import me.xginko.aef.utils.MaterialUtil; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.block.Action; import org.bukkit.event.player.PlayerInteractEvent; -public class CommandSign extends AEFModule { +public class CommandSign extends AEFModule implements Listener { private final Listener signCommandListener; @@ -20,16 +21,7 @@ public CommandSign() { "Patch signs that have run_command NBT tags attached, allowing the \n" + "to run a command with operator permissions on click. \n" + "Recommended to enable if you had a rogue admin or backdoor incident."); - this.signCommandListener = SignCommandListener.isSupported() ? new SignCommandListener() : new Listener() { - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - private void onInteract(PlayerInteractEvent event) { - if (event.getAction() != Action.RIGHT_CLICK_BLOCK) return; - if (!MaterialUtil.SIGNS.contains(event.getClickedBlock().getType())) return; - if (!CachingPermTool.hasPermission(AEFPermission.BYPASS_PREVENTION_COMMANDSIGN, event.getPlayer())) { - event.setCancelled(true); - } - } - }; + this.signCommandListener = SignCommandListener.isSupported() ? new SignCommandListener() : this; } @Override @@ -41,4 +33,20 @@ public void enable() { public boolean shouldEnable() { return config.getBoolean(configPath + ".enable", true); } + + @Override + public void disable() { + HandlerList.unregisterAll(signCommandListener); + } + + // Fallback listener, just cancels any right-clicking on signs + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onInteract(PlayerInteractEvent event) { + if (event.getAction() != Action.RIGHT_CLICK_BLOCK) return; + if (!MaterialUtil.SIGNS.contains(event.getClickedBlock().getType())) return; + + if (!CachingPermTool.hasPermission(AEFPermission.BYPASS_PREVENTION_COMMANDSIGN, event.getPlayer())) { + event.setCancelled(true); + } + } } diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/patches/crashexploits/DispenserCrash.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/patches/crashexploits/DispenserCrash.java index a4fcfa049..8cd71dd43 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/patches/crashexploits/DispenserCrash.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/patches/crashexploits/DispenserCrash.java @@ -6,6 +6,7 @@ import org.bukkit.World; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.block.BlockDispenseEvent; @@ -31,6 +32,11 @@ public boolean shouldEnable() { return config.getBoolean(configPath + ".enable", true); } + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onDispense(BlockDispenseEvent event) { World world = event.getBlock().getWorld(); diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/patches/crashexploits/EndGatewayCrash.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/patches/crashexploits/EndGatewayCrash.java index 878842a0f..4067f7265 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/patches/crashexploits/EndGatewayCrash.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/patches/crashexploits/EndGatewayCrash.java @@ -5,6 +5,7 @@ import org.bukkit.World; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.entity.EntityTeleportEvent; @@ -30,6 +31,11 @@ public boolean shouldEnable() { return config.getBoolean(configPath + ".enable", true); } + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onEntityTeleportEvent(EntityTeleportEvent event) { if (event.getEntity().getWorld().getEnvironment() == World.Environment.THE_END && !event.getEntity().isEmpty()) { diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/patches/crashexploits/MultipleEnderdragons.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/patches/crashexploits/MultipleEnderdragons.java index 013adfe96..2de326e8d 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/patches/crashexploits/MultipleEnderdragons.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/patches/crashexploits/MultipleEnderdragons.java @@ -5,6 +5,7 @@ import org.bukkit.World; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.entity.EntityTeleportEvent; @@ -27,6 +28,11 @@ public boolean shouldEnable() { return config.getBoolean(configPath + ".enable", false); } + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onEntityTeleportEvent(EntityTeleportEvent event) { if (event.getEntityType() == XEntityType.ENDER_DRAGON.get() && event.getEntity().getWorld().getEnvironment() == World.Environment.THE_END) { diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/patches/crashexploits/RedstoneOnTrapdoorCrash.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/patches/crashexploits/RedstoneOnTrapdoorCrash.java index d87fab891..694918fdf 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/patches/crashexploits/RedstoneOnTrapdoorCrash.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/patches/crashexploits/RedstoneOnTrapdoorCrash.java @@ -11,6 +11,7 @@ import org.bukkit.block.BlockFace; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.event.block.BlockRedstoneEvent; @@ -44,6 +45,11 @@ public boolean shouldEnable() { return config.getBoolean(configPath + ".enable", false); } + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onRedstonePowerTrapdoor(BlockRedstoneEvent event) { Block block = event.getBlock(); diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/patches/crashexploits/WorldChangeCrash.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/patches/crashexploits/WorldChangeCrash.java index a2b3206ab..7fddd8b8a 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/patches/crashexploits/WorldChangeCrash.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/patches/crashexploits/WorldChangeCrash.java @@ -5,6 +5,7 @@ import me.xginko.aef.utils.models.ExpiringSet; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.entity.EntityTeleportEvent; @@ -38,6 +39,11 @@ public boolean shouldEnable() { return config.getBoolean(configPath + ".enable", true); } + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onTeleport(EntityTeleportEvent event) { if (event.getTo() == null) return; diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/BedTrap.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/BedTrap.java index ab686b369..515a83b71 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/BedTrap.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/BedTrap.java @@ -7,6 +7,7 @@ import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.entity.PlayerDeathEvent; import org.bukkit.util.NumberConversions; @@ -45,6 +46,11 @@ public boolean shouldEnable() { return config.getBoolean(configPath + ".enable", false); } + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onDeath(PlayerDeathEvent event) { final Player player = event.getEntity(); diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/DisableFish.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/DisableFish.java index 37db631c7..211b75c91 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/DisableFish.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/DisableFish.java @@ -1,10 +1,11 @@ package me.xginko.aef.modules.preventions; -import io.papermc.lib.PaperLib; import me.xginko.aef.modules.AEFModule; +import me.xginko.aef.utils.PlatformUtil; import org.bukkit.entity.EntityType; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.entity.CreatureSpawnEvent; @@ -29,7 +30,7 @@ public DisableFish() { try { return EntityType.valueOf(configuredFish); } catch (IllegalArgumentException exception) { - if (PaperLib.getMinecraftVersion() > 12) + if (PlatformUtil.getMinecraftVersion() > 12) notRecognized(EntityType.class, configuredFish); return null; } @@ -45,7 +46,12 @@ public void enable() { @Override public boolean shouldEnable() { - return config.getBoolean(configPath + ".enable", false) && PaperLib.getMinecraftVersion() > 12; + return config.getBoolean(configPath + ".enable", false) && PlatformUtil.getMinecraftVersion() > 12; + } + + @Override + public void disable() { + HandlerList.unregisterAll(this); } @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/MapSpam.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/MapSpam.java index 481a78af8..673b5d31f 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/MapSpam.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/MapSpam.java @@ -9,6 +9,7 @@ import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.block.Action; import org.bukkit.event.player.PlayerInteractEvent; @@ -53,6 +54,11 @@ public boolean shouldEnable() { return config.getBoolean(configPath + ".enable", false); } + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = false) private void onPlayerInteract(PlayerInteractEvent event) { if (event.getAction() == Action.LEFT_CLICK_AIR || event.getAction() == Action.LEFT_CLICK_BLOCK) return; diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/NetherRoof.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/NetherRoof.java index 7147aa619..7a2150d75 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/NetherRoof.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/NetherRoof.java @@ -16,6 +16,7 @@ import org.bukkit.entity.Vehicle; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.event.player.PlayerMoveEvent; @@ -43,6 +44,11 @@ public boolean shouldEnable() { return config.getBoolean(configPath + ".enable", true); } + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onTeleport(PlayerTeleportEvent event) { if ( diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/PreventNonSurvival.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/PreventNonSurvival.java index d0894ca5e..4cf950c46 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/PreventNonSurvival.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/PreventNonSurvival.java @@ -5,6 +5,7 @@ import org.bukkit.entity.HumanEntity; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.inventory.InventoryEvent; import org.bukkit.event.player.AsyncPlayerChatEvent; @@ -50,6 +51,11 @@ private void checkForIllegalGamemode(HumanEntity player) { } } + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onInventory(InventoryEvent event) { checkForIllegalGamemode(event.getView().getPlayer()); diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/PreventOppedPlayers.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/PreventOppedPlayers.java index c885b3e44..4728d6a05 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/PreventOppedPlayers.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/PreventOppedPlayers.java @@ -4,6 +4,7 @@ import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.player.AsyncPlayerChatEvent; import org.bukkit.event.player.PlayerCommandPreprocessEvent; @@ -37,6 +38,11 @@ public boolean shouldEnable() { return config.getBoolean(configPath + ".enable", false); } + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + private void checkForIllegalOp(Player player) { if (allowedOperators.contains(player.getName())) return; diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/blockbreak/PistonExplodePermBlockRemoval.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/blockbreak/PistonExplodePermBlockRemoval.java index 1cfdea924..199b75d0d 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/blockbreak/PistonExplodePermBlockRemoval.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/blockbreak/PistonExplodePermBlockRemoval.java @@ -7,6 +7,7 @@ import org.bukkit.block.BlockFace; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.entity.EntityExplodeEvent; @@ -34,25 +35,37 @@ public boolean shouldEnable() { return config.getBoolean(configPath + ".enable", true); } + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - private void onPistonExplode(EntityExplodeEvent event) { + private void onEntityExplode(EntityExplodeEvent event) { if (whitelistedWorlds.contains(event.getEntity().getWorld().getName())) return; Set pistons_that_could_break_indestructible_blocks = new HashSet<>(); event.blockList().removeIf(block -> { - if (MaterialUtil.PISTONS.contains(block.getType())) { - for (BlockFace face : BlockFace.values()) { - if (MaterialUtil.INDESTRUCTIBLES.contains(block.getRelative(face).getType())) { - pistons_that_could_break_indestructible_blocks.add(block); - return true; - } + if (!MaterialUtil.PISTONS.contains(block.getType())) return false; + + for (BlockFace face : BlockFace.values()) { + // Check if any side of the piston touches an indestructible block + if (MaterialUtil.INDESTRUCTIBLES.contains(block.getRelative(face).getType())) { + pistons_that_could_break_indestructible_blocks.add(block); + return true; // Remove piston from the list of blocks to be affected by the explosion } } + return false; }); - plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> - pistons_that_could_break_indestructible_blocks.forEach(block -> block.setType(Material.AIR)), 5); + if (pistons_that_could_break_indestructible_blocks.isEmpty()) { + return; + } + + // Schedule a single delayed task to remove those blocks silently + plugin.getServer().getScheduler().runTaskLater(plugin, () -> + pistons_that_could_break_indestructible_blocks.forEach(block -> block.setType(Material.AIR)), 5L); } } diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/blockbreak/PistonPlaceWhileRetractPermBlockRemoval.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/blockbreak/PistonPlaceWhileRetractPermBlockRemoval.java index e4c4ad2a1..2a4c0df86 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/blockbreak/PistonPlaceWhileRetractPermBlockRemoval.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/blockbreak/PistonPlaceWhileRetractPermBlockRemoval.java @@ -4,6 +4,7 @@ import me.xginko.aef.utils.MaterialUtil; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.block.BlockPistonRetractEvent; @@ -31,6 +32,11 @@ public boolean shouldEnable() { return config.getBoolean(configPath + ".enable", true); } + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onPistonRetract(BlockPistonRetractEvent event) { if (whitelistedWorlds.contains(event.getBlock().getWorld().getName())) return; diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/blockbreak/StructureGrowPermBlockRemoval.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/blockbreak/StructureGrowPermBlockRemoval.java index 05464bde4..eb29574f2 100644 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/blockbreak/StructureGrowPermBlockRemoval.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/blockbreak/StructureGrowPermBlockRemoval.java @@ -7,6 +7,7 @@ import org.bukkit.block.BlockState; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.world.StructureGrowEvent; @@ -29,13 +30,19 @@ public boolean shouldEnable() { return config.getBoolean(configPath + ".enable", true); } + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void onStructureGrow(StructureGrowEvent event) { - final World world = event.getWorld(); - for (final BlockState blockState : event.getBlocks()) { + World world = event.getWorld(); + + for (BlockState blockState : event.getBlocks()) { if (MaterialUtil.INDESTRUCTIBLES.contains(world.getBlockAt(blockState.getLocation()).getType())) { event.setCancelled(true); - info("Prevented permanent block break by growing a structure at " + LocationUtil.toString(blockState.getLocation())); + info("Prevented permanent block break by "+ event.getSpecies().name() +" at " + LocationUtil.toString(blockState.getLocation())); return; } } diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/portals/EndPortalDestruction.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/portals/EndPortalDestruction.java index 25b36795b..d5ed97c98 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/portals/EndPortalDestruction.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/portals/EndPortalDestruction.java @@ -11,6 +11,7 @@ import org.bukkit.block.BlockFace; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.block.BlockDispenseEvent; import org.bukkit.event.block.BlockPistonRetractEvent; @@ -71,6 +72,11 @@ public boolean shouldEnable() { return config.getBoolean(configPath + ".enable", true); } + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onBlockDispense(BlockDispenseEvent event) { if (MaterialUtil.BLOCK_DISPENSE_BUCKETS.contains(event.getItem().getType()) && isNearEndPortal(event.getBlock())) { diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/portals/PreventAllEntitiesInPortals.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/portals/PreventAllEntitiesInPortals.java index 0485eeb07..e7859cb45 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/portals/PreventAllEntitiesInPortals.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/portals/PreventAllEntitiesInPortals.java @@ -4,6 +4,7 @@ import me.xginko.aef.modules.AEFModule; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.entity.EntityPortalEvent; @@ -24,6 +25,11 @@ public boolean shouldEnable() { return config.getBoolean(configPath, false); } + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onPortalUse(EntityPortalEvent event) { if (event.getEntityType() != XEntityType.PLAYER.get()) { diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/portals/PreventPortalTraps.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/portals/PreventPortalTraps.java index 787da0d6d..b975a96ee 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/portals/PreventPortalTraps.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/portals/PreventPortalTraps.java @@ -6,6 +6,7 @@ import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerPortalEvent; @@ -31,6 +32,11 @@ public boolean shouldEnable() { return config.getBoolean(configPath + ".enable", false); } + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) private void onPortal(PlayerPortalEvent event) { final Player player = event.getPlayer(); diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/portals/PreventProjectilesInPortals.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/portals/PreventProjectilesInPortals.java index 8b945b731..452d6468b 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/portals/PreventProjectilesInPortals.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/portals/PreventProjectilesInPortals.java @@ -4,6 +4,7 @@ import me.xginko.aef.utils.EntityUtil; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.entity.EntityPortalEvent; @@ -24,6 +25,11 @@ public boolean shouldEnable() { return config.getBoolean(configPath, false); } + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onEntityPortal(EntityPortalEvent event) { if (EntityUtil.isProjectile(event.getEntity())) { diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/portals/PreventSpecificEntitiesInPortals.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/portals/PreventSpecificEntitiesInPortals.java index 1922b874f..61f9ed15d 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/portals/PreventSpecificEntitiesInPortals.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/portals/PreventSpecificEntitiesInPortals.java @@ -4,6 +4,7 @@ import org.bukkit.entity.EntityType; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.entity.EntityPortalEvent; @@ -50,6 +51,11 @@ public boolean shouldEnable() { return config.getBoolean(configPath + ".enable", true); } + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onPortal(EntityPortalEvent event) { if (forbiddenTypes.contains(event.getEntityType())) { diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/withers/DisableWitherSkulls.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/withers/DisableWitherSkulls.java index db542eda8..890ff9c3d 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/withers/DisableWitherSkulls.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/withers/DisableWitherSkulls.java @@ -4,6 +4,7 @@ import me.xginko.aef.modules.AEFModule; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.entity.ProjectileLaunchEvent; @@ -24,6 +25,11 @@ public boolean shouldEnable() { return config.getBoolean(configPath, false); } + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onProjectileLaunch(ProjectileLaunchEvent event) { if (event.getEntityType() == XEntityType.WITHER_SKULL.get()) { diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/withers/RateLimitWitherSkulls.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/withers/RateLimitWitherSkulls.java index b10eaa4f8..81d75d4a1 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/withers/RateLimitWitherSkulls.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/withers/RateLimitWitherSkulls.java @@ -6,6 +6,7 @@ import org.bukkit.entity.Wither; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.entity.ProjectileLaunchEvent; @@ -43,6 +44,11 @@ public boolean shouldEnable() { return config.getBoolean(configPath + ".enable", false); } + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onProjectileLaunch(ProjectileLaunchEvent event) { if (event.getEntityType() != XEntityType.WITHER_SKULL.get()) return; diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/withers/RemoveAllSkullsPeriodically.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/withers/RemoveAllSkullsPeriodically.java index e15c2847d..341a7fe5e 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/withers/RemoveAllSkullsPeriodically.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/withers/RemoveAllSkullsPeriodically.java @@ -3,10 +3,12 @@ import me.xginko.aef.modules.AEFModule; import org.bukkit.World; import org.bukkit.entity.WitherSkull; +import org.bukkit.scheduler.BukkitTask; public class RemoveAllSkullsPeriodically extends AEFModule implements Runnable { private final long checkPeriod; + private BukkitTask bukkitTask; public RemoveAllSkullsPeriodically() { super("preventions.withers.remove-flying-wither-skulls.periodically-remove-all-flying-skulls"); @@ -16,7 +18,7 @@ public RemoveAllSkullsPeriodically() { @Override public void enable() { - plugin.getServer().getScheduler().scheduleSyncRepeatingTask(plugin, this, checkPeriod, checkPeriod); + bukkitTask = plugin.getServer().getScheduler().runTaskTimer(plugin, this, checkPeriod, checkPeriod); } @Override @@ -24,6 +26,11 @@ public boolean shouldEnable() { return config.getBoolean(configPath + ".enable", false); } + @Override + public void disable() { + if (bukkitTask != null) bukkitTask.cancel(); + } + @Override public void run() { for (World world : plugin.getServer().getWorlds()) { diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/withers/RemoveSkullsOnChunkload.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/withers/RemoveSkullsOnChunkload.java index 4832c2517..f107a2f94 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/withers/RemoveSkullsOnChunkload.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/withers/RemoveSkullsOnChunkload.java @@ -5,6 +5,7 @@ import org.bukkit.entity.Entity; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.world.ChunkLoadEvent; @@ -27,6 +28,11 @@ public boolean shouldEnable() { return config.getBoolean(configPath, true); } + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) private void onChunkLoad(ChunkLoadEvent event) { if (event.isNewChunk()) return; diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/withers/RemoveSkullsOnChunkunload.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/withers/RemoveSkullsOnChunkunload.java index f06969ac2..543ffee91 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/withers/RemoveSkullsOnChunkunload.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/withers/RemoveSkullsOnChunkunload.java @@ -5,6 +5,7 @@ import org.bukkit.entity.Entity; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.world.ChunkUnloadEvent; @@ -27,6 +28,11 @@ public boolean shouldEnable() { return config.getBoolean(configPath, true); } + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) private void onChunkUnload(ChunkUnloadEvent event) { for (Entity entity : event.getChunk().getEntities()) { diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/withers/WitherSpawningAtSpawn.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/withers/WitherSpawningAtSpawn.java index 54ed31c3b..53dd3c3e9 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/withers/WitherSpawningAtSpawn.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/withers/WitherSpawningAtSpawn.java @@ -10,6 +10,7 @@ import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.entity.CreatureSpawnEvent; @@ -35,7 +36,7 @@ public WitherSpawningAtSpawn() { ConfigSection section = config.getConfigSection(configPath + ".worlds", defaults); for (String world : section.getKeys(false)) { try { - Integer radius = Integer.valueOf(section.getString(world)); + Integer radius = Integer.parseInt(section.getString(world)); this.worldsAndTheirRadiuses.put(world, radius); } catch (NumberFormatException e) { warn("Radius for world '" + world + "' is not a valid integer."); @@ -53,6 +54,11 @@ public boolean shouldEnable() { return config.getBoolean(configPath + ".enable", false); } + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onCreatureSpawn(CreatureSpawnEvent event) { if (event.getEntityType() != XEntityType.WITHER_SKULL.get()) return; diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/utils/WorldUtil.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/utils/WorldUtil.java index 05c1408bc..a1feb5df3 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/utils/WorldUtil.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/utils/WorldUtil.java @@ -3,35 +3,62 @@ import me.xginko.aef.AnarchyExploitFixes; import org.bukkit.World; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; +import java.lang.invoke.MethodHandle; public class WorldUtil { - private static Method getMinWorldHeight; + private static final MethodHandle GET_MIN_WORLD_HEIGHT, RESPAWNANCHOR_WORKS, BED_WORKS; + public static final boolean GET_MIN_WORLD_HEIGHT_AVAILABLE, RESPAWN_ANCHOR_WORKS_AVAILABLE, BED_WORKS_AVAILABLE; static { + GET_MIN_WORLD_HEIGHT_AVAILABLE = Crafty.hasMethod(World.class, "getMinHeight"); + GET_MIN_WORLD_HEIGHT = Crafty.findMethod(World.class, "getMinHeight", int.class); + RESPAWN_ANCHOR_WORKS_AVAILABLE = Crafty.hasMethod(World.class, "isRespawnAnchorWorks"); + RESPAWNANCHOR_WORKS = Crafty.findMethod(World.class, "isRespawnAnchorWorks", boolean.class); + BED_WORKS_AVAILABLE = Crafty.hasMethod(World.class, "isBedWorks"); + BED_WORKS = Crafty.findMethod(World.class, "isBedWorks", boolean.class); + } + + public static int getMinWorldHeight(World world) { + if (!GET_MIN_WORLD_HEIGHT_AVAILABLE) { + return AnarchyExploitFixes.config().worldMinHeights.getOrDefault(world.getName(), getDefaultMinHeight(world)); + } + try { - getMinWorldHeight = World.class.getMethod("getMinHeight"); - } catch (NoSuchMethodException e) { - getMinWorldHeight = null; + return (int) GET_MIN_WORLD_HEIGHT.invoke(world); + } catch (Throwable t) { + AnarchyExploitFixes.prefixedLogger().error("Error getting min world height from world '{}'.", world.getName(), t); + return AnarchyExploitFixes.config().worldMinHeights.getOrDefault(world.getName(), getDefaultMinHeight(world)); } } - public static int getMinWorldHeight(World world) { - if (getMinWorldHeight == null) { - if (world.getEnvironment() == World.Environment.NORMAL) { - return AnarchyExploitFixes.config().overworld_floor_min_y; - } else { - return AnarchyExploitFixes.config().nether_floor_min_y; - } + private static int getDefaultMinHeight(World world) { + return world.getEnvironment() == World.Environment.NORMAL && PlatformUtil.getMinecraftVersion() > 17 ? -64 : 0; + } + + public static boolean isRespawnAnchorWorks(World world) { + if (!RESPAWN_ANCHOR_WORKS_AVAILABLE) { + return world.getEnvironment() != World.Environment.NORMAL; + } + + try { + return (boolean) RESPAWNANCHOR_WORKS.invoke(world); + } catch (Throwable t) { + AnarchyExploitFixes.prefixedLogger().error("Error checking if respawn anchors work in world '{}'.", world.getName(), t); + return world.getEnvironment() != World.Environment.NORMAL; + } + } + + public static boolean isBedWorks(World world) { + if (!BED_WORKS_AVAILABLE) { + return world.getEnvironment() == World.Environment.NORMAL; } try { - return (int) getMinWorldHeight.invoke(world); - } catch (InvocationTargetException | IllegalAccessException e) { - getMinWorldHeight = null; - return getMinWorldHeight(world); + return (boolean) BED_WORKS.invoke(world); + } catch (Throwable t) { + AnarchyExploitFixes.prefixedLogger().error("Error checking if beds work in world '{}'.", world.getName(), t); + return world.getEnvironment() == World.Environment.NORMAL; } } } diff --git a/AnarchyExploitFixesLegacy/src/main/resources/config.yml b/AnarchyExploitFixesLegacy/src/main/resources/config.yml new file mode 100644 index 000000000..e4da420d9 --- /dev/null +++ b/AnarchyExploitFixesLegacy/src/main/resources/config.yml @@ -0,0 +1,2056 @@ +plugin-version: 2.7.2 +server-version: 'git-Paper-1620 (MC: 1.12.2)' + +############## +# Language # +############## +language: + # The default language that will be used if auto-language is false + # or no matching language file was found. + default-language: en_us + # If set to true, will display messages based on client language + auto-language: true + +############# +# General # +############# +general: + # The time in ticks (1 sec = 20 ticks) a checked tps will be cached + # by the plugin. + max-tps-check-interval-in-ticks: 20 + # In case packet modules are causing trouble, you can disable them here. + disable-all-packet-listeners: false + # The Y-level at which the nether ceiling generates the last layer + # of bedrock on your server. + nether-ceiling-y: 127 + # If you see this config option, AEF is unable to get the minimum height + # of your worlds from the API. + # Please enter them here manually for each world you're currently using. + # Use the exact same name as your world folder. + world-min-heights: + world: 0 + world_the_end: 0 + world_nether: 0 + # A server restart is required when changing a command's enable status! + commands: + say: + enable: false + format: '&7Server: &6%message%' + help: + # Help command that shows a small command overview for players. + enable: false + toggleconnectionmsgs: + # If you don't use join leave/messages, you can set this to false. + enable: true + +################### +# Miscellaneous # +################### +misc: + join-leave-messages: + # If you want to hide yourself or someone else when logging + # into the game, use these permissions: + # aef.silentJoin, aef.silentLeave + enable: true + # If set to true, players will see join/leave messages by default + # and enter /toggleconnectionmsgs to disable them. + # If set to false will work the other way around. + connection-messages-on-by-default: true + show-in-console: false + first-join-messages: + # Configure message in lang folder. + # You can hide yourself and other players using the permission: + # aef.silentJoin + enable: false + show-in-console: true + kicks: + # Configure mask message in lang folder. + mask-kick-messages: false + prevent-message-kick: + # Cancels the kick for specific kick messages. + enable: false + kick-messages-to-listen-to: + - Kicked for spamming + - Stop spamming! + +########## +# Chat # +########## +chat: + command-whitelist: + # This will make it pretty much impossible to find your plugins as + # only the commands you specify will be able to work. + # Allow bypass using permission: aef.bypass.commandwhitelist + enable: true + # Will show logs when a command was denied. + log: false + # Recommended to use when on 1.12. Otherwise only use if you're having issues. + use-packets: true + # Add all commands you WANT your players to be able to access + # WITHOUT the '/'. Not case sensitive. + whitelisted-commands: + - help + - vote + - kill + - discord + - togglechat + - toggleconnectionmsgs + - toggletells + - togglewhispering + - toggleprivatemsgs + - ignore + - ignorelist + - ignorehard + - toggledeathmsg + - dmt + - worldstats + - stats + - tps + - msg + - whisper + - w + - m + - t + - pm + - tell + - r + - reply + - last + # Add all subcommands you DON'T want your players to be able + # to access. Case sensitive! + blacklisted-subcommands: + - help about + - vote List + - vote Best + - vote Total + - worldstats reload + - stats reload + prevent-scanning-server-plugins: + # Prevents hacked clients running .plugins to find out what plugins + # the server is using. + # Recommended to use in combination with command whitelist. + enable: true + +############ +# Elytra # +############ +elytra: + # NOTE: Set nocheatplus horizontal elytra settings to 500 or higher. + elytra-speed: + # Time in ticks that a chunk has to have been inhabited to count as old chunk. + # Note that the time is incremented once per tick per player within mob spawning + # distance of a chunk. + old-chunk-inhabited-ticks: 200 + # The period in millis players will be checked to determine their speed. + # If you have lagging players with consistent high ping, you can increase this number. + check-period-millis: 500 + # If set to false, will only calculate 2-Dimensional speed + # without taking height changes into consideration. + calculate-3D-speed: true + # Display info in Actionbar while flying. + display-actionbar: true + # Inform flying player if they are in old or new chunks. + display-chunk-info-in-actionbar: true + # Plays XP pickup sound to alert players when theyre going + # above the limit. + play-sound-when-too-fast: true + sound: ENTITY_EXPERIENCE_ORB_PICKUP + # Recommended to leave false if you dont experience any issues. + teleport-instead-of-canceling-movement: false + Global-Settings: + # Global settings. If nothing else is enabled, this will be used for all environments. + enable: true + # Can be slow with a lot of players. Enable only if needed. + use-bypass-permission: false + deny-elytra-usage: false + speed-old-chunks: 1.81 + speed-new-chunks: 1.81 + enable-bursting: true + burst-speed-old-chunks: 5.0 + burst-speed-old-chunk-TPS: 18.0 + burst-speed-new-chunks: 3.12 + burst-speed-new-chunk-TPS: 19.0 + deny-elytra-on-low-TPS: true + deny-elytra-TPS: 12.0 + also-remove-elytra-on-low-TPS: true + At-Spawn: + # Use separate values for players at spawn. + enable: false + # Radius in blocks around 00 that should count as spawn. + radius: 3000 + # Can be slow with a lot of players. Enable only if needed. + use-bypass-permission: false + deny-elytra-usage: false + speed-old-chunks: 1.0 + speed-new-chunks: 0.8 + deny-elytra-on-low-TPS: true + deny-elytra-TPS: 10.0 + also-remove-elytra-on-low-TPS: true + Nether-Ceiling: + # Use separate values for players above the nether ceiling. + enable: true + # Can be slow with a lot of players. Enable only if needed. + use-bypass-permission: false + deny-elytra-usage: false + speed-old-chunks: 0.5 + speed-new-chunks: 0.5 + enable-bursting: true + burst-speed-old-chunks: 1.0 + burst-speed-old-chunk-TPS: 18.0 + burst-speed-new-chunks: 1.0 + burst-speed-new-chunk-TPS: 18.0 + deny-elytra-on-low-TPS: true + deny-elytra-TPS: 12.0 + also-remove-elytra-on-low-TPS: true + packet-elytra-fly: + # Patches the future/rusherhack/kamiblue 2b2t elytra fly exploit + patch-packet-elytra-fly: false + # The fly exploit causes the player to constantly toggle gliding. + # If too many glide toggles occur within a timeframe, they are + # most likely using PacketFly. + # Still may trigger false positives when players are jumping and + # sprinting with elytra equipped, so recommended to play around + # with the values. + max-glide-toggles-per-time: 25 + # Time in seconds a elytra open count will be remembered by the plugin. + time-in-seconds: 8 + # Configure message in lang folder. + notify-player-to-disable-packetfly: true + # If enabled, player will be kicked with a message instead of + # getting their elytra dropped. + kick-instead-of-remove-elytra: false + +################## +# Chunk Limits # +################## +chunk-limits: + entity-limits: + dropped-item-limit: + # Limit the amount of dropped items in a chunk to combat lag. + # Be aware this does not prioritize items by value or anything, + # it just deletes whatever happens to get over the limit during + # counting. + enable: false + log-removals: true + max-dropped-items-per-chunk: 200 + # The delay in ticks the plugin will wait after an item in a chunk + # has dropped before the check logic will run. + # This improves performance as there will be no check for each single + # item entity that spawns. + post-item-drop-check-delay: 60 + # The period in ticks in which all loaded chunks should be regularly + # checked. Keep in mind: A lower number provides more accuracy but is + # also worse for performance. + check-period-in-ticks: 800 + # Runs item check when a chunk is loaded. + check-on-chunk-load: true + whitelist-specific-item-types: false + # Check the paper api for correct Material enums: + # https://jd.papermc.io/paper/1.20.6/org/bukkit/Material.html + # Make sure your minecraft version is matching as well. + whitelisted-types: + - BLACK_SHULKER_BOX + - BLUE_SHULKER_BOX + - BROWN_SHULKER_BOX + - CYAN_SHULKER_BOX + - GRAY_SHULKER_BOX + - GREEN_SHULKER_BOX + - LIGHT_BLUE_SHULKER_BOX + - LIME_SHULKER_BOX + - MAGENTA_SHULKER_BOX + - ORANGE_SHULKER_BOX + - PINK_SHULKER_BOX + - PURPLE_SHULKER_BOX + - RED_SHULKER_BOX + - SILVER_SHULKER_BOX + - WHITE_SHULKER_BOX + - YELLOW_SHULKER_BOX + tile-entity-limit: + # Limit the amount of tile entities in a chunk to prevent lag. + enable: false + log-removals: true + max-tile-entities-per-chunk: 100 + check-period-in-ticks: 20 + villager-limit: + enable: false + max-villagers-per-chunk: 25 + log-removals: false + # check all chunks every x ticks. + check-period-in-ticks: 600 + # Professions that are in the top of the list are going to be scheduled + # for removal first. + removal-priority: + - NITWIT + - BUTCHER + - FARMER + - LIBRARIAN + whitelist: + enable: false + professions: + - NITWIT + - BUTCHER + - FARMER + - LIBRARIAN + non-living-limit: + # Limit the amount of non living entities in a chunk to prevent lag. + # Ignores dropped items. + enable: false + log-removals: true + max-non-living-per-chunk: 100 + # 20 ticks = 1 second + check-period-in-ticks: 20 + custom-limit: + # Limit specific entity types per chunk. + enable: false + log-removals: true + # Check all loaded chunks every x ticks. + check-period-in-ticks: 1200 + # Check the paper api for correct EntityType enums: + # https://jd.papermc.io/paper/1.20/org/bukkit/entity/EntityType.html + # Make sure your minecraft version is matching as well. + limited-types: + BAT: 3 + BLAZE: 10 + CAVE_SPIDER: 10 + CHICKEN: 10 + COW: 10 + CREEPER: 10 + DONKEY: 10 + ENDERMAN: 10 + ENDERMITE: 3 + EVOKER: 15 + GUARDIAN: 20 + HORSE: 10 + HUSK: 10 + IRON_GOLEM: 15 + LLAMA: 10 + MAGMA_CUBE: 10 + MULE: 10 + MUSHROOM_COW: 10 + OCELOT: 3 + PARROT: 10 + PIG: 10 + POLAR_BEAR: 5 + RABBIT: 5 + SHEEP: 10 + SILVERFISH: 3 + SKELETON: 10 + SKELETON_HORSE: 10 + SLIME: 10 + SPIDER: 10 + SQUID: 20 + STRAY: 10 + VEX: 15 + VINDICATOR: 15 + WITCH: 15 + WITHER: 16 + WITHER_SKELETON: 10 + WITHER_SKULL: 10 + WOLF: 10 + ZOMBIE: 10 + ZOMBIE_HORSE: 10 + ZOMBIE_VILLAGER: 25 + vehicle-limit: + # Limit the amount of vehicles to prevent some lag machines. + # ex. dispenser that spawns a lot of boats into a single location + # then hitting it, causing all boats to explode in every direction. + enable: false + log-removals: false + max-vehicles-per-chunk: 25 + # 200 ticks = 10 seconds. + check-period-in-ticks: 400 + falling-block-limit: + # Prevent players from placing massive sand chunks, then collapsing + # them to kill the server. + enable: true + log: false + # Removes any falling block if there is more than x blocks actively + # falling in a chunk. + max-falling-gravity-blocks-per-chunk: 60 + # Delay in ticks until the same chunk can be checked again. + # Prevents overchecking, because physics events can be called many + # times in a short time for the same chunk. + chunk-check-delay-in-ticks: 20 + block-limit: + enable: false + # Attempt to prevent ChunkBan / Client FPS Lag + max-blocks-per-chunk: + BANNER: 12 + BEACON: 32 + CHEST: 500 + DISPENSER: 100 + ENCHANTMENT_TABLE: 16 + ENDER_CHEST: 64 + GLOWSTONE: 5000 + PISTON_BASE: 32 + PISTON_EXTENSION: 32 + PISTON_MOVING_PIECE: 32 + PISTON_STICKY_BASE: 32 + SIGN: 8 + SIGN_POST: 8 + SKULL_ITEM: 16 + SLIME_BLOCK: 128 + TRAPPED_CHEST: 200 + WALL_BANNER: 12 + WALL_SIGN: 8 + minecart-limit: + # Limit the amount of minecarts to prevent lag caused by collisions. + enable: false + log-removals: false + max-minecarts-per-chunk: 25 + # 200 ticks = 10 seconds. + check-period-in-ticks: 400 + exp-bottle-limit: + # Prevent players from crashing the server or other players by + # creating a ton of THROWN_EXP_BOTTLE entities, then loading + # them at once. + # Does not limit the EXP_ORBS, just the bottle entities. + enable: true + log: false + # Max in a chunk, doesn't limit the actual xp orbs. + max-exp-bottle-per-chunk: 25 + # 20 ticks = 1 second + check-period-in-ticks: 800 + +##################### +# Lag Preventions # +##################### +lag-preventions: + keep-stash-chunks-loaded: + # Idea by 3b3t admin kumori (Soft1k) + # Improves lag generated by large stash chunks constantly loading and + # unloading by setting them force loaded. This might cause increased ram + # usage, so keep an eye out for that. + # Only works on 1.15+. Will not enable on unsupported versions. + enable: false + log: false + # How many container blocks have to be in a chunk for it to be seen + # as a stash chunk to keep force loaded. + container-block-threshold: 50 + # The time in minutes a stash chunks will be kept force loaded before + # setting it back to normal. + keep-loaded-minutes: 120 + # Set to false if you want to check more blocks than just tile entities. + # Makes the overall speed of the module faster if set to true. + only-check-tile-entities: true + container-types: + - DISPENSER + - CHEST + - FURNACE + - BEACON + - TRAPPED_CHEST + - HOPPER + - DROPPER + - WHITE_SHULKER_BOX + - ORANGE_SHULKER_BOX + - MAGENTA_SHULKER_BOX + - LIGHT_BLUE_SHULKER_BOX + - YELLOW_SHULKER_BOX + - LIME_SHULKER_BOX + - PINK_SHULKER_BOX + - GRAY_SHULKER_BOX + - SILVER_SHULKER_BOX + - CYAN_SHULKER_BOX + - PURPLE_SHULKER_BOX + - BLUE_SHULKER_BOX + - BROWN_SHULKER_BOX + - GREEN_SHULKER_BOX + - RED_SHULKER_BOX + - BLACK_SHULKER_BOX + - BREWING_STAND_ITEM + # Radiuses around spawn in chunks (not blocks) that should not be checked. + # Worlds not on this list are exempt from all checking. + worlds: + world: 100 + world_the_end: 100 + world_nether: 100 + disable-item-drops-during-large-stash-explosions: + # Explodes containers without dropping items after a certain amount + # of exploded containers per chunk. + enable: false + log: false + # How many container blocks in a chunk can be blown up until items + # no longer drop from them. + min-explosions-before-drops-disable: 6 + # The time in seconds to wait after an explosion for another one to happen. + # If no explosion happens within x seconds after the first one, the count + # resets to 0. + time-in-seconds: 3 + container-types: + - DISPENSER + - CHEST + - FURNACE + - BEACON + - TRAPPED_CHEST + - HOPPER + - DROPPER + - BREWING_STAND_ITEM + prevent-lever-spam: + # Rate Limit levers to prevent a lag exploit. + enable: false + show-actionbar: true + kick-player: false + max-lever-usages-per-time: 15 + lever-time-in-ticks: 40 + entity-age-limits: + custom-limits: + # Kill certain entities after a custom amount of ticks lived. + enable: false + log-removals: false + # Check all loaded chunks every x ticks. + check-period-in-ticks: 1200 + # Check the paper api for correct EntityType enums: + # https://jd.papermc.io/paper/1.20/org/bukkit/entity/EntityType.html + # Make sure your minecraft version is matching as well. + limited-types: + ARROW: 120 + FALLING_BLOCK: 160 + SNOWBALL: 100 + SPECTRAL_ARROW: 120 + WITHER_SKULL: 100 + projectile-limit: + # Patches any lag exploit that abuses spawning a ton of projectile entities + # (ex. Snowball exploit).Skips over the following entities: ENDER_PEARL, FISHING_HOOK, WITHER_SKULL + # and ENDER_SIGNAL. You can configure those separately in the custom entity age + # limit section. + enable: false + # (20 ticks = 1 second) Will not touch Ender Pearls + max-alive-time-ticks: 300 + # How frequently we should check all projectiles for their alive time + check-period-seconds: 20 + prevent-flooding-machines: + # Will prevent pistons from pushing waterlogged blocks. + enable: false + delete-waterlogged-blocks: true + anti-shulker-drops: + # Disables shulkers dropping stored items when blown up. + # This helps fix client- and serverside lag when done often and fast. + enable: false + regional-activity: + block-spread: + # Limits blocks spreading or forming based on world conditions within a + # configurable radius and timeframe to help reduce lag by cancelling burst + # activity hotspots. + # + # Examples: + # + # - Snow forming due to a snow storm. + # - Ice forming in a snowy Biome like Taiga or Tundra. + # - Obsidian / Cobblestone forming due to contact with water. + # - Concrete forming due to mixing of concrete powder and water. + # - Mushrooms spreading. + # - Fire spreading. + enable: false + log: false + # The radius in blocks in which activity will be grouped together and measured. + check-radius-blocks: 1500.0 + # The time in milliseconds all related activity will be blocked if it exceeded + # the configured limit. + pause-time-millis: 6000 + # The time in milliseconds before a region and its data will be expired + # if no activity has been detected. + # For proper functionality, needs to be at least as long as your pause time. + data-keep-time-millis: 10000 + # The TPS at which to cancel the physics entirely. + pause-TPS: 10.0 + # The MSPT at which to cancel the physics entirely. + pause-MSPT: 120.0 + # Maximum number of times a block can form or spread within the configured + # timeframe before activity will be put on cooldown. + block-form-event-limit: 800 + creature-spawn: + # Limits entity spawning activity within a configurable radius and timeframe + # to help reduce lag by cancelling high activity hotspots. + # + # Examples: + # + # - A creature gets spawned naturally, by spawner or other reasons. + # - An entity gets spawned naturally, by spawner or other reasons. + # This does not include tile entities. + enable: false + log: true + # The radius in blocks in which activity will be grouped together and measured. + check-radius-blocks: 1500.0 + # The time in milliseconds all related activity will be blocked if it exceeded + # the configured limit. + pause-time-millis: 18000 + # The time in milliseconds before a region and its data will be expired + # if no activity has been detected. + # For proper functionality, needs to be at least as long as your pause time. + data-keep-time-millis: 20000 + # The TPS at which to cancel the physics entirely. + pause-TPS: 10.0 + # The MSPT at which to cancel the physics entirely. + pause-MSPT: 120.0 + # Maximum number of entity spawns within configured timeframe. + spawn-event-limit: 6000 + entity-targeting: + # Limits entities targeting other entities within a configurable radius + # and timeframe to help reduce lag by cancelling burst activity hotspots. + enable: false + log: true + # The radius in blocks in which activity will be grouped together and measured. + check-radius-blocks: 1500.0 + # The time in milliseconds all related activity will be blocked if it exceeded + # the configured limit. + pause-time-millis: 18000 + # The time in milliseconds before a region and its data will be expired + # if no activity has been detected. + # For proper functionality, needs to be at least as long as your pause time. + data-keep-time-millis: 20000 + # The TPS at which to cancel the physics entirely. + pause-TPS: 14.0 + # The MSPT at which to cancel the physics entirely. + pause-MSPT: 120.0 + # Maximum number of times an entity can target another entity within the + # configured timeframe before the area will be put on cooldown. + entity-target-event-limit: 8000 + distance-limit: + global-limit: + enable: false + # The max distance no target should exceed. + # You want this to be higher than your highest max distance + # for a specific mob. + max-target-distance: 20.0 + custom-limits: + enable: true + entities: + SKELETON: 6.0 + WITHER: 8.0 + WITHER_SKELETON: 8.0 + ZOMBIE: 6.0 + ZOMBIE_VILLAGER: 10.0 + entity-pathfinding: + # Limits entities deciding to pathfind to a specific location + # within a configurable radius and timeframe to help reduce lag + # by cancelling burst activity hotspots. + enable: false + log: false + # The radius in blocks in which activity will be grouped together and measured. + check-radius-blocks: 1500.0 + # The time in milliseconds all related activity will be blocked if it exceeded + # the configured limit. + pause-time-millis: 6000 + # The time in milliseconds before a region and its data will be expired + # if no activity has been detected. + # For proper functionality, needs to be at least as long as your pause time. + data-keep-time-millis: 10000 + # The TPS at which to cancel the physics entirely. + pause-TPS: 14.0 + # The MSPT at which to cancel the physics entirely. + pause-MSPT: 120.0 + # Maximum number of times an entity can decide to start moving + # towards a location within the configured timeframe before the + # area will be put on cooldown. + entity-pathfind-event-limit: 4000 + distance-limit: + global-limit: + enable: false + # The max distance no mob pathfinding should exceed. + # You always want this to be higher than your highest max distance + # for a specific mob. + max-target-distance: 20.0 + custom-limits: + enable: true + entities: + SKELETON: 6.0 + WITHER: 8.0 + WITHER_SKELETON: 8.0 + ZOMBIE: 6.0 + ZOMBIE_VILLAGER: 10.0 + liquid-spread: + # Limits liquid spreading within a configurable radius and timeframe + # to help reduce lag by cancelling high activity hotspots. + # + # Examples: + # + # - A lava block spreading by flowing. + # - A water block spreading by flowing. + # - (optional) A dragon egg is teleporting from one position to another. + enable: false + log: true + # The radius in blocks in which activity will be grouped together and measured. + check-radius-blocks: 1500.0 + # The time in milliseconds all related activity will be blocked if it exceeded + # the configured limit. + pause-time-millis: 18000 + # The time in milliseconds before a region and its data will be expired + # if no activity has been detected. + # For proper functionality, needs to be at least as long as your pause time. + data-keep-time-millis: 20000 + # The TPS at which to cancel the physics entirely. + pause-TPS: 12.0 + # The MSPT at which to cancel the physics entirely. + pause-MSPT: 100.0 + # Maximum number of times liquids are allowed to spread within the configured + # timeframe before they will be put on cooldown. + liquid-spread-event-limit: 2400 + ignore-dragon-egg: true + redstone: + # Limits redstone activity within a configurable radius and timeframe + # to help reduce lag by cancelling high activity hotspots. + # + # Examples: + # + # - A redstone current changes. + # - A redstone block gets powered on. + # - A redstone block gets powered off. + enable: false + log: true + # The radius in blocks in which activity will be grouped together and measured. + check-radius-blocks: 1500.0 + # The time in milliseconds all related activity will be blocked if it exceeded + # the configured limit. + pause-time-millis: 18000 + # The time in milliseconds before a region and its data will be expired + # if no activity has been detected. + # For proper functionality, needs to be at least as long as your pause time. + data-keep-time-millis: 20000 + # The TPS at which to cancel the physics entirely. + pause-TPS: 10.0 + # The MSPT at which to cancel the physics entirely. + pause-MSPT: 120.0 + # Maximum number of redstone events within configured timeframe. + redstone-event-limit: 6000 + noteblocks: + # Limits noteblocks being played within a configurable radius and timeframe + # to help reduce lag by cancelling high activity hotspots. + # + # Examples: + # + # - A noteblock is being played through player interaction. + # - A noteblock is being played through a redstone current. + enable: false + log: false + # The radius in blocks in which activity will be grouped together and measured. + check-radius-blocks: 1500.0 + # The time in milliseconds all related activity will be blocked if it exceeded + # the configured limit. + pause-time-millis: 6000 + # The time in milliseconds before a region and its data will be expired + # if no activity has been detected. + # For proper functionality, needs to be at least as long as your pause time. + data-keep-time-millis: 10000 + # The TPS at which to cancel the physics entirely. + pause-TPS: 10.0 + # The MSPT at which to cancel the physics entirely. + pause-MSPT: 120.0 + # Maximum number of times a noteblock can be played within the configured + # timeframe before they will be put on cooldown. + noteblock-play-limit: 2800 + explosions: + # Limits explosions within a configurable radius and timeframe + # to help reduce lag by cancelling high activity hotspots. + # + # Examples: + # + # - A block exploding. + # - An entity exploding. + # - An entity making the decision to explode. + enable: false + log: false + # The radius in blocks in which activity will be grouped together and measured. + check-radius-blocks: 1500.0 + # The time in milliseconds all related activity will be blocked if it exceeded + # the configured limit. + pause-time-millis: 6000 + # The time in milliseconds before a region and its data will be expired + # if no activity has been detected. + # For proper functionality, needs to be at least as long as your pause time. + data-keep-time-millis: 10000 + # The TPS at which to cancel the physics entirely. + pause-TPS: 10.0 + # The MSPT at which to cancel the physics entirely. + pause-MSPT: 120.0 + # Maximum number of explode events within the configured timeframe + # before the region will be put on cooldown. + explode-event-limit: 500 + pistons: + # Limits piston movement within a configurable radius and timeframe + # to help reduce lag by cancelling high activity hotspots. + # + # Examples: + # + # - A piston extends. + # - A piston retracts. + enable: false + log: false + # The radius in blocks in which activity will be grouped together and measured. + check-radius-blocks: 1500.0 + # The time in milliseconds all related activity will be blocked if it exceeded + # the configured limit. + pause-time-millis: 6000 + # The time in milliseconds before a region and its data will be expired + # if no activity has been detected. + # For proper functionality, needs to be at least as long as your pause time. + data-keep-time-millis: 10000 + # The TPS at which to cancel the physics entirely. + pause-TPS: 10.0 + # The MSPT at which to cancel the physics entirely. + pause-MSPT: 120.0 + # Maximum number of piston extend and/or retracts within the + # configured timeframe. + piston-movement-limit: 1000 + block-physics: + # Limits block physics within a configurable radius and timeframe + # to help reduce lag by cancelling burst activity hotspots. + # + # Note: + # + # The event used for this check (BlockPhysicsEvent) is a high frequency event, + # it may be called thousands of times per a second on a busy server. + # Where possible the event may also only be called for the "root" block of + # physics updates in order to limit event spam. + # Physics updates that cause other blocks to change their state may not result + # in an event for each of those blocks (usually adjacent). + enable: false + log: true + # The radius in blocks in which activity will be grouped together and measured. + check-radius-blocks: 1500.0 + # The time in milliseconds all related activity will be blocked if it exceeded + # the configured limit. + pause-time-millis: 18000 + # The time in milliseconds before a region and its data will be expired + # if no activity has been detected. + # For proper functionality, needs to be at least as long as your pause time. + data-keep-time-millis: 20000 + # The TPS at which to cancel the physics entirely. + pause-TPS: 10.0 + # The MSPT at which to cancel the physics entirely. + pause-MSPT: 120.0 + # Maximum number of times a physics check can be performed within the configured + # timeframe before they will be put on cooldown. + block-physics-event-limit: 256000 + sculk-sensor: + # Limits sculk activity within a configurable radius and timeframe + # to help reduce lag by cancelling high activity hotspots. + # + # Examples: + # + # - A redstone current changes for a sculk sensor. + # - A physics check is being performed for a sculk sensor. + enable: false + log: true + # The radius in blocks in which activity will be grouped together and measured. + check-radius-blocks: 1500.0 + # The time in milliseconds all related activity will be blocked if it exceeded + # the configured limit. + pause-time-millis: 18000 + # The time in milliseconds before a region and its data will be expired + # if no activity has been detected. + # For proper functionality, needs to be at least as long as your pause time. + data-keep-time-millis: 20000 + # The TPS at which to cancel the physics entirely. + pause-TPS: 10.0 + # The MSPT at which to cancel the physics entirely. + pause-MSPT: 120.0 + # Maximum number of sculk events within configured timeframe. + sculk-event-limit: 800 + sculk-blocks: [] + +############# +# Patches # +############# +patches: + prevent-end-gateway-crash: + # Prevents a crash exploit involving boats and end gateways: + # https://www.youtube.com/watch?v=c5nVBQeYo-I + enable: true + log: true + prevent-redstone-on-trapdoor-crash: + # prevents a powerful crash exploit present in 1.13 - 1.19.3 + enable: false + log: true + max-trapdoor-activations-by-redstone-per-time: 10 + # 1 sec = 20 ticks + time-in-ticks: 30 + # Patches the cow duplication exploit that allows duping cows using shears. + # Only affects lower versions + cow-dupe-patch: false + anti-book-ban: + enable: true + # If set to false, will use UTF-8. + # Charset to use to encode the result of NBTCompound#toString into + # a sequence of bytes. The length of that sequence is then used to + # get the approximate Byte-size of an ItemStack. + # Use the /aef bytesize command to get a better understanding. + use-UTF-16: false + max-book-size: 8000 + # Kicks players when they try to create a book bigger than the limit. + kick-on-too-large-book-edit: true + max-author-chars: 32 + max-title-chars: 32 + max-pages: 100 + max-item-size: 8260 + max-inventory-size: 50674 + # How long in ticks a dropped item's size should be cached after + # checking. + dropped-items-size-cache-ticks: 120 + # How long in ticks a player's inventory size should be cached after + # checking. + player-inventory-size-cache-ticks: 20 + pearl-phase: + # Attempts to patch a pearl phasing exploit by cancelling the teleport + # if the pearl is thrown at or near a specific block. + # At the time of the creation of this module, this is an issue with NoCheatPlus. + enable: false + # How many blocks around the teleport location should be searched + # for potential glitch blocks if the teleport location isn't one itself. + search-radius: 2 + maximum-distance-to-cancel-teleport: 3.0 + glitchy-materials: + - GOLD_PLATE + - IRON_PLATE + - IRON_TRAPDOOR + - PURPUR_SLAB + - STEP + - STONE_PLATE + - STONE_SLAB2 + - TRAP_DOOR + - WEB + - WOOD_PLATE + - WOOD_STEP + prevent-command-sign: + # Patch signs that have run_command NBT tags attached, allowing the + # to run a command with operator permissions on click. + # Recommended to enable if you had a rogue admin or backdoor incident. + enable: false + prevent-multiple-enderdragons: + enable: false + log: true + prevent-fast-world-teleport-crash: + # Prevents crash methods that involve very fast teleporting + # between different worlds in a short time. + enable: true + # Time in milliseconds until an entity can teleport to + # another world again. + teleport-delay-millis: 1000 + log: false + prevent-teleport-coordinate-exploit: + # Patches coordinate exploit for teleportation commands such as /tpa, + # /home AS WELL as respawn exploits. + # This is done by vanishing the player for x ticks before teleporting. + enable: true + min-distance-to-vanish-player: 100 + teleport-vanish-time-in-ticks: 10 + # Removes entities or players if they are invalid, dead or not located + # within a ticking chunk. Not sure if this works. + experimental-godmode-patch: false + prevent-dispenser-crash: + # Prevents dispensers from crashing the server when dispensing + # items out of bounds: https://www.youtube.com/watch?v=XL17P87O6xA + enable: true + log: false + tab-complete-crash-patch: + # Patches two lag exploits and an instant server shutdown exploit that + # works by sending a malicious TabComplete packet that triggers a + # StackOverflowError inside the TagParser class. + enable: true + log: false + kick-player: false + remove-beehive-coordinates: + # Patches an exploit that allows players to obtain another player's + # coordinates by trading them for Beehives or Beenests. + # If the traded item contains any bees, the stored bee's NBT data can + # then be read from the item. + # This data includes, but is not limited to: + # - XYZ coordinates of where the bee has its hive + # - XYZ of the bee's last coordinates before entering it's hive + # - XYZ coordinates of where the bee last visited a flower + # - XYZ coordinates of where the bee was first spawned into existence + # - UID of the world the bee was first spawned into existence + enable: true + # The NBT tags to filter from the item. These are the Keys that hold + # the position data. You may add more tags you want removed here. + tags: + - Pos + - HivePos + - FlowerPos + - Paper.Origin + - Paper.OriginWorld + - WorldUUIDMost + - WorldUUIDLeast + window-click-crash-patch: + # Patches a variety of different lag and crash methods that work + # by sending invalid Window Click packets, causing the server to + # dump error logs until it runs out of memory. + enable: true + log: true + kick-player: false + map-cursor-lag-patch: + # Patches the famous stacked map cursor lag that causes both + # client and server crashes. + enable: true + prevent-nocom-coordinate-exploit: + # Prevents the abusable mechanic used by the infamous "No Comment" + # coordinate exploit, where the server responds to requests that are + # far outside of the sending player's reach, therefore either crashing + # or revealing positions loaded by other players. + # More info on NoCom: https://www.youtube.com/watch?v=elqAh3GWRpA + # This is still useful to keep enabled even if your version is not + # affected by the NoCom vulnerability. + enable: true + max-distance: 24 + log: false + kick-player: false + inventory-lag: + # Checks if a player is requesting unusual amounts of traffic from the server + # using ItemStacks. + # If a player exceeds the limit, they will be put on a cooldown, during which + # they will be very limited in terms of ItemStack or Inventory interactions. + enable: false + # For debug purposes. Don't leave enabled for too long as it is very spammy. + log: false + # Whether to immediately close any open inventory of the player on limit exceed + # Note: Closing has to be scheduled so it will take a bit if the server is heavily + # lagging. + close-open-inventory: true + # The time in millis in which to check if the player exceeded the limit. + # Needs to be at least as long as your lockout duration millis. + byte-data-keep-time-millis: 30000 + rate-limit: + # The limit in bytes the server has sent the server in the form of ItemStacks, + # before the player will be put on a rate-limit. + # Should always be lower than your lockout bytesize limit. + bytesize-limit: 8000000 + # The time in millis in which a player is allowed to open x amounts of windows + # but not more. + timeframe-millis: 2500 + # The amount of windows that can be opened during the timeframe-millis. + max-window-opens-per-timeframe: 2 + lockout: + # The upper limit in bytes a player is allowed to request from the server + # within the configured timeframe before he will be put on cooldown. + # During the cooldown, he will not be able to open any inventory screens + # or interact with items. + bytesize-limit: 24000000 + # The time in milliseconds the player will have to wait before + # being able to open an inventory again after he exceeded the limit. + duration-millis: 15000 + check-packets: + - SET_SLOT + - WINDOW_ITEMS + lectern-crash-patch: + # Patches an instant server crash exploit that involves sending + # an invalid Window Click packet while taking a book out of a Lectern. + enable: true + log: false + kick-player: false + sequence-crash-patch: + # Patches a variety of lag/crash exploits that involves sending packets + # with invalid sequences. + enable: true + log: false + kick-player: false + sign-lag: + # Patches a lag exploit that involves sending specific oversized + # sign edit packets. + enable: true + # How many ticks a player needs to wait to be able to send + # another sign update packet (renaming or writing). + packet-delay-in-ticks: 10 + # Vanilla limit is 384 characters per line, which is too much. + line-character-limit: 80 + # General char limit for all lines combined. + total-char-limit: 384 + log: false + kick-player: false + beehive-crash-patch: + # Patches a server crash exploit exclusive to Purpur servers. + # This exploit works due to PurpurClient having a feature that + # lets clients request stored data of a clicked beehive from + # the server. The server does not check how far the clicked + # beehive is away from the client enabling a malicious sender + # to load chunks very fast at far away locations by telling + # the server it clicked a beehive there. + enable: true + channel: purpur:beehive_c2s + max-distance: 24 + log: false + kick-player: false + prevent-crafting-recipe-lag-exploit: + # Prevent lag or crash caused by flooding the server with + # crafting recipe book requests. This can even be done by hand on + # servers with low specs. Only affects versions < 1.16 + enable: true + # How many ticks a player needs to wait to be able to use + # the crafting recipe book again + crafting-recipe-delay-in-ticks: 5 + log: false + kick-player: false + +############## +# Illegals # +############## +illegals: + remove-placed-blocks: + on-chunkload: + # Remove illegally placed blocks on chunkload. + enable: false + blocks-to-remove: + - COMMAND_CHAIN + - COMMAND + - COMMAND_MINECART + - COMMAND_REPEATING + - BEDROCK + - BARRIER + exempted-worlds: + - exampleworld1 + - exampleworld2 + pause-on-low-TPS: true + pause-TPS: 14.0 + periodically: + enable: false + blocks-to-remove: + - COMMAND_CHAIN + - COMMAND + - COMMAND_MINECART + - COMMAND_REPEATING + - BEDROCK + - BARRIER + exempted-worlds: + - exampleworld1 + - exampleworld2 + check-period-in-seconds: 10 + pause-on-low-TPS: true + pause-TPS: 14.0 + remove-unnatural-spawners-on-chunkload: + enable: false + pause-on-low-TPS: true + pause-TPS: 14.0 + # You can add or remove as much world names here as you want. + natural-spawner-types-per-world: + world: + - SKELETON + - ZOMBIE + - SILVERFISH + - SPIDER + - CAVE_SPIDER + world_the_end: + - SKELETON + - SPIDER + world_nether: + - BLAZE + - MAGMA_CUBE + nbt: + ban-custom-tags: + # Bypass permission: aef.bypass.illegal.nbt.custom + # Deletes items that have one or more of the configured tags. + enable: false + # Available options: + # STRICT - Deletes or reverts illegals even on chest open + # ACTION_ON_USE - Deletes or reverts illegals when interacted with + # PREVENT_USE_ONLY - Only prevents usage of illegals if possible + handling: PREVENT_USE_ONLY + # Enable this if you have problems with the plugin removing items from chest guis. + gui-plugins-supported: false + # Prevents Hopper32k mechanic of placing a shulker containing illegals on top + # of a hopper, then using the illegal out of the hopper's inventory. + # WARNING: Hooks into InventoryMoveItemEvent, which can become VERY resource + # intense as the event fires in high frequencies as soon as players start using + # farms or item sorters. Recommended to leave off if not necessary. + prevent-hopper32k-mechanic: false + check-on-chunkload: + # WARNING: CHECKING ON CHUNKLOAD IS NOT RECOMMENDED AS IT IS VERY RESOURCE INTENSE. + # BE VERY SURE YOU ACTUALLY NEED THIS. + # Iterates over all blocks in a chunk when it is loaded and checks any inventories + # for illegals. If a container with illegals is found, the container will be REMOVED. + enable: false + # If set to true, immediately replaces the container with air. Otherwise, will try + # to handle items separately. + remove-container: false + # The time in milliseconds to wait before performing another check, + # if a check was positive. Helps with lag resulting from repeatedly + # checking illegals. + check-rate-limit-millis: 3000 + check-stored-items: false + # The exact, case sensitive value of the nbt tag. + tags: + - dmg + item-whitelist-enabled: false + use-as-blacklist-instead: false + whitelisted-items: + - GOLDEN_APPLE + impossibly-stored-items: + # Bypass permission: aef.bypass.illegal.nbt.storeditems + # Prevents usage of or deletes storage items that have been pre-filled + # with items using NBT tags. These can only be created by players with + # creative access. + # Most commonly dispensers, droppers and chests containing kit shulkers + # are created but there are more combinations possible. + enable: false + # Available options: + # STRICT - Deletes or reverts illegals even on chest open + # ACTION_ON_USE - Deletes or reverts illegals when interacted with + # PREVENT_USE_ONLY - Only prevents usage of illegals if possible + handling: PREVENT_USE_ONLY + # Enable this if you have problems with the plugin removing items from chest guis. + gui-plugins-supported: false + # Prevents Hopper32k mechanic of placing a shulker containing illegals on top + # of a hopper, then using the illegal out of the hopper's inventory. + # WARNING: Hooks into InventoryMoveItemEvent, which can become VERY resource + # intense as the event fires in high frequencies as soon as players start using + # farms or item sorters. Recommended to leave off if not necessary. + prevent-hopper32k-mechanic: false + check-on-chunkload: + # WARNING: CHECKING ON CHUNKLOAD IS NOT RECOMMENDED AS IT IS VERY RESOURCE INTENSE. + # BE VERY SURE YOU ACTUALLY NEED THIS. + # Iterates over all blocks in a chunk when it is loaded and checks any inventories + # for illegals. If a container with illegals is found, the container will be REMOVED. + enable: false + # If set to true, immediately replaces the container with air. Otherwise, will try + # to handle items separately. + remove-container: false + # The time in milliseconds to wait before performing another check, + # if a check was positive. Helps with lag resulting from repeatedly + # checking illegals. + check-rate-limit-millis: 3000 + # The exact name of the nbt tag that signals items are stored inside. + tag: BlockEntityTag + check-stored-items: false + storage-types: + - BEACON + - BREWING_STAND_ITEM + - CHEST + - DISPENSER + - DROPPER + - FURNACE + - HOPPER + - TRAPPED_CHEST + command-items: + # Bypass permission: aef.bypass.illegal.nbt.commanditem + # Deletes items with commands in their NBT data that run as operator. + # These can only be created by players with creative access. + # Most common items are books, since it allows storing multiple commands. + enable: false + # Available options: + # STRICT - Deletes or reverts illegals even on chest open + # ACTION_ON_USE - Deletes or reverts illegals when interacted with + # PREVENT_USE_ONLY - Only prevents usage of illegals if possible + handling: PREVENT_USE_ONLY + # Enable this if you have problems with the plugin removing items from chest guis. + gui-plugins-supported: false + # Prevents Hopper32k mechanic of placing a shulker containing illegals on top + # of a hopper, then using the illegal out of the hopper's inventory. + # WARNING: Hooks into InventoryMoveItemEvent, which can become VERY resource + # intense as the event fires in high frequencies as soon as players start using + # farms or item sorters. Recommended to leave off if not necessary. + prevent-hopper32k-mechanic: false + check-on-chunkload: + # WARNING: CHECKING ON CHUNKLOAD IS NOT RECOMMENDED AS IT IS VERY RESOURCE INTENSE. + # BE VERY SURE YOU ACTUALLY NEED THIS. + # Iterates over all blocks in a chunk when it is loaded and checks any inventories + # for illegals. If a container with illegals is found, the container will be REMOVED. + enable: false + # If set to true, immediately replaces the container with air. Otherwise, will try + # to handle items separately. + remove-container: false + # The time in milliseconds to wait before performing another check, + # if a check was positive. Helps with lag resulting from repeatedly + # checking illegals. + check-rate-limit-millis: 3000 + check-stored-items: false + enchantments: + inapplicable-enchants: + # Bypass permission: aef.bypass.illegal.enchants.inapplicable + # Reverts or prevents usage of ItemStacks with Enchantments that + # cannot be applied to that ItemStack in vanilla survival minecraft. + # Examples: A helmet with sharpness or a block of stone with fortune. + enable: false + # Available options: + # STRICT - Deletes or reverts illegals even on chest open + # ACTION_ON_USE - Deletes or reverts illegals when interacted with + # PREVENT_USE_ONLY - Only prevents usage of illegals if possible + handling: PREVENT_USE_ONLY + # Enable this if you have problems with the plugin removing items from chest guis. + gui-plugins-supported: false + # Prevents Hopper32k mechanic of placing a shulker containing illegals on top + # of a hopper, then using the illegal out of the hopper's inventory. + # WARNING: Hooks into InventoryMoveItemEvent, which can become VERY resource + # intense as the event fires in high frequencies as soon as players start using + # farms or item sorters. Recommended to leave off if not necessary. + prevent-hopper32k-mechanic: false + check-on-chunkload: + # WARNING: CHECKING ON CHUNKLOAD IS NOT RECOMMENDED AS IT IS VERY RESOURCE INTENSE. + # BE VERY SURE YOU ACTUALLY NEED THIS. + # Iterates over all blocks in a chunk when it is loaded and checks any inventories + # for illegals. If a container with illegals is found, the container will be REMOVED. + enable: false + # If set to true, immediately replaces the container with air. Otherwise, will try + # to handle items separately. + remove-container: false + # The time in milliseconds to wait before performing another check, + # if a check was positive. Helps with lag resulting from repeatedly + # checking illegals. + check-rate-limit-millis: 3000 + check-stored-items: false + item-whitelist-enabled: true + use-as-blacklist-instead: false + whitelisted-items: + - GOLDEN_APPLE + higher-enchants: + # Bypass permission: aef.bypass.illegal.enchants.higher + # Reverts or prevents usage of ItemStacks with Enchantments higher + # than the natural, in vanilla survival obtainable level (aka 32ks / 255s). + enable: false + # Available options: + # STRICT - Deletes or reverts illegals even on chest open + # ACTION_ON_USE - Deletes or reverts illegals when interacted with + # PREVENT_USE_ONLY - Only prevents usage of illegals if possible + handling: PREVENT_USE_ONLY + # Enable this if you have problems with the plugin removing items from chest guis. + gui-plugins-supported: false + # Prevents Hopper32k mechanic of placing a shulker containing illegals on top + # of a hopper, then using the illegal out of the hopper's inventory. + # WARNING: Hooks into InventoryMoveItemEvent, which can become VERY resource + # intense as the event fires in high frequencies as soon as players start using + # farms or item sorters. Recommended to leave off if not necessary. + prevent-hopper32k-mechanic: false + check-on-chunkload: + # WARNING: CHECKING ON CHUNKLOAD IS NOT RECOMMENDED AS IT IS VERY RESOURCE INTENSE. + # BE VERY SURE YOU ACTUALLY NEED THIS. + # Iterates over all blocks in a chunk when it is loaded and checks any inventories + # for illegals. If a container with illegals is found, the container will be REMOVED. + enable: false + # If set to true, immediately replaces the container with air. Otherwise, will try + # to handle items separately. + remove-container: false + # The time in milliseconds to wait before performing another check, + # if a check was positive. Helps with lag resulting from repeatedly + # checking illegals. + check-rate-limit-millis: 3000 + check-stored-items: false + only-specific-enchants: false + specific-enchants: + - DIG_SPEED + item-whitelist-enabled: true + use-as-blacklist-instead: false + whitelisted-items: + - GOLDEN_APPLE + incompatible-enchants: + # Bypass permission: aef.bypass.illegal.enchants.incompatible + # Reverts or prevents usage of ItemStacks with Enchantments that + # cannot coexist in vanilla survival minecraft. + # Examples: A bow with mending and infinity or armor with every + # protection enchantment. + enable: false + # Available options: + # STRICT - Deletes or reverts illegals even on chest open + # ACTION_ON_USE - Deletes or reverts illegals when interacted with + # PREVENT_USE_ONLY - Only prevents usage of illegals if possible + handling: PREVENT_USE_ONLY + # Enable this if you have problems with the plugin removing items from chest guis. + gui-plugins-supported: false + # Prevents Hopper32k mechanic of placing a shulker containing illegals on top + # of a hopper, then using the illegal out of the hopper's inventory. + # WARNING: Hooks into InventoryMoveItemEvent, which can become VERY resource + # intense as the event fires in high frequencies as soon as players start using + # farms or item sorters. Recommended to leave off if not necessary. + prevent-hopper32k-mechanic: false + check-on-chunkload: + # WARNING: CHECKING ON CHUNKLOAD IS NOT RECOMMENDED AS IT IS VERY RESOURCE INTENSE. + # BE VERY SURE YOU ACTUALLY NEED THIS. + # Iterates over all blocks in a chunk when it is loaded and checks any inventories + # for illegals. If a container with illegals is found, the container will be REMOVED. + enable: false + # If set to true, immediately replaces the container with air. Otherwise, will try + # to handle items separately. + remove-container: false + # The time in milliseconds to wait before performing another check, + # if a check was positive. Helps with lag resulting from repeatedly + # checking illegals. + check-rate-limit-millis: 3000 + check-stored-items: false + item-whitelist-enabled: true + use-as-blacklist-instead: false + whitelisted-items: + - BOW + ban-player-heads: + # Bypass permission: aef.bypass.illegal.playerhead + # Deletes or prevents usage of player heads. + enable: false + # Available options: + # STRICT - Deletes or reverts illegals even on chest open + # ACTION_ON_USE - Deletes or reverts illegals when interacted with + # PREVENT_USE_ONLY - Only prevents usage of illegals if possible + handling: PREVENT_USE_ONLY + # Enable this if you have problems with the plugin removing items from chest guis. + gui-plugins-supported: false + # Prevents Hopper32k mechanic of placing a shulker containing illegals on top + # of a hopper, then using the illegal out of the hopper's inventory. + # WARNING: Hooks into InventoryMoveItemEvent, which can become VERY resource + # intense as the event fires in high frequencies as soon as players start using + # farms or item sorters. Recommended to leave off if not necessary. + prevent-hopper32k-mechanic: false + check-on-chunkload: + # WARNING: CHECKING ON CHUNKLOAD IS NOT RECOMMENDED AS IT IS VERY RESOURCE INTENSE. + # BE VERY SURE YOU ACTUALLY NEED THIS. + # Iterates over all blocks in a chunk when it is loaded and checks any inventories + # for illegals. If a container with illegals is found, the container will be REMOVED. + enable: false + # If set to true, immediately replaces the container with air. Otherwise, will try + # to handle items separately. + remove-container: false + # The time in milliseconds to wait before performing another check, + # if a check was positive. Helps with lag resulting from repeatedly + # checking illegals. + check-rate-limit-millis: 3000 + # Will delete shulker/bundle if they contain any player heads. + check-stored-items: false + ban-specific-materials: + # Bypass permission: aef.bypass.illegal.bannedmaterial + # Prevents usage of or deletes items with material that you do not want + # your players to be able to use. + # Useful if your players have blocks that shouldn't be obtainable in survival. + enable: false + # Available options: + # STRICT - Deletes or reverts illegals even on chest open + # ACTION_ON_USE - Deletes or reverts illegals when interacted with + # PREVENT_USE_ONLY - Only prevents usage of illegals if possible + handling: PREVENT_USE_ONLY + # Enable this if you have problems with the plugin removing items from chest guis. + gui-plugins-supported: false + # Prevents Hopper32k mechanic of placing a shulker containing illegals on top + # of a hopper, then using the illegal out of the hopper's inventory. + # WARNING: Hooks into InventoryMoveItemEvent, which can become VERY resource + # intense as the event fires in high frequencies as soon as players start using + # farms or item sorters. Recommended to leave off if not necessary. + prevent-hopper32k-mechanic: false + check-on-chunkload: + # WARNING: CHECKING ON CHUNKLOAD IS NOT RECOMMENDED AS IT IS VERY RESOURCE INTENSE. + # BE VERY SURE YOU ACTUALLY NEED THIS. + # Iterates over all blocks in a chunk when it is loaded and checks any inventories + # for illegals. If a container with illegals is found, the container will be REMOVED. + enable: false + # If set to true, immediately replaces the container with air. Otherwise, will try + # to handle items separately. + remove-container: false + # The time in milliseconds to wait before performing another check, + # if a check was positive. Helps with lag resulting from repeatedly + # checking illegals. + check-rate-limit-millis: 3000 + check-stored-items: false + banned-materials: + - COMMAND_CHAIN + - COMMAND + - COMMAND_MINECART + - COMMAND_REPEATING + - BEDROCK + - BARRIER + - STRUCTURE_BLOCK + - STRUCTURE_VOID + - ENDER_PORTAL_FRAME + - ENDER_PORTAL + - PORTAL + potions: + # Bypass permission: aef.bypass.illegal.potions + # Prevents usage of or reverts items with any attribute modifiers + # or item flags. + enable: false + # Available options: + # STRICT - Deletes or reverts illegals even on chest open + # ACTION_ON_USE - Deletes or reverts illegals when interacted with + # PREVENT_USE_ONLY - Only prevents usage of illegals if possible + handling: PREVENT_USE_ONLY + # Enable this if you have problems with the plugin removing items from chest guis. + gui-plugins-supported: false + # Prevents Hopper32k mechanic of placing a shulker containing illegals on top + # of a hopper, then using the illegal out of the hopper's inventory. + # WARNING: Hooks into InventoryMoveItemEvent, which can become VERY resource + # intense as the event fires in high frequencies as soon as players start using + # farms or item sorters. Recommended to leave off if not necessary. + prevent-hopper32k-mechanic: false + check-on-chunkload: + # WARNING: CHECKING ON CHUNKLOAD IS NOT RECOMMENDED AS IT IS VERY RESOURCE INTENSE. + # BE VERY SURE YOU ACTUALLY NEED THIS. + # Iterates over all blocks in a chunk when it is loaded and checks any inventories + # for illegals. If a container with illegals is found, the container will be REMOVED. + enable: false + # If set to true, immediately replaces the container with air. Otherwise, will try + # to handle items separately. + remove-container: false + # The time in milliseconds to wait before performing another check, + # if a check was positive. Helps with lag resulting from repeatedly + # checking illegals. + check-rate-limit-millis: 3000 + check-stored-items: false + ban-spawn-eggs: + # Bypass permission: aef.bypass.illegal.spawnegg + # Deletes or prevents usage of spawn eggs. + enable: false + # Available options: + # STRICT - Deletes or reverts illegals even on chest open + # ACTION_ON_USE - Deletes or reverts illegals when interacted with + # PREVENT_USE_ONLY - Only prevents usage of illegals if possible + handling: PREVENT_USE_ONLY + # Enable this if you have problems with the plugin removing items from chest guis. + gui-plugins-supported: false + # Prevents Hopper32k mechanic of placing a shulker containing illegals on top + # of a hopper, then using the illegal out of the hopper's inventory. + # WARNING: Hooks into InventoryMoveItemEvent, which can become VERY resource + # intense as the event fires in high frequencies as soon as players start using + # farms or item sorters. Recommended to leave off if not necessary. + prevent-hopper32k-mechanic: false + check-on-chunkload: + # WARNING: CHECKING ON CHUNKLOAD IS NOT RECOMMENDED AS IT IS VERY RESOURCE INTENSE. + # BE VERY SURE YOU ACTUALLY NEED THIS. + # Iterates over all blocks in a chunk when it is loaded and checks any inventories + # for illegals. If a container with illegals is found, the container will be REMOVED. + enable: false + # If set to true, immediately replaces the container with air. Otherwise, will try + # to handle items separately. + remove-container: false + # The time in milliseconds to wait before performing another check, + # if a check was positive. Helps with lag resulting from repeatedly + # checking illegals. + check-rate-limit-millis: 3000 + # If remove-spawn-eggs is set to true Will delete shulker/bundle + # should they contain any spawneggs. + check-stored-items: false + whitelisted-items: + - MONSTER_EGG + data-values: + custom-data-values: + # 1.12 Only. Bypass permission: aef.bypass.illegal.data.custom + # Deletes items with configured illegal MaterialData values. + # Use '/aef datavalue' ingame while holding an item to add the + # specific value here. + enable: false + # Available options: + # STRICT - Deletes or reverts illegals even on chest open + # ACTION_ON_USE - Deletes or reverts illegals when interacted with + # PREVENT_USE_ONLY - Only prevents usage of illegals if possible + handling: PREVENT_USE_ONLY + # Enable this if you have problems with the plugin removing items from chest guis. + gui-plugins-supported: false + # Prevents Hopper32k mechanic of placing a shulker containing illegals on top + # of a hopper, then using the illegal out of the hopper's inventory. + # WARNING: Hooks into InventoryMoveItemEvent, which can become VERY resource + # intense as the event fires in high frequencies as soon as players start using + # farms or item sorters. Recommended to leave off if not necessary. + prevent-hopper32k-mechanic: false + check-on-chunkload: + # WARNING: CHECKING ON CHUNKLOAD IS NOT RECOMMENDED AS IT IS VERY RESOURCE INTENSE. + # BE VERY SURE YOU ACTUALLY NEED THIS. + # Iterates over all blocks in a chunk when it is loaded and checks any inventories + # for illegals. If a container with illegals is found, the container will be REMOVED. + enable: false + # If set to true, immediately replaces the container with air. Otherwise, will try + # to handle items separately. + remove-container: false + # The time in milliseconds to wait before performing another check, + # if a check was positive. Helps with lag resulting from repeatedly + # checking illegals. + check-rate-limit-millis: 3000 + check-stored-items: false + data-values: + - SuperIllegalItem(0) + item-whitelist-enabled: false + use-as-blacklist-instead: false + whitelisted-items: + - GOLDEN_APPLE + illegal-golden-apples: + # 1.12 Only. Bypass permission: aef.bypass.illegal.data.apple + # Deletes apples with illegal MaterialData values. Will use the + # API to determine what a natural value looks like. + enable: false + # Available options: + # STRICT - Deletes or reverts illegals even on chest open + # ACTION_ON_USE - Deletes or reverts illegals when interacted with + # PREVENT_USE_ONLY - Only prevents usage of illegals if possible + handling: PREVENT_USE_ONLY + # Enable this if you have problems with the plugin removing items from chest guis. + gui-plugins-supported: false + # Prevents Hopper32k mechanic of placing a shulker containing illegals on top + # of a hopper, then using the illegal out of the hopper's inventory. + # WARNING: Hooks into InventoryMoveItemEvent, which can become VERY resource + # intense as the event fires in high frequencies as soon as players start using + # farms or item sorters. Recommended to leave off if not necessary. + prevent-hopper32k-mechanic: false + check-on-chunkload: + # WARNING: CHECKING ON CHUNKLOAD IS NOT RECOMMENDED AS IT IS VERY RESOURCE INTENSE. + # BE VERY SURE YOU ACTUALLY NEED THIS. + # Iterates over all blocks in a chunk when it is loaded and checks any inventories + # for illegals. If a container with illegals is found, the container will be REMOVED. + enable: false + # If set to true, immediately replaces the container with air. Otherwise, will try + # to handle items separately. + remove-container: false + # The time in milliseconds to wait before performing another check, + # if a check was positive. Helps with lag resulting from repeatedly + # checking illegals. + check-rate-limit-millis: 3000 + check-stored-items: false + banned-item-names: + # Bypass permission: aef.bypass.illegal.bannedname + # Resets an item's name (or deletes the item) if it matches one of + # the configured regexes. + # Regexes can be complex. Use a tool like https://regex101.com/ or + # ChatGPT for good results. + enable: false + # Available options: + # STRICT - Deletes or reverts illegals even on chest open + # ACTION_ON_USE - Deletes or reverts illegals when interacted with + # PREVENT_USE_ONLY - Only prevents usage of illegals if possible + handling: PREVENT_USE_ONLY + # Enable this if you have problems with the plugin removing items from chest guis. + gui-plugins-supported: false + # Prevents Hopper32k mechanic of placing a shulker containing illegals on top + # of a hopper, then using the illegal out of the hopper's inventory. + # WARNING: Hooks into InventoryMoveItemEvent, which can become VERY resource + # intense as the event fires in high frequencies as soon as players start using + # farms or item sorters. Recommended to leave off if not necessary. + prevent-hopper32k-mechanic: false + check-on-chunkload: + # WARNING: CHECKING ON CHUNKLOAD IS NOT RECOMMENDED AS IT IS VERY RESOURCE INTENSE. + # BE VERY SURE YOU ACTUALLY NEED THIS. + # Iterates over all blocks in a chunk when it is loaded and checks any inventories + # for illegals. If a container with illegals is found, the container will be REMOVED. + enable: false + # If set to true, immediately replaces the container with air. Otherwise, will try + # to handle items separately. + remove-container: false + # The time in milliseconds to wait before performing another check, + # if a check was positive. Helps with lag resulting from repeatedly + # checking illegals. + check-rate-limit-millis: 3000 + # Will delete the item instead of resetting the name. + delete-item: false + regex: + - (?i)illegalstring + whitelisted-items: + - DIRT + illegally-stacked-items: + # Bypass permission: aef.bypass.illegal.overstacked + # Prevents usage of or reverts items with a higher or lower + # stack size than their vanilla limit. + enable: false + # Available options: + # STRICT - Deletes or reverts illegals even on chest open + # ACTION_ON_USE - Deletes or reverts illegals when interacted with + # PREVENT_USE_ONLY - Only prevents usage of illegals if possible + handling: PREVENT_USE_ONLY + # Enable this if you have problems with the plugin removing items from chest guis. + gui-plugins-supported: false + # Prevents Hopper32k mechanic of placing a shulker containing illegals on top + # of a hopper, then using the illegal out of the hopper's inventory. + # WARNING: Hooks into InventoryMoveItemEvent, which can become VERY resource + # intense as the event fires in high frequencies as soon as players start using + # farms or item sorters. Recommended to leave off if not necessary. + prevent-hopper32k-mechanic: false + check-on-chunkload: + # WARNING: CHECKING ON CHUNKLOAD IS NOT RECOMMENDED AS IT IS VERY RESOURCE INTENSE. + # BE VERY SURE YOU ACTUALLY NEED THIS. + # Iterates over all blocks in a chunk when it is loaded and checks any inventories + # for illegals. If a container with illegals is found, the container will be REMOVED. + enable: false + # If set to true, immediately replaces the container with air. Otherwise, will try + # to handle items separately. + remove-container: false + # The time in milliseconds to wait before performing another check, + # if a check was positive. Helps with lag resulting from repeatedly + # checking illegals. + check-rate-limit-millis: 3000 + item-whitelist-enabled: false + use-as-blacklist-instead: true + check-stored-items: false + whitelisted-items: + - TOTEM + revert-unbreakables: + # Bypass permission: aef.bypass.illegal.unbreakable + # Deletes and prevents usage of unbreakable items. + # This can be anything from items with illegal damage attributes to + # Metadata/NBT tags. + # Note: Due to the limitations of the API, we can only fully prevent + # usage of these items by deleting them. + enable: false + # Available options: + # STRICT - Deletes or reverts illegals even on chest open + # ACTION_ON_USE - Deletes or reverts illegals when interacted with + # PREVENT_USE_ONLY - Only prevents usage of illegals if possible + handling: PREVENT_USE_ONLY + # Enable this if you have problems with the plugin removing items from chest guis. + gui-plugins-supported: false + # Prevents Hopper32k mechanic of placing a shulker containing illegals on top + # of a hopper, then using the illegal out of the hopper's inventory. + # WARNING: Hooks into InventoryMoveItemEvent, which can become VERY resource + # intense as the event fires in high frequencies as soon as players start using + # farms or item sorters. Recommended to leave off if not necessary. + prevent-hopper32k-mechanic: false + check-on-chunkload: + # WARNING: CHECKING ON CHUNKLOAD IS NOT RECOMMENDED AS IT IS VERY RESOURCE INTENSE. + # BE VERY SURE YOU ACTUALLY NEED THIS. + # Iterates over all blocks in a chunk when it is loaded and checks any inventories + # for illegals. If a container with illegals is found, the container will be REMOVED. + enable: false + # If set to true, immediately replaces the container with air. Otherwise, will try + # to handle items separately. + remove-container: false + # The time in milliseconds to wait before performing another check, + # if a check was positive. Helps with lag resulting from repeatedly + # checking illegals. + check-rate-limit-millis: 3000 + item-whitelist-enabled: false + use-as-blacklist-instead: false + # Will delete shulkers and bundles if they contain unbreakables. + check-stored-items: false + # Make sure to keep enabled on 1.16+, otherwise netherite tools + # will mistakenly be set to max durability, due to some bug in paper. + skip-zero-durability: true + whitelisted-items: + - DIAMOND_CHESTPLATE + +###################### +# Dupe Preventions # +###################### +dupe-preventions: + # Prevent any possible dupes involving chested entities by making + # it impossible to put a chest on them. + # Only do this if you have reason to believe a dupe like that exists + # on your server. + prevent-chests-on-living-entities: false + # Closes open inventories of all entities that are in a chunk + # that will be unloaded. + close-entity-inventories-on-chunk-unload: false + # Prevents entities that can carry chests from using portals to + # block some common dupe tactics. + # CAUTION: Will remove chests and their contents from a chested + # entity if it touches a portal on Folia! + prevent-chested-living-entities-in-portals: false + # Patches a dupe that involves pushing a chestable entity + # with low hp into an end portal, duplicating its inventory + # content on death + prevent-end-portal-dupe: false + # Closes open inventories of entities that disappeared when the + # player riding it disconnects. + close-entity-inventories-on-player-disconnect: false + +################# +# Preventions # +################# +preventions: + withers: + disable-wither-spawning-at-spawn: + # Disables spawning withers near a configurable radius around + # spawn. Helps if players are generating endless amounts of withers + # to lag the server. + enable: false + inform-players: true + worlds: + world: 5000 + world_the_end: 5000 + world_nether: 5000 + remove-flying-wither-skulls: + # Removes wither skulls when the chunk gets unloaded. + # Use if you have a ton of them at spawn and they are causing lag. + on-chunk-unload: true + # Removes wither skulls when the chunk gets loaded. + # Use if you have a ton of them at spawn and they are causing lag. + on-chunk-load: true + periodically-remove-all-flying-skulls: + # Enable if a lot of wither skulls at spawn are causing lag. + enable: false + check-period-in-ticks: 80 + # Prevents wither skulls from being shot. + disable-withers-from-shooting-skulls: false + rate-limit-wither-skulls: + # This can help combat lag caused by a ton of wither skulls + # spawning but weakens withers. + enable: false + # Cooldown until another skull will be shot at a player + player-target-cooldown-in-ticks: 20 + # Cooldown until another skull can be shot at anything + # else other than a player. + other-target-cooldown-in-ticks: 40 + # Cooldown when wither has no target + no-target-cooldown-in-ticks: 100 + portals: + prevent-specific-types: + # Configure entities here that you suspect might be used in a dupe + # with portals. + # CAUTION: Will kill the entity on folia due to broken portal event. + # There is sadly no other efficient way. + enable: true + # Defaults prevent common lag methods. + entities: + - DROPPED_ITEM + - FIREWORK + - PRIMED_TNT + - THROWN_EXP_BOTTLE + - EXPERIENCE_ORB + - ARMOR_STAND + # Only enable if you must. Does not affect players. + prevent-all-entities-in-portals: false + prevent-destroying-end-portals: + enable: true + log: true + end: + bedrock-protection-radius-blocks: 8 + # Add block locations that should be protected as well. + # Format: ::: + # If you don't want to use this, just configure an empty list: + # pillar-blocks: [] + pillar-blocks: + - world_the_end:143:140:-50 + - world_the_end:112:90:-90 + prevent-portal-traps: + # Teleports a player back to the original location if they have been + # standing in a portal for too long. + enable: false + wait-time-until-tp-back-in-seconds: 10 + # Prevents a lag exploit. Might disable some chunk loader designs. + prevent-projectiles-in-portals: false + permanent-block-breaking: + by-placing-piston-on-retract: + enable: true + whitelisted-worlds: + - example_world_name + by-exploding-pistons: + enable: true + whitelisted-worlds: + - example_world_name + by-growing-structures: + # Prevents removal of permanent blocks by growing structures + # like mushrooms into them. + enable: true + prevent-map-reset-spam: + # Puts a cooldown on creating maps so players cant reset + # map arts that easily. + # Only needed on versions below 1.12 and lower. + # Bypass permission: aef.bypass.mapspam + enable: false + # Sends a message to players telling them how many maps + # they can create per time + notify-players: true + cooldown-time-in-minutes: 60 + max-amount-of-maps-per-time: 4 + prevent-ambient-fish-spawns: + # Prevent certain fish types from spawning in newer versions to combat lag. + enable: false + fish-types-to-prevent: + - COD + - SALMON + - PUFFERFISH + - TROPICAL_FISH + prevent-opped-players: + # Useful if you suspect a backdoor has happened. + enable: false + log: true + whitelisted-players: + - Notch + prevent-nether-roof: + # Prevent players from going above the nether roof. + enable: true + safely-teleport-players: true + anti-bed-trap: + # Resets a players bed respawn they die too many times within + # a certain timeframe. + enable: false + log: false + # Amount of times player can die until he is determined as bed-trapped. + max-deaths-per-time: 7 + # Time until death counter will be reset again + time-in-seconds: 5 + max-distance-from-bed: 6.0 + prevent-non-survival-players: + # Checks if player is in survival and if not, puts him back into survival. + # Useful if you had a backdoor incident. + enable: false + log: true + whitelisted-players: + - Notch + +############ +# Combat # +############ +combat: + anchor-aura-delay: + enable: false + # 1 tick = 50 ms + break-delay-millis: 0 + # 1 tick = 50 ms + place-delay-millis: 400 + # Can help with desync but recommended to leave off unless you have issues. + update-inventory-on-cancel: false + silent-swap-delay: + enable: false + # The delay in millis a player cant swap hotbar items after placing + # a block, clicking a block (for example to place a crystal) or + # damaging an entity. (50 ms = 1 tick) + min-swap-delay-millis: 40 + prevent-bow-bomb: + enable: false + # Fully pulled bow is ~9-10. 15 is default just to be safe. + max-bow-squared-velocity: 15 + portal-god-mode-patch: + # Prevents an exploit that allows players to stand in nether portals and not + # take damage indefinitely by just never sending a TeleportConfirm packet to + # the server. + # A similar method is used for the chorus tp exploit, which is not covered + # by this module. + enable: false + # If the player stays inside the nether portal for this time without teleporting, + # the portal will be broken, making the player inside vulnerable again. + # Nether portal teleports normally happen within ~3s after enter, so 5s (100ticks) + # should be a safe value. + break-portal-delay-ticks: 100 + crystal-aura: + piston-aura-delay: + # Rate-limits pistons that extend into crystals + enable: false + piston-extend-delay-in-ticks: 40 + regular-delay: + enable: false + # 1 tick = 50 ms + break-delay-millis: 200 + # 1 tick = 50 ms + place-delay-millis: 0 + # Can help with desync but recommended to leave off unless you have issues. + update-inventory-on-cancel: false + prevent-burrow: + enable: false + # 1.0 = Half a heart of damage every time you move. + damage-when-moving: 1.0 + teleport-above-block: true + # Prevent burrow even if there is a block above the block they + # are burrowing in. + # Please note this may allow creating an 'elevator', players will + # keep teleporting up until they hit air. + prevent-if-block-above-burrow: false + break-anvil-instead-of-teleport: true + # Needs to be enabled to prevent a bug where players are teleported + # above a slab when the slab is underwater. + allow-slabs-in-burrow: true + ignored-materials: + - BLACK_SHULKER_BOX + - BLUE_SHULKER_BOX + - BROWN_SHULKER_BOX + - CYAN_SHULKER_BOX + - GRAY_SHULKER_BOX + - GREEN_SHULKER_BOX + - LIGHT_BLUE_SHULKER_BOX + - SILVER_SHULKER_BOX + - LIME_SHULKER_BOX + - MAGENTA_SHULKER_BOX + - ORANGE_SHULKER_BOX + - PINK_SHULKER_BOX + - PURPLE_SHULKER_BOX + - RED_SHULKER_BOX + - PURPLE_SHULKER_BOX + - WHITE_SHULKER_BOX + - YELLOW_SHULKER_BOX + - AIR + - DIRT + - GRASS_PATH + - SAND + - GRAVEL + multi-task-patch: + enable: false + piston-push: + # Disables pistons from extending if it would push certain configured entities. + # This can be used to prevent players from pushing other players out of burrows, by + # configuring PLAYER, or to disable piston-crystal by adding ENDER_CRYSTAL to the list. + enable: false + piston-push-blocked-entities: + - PLAYER + bed-aura-delay: + enable: false + # 1 tick = 50 ms + break-delay-millis: 0 + # 1 tick = 50 ms + place-delay-millis: 250 + # Can help with desync but recommended to leave off unless you have issues. + update-inventory-on-cancel: false + +############# +# Bedrock # +############# +bedrock: + fill-in-bedrock: + overworld-floor: + periodically-check-and-fill: + # only checks loaded chunks + enable: false + check-period-in-seconds: 10 + # Uses the exact name of the world's folder in your server directory. + exempted-worlds: + - exampleworld + - exampleworld2 + pause-on-low-tps: true + # The TPS at which bedrock filling will pause to avoid adding to the lag. + pause-tps: 16.0 + fill-on-chunkload: + enable: false + # Recommended to leave off. Only useful if world generation is broken. + also-check-new-chunks: false + # Uses the exact name of the world's folder in your server directory. + exempted-worlds: + - exampleworld + - exampleworld2 + pause-on-low-tps: true + # The TPS at which bedrock filling will pause to avoid adding to the lag. + pause-tps: 16.0 + nether-ceiling: + periodically-check-and-fill: + # Only checks loaded chunks. + enable: false + check-period-in-seconds: 10 + # Uses the exact name of the world's folder in your server directory. + exempted-worlds: + - exampleworld + - exampleworld2 + pause-on-low-tps: true + # The TPS at which bedrock filling will pause to avoid adding to the lag. + pause-tps: 16.0 + fill-on-chunkload: + enable: false + # Recommended to leave off. Only useful if world generation is broken. + also-check-new-chunks: false + # Uses the exact name of the world's folder in your server directory. + exempted-worlds: + - exampleworld + - exampleworld2 + # Pauses the task during low tps to avoid adding to the lag. + pause-on-low-tps: true + # The TPS at which bedrock filling will pause to avoid adding to the lag. + pause-tps: 16.0 + nether-floor: + periodically-check-and-fill: + # Only checks loaded chunks. + enable: false + check-period-in-seconds: 10 + # Uses the exact name of the world's folder in your server directory. + exempted-worlds: + - exampleworld + - exampleworld2 + pause-on-low-tps: true + # The TPS at which bedrock filling will pause to avoid adding to the lag. + pause-tps: 16.0 + fill-on-chunkload: + enable: false + # Recommended to leave off. Only useful if world generation is broken. + also-check-new-chunks: false + # Uses the exact name of the world's folder in your server directory. + exempted-worlds: + - exampleworld + - exampleworld2 + pause-on-low-tps: true + # The TPS at which bedrock filling will pause to avoid adding to the lag. + pause-tps: 16.0 + prevent-going-below-bedrock-floor: + # Prevents players from going below the bedrock floor. + enable: true + # Eject player from the vehicle + eject-player: true + # Disables a player's elytra flight + stop-elytra: true + # Teleport player on top of that bedrock + teleport: true + # 1.0 = Half a heart of damage every time you move. Set 0 to disable + damage-when-moving: 8.0 + # Whether the bedrock hole should be filled or not + fill-bedrock-hole: true + exempted-worlds: + - world_the_end + - skyblock_world + filler-material: BEDROCK diff --git a/README.md b/README.md index a4f528efc..9b45f00e7 100755 --- a/README.md +++ b/README.md @@ -209,8 +209,21 @@ misc: # Cancels the kick for specific kick messages. enable: false kick-messages-to-listen-to: - - Kicked for spamming - - Stop spamming! + - Kicked for spamming + - Stop spamming! + auto-bed: + # Re-enables SPIGOT-5988, also known as 'auto-bed' + # From Minecraft version 1.16 (≈June 2020) to version 1.17.1(≈October 2021) + # there was a bug (SPIGOT-5988) which did not reset the respawn point of the + # player after death, if his bed was blocked with a shulker. + # After dying a second time, the player will be back at his bed again. + # This bug persisted from the Spigot server to Paper and all its forks until + # October 2021, after which it was fixed by the Spigot development team. + # Attempts by players to reach out to Spigot to allow them to disable the patch + # that fixes the SPIGOT-5988 bug have failed. + # Demonstration of how the patch works: + # https://www.youtube.com/watch?v=3y5SbQXzMss + enable: false ########## # Chat # @@ -229,42 +242,42 @@ chat: # Add all commands you WANT your players to be able to access # WITHOUT the '/'. Not case sensitive. whitelisted-commands: - - help - - vote - - kill - - discord - - togglechat - - toggleconnectionmsgs - - toggletells - - togglewhispering - - toggleprivatemsgs - - ignore - - ignorelist - - ignorehard - - toggledeathmsg - - dmt - - worldstats - - stats - - tps - - msg - - whisper - - w - - m - - t - - pm - - tell - - r - - reply - - last + - help + - vote + - kill + - discord + - togglechat + - toggleconnectionmsgs + - toggletells + - togglewhispering + - toggleprivatemsgs + - ignore + - ignorelist + - ignorehard + - toggledeathmsg + - dmt + - worldstats + - stats + - tps + - msg + - whisper + - w + - m + - t + - pm + - tell + - r + - reply + - last # Add all subcommands you DON'T want your players to be able # to access. Case sensitive! blacklisted-subcommands: - - help about - - vote List - - vote Best - - vote Total - - worldstats reload - - stats reload + - help about + - vote List + - vote Best + - vote Total + - worldstats reload + - stats reload prevent-scanning-server-plugins: # Prevents hacked clients running .plugins to find out what plugins # the server is using. @@ -277,6 +290,8 @@ chat: elytra: # NOTE: Set nocheatplus horizontal elytra settings to 500 or higher. elytra-speed: + # The period in ticks players will be checked to determine their speed. + check-period-ticks: 10 # If set to false, will only calculate 2-Dimensional speed without taking height # changes into consideration. calculate-3D-speed: false @@ -346,11 +361,6 @@ elytra: max-elytra-opens-per-time: 25 # Time in seconds a elytra open count will be remembered by the plugin. time-in-seconds: 8 - # Configure message in lang folder. - notify-player-to-disable-packetfly: true - # If enabled, player will be kicked with a message instead of - # getting their elytra dropped. - kick-instead-of-remove-elytra: false ################## # Chunk Limits # @@ -381,23 +391,23 @@ chunk-limits: # https://jd.papermc.io/paper/1.20.6/org/bukkit/Material.html # Make sure your minecraft version is matching as well. whitelisted-types: - - BLACK_SHULKER_BOX - - BLUE_SHULKER_BOX - - BROWN_SHULKER_BOX - - CYAN_SHULKER_BOX - - GRAY_SHULKER_BOX - - GREEN_SHULKER_BOX - - LIGHT_BLUE_SHULKER_BOX - - LIGHT_GRAY_SHULKER_BOX - - LIME_SHULKER_BOX - - MAGENTA_SHULKER_BOX - - ORANGE_SHULKER_BOX - - PINK_SHULKER_BOX - - PURPLE_SHULKER_BOX - - RED_SHULKER_BOX - - SHULKER_BOX - - WHITE_SHULKER_BOX - - YELLOW_SHULKER_BOX + - BLACK_SHULKER_BOX + - BLUE_SHULKER_BOX + - BROWN_SHULKER_BOX + - CYAN_SHULKER_BOX + - GRAY_SHULKER_BOX + - GREEN_SHULKER_BOX + - LIGHT_BLUE_SHULKER_BOX + - LIGHT_GRAY_SHULKER_BOX + - LIME_SHULKER_BOX + - MAGENTA_SHULKER_BOX + - ORANGE_SHULKER_BOX + - PINK_SHULKER_BOX + - PURPLE_SHULKER_BOX + - RED_SHULKER_BOX + - SHULKER_BOX + - WHITE_SHULKER_BOX + - YELLOW_SHULKER_BOX tile-entity-limit: # Limit the amount of tile entities in a chunk to prevent lag. enable: false @@ -413,21 +423,21 @@ chunk-limits: # Professions that are in the top of the list are going to be scheduled # for removal first. removal-priority: - - NONE - - NITWIT - - SHEPHERD - - FISHERMAN - - BUTCHER - - CARTOGRAPHER - - LEATHERWORKER - - FLETCHER - - MASON - - FARMER - - ARMORER - - TOOLSMITH - - WEAPONSMITH - - CLERIC - - LIBRARIAN + - NONE + - NITWIT + - SHEPHERD + - FISHERMAN + - BUTCHER + - CARTOGRAPHER + - LEATHERWORKER + - FLETCHER + - MASON + - FARMER + - ARMORER + - TOOLSMITH + - WEAPONSMITH + - CLERIC + - LIBRARIAN non-living-limit: # Limit the amount of non living entities in a chunk to prevent lag. # Ignores dropped items. @@ -665,63 +675,8 @@ chunk-limits: # Lag Preventions # ##################### lag-preventions: - prevent-falling-block-stasis: - # Patches a lag exploit where FALLING_BLOCK entities are being held in - # a stasis using piston heads, allowing them to accumulate in large - # numbers that are able to crash the server. - enable: true - log: false - # (20 ticks = 1 second) - falling-blocks-max-alive-time-in-ticks: 300 - # How frequently we should check the alive time of all falling blocks. - check-period-in-seconds: 12 - disable-physics-during-low-tps: - noteblocks: - # Some lag machines use noteblocks to work around redstone limitations. - enable: false - disable-TPS: 16.0 - log: false - sculk-bloom: - enable: false - disable-TPS: 14.0 - log: false - leave-decay: - enable: false - disable-TPS: 14.0 - log: false - block-spread: - enable: false - disable-TPS: 14.0 - log: false - block-physics: - # Stop block physics (like falling blocks) when the TPS gets below a certain value. - enable: false - disable-TPS: 16.0 - log: false - redstone: - # Disable redstone during low TPS to prevent some lag machines. - enable: false - disable-TPS: 16.0 - log: false - fire-spread: - enable: false - disable-TPS: 14.0 - log: false - liquid-spread: - enable: false - disable-TPS: 16.0 - log: false - melting-blocks: - enable: false - disable-TPS: 16.0 - log: false - explosions: - # Disable explosions during low tps to combat lag. - enable: false - disable-TPS: 14.0 - log: false keep-stash-chunks-loaded: - # Idea by 6g6s admin kumori: + # Idea by 6g6s admin kumorio: # Improves lag generated by large stash chunks constantly loading and # unloading by setting them force loaded. This might cause increased ram # usage, so keep an eye out for that. @@ -734,40 +689,49 @@ lag-preventions: min-chunk-inhabited-time-ticks: 1000 # The time in minutes a stash chunks will be kept force loaded before # setting it back to normal. - keep-loaded-minutes: 60 + keep-loaded-minutes: 120 + # Set to false if you want to check more blocks than just tile entities. + # Makes the overall speed of the module faster if set to true. + only-check-tile-entities: true container-types: - - CHISELED_BOOKSHELF - - DECORATED_POT - - CHEST - - FURNACE - - JUKEBOX - - SHULKER_BOX - - WHITE_SHULKER_BOX - - ORANGE_SHULKER_BOX - - MAGENTA_SHULKER_BOX - - LIGHT_BLUE_SHULKER_BOX - - YELLOW_SHULKER_BOX - - LIME_SHULKER_BOX - - PINK_SHULKER_BOX - - GRAY_SHULKER_BOX - - LIGHT_GRAY_SHULKER_BOX - - CYAN_SHULKER_BOX - - PURPLE_SHULKER_BOX - - BLUE_SHULKER_BOX - - BROWN_SHULKER_BOX - - GREEN_SHULKER_BOX - - RED_SHULKER_BOX - - BLACK_SHULKER_BOX - - HOPPER - - DISPENSER - - DROPPER - - LECTERN - - TRAPPED_CHEST - - CRAFTER - - BREWING_STAND - - BARREL - - SMOKER - - BLAST_FURNACE + - CHISELED_BOOKSHELF + - DECORATED_POT + - CHEST + - FURNACE + - JUKEBOX + - SHULKER_BOX + - WHITE_SHULKER_BOX + - ORANGE_SHULKER_BOX + - MAGENTA_SHULKER_BOX + - LIGHT_BLUE_SHULKER_BOX + - YELLOW_SHULKER_BOX + - LIME_SHULKER_BOX + - PINK_SHULKER_BOX + - GRAY_SHULKER_BOX + - LIGHT_GRAY_SHULKER_BOX + - CYAN_SHULKER_BOX + - PURPLE_SHULKER_BOX + - BLUE_SHULKER_BOX + - BROWN_SHULKER_BOX + - GREEN_SHULKER_BOX + - RED_SHULKER_BOX + - BLACK_SHULKER_BOX + - HOPPER + - DISPENSER + - DROPPER + - LECTERN + - TRAPPED_CHEST + - CRAFTER + - BREWING_STAND + - BARREL + - SMOKER + - BLAST_FURNACE + # Radiuses around spawn in chunks (not blocks) that should not be checked. + # Worlds not on this list are exempt from all checking. + worlds: + world: 100 + world_the_end: 100 + world_nether: 100 target-distance-limits: enable: false log: false @@ -798,21 +762,21 @@ lag-preventions: # If no explosion happens within x seconds after the first one, the count resets to 0. time-in-seconds: 3 container-types: - - CHISELED_BOOKSHELF - - DECORATED_POT - - CHEST - - FURNACE - - JUKEBOX - - HOPPER - - DISPENSER - - DROPPER - - LECTERN - - TRAPPED_CHEST - - CRAFTER - - BREWING_STAND - - BARREL - - SMOKER - - BLAST_FURNACE + - CHISELED_BOOKSHELF + - DECORATED_POT + - CHEST + - FURNACE + - JUKEBOX + - HOPPER + - DISPENSER + - DROPPER + - LECTERN + - TRAPPED_CHEST + - CRAFTER + - BREWING_STAND + - BARREL + - SMOKER + - BLAST_FURNACE prevent-lever-spam: # Rate Limit levers to prevent a lag exploit. enable: false @@ -855,6 +819,7 @@ lag-preventions: # Make sure your minecraft version is matching as well. limited-types: ARROW: 120 + FALLING_BLOCK: 160 SNOWBALL: 100 SPECTRAL_ARROW: 120 WITHER_SKULL: 100 @@ -895,19 +860,162 @@ lag-preventions: # Disables shulkers dropping stored items when blown up. # This helps fix client- and serverside lag when done often and fast. enable: false - prevent-liquid-update-lag: + prevent-inventory-open-spam: + # Rate-limit interactions with inventory holders to prevent a lag exploit. enable: false - # WARNING: DEFAULTS ARE VERY ROUGH, DEFINITELY TWEAK THIS! - # Number is the result of: - # Amount of liquid source blocks - # multiplied by sides it can spread to - # multiplied by block spread length. - max-liquid-events-in-same-chunk-per-time: 1200 - # Record time after first liquid spread. - # When this time runs out, the spread counter resets - time-in-ticks: 100 - # Very spammy, use for testing/debugging only - log: false + max-interacts-per-time: 2 + time-in-ticks: 20 + physics: + redstone: + enable: false + log: false + # The radius in blocks in which activity will be grouped together and measured. + check-radius-blocks: 1500.0 + # The time in milliseconds all related activity will be blocked if it exceeded + # the configured limit. + pause-time-millis: 5000 + # The time in milliseconds before a region and its data will be expired + # if no activity has been detected. + # For proper functionality, needs to be at least as long as your pause time. + data-keep-time-millis: 8000 + # The TPS at which to cancel the physics entirely. + pause-TPS: 16.0 + # The MSPT at which to cancel the physics entirely. + pause-MSPT: 100.0 + # Maximum number of redstone events within configured timeframe. + redstone-event-limit: 5000 + sculk-bloom: + enable: false + log: false + # The radius in blocks in which activity will be grouped together and measured. + check-radius-blocks: 1500.0 + # The time in milliseconds all related activity will be blocked if it exceeded + # the configured limit. + pause-time-millis: 5000 + # The time in milliseconds before a region and its data will be expired + # if no activity has been detected. + # For proper functionality, needs to be at least as long as your pause time. + data-keep-time-millis: 8000 + # The TPS at which to cancel the physics entirely. + pause-TPS: 16.0 + # The MSPT at which to cancel the physics entirely. + pause-MSPT: 100.0 + # Maximum number of sculk bloom events within the configured timeframe. + sculk-bloom-limit: 300 + block-spread: + enable: false + log: false + # The radius in blocks in which activity will be grouped together and measured. + check-radius-blocks: 1500.0 + # The time in milliseconds all related activity will be blocked if it exceeded + # the configured limit. + pause-time-millis: 5000 + # The time in milliseconds before a region and its data will be expired + # if no activity has been detected. + # For proper functionality, needs to be at least as long as your pause time. + data-keep-time-millis: 8000 + # The TPS at which to cancel the physics entirely. + pause-TPS: 16.0 + # The MSPT at which to cancel the physics entirely. + pause-MSPT: 100.0 + # Maximum number of times a block can form due to natural causes like + # Snow storms, mushrooms growing or cobblestone forming due to water and lava + # within the configured timeframe before they will be paused. + block-form-event-limit: 500 + noteblocks: + enable: false + log: false + # The radius in blocks in which activity will be grouped together and measured. + check-radius-blocks: 1500.0 + # The time in milliseconds all related activity will be blocked if it exceeded + # the configured limit. + pause-time-millis: 5000 + # The time in milliseconds before a region and its data will be expired + # if no activity has been detected. + # For proper functionality, needs to be at least as long as your pause time. + data-keep-time-millis: 8000 + # The TPS at which to cancel the physics entirely. + pause-TPS: 16.0 + # The MSPT at which to cancel the physics entirely. + pause-MSPT: 100.0 + # Maximum number of times a noteblock can be played within the configured + # timeframe before they will be disabled. + noteblock-play-limit: 300 + explosions: + enable: false + log: false + # The radius in blocks in which activity will be grouped together and measured. + check-radius-blocks: 1500.0 + # The time in milliseconds all related activity will be blocked if it exceeded + # the configured limit. + pause-time-millis: 5000 + # The time in milliseconds before a region and its data will be expired + # if no activity has been detected. + # For proper functionality, needs to be at least as long as your pause time. + data-keep-time-millis: 8000 + # The TPS at which to cancel the physics entirely. + pause-TPS: 16.0 + # The MSPT at which to cancel the physics entirely. + pause-MSPT: 100.0 + # Maximum number of explode events within configured timeframe. + explode-event-limit: 5000 + pistons: + enable: false + log: false + # The radius in blocks in which activity will be grouped together and measured. + check-radius-blocks: 1500.0 + # The time in milliseconds all related activity will be blocked if it exceeded + # the configured limit. + pause-time-millis: 5000 + # The time in milliseconds before a region and its data will be expired + # if no activity has been detected. + # For proper functionality, needs to be at least as long as your pause time. + data-keep-time-millis: 8000 + # The TPS at which to cancel the physics entirely. + pause-TPS: 16.0 + # The MSPT at which to cancel the physics entirely. + pause-MSPT: 100.0 + # Maximum number of piston extend and/or retract events + # within the configured timeframe. + piston-movement-limit: 300 + liquid-spread: + enable: false + log: false + # The radius in blocks in which activity will be grouped together and measured. + check-radius-blocks: 1500.0 + # The time in milliseconds all related activity will be blocked if it exceeded + # the configured limit. + pause-time-millis: 5000 + # The time in milliseconds before a region and its data will be expired + # if no activity has been detected. + # For proper functionality, needs to be at least as long as your pause time. + data-keep-time-millis: 8000 + # The TPS at which to cancel the physics entirely. + pause-TPS: 16.0 + # The MSPT at which to cancel the physics entirely. + pause-MSPT: 100.0 + # Maximum number of times a physics check can be performed within the configured + # timeframe before they will be paused. + liquid-spread-event-limit: 1300 + block-physics: + enable: false + log: false + # The radius in blocks in which activity will be grouped together and measured. + check-radius-blocks: 1500.0 + # The time in milliseconds all related activity will be blocked if it exceeded + # the configured limit. + pause-time-millis: 5000 + # The time in milliseconds before a region and its data will be expired + # if no activity has been detected. + # For proper functionality, needs to be at least as long as your pause time. + data-keep-time-millis: 8000 + # The TPS at which to cancel the physics entirely. + pause-TPS: 16.0 + # The MSPT at which to cancel the physics entirely. + pause-MSPT: 100.0 + # Maximum number of times a physics check can be performed within the configured + # timeframe before they will be paused. + block-physics-event-limit: 900 ############# # Patches # @@ -935,23 +1043,123 @@ patches: # How long in ticks a player's inventory size should be cached after # checking. player-inventory-size-cache-ticks: 20 + prevent-command-sign: + # Patch signs that have run_command NBT tags attached, allowing the + # to run a command with operator permissions on click. + # Recommended to enable if you had a rogue admin or backdoor incident. + enable: true pearl-phase: # Attempts to patch a pearl phasing exploit by cancelling the teleport - # if the pearl is thrown at or near a cobweb. + # if the pearl is thrown at or near a specific block type. enable: false # How many blocks around the teleport location should be searched - # for cobwebs if the teleport location isn't one itself. - cobweb-block-radius: 2 + # for potential glitch blocks if the teleport location isn't one itself. + search-radius: 2 + glitchy-materials: + - ACACIA_PRESSURE_PLATE + - ACACIA_SLAB + - ACACIA_TRAPDOOR + - ANDESITE_SLAB + - BAMBOO_MOSAIC_SLAB + - BAMBOO_PRESSURE_PLATE + - BAMBOO_SLAB + - BAMBOO_TRAPDOOR + - BIRCH_PRESSURE_PLATE + - BIRCH_SLAB + - BIRCH_TRAPDOOR + - BLACKSTONE_SLAB + - BRICK_SLAB + - CALIBRATED_SCULK_SENSOR + - CHERRY_PRESSURE_PLATE + - CHERRY_SLAB + - CHERRY_TRAPDOOR + - COBBLED_DEEPSLATE_SLAB + - COBBLESTONE_SLAB + - COBWEB + - COPPER_TRAPDOOR + - CRIMSON_PRESSURE_PLATE + - CRIMSON_SLAB + - CRIMSON_TRAPDOOR + - CUT_COPPER_SLAB + - CUT_RED_SANDSTONE_SLAB + - CUT_SANDSTONE_SLAB + - DARK_OAK_PRESSURE_PLATE + - DARK_OAK_SLAB + - DARK_OAK_TRAPDOOR + - DARK_PRISMARINE_SLAB + - DEEPSLATE_BRICK_SLAB + - DEEPSLATE_TILE_SLAB + - DIORITE_SLAB + - END_STONE_BRICK_SLAB + - EXPOSED_COPPER_TRAPDOOR + - EXPOSED_CUT_COPPER_SLAB + - GRANITE_SLAB + - HEAVY_WEIGHTED_PRESSURE_PLATE + - IRON_TRAPDOOR + - JUNGLE_PRESSURE_PLATE + - JUNGLE_SLAB + - JUNGLE_TRAPDOOR + - LIGHT_WEIGHTED_PRESSURE_PLATE + - MANGROVE_PRESSURE_PLATE + - MANGROVE_SLAB + - MANGROVE_TRAPDOOR + - MOSSY_COBBLESTONE_SLAB + - MOSSY_STONE_BRICK_SLAB + - MUD_BRICK_SLAB + - NETHER_BRICK_SLAB + - OAK_PRESSURE_PLATE + - OAK_SLAB + - OAK_TRAPDOOR + - OXIDIZED_COPPER_TRAPDOOR + - OXIDIZED_CUT_COPPER_SLAB + - PETRIFIED_OAK_SLAB + - POLISHED_ANDESITE_SLAB + - POLISHED_BLACKSTONE_BRICK_SLAB + - POLISHED_BLACKSTONE_PRESSURE_PLATE + - POLISHED_BLACKSTONE_SLAB + - POLISHED_DEEPSLATE_SLAB + - POLISHED_DIORITE_SLAB + - POLISHED_GRANITE_SLAB + - POLISHED_TUFF_SLAB + - POWDER_SNOW + - PRISMARINE_BRICK_SLAB + - PRISMARINE_SLAB + - PURPUR_SLAB + - QUARTZ_SLAB + - RED_NETHER_BRICK_SLAB + - RED_SANDSTONE_SLAB + - SANDSTONE_SLAB + - SCULK_SENSOR + - SCULK_SHRIEKER + - SMOOTH_QUARTZ_SLAB + - SMOOTH_RED_SANDSTONE_SLAB + - SMOOTH_SANDSTONE_SLAB + - SMOOTH_STONE_SLAB + - SPRUCE_PRESSURE_PLATE + - SPRUCE_SLAB + - SPRUCE_TRAPDOOR + - STONE_BRICK_SLAB + - STONE_PRESSURE_PLATE + - STONE_SLAB + - TUFF_BRICK_SLAB + - TUFF_SLAB + - WARPED_PRESSURE_PLATE + - WARPED_SLAB + - WARPED_TRAPDOOR + - WAXED_COPPER_TRAPDOOR + - WAXED_CUT_COPPER_SLAB + - WAXED_EXPOSED_COPPER_TRAPDOOR + - WAXED_EXPOSED_CUT_COPPER_SLAB + - WAXED_OXIDIZED_COPPER_TRAPDOOR + - WAXED_OXIDIZED_CUT_COPPER_SLAB + - WAXED_WEATHERED_COPPER_TRAPDOOR + - WAXED_WEATHERED_CUT_COPPER_SLAB + - WEATHERED_COPPER_TRAPDOOR + - WEATHERED_CUT_COPPER_SLAB # We will have to schedule the check on folia, meaning theres a chance # the task might take longer than expected. To make sure that does not cause # more lag, we set a time limit here. - # Only relevant on folia. - check-timeout-millis: 1000 - prevent-command-sign: - # Patch signs that have run_command NBT tags attached, allowing the - # to run a command with operator permissions on click. - # Recommended to enable if you had a rogue admin or backdoor incident. - enable: true + check-timeout-millis: 800 map-cursor-lag-patch: # Patches the famous stacked map cursor lag that causes both # client and server crashes. @@ -996,13 +1204,13 @@ patches: # The NBT tags to filter from the item. These are the Keys that hold # the position data. You may add more tags you want removed here. tags: - - Pos - - HivePos - - FlowerPos - - Paper.Origin - - Paper.OriginWorld - - WorldUUIDMost - - WorldUUIDLeast + - Pos + - HivePos + - FlowerPos + - Paper.Origin + - Paper.OriginWorld + - WorldUUIDMost + - WorldUUIDLeast window-click-crash-patch: # Patches a variety of different lag and crash methods that work # by sending invalid Window Click packets, causing the server to @@ -1049,32 +1257,32 @@ illegals: enable: false # Enter PLAYER_HEAD here if you want to remove placed playerheads. blocks-to-remove: - - PLAYER_HEAD - - CHAIN_COMMAND_BLOCK - - COMMAND_BLOCK - - COMMAND_BLOCK_MINECART - - REPEATING_COMMAND_BLOCK - - BEDROCK - - BARRIER + - PLAYER_HEAD + - CHAIN_COMMAND_BLOCK + - COMMAND_BLOCK + - COMMAND_BLOCK_MINECART + - REPEATING_COMMAND_BLOCK + - BEDROCK + - BARRIER exempted-worlds: - - exampleworld1 - - exampleworld2 + - exampleworld1 + - exampleworld2 pause-on-low-TPS: false pause-TPS: 14.0 periodically: enable: false # Enter PLAYER_HEAD here if you want to remove placed playerheads. blocks-to-remove: - - PLAYER_HEAD - - CHAIN_COMMAND_BLOCK - - COMMAND_BLOCK - - COMMAND_BLOCK_MINECART - - REPEATING_COMMAND_BLOCK - - BEDROCK - - BARRIER + - PLAYER_HEAD + - CHAIN_COMMAND_BLOCK + - COMMAND_BLOCK + - COMMAND_BLOCK_MINECART + - REPEATING_COMMAND_BLOCK + - BEDROCK + - BARRIER exempted-worlds: - - exampleworld1 - - exampleworld2 + - exampleworld1 + - exampleworld2 check-period-in-seconds: 10 pause-on-low-TPS: false pause-TPS: 14.0 @@ -1085,23 +1293,30 @@ illegals: # You can add or remove as much world names here as you want. natural-spawner-types-per-world: world: - - SKELETON - - ZOMBIE - - SILVERFISH - - SPIDER - - !!org.bukkit.entity.EntityType 'CAVE_SPIDER' + - SKELETON + - ZOMBIE + - SILVERFISH + - SPIDER + - CAVE_SPIDER world_the_end: - - SKELETON - - SPIDER + - SKELETON + - SPIDER world_nether: - - BLAZE - - MAGMA_CUBE + - BLAZE + - MAGMA_CUBE enchantments: higher-enchants: # Bypass permission: aef.bypass.illegal.enchants.higher # Reverts or prevents usage of ItemStacks with Enchantments higher # than the natural, in vanilla survival obtainable level (aka 32ks / 255s). enable: false + # Enable this if you have problems with the plugin removing items from chest guis. + # Check if the inventory is connected to a location in the game. + # If it is not, its very likely created by custom gui plugin. + # Enable this if you have problems with the plugin removing items from chest guis. + # Check if the inventory is connected to a location in the game. + # If it is not, its very likely created by custom gui plugin. + gui-plugins-supported: false # Available options: # STRICT - Deletes or reverts illegals even on chest open # ACTION_ON_USE - Deletes or reverts illegals when interacted with @@ -1125,11 +1340,11 @@ illegals: check-stored-items: false only-specific-enchants: false specific-enchants: - - DIG_SPEED + - DIG_SPEED item-whitelist-enabled: true use-as-blacklist-instead: false whitelisted-items: - - GOLDEN_APPLE + - GOLDEN_APPLE incompatible-enchants: # Bypass permission: aef.bypass.illegal.enchants.incompatible # Reverts or prevents usage of ItemStacks with Enchantments that @@ -1137,6 +1352,13 @@ illegals: # Examples: A bow with mending and infinity or armor with every # protection enchantment. enable: false + # Enable this if you have problems with the plugin removing items from chest guis. + # Check if the inventory is connected to a location in the game. + # If it is not, its very likely created by custom gui plugin. + # Enable this if you have problems with the plugin removing items from chest guis. + # Check if the inventory is connected to a location in the game. + # If it is not, its very likely created by custom gui plugin. + gui-plugins-supported: false # Available options: # STRICT - Deletes or reverts illegals even on chest open # ACTION_ON_USE - Deletes or reverts illegals when interacted with @@ -1161,13 +1383,20 @@ illegals: item-whitelist-enabled: true use-as-blacklist-instead: false whitelisted-items: - - BOW + - BOW inapplicable-enchants: # Bypass permission: aef.bypass.illegal.enchants.inapplicable # Reverts or prevents usage of ItemStacks with Enchantments that # cannot be applied to that ItemStack in vanilla survival minecraft. # Examples: A helmet with sharpness or a block of stone with fortune. enable: false + # Enable this if you have problems with the plugin removing items from chest guis. + # Check if the inventory is connected to a location in the game. + # If it is not, its very likely created by custom gui plugin. + # Enable this if you have problems with the plugin removing items from chest guis. + # Check if the inventory is connected to a location in the game. + # If it is not, its very likely created by custom gui plugin. + gui-plugins-supported: false # Available options: # STRICT - Deletes or reverts illegals even on chest open # ACTION_ON_USE - Deletes or reverts illegals when interacted with @@ -1192,12 +1421,19 @@ illegals: item-whitelist-enabled: true use-as-blacklist-instead: false whitelisted-items: - - GOLDEN_APPLE + - GOLDEN_APPLE nbt: ban-custom-tags: # Bypass permission: aef.bypass.illegal.nbt.custom # Deletes items that have one or more of the configured tags. enable: false + # Enable this if you have problems with the plugin removing items from chest guis. + # Check if the inventory is connected to a location in the game. + # If it is not, its very likely created by custom gui plugin. + # Enable this if you have problems with the plugin removing items from chest guis. + # Check if the inventory is connected to a location in the game. + # If it is not, its very likely created by custom gui plugin. + gui-plugins-supported: false # Available options: # STRICT - Deletes or reverts illegals even on chest open # ACTION_ON_USE - Deletes or reverts illegals when interacted with @@ -1221,11 +1457,11 @@ illegals: check-stored-items: false # The exact, case sensitive value of the nbt tag. tags: - - dmg + - dmg item-whitelist-enabled: false use-as-blacklist-instead: false whitelisted-items: - - GOLDEN_APPLE + - GOLDEN_APPLE impossibly-stored-items: # Bypass permission: aef.bypass.illegal.nbt.storeditems # Prevents usage of or deletes storage items that have been pre-filled @@ -1234,6 +1470,13 @@ illegals: # Most commonly dispensers, droppers and chests containing kit shulkers # are created but there are more combinations possible. enable: false + # Enable this if you have problems with the plugin removing items from chest guis. + # Check if the inventory is connected to a location in the game. + # If it is not, its very likely created by custom gui plugin. + # Enable this if you have problems with the plugin removing items from chest guis. + # Check if the inventory is connected to a location in the game. + # If it is not, its very likely created by custom gui plugin. + gui-plugins-supported: false # Available options: # STRICT - Deletes or reverts illegals even on chest open # ACTION_ON_USE - Deletes or reverts illegals when interacted with @@ -1258,27 +1501,34 @@ illegals: tag: BlockEntityTag check-stored-items: false storage-types: - - BARREL - - BLAST_FURNACE - - BREWING_STAND - - CHEST - - CHISELED_BOOKSHELF - - CRAFTER - - DECORATED_POT - - DISPENSER - - DROPPER - - FURNACE - - HOPPER - - JUKEBOX - - LECTERN - - SMOKER - - TRAPPED_CHEST + - BARREL + - BLAST_FURNACE + - BREWING_STAND + - CHEST + - CHISELED_BOOKSHELF + - CRAFTER + - DECORATED_POT + - DISPENSER + - DROPPER + - FURNACE + - HOPPER + - JUKEBOX + - LECTERN + - SMOKER + - TRAPPED_CHEST command-items: # Bypass permission: aef.bypass.illegal.nbt.commanditem # Deletes items with commands in their NBT data that run as operator. # These can only be created by players with creative access. # Most common items are books, since it allows storing multiple commands. enable: false + # Enable this if you have problems with the plugin removing items from chest guis. + # Check if the inventory is connected to a location in the game. + # If it is not, its very likely created by custom gui plugin. + # Enable this if you have problems with the plugin removing items from chest guis. + # Check if the inventory is connected to a location in the game. + # If it is not, its very likely created by custom gui plugin. + gui-plugins-supported: false # Available options: # STRICT - Deletes or reverts illegals even on chest open # ACTION_ON_USE - Deletes or reverts illegals when interacted with @@ -1307,6 +1557,13 @@ illegals: # Regexes can be complex. Use a tool like https://regex101.com/ or # ChatGPT for good results. enable: false + # Enable this if you have problems with the plugin removing items from chest guis. + # Check if the inventory is connected to a location in the game. + # If it is not, its very likely created by custom gui plugin. + # Enable this if you have problems with the plugin removing items from chest guis. + # Check if the inventory is connected to a location in the game. + # If it is not, its very likely created by custom gui plugin. + gui-plugins-supported: false # Available options: # STRICT - Deletes or reverts illegals even on chest open # ACTION_ON_USE - Deletes or reverts illegals when interacted with @@ -1330,14 +1587,21 @@ illegals: # Will delete the item instead of resetting the name. delete-item: false regex: - - (?i)illegalstring + - (?i)illegalstring whitelisted-items: - - DIRT + - DIRT illegally-stacked-items: # Bypass permission: aef.bypass.illegal.overstacked # Prevents usage of or reverts items with a higher or lower # stack size than their vanilla limit. enable: false + # Enable this if you have problems with the plugin removing items from chest guis. + # Check if the inventory is connected to a location in the game. + # If it is not, its very likely created by custom gui plugin. + # Enable this if you have problems with the plugin removing items from chest guis. + # Check if the inventory is connected to a location in the game. + # If it is not, its very likely created by custom gui plugin. + gui-plugins-supported: false # Available options: # STRICT - Deletes or reverts illegals even on chest open # ACTION_ON_USE - Deletes or reverts illegals when interacted with @@ -1362,11 +1626,18 @@ illegals: use-as-blacklist-instead: true check-stored-items: false whitelisted-items: - - TOTEM_OF_UNDYING + - TOTEM_OF_UNDYING ban-player-heads: # Bypass permission: aef.bypass.illegal.playerhead # Deletes or prevents usage of player heads. enable: false + # Enable this if you have problems with the plugin removing items from chest guis. + # Check if the inventory is connected to a location in the game. + # If it is not, its very likely created by custom gui plugin. + # Enable this if you have problems with the plugin removing items from chest guis. + # Check if the inventory is connected to a location in the game. + # If it is not, its very likely created by custom gui plugin. + gui-plugins-supported: false # Available options: # STRICT - Deletes or reverts illegals even on chest open # ACTION_ON_USE - Deletes or reverts illegals when interacted with @@ -1395,6 +1666,13 @@ illegals: # your players to be able to use. # Useful if your players have blocks that shouldn't be obtainable in survival. enable: false + # Enable this if you have problems with the plugin removing items from chest guis. + # Check if the inventory is connected to a location in the game. + # If it is not, its very likely created by custom gui plugin. + # Enable this if you have problems with the plugin removing items from chest guis. + # Check if the inventory is connected to a location in the game. + # If it is not, its very likely created by custom gui plugin. + gui-plugins-supported: false # Available options: # STRICT - Deletes or reverts illegals even on chest open # ACTION_ON_USE - Deletes or reverts illegals when interacted with @@ -1417,18 +1695,18 @@ illegals: remove-container: false check-stored-items: false banned-materials: - - CHAIN_COMMAND_BLOCK - - COMMAND_BLOCK - - COMMAND_BLOCK_MINECART - - REPEATING_COMMAND_BLOCK - - BEDROCK - - BARRIER - - STRUCTURE_BLOCK - - STRUCTURE_VOID - - END_PORTAL_FRAME - - END_PORTAL - - NETHER_PORTAL - - LIGHT + - CHAIN_COMMAND_BLOCK + - COMMAND_BLOCK + - COMMAND_BLOCK_MINECART + - REPEATING_COMMAND_BLOCK + - BEDROCK + - BARRIER + - STRUCTURE_BLOCK + - STRUCTURE_VOID + - END_PORTAL_FRAME + - END_PORTAL + - NETHER_PORTAL + - LIGHT revert-unbreakables: # Bypass permission: aef.bypass.illegal.unbreakable # Deletes and prevents usage of unbreakable items. @@ -1437,6 +1715,13 @@ illegals: # Note: Due to the limitations of the API, we can only fully prevent # usage of these items by deleting them. enable: false + # Enable this if you have problems with the plugin removing items from chest guis. + # Check if the inventory is connected to a location in the game. + # If it is not, its very likely created by custom gui plugin. + # Enable this if you have problems with the plugin removing items from chest guis. + # Check if the inventory is connected to a location in the game. + # If it is not, its very likely created by custom gui plugin. + gui-plugins-supported: false # Available options: # STRICT - Deletes or reverts illegals even on chest open # ACTION_ON_USE - Deletes or reverts illegals when interacted with @@ -1462,11 +1747,18 @@ illegals: # Will delete shulkers and bundles if they contain unbreakables. check-stored-items: false whitelisted-items: - - DIAMOND_CHESTPLATE + - DIAMOND_CHESTPLATE ban-spawn-eggs: # Bypass permission: aef.bypass.illegal.spawnegg # Deletes or prevents usage of spawn eggs. enable: false + # Enable this if you have problems with the plugin removing items from chest guis. + # Check if the inventory is connected to a location in the game. + # If it is not, its very likely created by custom gui plugin. + # Enable this if you have problems with the plugin removing items from chest guis. + # Check if the inventory is connected to a location in the game. + # If it is not, its very likely created by custom gui plugin. + gui-plugins-supported: false # Available options: # STRICT - Deletes or reverts illegals even on chest open # ACTION_ON_USE - Deletes or reverts illegals when interacted with @@ -1491,7 +1783,7 @@ illegals: # should they contain any spawneggs. check-stored-items: false whitelisted-items: - - VILLAGER_SPAWN_EGG + - VILLAGER_SPAWN_EGG ###################### # Dupe Preventions # @@ -1562,12 +1854,12 @@ preventions: enable: true # Defaults prevent common lag methods. entities: - - DROPPED_ITEM - - FIREWORK - - PRIMED_TNT - - THROWN_EXP_BOTTLE - - EXPERIENCE_ORB - - ARMOR_STAND + - DROPPED_ITEM + - FIREWORK + - PRIMED_TNT + - THROWN_EXP_BOTTLE + - EXPERIENCE_ORB + - ARMOR_STAND # Only enable if you must. Does not affect players. # CAUTION: Will kill the entity on folia due to broken portal event. prevent-all-entities-in-portals: false @@ -1581,8 +1873,8 @@ preventions: # If you don't want to use this, just configure an empty list: # pillar-blocks: [] pillar-blocks: - - world_the_end:143:140:-50 - - world_the_end:112:90:-90 + - world_the_end:143:140:-50 + - world_the_end:112:90:-90 prevent-portal-traps: # Teleports a player back to the original location if they have been # standing in a portal for too long. @@ -1594,11 +1886,11 @@ preventions: by-placing-piston-on-retract: enable: true whitelisted-worlds: - - example_world_name + - example_world_name by-exploding-pistons: enable: true whitelisted-worlds: - - example_world_name + - example_world_name # If enabled, will only protect portals and end gateways only-for-portals-and-gateways: false by-growing-structures: @@ -1610,7 +1902,7 @@ preventions: enable: false log: false whitelisted-players: - - Notch + - Notch prevent-nether-roof: # Prevent players from going above the nether roof. enable: true @@ -1630,7 +1922,7 @@ preventions: enable: false log: false whitelisted-players: - - Notch + - Notch ############ # Combat # @@ -1650,37 +1942,37 @@ combat: # Only delay when switched to specific materials only-delay-specific-materials: true delayed-specific-materials: - - BOW - - DIAMOND_SWORD - - DIAMOND_AXE - - TRIDENT - - GOLDEN_SWORD - - GOLDEN_AXE - - IRON_SWORD - - IRON_AXE - - STONE_SWORD - - STONE_AXE - - WOODEN_SWORD - - WOODEN_AXE - - BLACK_BED - - BLUE_BED - - BROWN_BED - - CYAN_BED - - GRAY_BED - - GREEN_BED - - LIGHT_BLUE_BED - - LIGHT_GRAY_BED - - LIME_BED - - MAGENTA_BED - - ORANGE_BED - - PINK_BED - - PURPLE_BED - - RED_BED - - WHITE_BED - - YELLOW_BED - - CROSSBOW - - NETHERITE_SWORD - - NETHERITE_AXE + - BOW + - DIAMOND_SWORD + - DIAMOND_AXE + - TRIDENT + - GOLDEN_SWORD + - GOLDEN_AXE + - IRON_SWORD + - IRON_AXE + - STONE_SWORD + - STONE_AXE + - WOODEN_SWORD + - WOODEN_AXE + - BLACK_BED + - BLUE_BED + - BROWN_BED + - CYAN_BED + - GRAY_BED + - GREEN_BED + - LIGHT_BLUE_BED + - LIGHT_GRAY_BED + - LIME_BED + - MAGENTA_BED + - ORANGE_BED + - PINK_BED + - PURPLE_BED + - RED_BED + - WHITE_BED + - YELLOW_BED + - CROSSBOW + - NETHERITE_SWORD + - NETHERITE_AXE regular-delay: enable: false # Can help with desync but recommended to leave off unless needed. @@ -1692,6 +1984,24 @@ combat: # Rate-limits pistons that extend into crystals. enable: false piston-extend-delay-in-ticks: 40 + auto-bed: + # As it happens, players on a server with a huge game world somehow need to travel long distances. + # From Minecraft version 1.16 (≈June 2020) to version 1.17.1(≈October 2021) in the game there was a bug SPIGOT-5988 which did not reset the respawn point of the player after death, if his bed was blocked. + # After some manipulations, it turned out to be a simple mechanism that allowed the player to get to the spavn by blocking his bed with a shalker before death, and after the second death to be near his bed again. + # This bug persisted from the Spigot server to Paper and all its forks until October 2021, after which it was fixed by the Spigot development team. + # Attempts by players to reach out to Spigot to allow them to disable the patch that fixes the SPIGOT-5988 bug have failed. + # + # Demonstration of how the patch works: + # https://www.youtube.com/watch?v=3y5SbQXzMss&feature=youtu.be + # As it happens, players on a server with a huge game world somehow need to travel long distances. + # From Minecraft version 1.16 (≈June 2020) to version 1.17.1(≈October 2021) in the game there was a bug SPIGOT-5988 which did not reset the respawn point of the player after death, if his bed was blocked. + # After some manipulations, it turned out to be a simple mechanism that allowed the player to get to the spavn by blocking his bed with a shalker before death, and after the second death to be near his bed again. + # This bug persisted from the Spigot server to Paper and all its forks until October 2021, after which it was fixed by the Spigot development team. + # Attempts by players to reach out to Spigot to allow them to disable the patch that fixes the SPIGOT-5988 bug have failed. + # + # Demonstration of how the patch works: + # https://www.youtube.com/watch?v=3y5SbQXzMss&feature=youtu.be + enable: false prevent-bow-bomb: enable: false # Fully pulled bow is ~9-10. 15 is default just to be safe. @@ -1728,8 +2038,8 @@ bedrock: check-period-in-seconds: 10 # Uses the exact name of the world's folder in your server directory. exempted-worlds: - - exampleworld - - exampleworld2 + - exampleworld + - exampleworld2 # Pauses the task during low tps to avoid lag. pause-on-low-tps: true # The TPS at which bedrock filling will pause to avoid adding to the lag. @@ -1740,8 +2050,8 @@ bedrock: also-check-new-chunks: false # Uses the exact name of the world's folder in your server directory. exempted-worlds: - - exampleworld - - exampleworld2 + - exampleworld + - exampleworld2 pause-on-low-tps: true # The TPS at which bedrock filling will pause to avoid adding to the lag. pause-tps: 16.0 @@ -1752,8 +2062,8 @@ bedrock: check-period-in-seconds: 10 # Uses the exact name of the world's folder in your server directory. exempted-worlds: - - exampleworld - - exampleworld2 + - exampleworld + - exampleworld2 pause-on-low-tps: true # The TPS at which bedrock filling will pause to avoid adding to the lag. pause-tps: 16.0 @@ -1763,8 +2073,8 @@ bedrock: also-check-new-chunks: false # Uses the exact name of the world's folder in your server directory. exempted-worlds: - - exampleworld - - exampleworld2 + - exampleworld + - exampleworld2 pause-on-low-tps: true # The TPS at which bedrock filling will pause to avoid adding to the lag. pause-tps: 16.0 @@ -1775,8 +2085,8 @@ bedrock: check-period-in-seconds: 10 # Uses the exact name of the world's folder in your server directory. exempted-worlds: - - exampleworld - - exampleworld2 + - exampleworld + - exampleworld2 pause-on-low-tps: true # The TPS at which bedrock filling will pause to avoid adding to the lag. pause-tps: 16.0 @@ -1786,8 +2096,8 @@ bedrock: also-check-new-chunks: false # Uses the exact name of the world's folder in your server directory. exempted-worlds: - - exampleworld - - exampleworld2 + - exampleworld + - exampleworld2 pause-on-low-tps: true # The TPS at which bedrock filling will pause to avoid adding to the lag. pause-tps: 16.0 @@ -1801,8 +2111,8 @@ bedrock: # Whether the bedrock hole should be filled or not. fill-bedrock-hole: true exempted-worlds: - - world_the_end - - skyblock_world + - world_the_end + - skyblock_world filler-material: BEDROCK ``` diff --git a/build-logic/src/main/kotlin/me.xginko.aef.wrapper.gradle.kts b/build-logic/src/main/kotlin/me.xginko.aef.wrapper.gradle.kts index 8f17c4ea5..a1b288030 100755 --- a/build-logic/src/main/kotlin/me.xginko.aef.wrapper.gradle.kts +++ b/build-logic/src/main/kotlin/me.xginko.aef.wrapper.gradle.kts @@ -3,8 +3,8 @@ plugins { `maven-publish` } -group = "me.xginko.aef" -version = "2.7.1" +group = "me.xginko" +version = "2.7.2" description = "Prevent many exploits that affect anarchy servers." var url: String? = "github.com/xGinko/AnarchyExploitFixes" @@ -43,11 +43,10 @@ repositories { } dependencies { - compileOnly("com.github.retrooper.packetevents:spigot:2.3.0") // PacketEvents to patch packet based exploits - api("com.github.cryptomorin:XSeries:11.2.0") // Crossversion entitytype and material support + compileOnly("com.github.retrooper:packetevents-spigot:2.4.0") // PacketEvents to patch packet based exploits + api("com.github.cryptomorin:XSeries:11.2.0.1") // Crossversion entitytype and material support api("com.github.thatsmusic99:ConfigurationMaster-API:v2.0.0-rc.1") // ConfigurationMaster for enhanced config management - api("de.tr7zw:item-nbt-api:2.13.1") // NBT API for cross version nbt tag handling - api("io.papermc:paperlib:1.0.8") // Useful Paper related tools + api("de.tr7zw:item-nbt-api:2.13.2") // NBT API for cross version nbt tag handling api("org.bstats:bstats-bukkit:3.0.2") // Bukkit bStats api("org.apache.commons:commons-math3:3.6.1") // FastMath api("org.reflections:reflections:0.10.2") // Reflections diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 2023802af..32b468dfc 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -13,13 +13,13 @@ bstats = "org.bstats:bstats-bukkit:3.0.2" caffeineJ17 = "com.github.ben-manes.caffeine:caffeine:3.1.8" caffeineJ8 = "com.github.ben-manes.caffeine:caffeine:2.9.3" hikaricp = "com.zaxxer:HikariCP:5.1.0" -xseries = "com.github.cryptomorin:XSeries:11.0.0" +xseries = "com.github.cryptomorin:XSeries:11.2.0.1" reflections = "org.reflections:reflections:0.10.2" [plugins] runpaper = { id = "xyz.jpenilla.run-paper", version = "2.3.0" } -shadow = { id = "com.github.johnrengelman.shadow", version = "8.1.1" } -userdev = { id = "io.papermc.paperweight.userdev", version = "1.7.0" } -downgradeJava = { id = "net.raphimc.java-downgrader", version = "1.1.1" } +shadow = { id = "io.github.goooler.shadow", version = "8.1.8" } +userdev = { id = "io.papermc.paperweight.userdev", version = "1.7.1" } +downgradeJava = { id = "xyz.wagyourtail.jvmdowngrader", version = "1.0.0" } [bundles] diff --git a/shared/src/main/java/me/xginko/aef/enums/AEFPermission.java b/shared/src/main/java/me/xginko/aef/enums/AEFPermission.java index 7c685acce..b6be7976a 100644 --- a/shared/src/main/java/me/xginko/aef/enums/AEFPermission.java +++ b/shared/src/main/java/me/xginko/aef/enums/AEFPermission.java @@ -11,6 +11,8 @@ public enum AEFPermission { BYPASS_CMD_WHITELIST("bypass.commandwhitelist", "Bypass command whitelist if enabled", PermissionDefault.OP), BYPASS_CHAT("bypass.chat", "Bypass for any kind of other chat restrictions", PermissionDefault.OP), BYPASS_PREVENTION_COMMANDSIGN("bypass.prevention.commandsign", "Bypass commandsign prevention", PermissionDefault.FALSE), + BYPASS_ILLEGAL_POTIONS("bypass.illegal.potions", "Bypass illegal potion checks", PermissionDefault.FALSE), + BYPASS_ILLEGAL_ATTRIBUTES("bypass.illegal.attributes", "Bypass attribute and itemflag checks", PermissionDefault.FALSE), BYPASS_ILLEGAL_OVERSTACKED("bypass.illegal.overstacked", "Bypass overstacked item checks", PermissionDefault.FALSE), BYPASS_ILLEGAL_BANNEDMATERIAL("bypass.illegal.bannedmaterial", "Bypass banned material checks", PermissionDefault.FALSE), BYPASS_ILLEGAL_BANNEDNAME("bypass.illegal.bannedname", "Bypass banned item name checks", PermissionDefault.FALSE), diff --git a/shared/src/main/java/me/xginko/aef/modules/chat/commandwhitelist/CWCommandSendListener.java b/shared/src/main/java/me/xginko/aef/modules/chat/commandwhitelist/CWCommandSendListener.java index 913b91408..ea1805e16 100755 --- a/shared/src/main/java/me/xginko/aef/modules/chat/commandwhitelist/CWCommandSendListener.java +++ b/shared/src/main/java/me/xginko/aef/modules/chat/commandwhitelist/CWCommandSendListener.java @@ -11,24 +11,29 @@ public class CWCommandSendListener implements Listener { private final Set allowedCommands; + private static boolean isSupported; + + static { + try { + Class.forName("org.bukkit.event.player.PlayerCommandSendEvent"); + isSupported = true; + } catch (ClassNotFoundException e) { + isSupported = false; + } + } public CWCommandSendListener(Set allowedCommands) { this.allowedCommands = allowedCommands; } - @EventHandler(priority = EventPriority.HIGH) + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onInitialTabCompleteListSend(PlayerCommandSendEvent event) { - if (!event.getPlayer().hasPermission(AEFPermission.BYPASS_CMD_WHITELIST.string())) { - event.getCommands().removeIf(cmd -> !allowedCommands.contains(cmd)); + if (!event.getPlayer().hasPermission(AEFPermission.BYPASS_CMD_WHITELIST.bukkit())) { + event.getCommands().retainAll(allowedCommands); } } public static boolean isSupported() { - try { - Class.forName("org.bukkit.event.player.PlayerCommandSendEvent"); - return true; - } catch (ClassNotFoundException e) { - return false; - } + return isSupported; } } diff --git a/shared/src/main/java/me/xginko/aef/modules/patches/commandsign/SignCommandListener.java b/shared/src/main/java/me/xginko/aef/modules/patches/commandsign/SignCommandListener.java index d75e93a01..eef8d0697 100644 --- a/shared/src/main/java/me/xginko/aef/modules/patches/commandsign/SignCommandListener.java +++ b/shared/src/main/java/me/xginko/aef/modules/patches/commandsign/SignCommandListener.java @@ -8,19 +8,25 @@ public class SignCommandListener implements Listener { + private static boolean isSupported; + + static { + try { + Class.forName("io.papermc.paper.event.player.PlayerSignCommandPreprocessEvent"); + isSupported = true; + } catch (ClassNotFoundException e) { + isSupported = false; + } + } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onSignCommandPreprocess(PlayerSignCommandPreprocessEvent event) { - if (!event.getPlayer().hasPermission(AEFPermission.BYPASS_PREVENTION_COMMANDSIGN.string())) { + if (!event.getPlayer().hasPermission(AEFPermission.BYPASS_PREVENTION_COMMANDSIGN.bukkit())) { event.setCancelled(true); } } public static boolean isSupported() { - try { - Class.forName("io.papermc.paper.event.player.PlayerSignCommandPreprocessEvent"); - return true; - } catch (ClassNotFoundException e) { - return false; - } + return isSupported; } } diff --git a/shared/src/main/java/me/xginko/aef/utils/BlockUtil.java b/shared/src/main/java/me/xginko/aef/utils/BlockUtil.java index e9a2b431d..13056d28d 100644 --- a/shared/src/main/java/me/xginko/aef/utils/BlockUtil.java +++ b/shared/src/main/java/me/xginko/aef/utils/BlockUtil.java @@ -9,6 +9,16 @@ public class BlockUtil { + private static final boolean WATERLOGGED_AVAILABLE; + + static { + WATERLOGGED_AVAILABLE = Crafty.hasClass("org.bukkit.block.data.Waterlogged"); + } + + public static boolean isWaterloggedAvailable() { + return WATERLOGGED_AVAILABLE; + } + private static final Map IS_WATERLOGGABLE_CACHE = new EnumMap<>(Material.class); public static boolean isWaterlogged(BlockState blockState) { return blockState != null diff --git a/shared/src/main/java/me/xginko/aef/utils/BundleUtil.java b/shared/src/main/java/me/xginko/aef/utils/BundleUtil.java deleted file mode 100644 index 7ae8fc4b8..000000000 --- a/shared/src/main/java/me/xginko/aef/utils/BundleUtil.java +++ /dev/null @@ -1,33 +0,0 @@ -package me.xginko.aef.utils; - -import org.bukkit.Material; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.BundleMeta; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public class BundleUtil { - - private static Material BUNDLE; - private static boolean isSupported = true; - - static { - try { - Class.forName("org.bukkit.inventory.meta.BundleMeta"); - BUNDLE = Material.valueOf("BUNDLE"); - } catch (ClassNotFoundException | IllegalArgumentException e) { - BUNDLE = null; - isSupported = false; - } - } - - public static boolean isSupported() { - return isSupported; - } - - @SuppressWarnings("UnstableApiUsage") - public static @Nullable Iterable getItems(@NotNull ItemStack item) { - if (item.getType() != BUNDLE) return null; - return item.hasItemMeta() ? ((BundleMeta) item.getItemMeta()).getItems() : null; - } -} diff --git a/shared/src/main/java/me/xginko/aef/utils/ChunkUtil.java b/shared/src/main/java/me/xginko/aef/utils/ChunkUtil.java index c6ee81cbb..0e393cc61 100644 --- a/shared/src/main/java/me/xginko/aef/utils/ChunkUtil.java +++ b/shared/src/main/java/me/xginko/aef/utils/ChunkUtil.java @@ -1,54 +1,45 @@ package me.xginko.aef.utils; +import com.cryptomorin.xseries.XMaterial; import org.bukkit.Chunk; import org.bukkit.Location; -import org.bukkit.Material; import org.bukkit.util.NumberConversions; public class ChunkUtil { - private static boolean setForceLoadedAvailable, getInhabitedTimeAvailable; + private static final boolean SET_FORCE_LOADED_AVAILABLE, GET_INHABITED_TIME_AVAILABLE; static { - try { - Chunk.class.getMethod("setForceLoaded", boolean.class); - setForceLoadedAvailable = true; - } catch (NoSuchMethodException e) { - setForceLoadedAvailable = false; - } - - try { - Chunk.class.getMethod("getInhabitedTime"); - getInhabitedTimeAvailable = true; - } catch (NoSuchMethodException e) { - getInhabitedTimeAvailable = false; - } + SET_FORCE_LOADED_AVAILABLE + = Crafty.hasMethod(Chunk.class, "setForceLoaded", boolean.class); + GET_INHABITED_TIME_AVAILABLE + = Crafty.hasMethod(Chunk.class, "getInhabitedTime"); } public static boolean canSetChunksForceLoaded() { - return setForceLoadedAvailable; + return SET_FORCE_LOADED_AVAILABLE; } public static void setForceLoaded(Chunk chunk, boolean forced) { - if (setForceLoadedAvailable) { + if (SET_FORCE_LOADED_AVAILABLE) { chunk.setForceLoaded(forced); } } public static boolean canGetInhabitedTime() { - return getInhabitedTimeAvailable; + return GET_INHABITED_TIME_AVAILABLE; } public static long getInhabitedTime(Chunk chunk) { - return getInhabitedTimeAvailable ? chunk.getInhabitedTime() : 0L; + return GET_INHABITED_TIME_AVAILABLE ? chunk.getInhabitedTime() : 0L; } public static void createBedrockLayer(Chunk chunk, int y) { for (int x = 0; x < 16; x++) { for (int z = 0; z < 16; z++) { - if (chunk.getBlock(x, y, z).getType() != Material.BEDROCK) { + if (chunk.getBlock(x, y, z).getType() != XMaterial.BEDROCK.parseMaterial()) { // prevent physics to avoid loading nearby chunks which causes infinite chunk loading loops - chunk.getBlock(x, y, z).setType(Material.BEDROCK, false); + chunk.getBlock(x, y, z).setType(XMaterial.BEDROCK.parseMaterial(), false); } } } diff --git a/shared/src/main/java/me/xginko/aef/utils/Crafty.java b/shared/src/main/java/me/xginko/aef/utils/Crafty.java new file mode 100644 index 000000000..716ed8878 --- /dev/null +++ b/shared/src/main/java/me/xginko/aef/utils/Crafty.java @@ -0,0 +1,447 @@ +package me.xginko.aef.utils; + +/* + * This file is part of adventure-platform, licensed under the MIT License. + * + * Copyright (c) 2018-2020 KyoriPowered + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import org.bukkit.Bukkit; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.common.reflection.qual.ForName; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.reflect.Field; +import java.util.Arrays; + +import static java.util.Objects.requireNonNull; + +/** + * Reflection utilities for accessing {@code net.minecraft.server}. + * + *

This is not an official API and can break at any time. You've been warned.

+ */ +public final class Crafty { + + private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); + private static final String PREFIX_NMS = "net.minecraft.server"; + private static final String PREFIX_CRAFTBUKKIT = "org.bukkit.craftbukkit"; + private static final String CRAFT_SERVER = "CraftServer"; + private static final @Nullable String VERSION; + + static { + final Class serverClass = Bukkit.getServer().getClass(); // TODO: use reflection here too? + if (!serverClass.getSimpleName().equals(CRAFT_SERVER)) { + VERSION = null; + } else if (serverClass.getName().equals(PREFIX_CRAFTBUKKIT + "." + CRAFT_SERVER)) { + VERSION = "."; + } else { + String name = serverClass.getName(); + name = name.substring(PREFIX_CRAFTBUKKIT.length()); + name = name.substring(0, name.length() - CRAFT_SERVER.length()); + VERSION = name; + } + } + + public static @NonNull Class needNMSClassOrElse( + final @NonNull String nms, + final @NonNull String... classNames + ) throws RuntimeException { + final Class nmsClass = findNmsClass(nms); + if (nmsClass != null) { + return nmsClass; + } + for (final String name : classNames) { + final Class maybe = findClass(name); + if (maybe != null) { + return maybe; + } + } + throw new IllegalStateException(String.format( + "Couldn't find a class! NMS: '%s' or '%s'.", + nms, + Arrays.toString(classNames) + )); + } + + /** + * Gets a class by its name. + * + * @param className a class name + * @return a class or {@code null} if not found + */ + @ForName + public static @Nullable Class findClass(final @NonNull String className) { + try { + return Class.forName(className); + } catch (final ClassNotFoundException e) { + return null; + } + } + + /** + * Gets whether a class is loaded. + * + * @param className a class name + * @return if the class is loaded + */ + public static boolean hasClass(final @NonNull String className) { + return findClass(className) != null; + } + + /** + * Gets a handle for a class method. + * + * @param holderClass a class + * @param methodName a method name + * @param returnClass a method return class + * @param parameterClasses an array of method parameter classes + * @return a method handle or {@code null} if not found + */ + public static @Nullable MethodHandle findMethod(final @Nullable Class holderClass, final String methodName, final @Nullable Class returnClass, final Class... parameterClasses) { + if (holderClass == null || returnClass == null) return null; + for (final Class parameterClass : parameterClasses) { + if (parameterClass == null) return null; + } + + try { + return LOOKUP.findVirtual(holderClass, methodName, MethodType.methodType(returnClass, parameterClasses)); + } catch (final NoSuchMethodException | IllegalAccessException e) { + return null; + } + } + + /** + * Gets a handle for a class method. + * + * @param holderClass a class + * @param methodName a method name + * @param returnClass a method return class + * @param parameterClasses an array of method parameter classes + * @return a method handle or {@code null} if not found + */ + public static @Nullable MethodHandle findStaticMethod(final @Nullable Class holderClass, final String methodName, final @Nullable Class returnClass, final Class... parameterClasses) { + if (holderClass == null || returnClass == null) return null; + for (final Class parameterClass : parameterClasses) { + if (parameterClass == null) return null; + } + + try { + return LOOKUP.findStatic(holderClass, methodName, MethodType.methodType(returnClass, parameterClasses)); + } catch (final NoSuchMethodException | IllegalAccessException e) { + return null; + } + } + + /** + * Gets whether a class has a method. + * + * @param holderClass a class + * @param name a method name + * @param type the field type + * @return if the method exists + */ + public static boolean hasField(final @Nullable Class holderClass, final String name, final Class type) { + if (holderClass == null) return false; + + try { + final Field field = holderClass.getDeclaredField(name); + return field.getType() == type; + } catch (final NoSuchFieldException e) { + return false; + } + } + + /** + * Gets whether a class has a method. + * + * @param holderClass a class + * @param methodName a method name + * @param parameterClasses an array of method parameter classes + * @return if the method exists + */ + public static boolean hasMethod(final @Nullable Class holderClass, final String methodName, final Class... parameterClasses) { + if (holderClass == null) return false; + for (final Class parameterClass : parameterClasses) { + if (parameterClass == null) return false; + } + + try { + holderClass.getMethod(methodName, parameterClasses); + return true; + } catch (final NoSuchMethodException e) { + return false; + } + } + + /** + * Gets a handle for a class constructor. + * + * @param holderClass a class + * @param parameterClasses an array of method parameter classes + * @return a method handle or {@code null} if not found + */ + public static @Nullable MethodHandle findConstructor(final @Nullable Class holderClass, final @Nullable Class... parameterClasses) { + if (holderClass == null) return null; + for (final Class parameterClass : parameterClasses) { + if (parameterClass == null) return null; + } + + try { + return LOOKUP.findConstructor(holderClass, MethodType.methodType(void.class, parameterClasses)); + } catch (final NoSuchMethodException | IllegalAccessException e) { + return null; + } + } + + /** + * Gets a class field and makes it accessible. + * + * @param holderClass a class + * @param fieldName a field name + * @return an accessible field + * @throws NoSuchFieldException when thrown by {@link Class#getDeclaredField(String)} + */ + public static @NonNull Field needField(final @NonNull Class holderClass, final @NonNull String fieldName) throws NoSuchFieldException { + final Field field = holderClass.getDeclaredField(fieldName); + field.setAccessible(true); + return field; + } + + /** + * Gets a class field if possible and makes it accessible. + * + * @param holderClass a class + * @param fieldName a field name + * @return an accessible field + */ + public static @Nullable Field findField(final @Nullable Class holderClass, final @NonNull String fieldName) { + return findField(holderClass, fieldName, null); + } + + /** + * Gets a class field if it exists and is of the appropriate type and makes it accessible. + * + * @param holderClass a class + * @param fieldName a field name + * @return an accessible field + */ + public static @Nullable Field findField(final @Nullable Class holderClass, final @NonNull String fieldName, final @Nullable Class expectedType) { + if (holderClass == null) return null; + + final Field field; + try { + field = holderClass.getDeclaredField(fieldName); + } catch (final NoSuchFieldException ex) { + return null; + } + + field.setAccessible(true); + if (expectedType != null && !expectedType.isAssignableFrom(field.getType())) { + return null; + } + + return field; + } + + /** + * Return a method handle that can set the value of the provided field. + * + * @param field the field to set + * @return a handle, if accessible + */ + public static @Nullable MethodHandle findSetterOf(final @Nullable Field field) { + if (field == null) return null; + + try { + return LOOKUP.unreflectSetter(field); + } catch (final IllegalAccessException e) { + return null; + } + } + + /** + * Return a method handle that can get the value of the provided field. + * + * @param field the field to get + * @return a handle, if accessible + */ + public static @Nullable MethodHandle findGetterOf(final @Nullable Field field) { + if (field == null) return null; + + try { + return LOOKUP.unreflectGetter(field); + } catch (final IllegalAccessException e) { + return null; + } + } + + /** + * Gets an enum value. + * + * @param enumClass an enum class + * @param enumName an enum name + * @return an enum value or {@code null} if not found + */ + public static @Nullable Object findEnum(final @Nullable Class enumClass, final @NonNull String enumName) { + return findEnum(enumClass, enumName, Integer.MAX_VALUE); + } + + /** + * Gets an enum value. + * + * @param enumClass an enum class + * @param enumName an enum name + * @param enumFallbackOrdinal an enum ordinal, when the name is not found + * @return an enum value or {@code null} if not found + */ + @SuppressWarnings("unchecked") + public static @Nullable Object findEnum(final @Nullable Class enumClass, final @NonNull String enumName, final int enumFallbackOrdinal) { + if (enumClass == null || !Enum.class.isAssignableFrom(enumClass)) { + return null; + } + + try { + return Enum.valueOf(enumClass.asSubclass(Enum.class), enumName); + } catch (final IllegalArgumentException e) { + final Object[] constants = enumClass.getEnumConstants(); + if (constants.length > enumFallbackOrdinal) { + return constants[enumFallbackOrdinal]; + } + } + + return null; + } + + /** + * Gets whether CraftBukkit is available. + * + * @return if CraftBukkit is available + */ + public static boolean isCraftBukkit() { + return VERSION != null; + } + + /** + * Gets a {@code org.bukkit.craftbukkit} class name. + * + * @param className a class name, without the {@code org.bukkit.craftbukkit} prefix + * @return a class name or {@code null} if not found + */ + public static @Nullable String findCraftClassName(final @NonNull String className) { + return isCraftBukkit() ? PREFIX_CRAFTBUKKIT + VERSION + className : null; + } + + /** + * Gets a {@code org.bukkit.craftbukkit} class. + * + * @param className a class name, without the {@code org.bukkit.craftbukkit} prefix + * @return a class or {@code null} if not found + */ + @ForName + public static @Nullable Class findCraftClass(final @NonNull String className) { + final String craftClassName = findCraftClassName(className); + if (craftClassName == null) { + return null; + } + + return findClass(craftClassName); + } + + /** + * Gets a {@code org.bukkit.craftbukkit} class. + * + * @param className a class name, without the {@code org.bukkit.craftbukkit} prefix + * @param superClass a super class + * @param a super type + * @return a class or {@code null} if not found + */ + @ForName + public static @Nullable Class findCraftClass(final @NonNull String className, final @NonNull Class superClass) { + final Class craftClass = findCraftClass(className); + if (craftClass == null || !requireNonNull(superClass, "superClass").isAssignableFrom(craftClass)) { + return null; + } + return craftClass.asSubclass(superClass); + } + + /** + * Gets a {@code org.bukkit.craftbukkit} class. + * + * @param className a class name, without the {@code org.bukkit.craftbukkit} prefix + * @return a class + * @throws NullPointerException if the class was not found + */ + @ForName + public static @NonNull Class needCraftClass(final @NonNull String className) { + return requireNonNull(findCraftClass(className), "Could not find org.bukkit.craftbukkit class " + className); + } + + /** + * Gets a {@code net.minecraft.server} class name. + * + * @param className a class name, without the {@code net.minecraft.server} prefix + * @return a class name or {@code null} if not found + */ + public static @Nullable String findNmsClassName(final @NonNull String className) { + return isCraftBukkit() ? PREFIX_NMS + VERSION + className : null; + } + + /** + * Get a {@code net.minecraft.server} class. + * + * @param className a class name, without the {@code net.minecraft.server} prefix + * @return a class name or {@code null} if not found + */ + @ForName + public static @Nullable Class findNmsClass(final @NonNull String className) { + final String nmsClassName = findNmsClassName(className); + if (nmsClassName == null) { + return null; + } + + return findClass(nmsClassName); + } + + /** + * Gets a {@code net.minecraft.server} class. + * + * @param className a class name, without the {@code org.bukkit.craftbukkit} prefix + * @return a class + * @throws NullPointerException if the class was not found + */ + @ForName + public static @NonNull Class needNmsClass(final @NonNull String className) { + return requireNonNull(findNmsClass(className), "Could not find net.minecraft.server class " + className); + } + + /** + * Gets the singleton method handle lookup. + * + * @return the method handle lookup + */ + public static MethodHandles.@NonNull Lookup lookup() { + return LOOKUP; + } +} diff --git a/shared/src/main/java/me/xginko/aef/utils/EntityUtil.java b/shared/src/main/java/me/xginko/aef/utils/EntityUtil.java index d91838816..687448f5a 100644 --- a/shared/src/main/java/me/xginko/aef/utils/EntityUtil.java +++ b/shared/src/main/java/me/xginko/aef/utils/EntityUtil.java @@ -1,13 +1,19 @@ package me.xginko.aef.utils; import com.cryptomorin.xseries.XEntityType; +import com.cryptomorin.xseries.XMaterial; import org.bukkit.entity.ChestedHorse; import org.bukkit.entity.Entity; import org.bukkit.entity.EntityType; +import org.bukkit.entity.ItemFrame; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Projectile; import org.bukkit.entity.Vehicle; import org.bukkit.inventory.InventoryHolder; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.MapMeta; +import org.bukkit.map.MapView; +import org.jetbrains.annotations.NotNull; import java.util.Arrays; import java.util.EnumMap; @@ -18,6 +24,54 @@ public class EntityUtil { + private static final boolean MAP_SET_TRACKING_POS_AVAILABLE; + private static final boolean IS_SWIMMING_AVAILABLE; + + static { + MAP_SET_TRACKING_POS_AVAILABLE + = Crafty.hasClass("org.bukkit.map.MapView") + && Crafty.hasMethod(MapView.class, "setTrackingPosition", boolean.class); + IS_SWIMMING_AVAILABLE + = Crafty.hasMethod(LivingEntity.class, "isSwimming", boolean.class); + } + + public static boolean canDisableMapPositionCursor() { + return MAP_SET_TRACKING_POS_AVAILABLE; + } + + /** + * Disables that a position cursor will be shown when the map is near its center. + * Check {@link EntityUtil#canDisableMapPositionCursor()} before calling this method. + * + * @param itemFrame the {@link ItemFrame} entity to disable the tracking status of + */ + public static void disableMapPositionCursor(@NotNull ItemFrame itemFrame) { + ItemStack itemInsideFrame = itemFrame.getItem(); + if (itemInsideFrame == null) return; // Shouldn't be null but just in case + if (itemInsideFrame.getType() != XMaterial.MAP.parseMaterial() + && itemInsideFrame.getType() != XMaterial.FILLED_MAP.parseMaterial()) return; + if (!itemInsideFrame.hasItemMeta()) return; + + MapMeta mapMeta = (MapMeta) itemInsideFrame.getItemMeta(); + if (!mapMeta.hasMapView()) return; + MapView mapView = mapMeta.getMapView(); + if (mapView == null) return; + + mapView.setTrackingPosition(false); + + itemInsideFrame.setItemMeta(mapMeta); + itemFrame.setItem(itemInsideFrame); + } + + /** + * Checks to see if an entity is swimming. + * + * @return True if this entity is swimming. + */ + public static boolean isSwimming(LivingEntity livingEntity) { + return IS_SWIMMING_AVAILABLE && livingEntity.isSwimming(); + } + public static final Set MINECARTS = Arrays.stream(XEntityType.values()) .filter(xEntityType -> xEntityType.name().toUpperCase().contains("MINECART")) .filter(XEntityType::isSupported) diff --git a/shared/src/main/java/me/xginko/aef/utils/FramedMapUtil.java b/shared/src/main/java/me/xginko/aef/utils/FramedMapUtil.java deleted file mode 100644 index 31d45901d..000000000 --- a/shared/src/main/java/me/xginko/aef/utils/FramedMapUtil.java +++ /dev/null @@ -1,37 +0,0 @@ -package me.xginko.aef.utils; - -import org.bukkit.Material; -import org.bukkit.entity.ItemFrame; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.MapMeta; -import org.bukkit.map.MapView; -import org.jetbrains.annotations.NotNull; - -public class FramedMapUtil { - - public static boolean canDisableTracker() { - try { - Class.forName("org.bukkit.map.MapView"); - MapView.class.getMethod("setTrackingPosition", boolean.class); - return true; - } catch (ClassNotFoundException | NoSuchMethodException e) { - return false; - } - } - - public static void disableTracker(@NotNull ItemFrame itemFrame) { - ItemStack itemInsideFrame = itemFrame.getItem(); - if (itemInsideFrame == null) return; - if (itemInsideFrame.getType() != Material.MAP && itemInsideFrame.getType() != Material.FILLED_MAP) return; - if (!(itemInsideFrame.getItemMeta() instanceof MapMeta)) return; - - MapMeta mapMeta = (MapMeta) itemInsideFrame.getItemMeta(); - if (!mapMeta.hasMapView()) return; - MapView mapView = mapMeta.getMapView(); - if (mapView == null) return; - - mapView.setTrackingPosition(false); - itemInsideFrame.setItemMeta(mapMeta); - itemFrame.setItem(itemInsideFrame); - } -} diff --git a/shared/src/main/java/me/xginko/aef/utils/ItemUtil.java b/shared/src/main/java/me/xginko/aef/utils/ItemUtil.java index cb8520aaf..21cafb4ca 100644 --- a/shared/src/main/java/me/xginko/aef/utils/ItemUtil.java +++ b/shared/src/main/java/me/xginko/aef/utils/ItemUtil.java @@ -1,12 +1,14 @@ package me.xginko.aef.utils; -import de.tr7zw.changeme.nbtapi.NBTItem; +import com.cryptomorin.xseries.XMaterial; +import de.tr7zw.changeme.nbtapi.NBT; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.minimessage.MiniMessage; import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.BlockStateMeta; import org.bukkit.inventory.meta.BookMeta; +import org.bukkit.inventory.meta.BundleMeta; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -14,85 +16,122 @@ public class ItemUtil { - private static final boolean miniMessage_supported; + private static final boolean BUNDLES_SUPPPORTED, USE_MINIMSG_BOOKMETA; static { - boolean supported = true; - try { - Class.forName("net.kyori.adventure.text.minimessage.MiniMessage"); - MiniMessage.class.getMethod("miniMessage"); - } catch (Throwable t) { - supported = false; - } - miniMessage_supported = supported; + USE_MINIMSG_BOOKMETA + = Crafty.hasClass("net.kyori.adventure.text.minimessage.MiniMessage") + && Crafty.hasMethod(MiniMessage.class, "miniMessage") + && Crafty.hasMethod(BookMeta.class, "pages"); + + BUNDLES_SUPPPORTED + = XMaterial.BUNDLE.isSupported() + && Crafty.hasClass("org.bukkit.inventory.meta.BundleMeta"); } + /** + * Gets the stored items from an ItemStack. + * + * @param itemStack ItemStack to check. + * @return Iterable containing the ItemStacks stored inside the item or {@code null} if there are none. + */ + @SuppressWarnings("UnstableApiUsage") public static @Nullable Iterable getStoredItems(@NotNull ItemStack itemStack) { - if (!itemStack.hasItemMeta()) + if (!itemStack.hasItemMeta()) { return null; - if (MaterialUtil.INVENTORY_HOLDER_ITEMS.contains(itemStack.getType())) { + } + + if (MaterialUtil.INVENTORY_HOLDERS.contains(itemStack.getType())) { BlockStateMeta blockStateMeta = (BlockStateMeta) itemStack.getItemMeta(); - if (blockStateMeta.hasBlockState()) + if (blockStateMeta.hasBlockState()) { return ((InventoryHolder) blockStateMeta.getBlockState()).getInventory(); + } } - return BundleUtil.isSupported() ? BundleUtil.getItems(itemStack) : null; + + if (BUNDLES_SUPPPORTED && itemStack.getType() == XMaterial.BUNDLE.parseMaterial()) { + return ((BundleMeta) itemStack.getItemMeta()).getItems(); + } + + return null; + } + + public static String getNBTString(ItemStack itemStack) { + return NBT.readNbt(itemStack).toString(); } public static int getApproximateByteSize(@Nullable ItemStack itemStack, boolean utf16) { - if (itemStack == null || !itemStack.hasItemMeta()) + if (itemStack == null || !itemStack.hasItemMeta()) { return 0; - Iterable stored = getStoredItems(itemStack); - if (stored != null) { - return getApproximateByteSize(stored, utf16); } - if (MaterialUtil.TEXT_BOOKS.contains(itemStack.getType())) { + + Iterable storedItems = getStoredItems(itemStack); + if (storedItems != null) { + return getApproximateByteSize(storedItems, utf16); + } + + if (MaterialUtil.LECTERN_BOOKS.contains(itemStack.getType())) { return getApproximateByteSize((BookMeta) itemStack.getItemMeta(), utf16); } - return new NBTItem(itemStack).toString().getBytes(utf16 ? StandardCharsets.UTF_16 : StandardCharsets.UTF_8).length; + + return getNBTString(itemStack).getBytes(utf16 ? StandardCharsets.UTF_16 : StandardCharsets.UTF_8).length; } public static int getApproximateByteSize(@Nullable Iterable inventory, boolean utf16) { if (inventory == null) return 0; + int collectiveSize = 0; for (ItemStack stack : inventory) { collectiveSize += getApproximateByteSize(stack, utf16); } + return collectiveSize; } public static int getApproximateByteSize(@NotNull BookMeta bookMeta, boolean utf16) { - return miniMessage_supported ? getApproximateByteSizeMM(bookMeta, utf16) : getApproximateByteSizeLegacy(bookMeta, utf16); + return USE_MINIMSG_BOOKMETA ? getApproximateByteSizeModern(bookMeta, utf16) : getApproximateByteSizeLegacy(bookMeta, utf16); } @SuppressWarnings("DataFlowIssue") // Legitimate because we make sure no values are null by testing .hasX() - private static int getApproximateByteSizeMM(@NotNull BookMeta bookMeta, boolean utf16) { + private static int getApproximateByteSizeModern(@NotNull BookMeta bookMeta, boolean utf16) { StringBuilder content = new StringBuilder(); + if (bookMeta.hasTitle()) content.append(MiniMessage.miniMessage().serialize(bookMeta.title())); + if (bookMeta.hasAuthor()) content.append(MiniMessage.miniMessage().serialize(bookMeta.author())); + if (bookMeta.hasPages()) { - for (Component page : bookMeta.pages()) + for (Component page : bookMeta.pages()) { content.append(MiniMessage.miniMessage().serialize(page)); + } } + if (bookMeta.hasLore()) { - for (Component loreLine : bookMeta.lore()) + for (Component loreLine : bookMeta.lore()) { content.append(MiniMessage.miniMessage().serialize(loreLine)); + } } + return content.toString().getBytes(utf16 ? StandardCharsets.UTF_16 : StandardCharsets.UTF_8).length; } private static int getApproximateByteSizeLegacy(@NotNull BookMeta bookMeta, boolean utf16) { StringBuilder content = new StringBuilder(); + if (bookMeta.hasTitle()) content.append(bookMeta.getTitle()); + if (bookMeta.hasAuthor()) content.append(bookMeta.getAuthor()); + if (bookMeta.hasPages()) bookMeta.getPages().forEach(content::append); + if (bookMeta.hasLore()) bookMeta.getLore().forEach(content::append); + return content.toString().getBytes(utf16 ? StandardCharsets.UTF_16 : StandardCharsets.UTF_8).length; } } diff --git a/shared/src/main/java/me/xginko/aef/utils/MaterialUtil.java b/shared/src/main/java/me/xginko/aef/utils/MaterialUtil.java index 2144001f1..89ac27ebb 100755 --- a/shared/src/main/java/me/xginko/aef/utils/MaterialUtil.java +++ b/shared/src/main/java/me/xginko/aef/utils/MaterialUtil.java @@ -2,7 +2,6 @@ import com.cryptomorin.xseries.XMaterial; import com.cryptomorin.xseries.XTag; -import io.papermc.lib.PaperLib; import org.bukkit.Material; import org.bukkit.SkullType; import org.bukkit.block.BlockState; @@ -22,87 +21,52 @@ public class MaterialUtil { - // Blocks that the player gets lowered into slightly when walking on them - public static final Set SINK_IN_BLOCKS = Stream.of( - XMaterial.SOUL_SAND, - XMaterial.FARMLAND, - XMaterial.MUD) - .filter(XMaterial::isSupported) - .map(XMaterial::parseMaterial) + public static final Set INVENTORY_HOLDERS = Arrays.stream(Material.values()) + .filter(Material::isItem) // Prevents loading issues in 1.20.6+ + .map(ItemStack::new) + .filter(itemStack -> itemStack.getItemMeta() instanceof BlockStateMeta) + .map(itemStack -> ((BlockStateMeta) itemStack.getItemMeta()).getBlockState()) + .filter(blockState -> blockState instanceof InventoryHolder) + .map(BlockState::getType) .collect(Collectors.toCollection(() -> EnumSet.noneOf(Material.class))); - public static final Set PLAYER_HEADS = Stream.of( - XMaterial.PLAYER_HEAD, - XMaterial.PLAYER_WALL_HEAD) + public static final Set LECTERN_BOOKS = XTag.ITEMS_LECTERN_BOOKS.getValues().stream() .filter(XMaterial::isSupported) .map(XMaterial::parseMaterial) .collect(Collectors.toCollection(() -> EnumSet.noneOf(Material.class))); - @SuppressWarnings("deprecation") - public static boolean isPlayerHead(BlockState state) { - if (!PLAYER_HEADS.contains(state.getType())) - return false; - if (PaperLib.getMinecraftVersion() > 12) - return true; - Skull skull = (Skull) state; - return skull.getSkullType() == SkullType.PLAYER || skull.hasOwner(); - } - - public static boolean isPlayerHead(ItemStack itemStack) { - if (!PLAYER_HEADS.contains(itemStack.getType())) - return false; - if (PaperLib.getMinecraftVersion() > 12) - return true; - return ((SkullMeta) itemStack.getItemMeta()).hasOwner(); - } - - public static final Set SLAB_LIKE = Stream.concat( - Arrays.stream(XMaterial.values()).filter(xMaterial -> xMaterial.name().toUpperCase().endsWith("_SLAB")), - Stream.of(XMaterial.SCULK_SENSOR, XMaterial.CALIBRATED_SCULK_SENSOR, XMaterial.SCULK_SHRIEKER)) + public static final Set SIGNS = XTag.SIGNS.getValues().stream() .filter(XMaterial::isSupported) .map(XMaterial::parseMaterial) .collect(Collectors.toCollection(() -> EnumSet.noneOf(Material.class))); - public static final Set REDSTONE = Stream.of( - XMaterial.REDSTONE, - XMaterial.REDSTONE_WIRE) + public static final Set TRAPDOORS = XTag.TRAPDOORS.getValues().stream() .filter(XMaterial::isSupported) .map(XMaterial::parseMaterial) .collect(Collectors.toCollection(() -> EnumSet.noneOf(Material.class))); - public static final Set SOLID_INDESTRUCTIBLES = Stream.of( - XMaterial.BEDROCK, - XMaterial.END_PORTAL_FRAME, - XMaterial.REINFORCED_DEEPSLATE, - XMaterial.STRUCTURE_BLOCK, - XMaterial.STRUCTURE_VOID, - XMaterial.BARRIER, - XMaterial.COMMAND_BLOCK) + public static final Set PRESSURE_PLATES = XTag.PRESSURE_PLATES.getValues().stream() .filter(XMaterial::isSupported) .map(XMaterial::parseMaterial) .collect(Collectors.toCollection(() -> EnumSet.noneOf(Material.class))); - public static final Set TEXT_BOOKS = Arrays.stream(XMaterial.values()) - .filter(xMaterial -> xMaterial.name().toUpperCase().contains("BOOK")) - .filter(xMaterial -> xMaterial != XMaterial.ENCHANTED_BOOK) + public static final Set SHULKER_BOXES = XTag.SHULKER_BOXES.getValues().stream() .filter(XMaterial::isSupported) .map(XMaterial::parseMaterial) .collect(Collectors.toCollection(() -> EnumSet.noneOf(Material.class))); - public static final Set INDESTRUCTIBLES = Stream.concat( - Stream.of(XMaterial.END_PORTAL.parseMaterial()), - SOLID_INDESTRUCTIBLES.stream()) - .filter(Objects::nonNull) + public static final Set ANVILS = XTag.ANVIL.getValues().stream() + .filter(XMaterial::isSupported) + .map(XMaterial::parseMaterial) .collect(Collectors.toCollection(() -> EnumSet.noneOf(Material.class))); - public static final Set SHULKER_BOXES = Arrays.stream(XMaterial.values()) - .filter(xMaterial -> xMaterial.name().toUpperCase().contains("SHULKER_BOX")) + public static final Set BEDS = XTag.BEDS.getValues().stream() .filter(XMaterial::isSupported) .map(XMaterial::parseMaterial) .collect(Collectors.toCollection(() -> EnumSet.noneOf(Material.class))); - public static final Set SIGNS = Arrays.stream(XMaterial.values()) - .filter(xMaterial -> xMaterial.name().toUpperCase().endsWith("_SIGN")) + public static final Set POTIONS = Arrays.stream(XMaterial.values()) + .filter(xMaterial -> xMaterial.name().toUpperCase().contains("POTION")) .filter(XMaterial::isSupported) .map(XMaterial::parseMaterial) .collect(Collectors.toCollection(() -> EnumSet.noneOf(Material.class))); @@ -113,53 +77,102 @@ public static boolean isPlayerHead(ItemStack itemStack) { .map(XMaterial::parseMaterial) .collect(Collectors.toCollection(() -> EnumSet.noneOf(Material.class))); - public static final Set TRAPDOORS = Arrays.stream(XMaterial.values()) - .filter(xMaterial -> xMaterial.name().toUpperCase().endsWith("_TRAPDOOR")) + public static final Set SPAWN_EGGS = Arrays.stream(XMaterial.values()) + .filter(xMaterial -> xMaterial.name().toUpperCase().endsWith("_SPAWN_EGG")) .filter(XMaterial::isSupported) .map(XMaterial::parseMaterial) .collect(Collectors.toCollection(() -> EnumSet.noneOf(Material.class))); - public static final Set PRESSURE_PLATES = Arrays.stream(XMaterial.values()) - .filter(xMaterial -> xMaterial.name().toUpperCase().contains("PRESSURE_PLATE")) + public static final Set SLAB_LIKE = Stream.concat( + Arrays.stream(XMaterial.values()).filter(xMaterial -> xMaterial.name().toUpperCase().endsWith("_SLAB")), + Stream.of(XMaterial.SCULK_SENSOR, XMaterial.CALIBRATED_SCULK_SENSOR, XMaterial.SCULK_SHRIEKER)) .filter(XMaterial::isSupported) .map(XMaterial::parseMaterial) .collect(Collectors.toCollection(() -> EnumSet.noneOf(Material.class))); - public static final Set ANVILS = XTag.ANVIL.getValues().stream() + public static final Set PLAYER_HEADS = Stream.of( + XMaterial.PLAYER_HEAD, + XMaterial.PLAYER_WALL_HEAD) .filter(XMaterial::isSupported) .map(XMaterial::parseMaterial) .collect(Collectors.toCollection(() -> EnumSet.noneOf(Material.class))); - public static final Set BLOCK_DISPENSE_BUCKETS = Stream.of( - XMaterial.WATER_BUCKET, - XMaterial.LAVA_BUCKET, - XMaterial.COD_BUCKET, - XMaterial.SALMON_BUCKET, - XMaterial.PUFFERFISH_BUCKET, - XMaterial.TROPICAL_FISH_BUCKET, - XMaterial.AXOLOTL_BUCKET, - XMaterial.TADPOLE_BUCKET, - XMaterial.POWDER_SNOW_BUCKET) + public static final Set REDSTONE = Stream.of( + XMaterial.REDSTONE, + XMaterial.REDSTONE_WIRE) .filter(XMaterial::isSupported) .map(XMaterial::parseMaterial) .collect(Collectors.toCollection(() -> EnumSet.noneOf(Material.class))); - public static final Set SPAWN_EGGS = Arrays.stream(XMaterial.values()) - .filter(xMaterial -> xMaterial.name().toUpperCase().endsWith("_SPAWN_EGG")) + // Blocks that the player gets lowered into slightly when walking on them + public static final Set SINK_IN_BLOCKS = Stream.of( + XMaterial.SOUL_SAND, + XMaterial.FARMLAND, + XMaterial.MUD) .filter(XMaterial::isSupported) .map(XMaterial::parseMaterial) .collect(Collectors.toCollection(() -> EnumSet.noneOf(Material.class))); - public static final Set INVENTORY_HOLDER_ITEMS = Arrays.stream(Material.values()) - .filter(Material::isItem) - .map(ItemStack::new) - .filter(itemStack -> itemStack.getItemMeta() instanceof BlockStateMeta) - .map(itemStack -> ((BlockStateMeta) itemStack.getItemMeta()).getBlockState()) - .filter(blockState -> blockState instanceof InventoryHolder) - .map(BlockState::getType) + public static final Set SOLID_INDESTRUCTIBLES = Stream.of( + XMaterial.BEDROCK, + XMaterial.END_PORTAL_FRAME, + XMaterial.REINFORCED_DEEPSLATE, + XMaterial.STRUCTURE_BLOCK, + XMaterial.STRUCTURE_VOID, + XMaterial.BARRIER, + XMaterial.COMMAND_BLOCK) + .filter(XMaterial::isSupported) + .map(XMaterial::parseMaterial) + .collect(Collectors.toCollection(() -> EnumSet.noneOf(Material.class))); + + public static final Set INDESTRUCTIBLES = Stream.concat(SOLID_INDESTRUCTIBLES.stream(), + Stream.of(XMaterial.END_PORTAL.parseMaterial())) + .filter(Objects::nonNull) + .collect(Collectors.toCollection(() -> EnumSet.noneOf(Material.class))); + + public static final Set BLOCK_DISPENSE_BUCKETS = Stream.of( + XMaterial.WATER_BUCKET, + XMaterial.LAVA_BUCKET, + XMaterial.COD_BUCKET, + XMaterial.SALMON_BUCKET, + XMaterial.PUFFERFISH_BUCKET, + XMaterial.TROPICAL_FISH_BUCKET, + XMaterial.AXOLOTL_BUCKET, + XMaterial.TADPOLE_BUCKET, + XMaterial.POWDER_SNOW_BUCKET) + .filter(XMaterial::isSupported) + .map(XMaterial::parseMaterial) .collect(Collectors.toCollection(() -> EnumSet.noneOf(Material.class))); - public static boolean isElytra(@Nullable ItemStack item) { - return item != null && item.getType() == XMaterial.ELYTRA.parseMaterial(); + public static boolean isElytra(@Nullable ItemStack itemStack) { + return itemStack != null && itemStack.getType() == XMaterial.ELYTRA.parseMaterial(); + } + + @SuppressWarnings("deprecation") + public static boolean isPlayerHead(BlockState blockState) { + if (!PLAYER_HEADS.contains(blockState.getType())) + return false; + if (PlatformUtil.getMinecraftVersion() > 12) + return true; + Skull skull = (Skull) blockState; + return skull.getSkullType() == SkullType.PLAYER || skull.hasOwner(); + } + + @SuppressWarnings("deprecation") + public static boolean isPlayerHead(ItemStack itemStack) { + if (!PLAYER_HEADS.contains(itemStack.getType())) + return false; + if (PlatformUtil.getMinecraftVersion() > 12) + return true; // Player heads have their own Material enum post 1.12.2 + if (!itemStack.hasItemMeta()) + return false; + if (((SkullMeta) itemStack.getItemMeta()).hasOwner()) + return true; + if (itemStack.getItemMeta() instanceof BlockStateMeta) { + BlockStateMeta blockStateMeta = (BlockStateMeta) itemStack.getItemMeta(); + if (blockStateMeta.hasBlockState() && blockStateMeta.getBlockState() instanceof Skull) + return ((Skull) blockStateMeta.getBlockState()).getSkullType() == SkullType.PLAYER; + } + return false; } } \ No newline at end of file diff --git a/shared/src/main/java/me/xginko/aef/utils/PlatformUtil.java b/shared/src/main/java/me/xginko/aef/utils/PlatformUtil.java new file mode 100644 index 000000000..3f364bdba --- /dev/null +++ b/shared/src/main/java/me/xginko/aef/utils/PlatformUtil.java @@ -0,0 +1,80 @@ +package me.xginko.aef.utils; + +import org.bukkit.Bukkit; + +import java.util.Locale; +import java.util.regex.MatchResult; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class PlatformUtil { + + private static int minecraftVersion, minecraftPatchVersion, minecraftPreReleaseVersion, minecraftReleaseCandidateVersion; + private static boolean isServerPaper, isServerFolia, isServerPurpur; + + public static void load() { + isServerPurpur + = Crafty.hasClass("org.purpurmc.purpur.PurpurConfig"); + isServerPaper + = Crafty.hasClass("com.destroystokyo.paper.PaperConfig") + || Crafty.hasClass("io.papermc.paper.configuration.Configuration"); + isServerFolia + = Crafty.hasClass("io.papermc.paper.threadedregions.RegionizedServer"); + + // From PaperLib + Matcher matcher = Pattern.compile("(?i)\\(MC: (\\d)\\.(\\d+)\\.?(\\d+?)?(?: (Pre-Release|Release Candidate) )?(\\d)?\\)") + .matcher(Bukkit.getVersion()); + if (matcher.find()) { + MatchResult matchResult = matcher.toMatchResult(); + + try { + minecraftVersion = Integer.parseInt(matchResult.group(2), 10); + } catch (Exception ignored) {} + + if (matchResult.groupCount() >= 3) { + try { + minecraftPatchVersion = Integer.parseInt(matchResult.group(3), 10); + } catch (Exception ignored) {} + } + + if (matchResult.groupCount() >= 5) { + try { + int ver = Integer.parseInt(matcher.group(5)); + if (matcher.group(4).toLowerCase(Locale.ENGLISH).contains("pre")) { + minecraftPreReleaseVersion = ver; + } else { + minecraftReleaseCandidateVersion = ver; + } + } catch (Exception ignored) {} + } + } + } + + public static boolean isPaper() { + return isServerPaper; + } + + public static boolean isFolia() { + return isServerFolia; + } + + public static boolean isPurpur() { + return isServerPurpur; + } + + public static int getMinecraftVersion() { + return minecraftVersion; + } + + public static int getMinecraftPatchVersion() { + return minecraftPatchVersion; + } + + public static int getMinecraftPreReleaseVersion() { + return minecraftPreReleaseVersion; + } + + public static int getMinecraftReleaseCandidateVersion() { + return minecraftReleaseCandidateVersion; + } +} diff --git a/shared/src/main/java/me/xginko/aef/utils/models/BlockRegion2D.java b/shared/src/main/java/me/xginko/aef/utils/models/BlockRegion2D.java new file mode 100644 index 000000000..c8a18f0f6 --- /dev/null +++ b/shared/src/main/java/me/xginko/aef/utils/models/BlockRegion2D.java @@ -0,0 +1,102 @@ +package me.xginko.aef.utils.models; + +import org.bukkit.Location; +import org.bukkit.World; + +import java.util.Objects; +import java.util.UUID; + +public class BlockRegion2D { + + private final UUID worldUID; + private final double halfSideLength, centerX, centerZ; + + /** + * A square region on a minecraft world map. + * + * @param worldUID The UUID of the world this region is in. + * @param centerX The X-axis of the center location on the map. + * @param centerZ The Z-axis of the center location on the map. + * @param halfSideLength Half the length of the square's side. Acts like a radius would on circular regions. + */ + public BlockRegion2D(UUID worldUID, double centerX, double centerZ, double halfSideLength) { + this.worldUID = worldUID; + this.centerX = centerX; + this.centerZ = centerZ; + this.halfSideLength = halfSideLength; + } + + /** + * Creates a square region on a minecraft world map. + * + * @param worldUID The UUID of the world this region is in. + * @param centerX The X-axis of the center location on the map. + * @param centerZ The Z-axis of the center location on the map. + * @param halfSideLength Half the length of the square's side. Acts like a radius would on circular regions. + */ + public static BlockRegion2D of(UUID worldUID, double centerX, double centerZ, double halfSideLength) { + return new BlockRegion2D(worldUID, centerX, centerZ, halfSideLength); + } + + /** + * Creates a square region on a minecraft world map. + * + * @param world The world this region is in. + * @param centerX The X-axis of the center location on the map. + * @param centerZ The Z-axis of the center location on the map. + * @param halfSideLength Half the length of the square's side. Acts like a radius would on circular regions. + */ + public static BlockRegion2D of(World world, double centerX, double centerZ, double halfSideLength) { + return BlockRegion2D.of(world.getUID(), centerX, centerZ, halfSideLength); + } + + public UUID getWorldUID() { + return this.worldUID; + } + + public double getHalfSideLength() { + return this.halfSideLength; + } + + public double getCenterX() { + return this.centerX; + } + + public double getCenterZ() { + return this.centerZ; + } + + public boolean contains(Location location) { + if (!location.getWorld().getUID().equals(this.worldUID)) { + return false; + } + + return location.getX() >= this.centerX - this.halfSideLength + && location.getX() <= this.centerX + this.halfSideLength + && location.getZ() >= this.centerZ - this.halfSideLength + && location.getZ() <= this.centerZ + this.halfSideLength; + } + + @Override + public boolean equals(Object obj) { + if (null == obj || obj.getClass() != BlockRegion2D.class) + return false; + BlockRegion2D blockRegion2D = (BlockRegion2D)obj; + return blockRegion2D.worldUID.equals(this.worldUID) && blockRegion2D.centerX == this.centerX && blockRegion2D.centerZ == this.centerZ; + } + + @Override + public int hashCode() { + return Objects.hash(this.worldUID, this.centerX, this.centerZ, this.halfSideLength); + } + + @Override + public String toString() { + return "BlockRegion2D{" + + " radius(half side length)=" + halfSideLength + + ", centerX=" + centerX + + ", centerZ=" + centerZ + + ", worldUID=" + worldUID + + "}"; + } +} diff --git a/shared/src/main/java/me/xginko/aef/utils/models/ChunkUID.java b/shared/src/main/java/me/xginko/aef/utils/models/ChunkUID.java index f9944b5f7..e30a94902 100644 --- a/shared/src/main/java/me/xginko/aef/utils/models/ChunkUID.java +++ b/shared/src/main/java/me/xginko/aef/utils/models/ChunkUID.java @@ -5,66 +5,57 @@ import org.bukkit.World; import org.jetbrains.annotations.Nullable; +import java.util.Objects; import java.util.UUID; +import java.util.concurrent.CompletableFuture; public final class ChunkUID { - private final long mostSigBits, leastSigBits; + private final UUID worldUID; private final int x, z; public static ChunkUID of(Chunk chunk) { - final UUID worldUID = chunk.getWorld().getUID(); - return new ChunkUID(worldUID.getMostSignificantBits(), worldUID.getLeastSignificantBits(), chunk.getX(), chunk.getZ()); + return new ChunkUID(chunk.getWorld().getUID(), chunk.getX(), chunk.getZ()); } - public ChunkUID(long mostSigBits, long leastSigBits, int x, int z) { - this.mostSigBits = mostSigBits; - this.leastSigBits = leastSigBits; + private ChunkUID(UUID worldUID, int x, int z) { + this.worldUID = worldUID; this.x = x; this.z = z; } - public long getMostSigBits() { - return mostSigBits; - } - - public long getLeastSigBits() { - return leastSigBits; + public UUID getWorldUID() { + return this.worldUID; } public int getX() { - return x; + return this.x; } public int getZ() { - return z; + return this.z; } - public @Nullable Chunk getChunk() { - World world = Bukkit.getWorld(new UUID(mostSigBits, leastSigBits)); - return world == null ? null : world.getChunkAt(x, z, false); + public CompletableFuture<@Nullable Chunk> getChunkAsync(boolean gen) { + World world = Bukkit.getWorld(worldUID); + return world == null ? CompletableFuture.completedFuture(null) : world.getChunkAtAsync(x, z, gen); } @Override public String toString() { - Chunk chunk = getChunk(); - return "[ x: "+x+", z:"+z+" world: "+(chunk != null ? chunk.getWorld().getName() : "invalid")+" ]"; + return "ChunkUID{" + "x=" + x + ", z=" + z + ", worldUID=" + worldUID + "}"; } @Override public int hashCode() { - long hilo = mostSigBits ^ leastSigBits; - int result = ((int)(hilo >> 32)) ^ (int) hilo; - result = 31 * result + x; // The use of 31 as a multiplier is a common practice in hash code implementations - result = 31 * result + z; // because it is an odd prime and helps distribute the hash values more uniformly. - return result; + return Objects.hash(this.worldUID, this.x, this.z); } @Override public boolean equals(Object obj) { - if ((null == obj) || (obj.getClass() != ChunkUID.class)) + if (null == obj || obj.getClass() != ChunkUID.class) return false; ChunkUID id = (ChunkUID)obj; - return id.x == this.x && id.z == this.z && id.mostSigBits == this.mostSigBits && id.leastSigBits == this.leastSigBits; + return id.x == this.x && id.z == this.z && id.worldUID.equals(this.worldUID); } } diff --git a/shared/src/main/java/me/xginko/aef/utils/models/ConditionalEnableable.java b/shared/src/main/java/me/xginko/aef/utils/models/ConditionalEnableable.java new file mode 100644 index 000000000..a843f77d7 --- /dev/null +++ b/shared/src/main/java/me/xginko/aef/utils/models/ConditionalEnableable.java @@ -0,0 +1,5 @@ +package me.xginko.aef.utils.models; + +public interface ConditionalEnableable extends Enableable { + boolean shouldEnable(); +} diff --git a/shared/src/main/java/me/xginko/aef/utils/models/ExpiringSet.java b/shared/src/main/java/me/xginko/aef/utils/models/ExpiringSet.java index 934b49cb2..48cb24829 100644 --- a/shared/src/main/java/me/xginko/aef/utils/models/ExpiringSet.java +++ b/shared/src/main/java/me/xginko/aef/utils/models/ExpiringSet.java @@ -177,7 +177,7 @@ public boolean contains(Object item) { * prevents it from being added to this set */ public boolean add(E item) { - boolean containedBefore = contains(item); + boolean containedBefore = this.contains(item); this.cache.put(item, PRESENT); return !containedBefore; } @@ -205,8 +205,8 @@ public boolean add(E item) { */ @Override public boolean remove(Object o) { - boolean present = contains(o); - this.cache.asMap().remove(o); + boolean present = this.contains(o); + if (present) this.cache.invalidate((E) o); return present; } @@ -232,7 +232,7 @@ public boolean remove(Object o) { @Override public boolean containsAll(@NotNull Collection c) { for (Object o : c) { - if (!contains(o)) { + if (!this.contains(o)) { return false; } } @@ -264,7 +264,7 @@ public boolean containsAll(@NotNull Collection c) { public boolean addAll(@NotNull Collection c) { boolean changed = false; for (E o : c) { - if (add(o)) { + if (this.add(o)) { changed = true; } } @@ -296,7 +296,7 @@ public boolean addAll(@NotNull Collection c) { public boolean retainAll(@NotNull Collection c) { boolean changed = false; for (E e : this.cache.asMap().keySet()) { - if (!c.contains(e) && remove(e)) { + if (!c.contains(e) && this.remove(e)) { changed = true; } } @@ -328,7 +328,7 @@ public boolean retainAll(@NotNull Collection c) { public boolean removeAll(@NotNull Collection c) { boolean changed = false; for (E e : this.cache.asMap().keySet()) { - if (remove(e)) { + if (this.remove(e)) { changed = true; } } @@ -344,6 +344,6 @@ public boolean removeAll(@NotNull Collection c) { */ @Override public void clear() { - this.cache.asMap().clear(); + this.cache.invalidateAll(); } } \ No newline at end of file diff --git a/shared/src/main/java/me/xginko/aef/utils/tickdata/FallbackTickReporter.java b/shared/src/main/java/me/xginko/aef/utils/tickdata/FallbackTickReporter.java new file mode 100755 index 000000000..2726c92e6 --- /dev/null +++ b/shared/src/main/java/me/xginko/aef/utils/tickdata/FallbackTickReporter.java @@ -0,0 +1,44 @@ +package me.xginko.aef.utils.tickdata; + +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; + +import java.time.Duration; + +@SuppressWarnings("DataFlowIssue") +public final class FallbackTickReporter implements TickReporter { + + private final Cache cache; + private final SpigotReflection spigotReflection; + + public FallbackTickReporter(Duration cacheDuration) { + this.cache = Caffeine.newBuilder().expireAfterWrite(cacheDuration).build(); + this.spigotReflection = SpigotReflection.getInstance(); + } + + @Override + public void disable() { + cache.invalidateAll(); + cache.cleanUp(); + } + + @Override + public double getGlobalTPS() { + return cache.get(true, k -> spigotReflection.getTPS()[0]); + } + + @Override + public double getTPS() { + return getGlobalTPS(); + } + + @Override + public double getGlobalMSPT() { + return cache.get(false, k -> spigotReflection.getAverageTickTime()); + } + + @Override + public double getMSPT() { + return getGlobalMSPT(); + } +} \ No newline at end of file diff --git a/shared/src/main/java/me/xginko/aef/utils/tickdata/FoliaTickReporter.java b/shared/src/main/java/me/xginko/aef/utils/tickdata/FoliaTickReporter.java index 3429cf2b9..74a3a162a 100644 --- a/shared/src/main/java/me/xginko/aef/utils/tickdata/FoliaTickReporter.java +++ b/shared/src/main/java/me/xginko/aef/utils/tickdata/FoliaTickReporter.java @@ -6,8 +6,7 @@ import io.papermc.paper.threadedregions.ThreadedRegionizer; import io.papermc.paper.threadedregions.TickRegionScheduler; import io.papermc.paper.threadedregions.TickRegions; -import io.papermc.paper.threadedregions.scheduler.EntityScheduler; -import io.papermc.paper.threadedregions.scheduler.RegionScheduler; +import me.xginko.aef.utils.Crafty; import java.time.Duration; @@ -22,20 +21,17 @@ public FoliaTickReporter(Duration cacheTime) { } public static boolean isSupported() { - try { - Class.forName("io.papermc.paper.threadedregions.RegionizedServer"); - Class.forName("io.papermc.paper.threadedregions.ThreadedRegionizer"); - Class.forName("io.papermc.paper.threadedregions.TickRegionScheduler"); - Class.forName("io.papermc.paper.threadedregions.TickRegions"); - return true; - } catch (ClassNotFoundException e) { - return false; - } + return Crafty.hasClass("io.papermc.paper.threadedregions.RegionizedServer") + && Crafty.hasClass("io.papermc.paper.threadedregions.ThreadedRegionizer") + && Crafty.hasClass("io.papermc.paper.threadedregions.TickRegionScheduler") + && Crafty.hasClass("io.papermc.paper.threadedregions.TickRegions"); } @Override public void disable() { + tps_cache.invalidateAll(); tps_cache.cleanUp(); + mspt_cache.invalidateAll(); mspt_cache.cleanUp(); } @@ -45,47 +41,29 @@ public double getGlobalTPS() { regionScheduleHandle.getTickReport5s(System.nanoTime()).tpsData().segmentAll().average()); } - /** - * Please note: - * This method needs to be called from the same thread as the region you would like to get the TPS of. - * The recommended way of doing so is by using either the {@link RegionScheduler} or {@link EntityScheduler}. - * It can be called from within an event as well but there's no guarantee that the TPS for the correct region - * is returned (Read the Folia README). - * - * @return The TPS of the last 5 seconds from the region this method was called from, otherwise the global TPS - */ @Override public double getTPS() { final ThreadedRegionizer.ThreadedRegion region = TickRegionScheduler.getCurrentRegion(); if (region == null) return getGlobalTPS(); - return tps_cache.get(region.getData().getRegionSchedulingHandle(), regionSchedulingHandle -> - regionSchedulingHandle.getTickReport5s(System.nanoTime()).tpsData().segmentAll().average()); + return tps_cache.get(region.getData().getRegionSchedulingHandle(), regionScheduleHandle -> + regionScheduleHandle.getTickReport5s(System.nanoTime()).tpsData().segmentAll().average()); } @Override public double getGlobalMSPT() { - return mspt_cache.get(RegionizedServer.getGlobalTickData(), regionSchedulingHandle -> - regionSchedulingHandle.getTickReport5s(System.nanoTime()).timePerTickData().segmentAll().average() / 1000000); + return mspt_cache.get(RegionizedServer.getGlobalTickData(), regionScheduleHandle -> + regionScheduleHandle.getTickReport5s(System.nanoTime()).timePerTickData().segmentAll().average() / 1000000); } - /** - * Please note: - * This method needs to be called from the same thread as the region you would like to get the MSPT of. - * The recommended way of doing so is by using either the {@link RegionScheduler} or {@link EntityScheduler}. - * It can be called from within an event as well but there's no guarantee that the MSPT for the correct region - * is returned (Read the Folia README). - * - * @return The MSPT of the last 5 seconds from the region this method was called from, otherwise the global MSPT - */ @Override public double getMSPT() { final ThreadedRegionizer.ThreadedRegion region = TickRegionScheduler.getCurrentRegion(); if (region == null) return getGlobalMSPT(); - return mspt_cache.get(region.getData().getRegionSchedulingHandle(), regionSchedulingHandle -> - regionSchedulingHandle.getTickReport5s(System.nanoTime()).timePerTickData().segmentAll().average() / 1000000); + return mspt_cache.get(region.getData().getRegionSchedulingHandle(), regionScheduleHandle -> + regionScheduleHandle.getTickReport5s(System.nanoTime()).timePerTickData().segmentAll().average() / 1000000); } } diff --git a/shared/src/main/java/me/xginko/aef/utils/tickdata/LegacyPaperTickReporter.java b/shared/src/main/java/me/xginko/aef/utils/tickdata/LegacyPaperTickReporter.java index ac620fc9e..15269f148 100755 --- a/shared/src/main/java/me/xginko/aef/utils/tickdata/LegacyPaperTickReporter.java +++ b/shared/src/main/java/me/xginko/aef/utils/tickdata/LegacyPaperTickReporter.java @@ -2,51 +2,35 @@ import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; -import me.xginko.aef.utils.models.Disableable; +import me.xginko.aef.utils.Crafty; import org.bukkit.Server; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.HandlerList; -import org.bukkit.event.Listener; import org.bukkit.plugin.java.JavaPlugin; -import org.bukkit.scheduler.BukkitTask; import java.time.Duration; -import java.util.Arrays; @SuppressWarnings("DataFlowIssue") public final class LegacyPaperTickReporter implements TickReporter { private final Server server; private final Cache cache; - // private final MSPTGetter msptGetter; + private final SpigotReflection spigotReflection; public LegacyPaperTickReporter(JavaPlugin plugin, Duration cacheDuration) { this.server = plugin.getServer(); this.cache = Caffeine.newBuilder().expireAfterWrite(cacheDuration).build(); - // this.msptGetter = PaperLib.getMinecraftVersion() <= 12 ? new v_1_12_and_lower(plugin) : new v_1_13_and_later(plugin); + this.spigotReflection = SpigotReflection.getInstance(); } public static boolean isSupported() { - try { - Server.class.getMethod("getTPS"); - return true; - } catch (NoSuchMethodException e) { - return false; - } + return Crafty.hasMethod(Server.class, "getTPS"); } @Override public void disable() { + cache.invalidateAll(); cache.cleanUp(); - // msptGetter.disable(); } - /** - * Gets the current server TPS - * - * @return the TPS of the last 1m in Paper-Server - */ @Override public double getGlobalTPS() { return cache.get(true, k -> server.getTPS()[0]); @@ -57,90 +41,13 @@ public double getTPS() { return getGlobalTPS(); } - /** - * Get the average tick time of the last 5s (in millis) - * - * @return Average tick time (in millis) - */ @Override public double getGlobalMSPT() { - return 0; - // return cache.get(false, k -> msptGetter.getAverageTickTime()); + return cache.get(false, k -> spigotReflection.getAverageTickTime()); } @Override public double getMSPT() { return getGlobalMSPT(); } - - private interface MSPTGetter extends Disableable { - double getAverageTickTime(); - } - - private static class v_1_13_and_later implements MSPTGetter, Listener { - - private long startTickNanoTime; - private final long[] times5s; - - public v_1_13_and_later(JavaPlugin plugin) { - times5s = new long[100]; - Arrays.fill(times5s, 0); - plugin.getServer().getPluginManager().registerEvents(this, plugin); - } - - @Override - public void disable() { - HandlerList.unregisterAll(this); - } - - @EventHandler(priority = EventPriority.MONITOR) - private void onServerTickStart(com.destroystokyo.paper.event.server.ServerTickStartEvent event) { - startTickNanoTime = System.nanoTime(); - } - - @EventHandler(priority = EventPriority.MONITOR) - private void onServerTickEnd(com.destroystokyo.paper.event.server.ServerTickEndEvent event) { - times5s[event.getTickNumber() % times5s.length] = System.nanoTime() - startTickNanoTime; - } - - @Override - public double getAverageTickTime() { - long total = 0L; - for (long value : times5s) { - total += value; - } - return ((double) total / (double) times5s.length) * 1.0E-6D; - } - } - - public static class v_1_12_and_lower implements MSPTGetter, Runnable { - - private final BukkitTask tickTask; - private final long[] times5s; - private int tickIndex = 0; - - public v_1_12_and_lower(JavaPlugin plugin) { - times5s = new long[100]; - Arrays.fill(times5s, 0); - tickTask = plugin.getServer().getScheduler().runTaskTimer(plugin, this, 0L, 1L); - } - - @Override - public void disable() { - tickTask.cancel(); - } - - @Override - public void run() { - times5s[tickIndex] = System.nanoTime(); - tickIndex = (tickIndex + 1) % 100; - } - - @Override - public double getAverageTickTime() { - int targetIndex = (tickIndex - 1) % 100; - double elapsed = (System.nanoTime() - times5s[targetIndex]) / 100.0; - return elapsed / 1E6; - } - } } \ No newline at end of file diff --git a/shared/src/main/java/me/xginko/aef/utils/tickdata/ModernPaperTickReporter.java b/shared/src/main/java/me/xginko/aef/utils/tickdata/ModernPaperTickReporter.java index 18e52a797..47449490b 100644 --- a/shared/src/main/java/me/xginko/aef/utils/tickdata/ModernPaperTickReporter.java +++ b/shared/src/main/java/me/xginko/aef/utils/tickdata/ModernPaperTickReporter.java @@ -2,6 +2,7 @@ import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; +import me.xginko.aef.utils.Crafty; import org.bukkit.Server; import org.bukkit.plugin.java.JavaPlugin; @@ -19,24 +20,15 @@ public ModernPaperTickReporter(JavaPlugin plugin, Duration cacheTime) { } public static boolean isSupported() { - try { - Server.class.getMethod("getAverageTickTime"); - return true; - } catch (NoSuchMethodException e) { - return false; - } + return Crafty.hasMethod(Server.class, "getAverageTickTime"); } @Override public void disable() { + cache.invalidateAll(); cache.cleanUp(); } - /** - * Gets the current server TPS - * - * @return the TPS of the last 1m in Paper-Server - */ @Override public double getGlobalTPS() { return cache.get(true, k -> server.getTPS()[0]); @@ -47,11 +39,6 @@ public double getTPS() { return getGlobalTPS(); } - /** - * Get the average tick time of the last 5s (in millis) - * - * @return Average tick time (in millis) - */ @Override public double getGlobalMSPT() { return cache.get(false, k -> server.getAverageTickTime()); diff --git a/shared/src/main/java/me/xginko/aef/utils/tickdata/SpigotReflection.java b/shared/src/main/java/me/xginko/aef/utils/tickdata/SpigotReflection.java new file mode 100644 index 000000000..99e25c6bb --- /dev/null +++ b/shared/src/main/java/me/xginko/aef/utils/tickdata/SpigotReflection.java @@ -0,0 +1,146 @@ +/* + * This file is part of TabTPS, licensed under the MIT License. + * + * Copyright (c) 2020-2024 Jason Penilla + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package me.xginko.aef.utils.tickdata; + +import me.xginko.aef.utils.Crafty; +import me.xginko.aef.utils.PlatformUtil; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; + +import java.lang.invoke.MethodHandle; +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.Objects; + +import static me.xginko.aef.utils.Crafty.needNMSClassOrElse; + +public final class SpigotReflection { + + private static SpigotReflection instance; + private static Class MinecraftServer_class; + private static MethodHandle MinecraftServer_getServer_method; + private static Field MinecraftServer_recentTps_field, MinecraftServer_recentTickTimes_field; + + public static SpigotReflection getInstance() { + if (instance == null) { + MinecraftServer_class = needNMSClassOrElse("MinecraftServer", "net.minecraft.server.MinecraftServer"); + MinecraftServer_getServer_method = needStaticMethod(MinecraftServer_class, "getServer", MinecraftServer_class); + MinecraftServer_recentTps_field = needField(MinecraftServer_class, "recentTps"); // Spigot added field + MinecraftServer_recentTickTimes_field = tickTimesField(); + instance = new SpigotReflection(); + } + return instance; + } + + private static @NonNull Field tickTimesField() { + final String tickTimes; + final int ver = PlatformUtil.getMinecraftVersion(); + if (ver < 13) { + tickTimes = "h"; + } else if (ver == 13) { + tickTimes = "d"; + } else if (ver == 14 || ver == 15) { + tickTimes = "f"; + } else if (ver == 16) { + tickTimes = "h"; + } else if (ver == 17) { + tickTimes = "n"; + } else if (ver == 18) { + tickTimes = "o"; + } else if (ver == 19 || ver == 20 && PlatformUtil.getMinecraftPatchVersion() < 3) { + tickTimes = "k"; + } else if (ver == 20 && PlatformUtil.getMinecraftPatchVersion() < 6) { + tickTimes = "ac"; + } else if (ver == 20 || ver == 21) { + tickTimes = "ab"; + } else { + throw new IllegalStateException("Don't know tickTimes field name!"); + } + return needField(MinecraftServer_class, tickTimes); + } + + public double getAverageTickTime() { + final Object server = invokeOrThrow(MinecraftServer_getServer_method); + try { + final long[] recentMspt = (long[]) MinecraftServer_recentTickTimes_field.get(server); + return toMilliseconds(average(recentMspt)); + } catch (final IllegalAccessException e) { + throw new IllegalStateException("Failed to get server mspt", e); + } + } + + private static double toMilliseconds(final double time) { + return time * 1.0E-6D; + } + + private static double average(final long @NonNull [] longs) { + long i = 0L; + for (final long l : longs) { + i += l; + } + return i / (double) longs.length; + } + + public double @NonNull [] getTPS() { + final Object server = invokeOrThrow(MinecraftServer_getServer_method); + try { + return (double[]) MinecraftServer_recentTps_field.get(server); + } catch (final IllegalAccessException e) { + throw new IllegalStateException("Failed to get server TPS", e); + } + } + + private static @NonNull MethodHandle needStaticMethod(final @NonNull Class holderClass, final @NonNull String methodName, final @NonNull Class returnClass, final @NonNull Class @NonNull ... parameterClasses) { + return Objects.requireNonNull( + Crafty.findStaticMethod(holderClass, methodName, returnClass, parameterClasses), + String.format( + "Could not locate static method '%s' in class '%s'", + methodName, + holderClass.getCanonicalName() + ) + ); + } + + private static @NonNull Field needField(final @NonNull Class holderClass, final @NonNull String fieldName) { + final Field field; + try { + field = holderClass.getDeclaredField(fieldName); + field.setAccessible(true); + return field; + } catch (final NoSuchFieldException e) { + throw new IllegalStateException(String.format("Unable to find field '%s' in class '%s'", fieldName, holderClass.getCanonicalName()), e); + } + } + + private static @Nullable Object invokeOrThrow(final @NonNull MethodHandle methodHandle, final @Nullable Object @NonNull ... params) { + try { + if (params.length == 0) { + return methodHandle.invoke(); + } + return methodHandle.invokeWithArguments(params); + } catch (final Throwable throwable) { + throw new IllegalStateException(String.format("Unable to invoke method with args '%s'", Arrays.toString(params)), throwable); + } + } +} diff --git a/shared/src/main/java/me/xginko/aef/utils/tickdata/TickReporter.java b/shared/src/main/java/me/xginko/aef/utils/tickdata/TickReporter.java index 6d320ff27..ecaea4e3e 100755 --- a/shared/src/main/java/me/xginko/aef/utils/tickdata/TickReporter.java +++ b/shared/src/main/java/me/xginko/aef/utils/tickdata/TickReporter.java @@ -1,18 +1,14 @@ package me.xginko.aef.utils.tickdata; +import io.papermc.paper.threadedregions.scheduler.EntityScheduler; +import io.papermc.paper.threadedregions.scheduler.RegionScheduler; import me.xginko.aef.utils.models.Disableable; -import org.apache.commons.lang.NotImplementedException; import org.bukkit.plugin.java.JavaPlugin; import java.time.Duration; public interface TickReporter extends Disableable { - double getGlobalTPS(); - double getTPS(); - double getGlobalMSPT(); - double getMSPT(); - static TickReporter create(JavaPlugin plugin, Duration cacheDuration) { if (FoliaTickReporter.isSupported()) { return new FoliaTickReporter(cacheDuration); @@ -26,6 +22,39 @@ static TickReporter create(JavaPlugin plugin, Duration cacheDuration) { return new LegacyPaperTickReporter(plugin, cacheDuration); } - throw new NotImplementedException("This Server version is unsupported."); + return new FallbackTickReporter(cacheDuration); } + + /** + * @return The most recent global TPS + */ + double getGlobalTPS(); + + /** + * Folia note: + * This method needs to be called from the same thread as the region you would like to get the TPS of. + * The recommended way of doing so is by using either the {@link RegionScheduler} or {@link EntityScheduler}. + * It can be called from within an event as well but there's no guarantee that the TPS for the correct region + * is returned (Read the Folia README). + * + * @return The TPS of the region this method was called from, otherwise the global TPS + */ + double getTPS(); + + /** + * @return The most recent average global tick time (MSPT) + */ + double getGlobalMSPT(); + + /** + * Folia note: + * This method needs to be called from the same thread as the region you would like to get the MSPT of. + * The recommended way of doing so is by using either the {@link RegionScheduler} or {@link EntityScheduler}. + * It can be called from within an event as well but there's no guarantee that the MSPT for the correct region + * is returned (Read the Folia README). + * + * @return The average tick time (MSPT) of the region this method was called from, otherwise the global MSPT + */ + double getMSPT(); + } \ No newline at end of file