Skip to content

Commit

Permalink
improve lagprevention modules
Browse files Browse the repository at this point in the history
  • Loading branch information
xGinko committed Dec 27, 2023
1 parent 0762016 commit 3b4b159
Show file tree
Hide file tree
Showing 8 changed files with 190 additions and 203 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,12 @@ public FallingBlockStasis() {
shouldEnable();
this.plugin = AnarchyExploitFixes.getInstance();
Config config = AnarchyExploitFixes.getConfiguration();
config.addComment("lag-preventions.prevent-falling-block-stasis-exploit.enable", "Patches a lag exploit using falling blocks and glitched piston heads: https://www.youtube.com/watch?v=y2CjPlvtj58");
this.max_alive_time = config.getInt("lag-preventions.prevent-falling-block-stasis-exploit.falling-blocks-max-alive-time-in-ticks", 300, "(20 ticks = 1 second)");
this.check_period_in_ticks = config.getInt("lag-preventions.prevent-falling-block-stasis-exploit.check-period-in-seconds", 120, "How frequently we should check for all projectile's alive time") * 20L;
config.addComment("lag-preventions.prevent-falling-block-stasis-exploit.enable",
"Patches a lag exploit using falling blocks and glitched piston heads: https://www.youtube.com/watch?v=y2CjPlvtj58");
this.max_alive_time = config.getInt("lag-preventions.prevent-falling-block-stasis-exploit.falling-blocks-max-alive-time-in-ticks", 300,
"(20 ticks = 1 second)");
this.check_period_in_ticks = config.getInt("lag-preventions.prevent-falling-block-stasis-exploit.check-period-in-seconds", 120,
"How frequently we should check for all projectile's alive time") * 20L;
}

@Override
Expand Down Expand Up @@ -51,11 +54,9 @@ public void disable() {
private void run() {
for (World world : plugin.getServer().getWorlds()) {
for (FallingBlock fallingBlock : world.getEntitiesByClass(FallingBlock.class)) {
plugin.getServer().getRegionScheduler().run(plugin, fallingBlock.getLocation(), task -> {
if (fallingBlock.getTicksLived() > max_alive_time) {
fallingBlock.remove();
}
});
if (fallingBlock.getTicksLived() > max_alive_time) {
fallingBlock.getScheduler().run(plugin, delete -> fallingBlock.remove(), null);
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,37 +1,41 @@
package me.moomoo.anarchyexploitfixes.modules.lagpreventions;

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import me.moomoo.anarchyexploitfixes.AnarchyExploitFixes;
import me.moomoo.anarchyexploitfixes.config.Config;
import me.moomoo.anarchyexploitfixes.modules.AnarchyExploitFixesModule;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
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;
import org.bukkit.event.player.PlayerQuitEvent;

import java.util.HashMap;
import java.time.Duration;
import java.util.UUID;

public class LeverSpam implements AnarchyExploitFixesModule, Listener {

private final HashMap<UUID, Long> cooldowns = new HashMap<>();
private final HashMap<UUID, Integer> leverCounts = new HashMap<>();
private final boolean shouldKickPlayer, sendActionBar;
private final Cache<Location, Integer> leverLocationCooldowns;
private final Cache<UUID, Integer> playersUsingLeversCooldowns;
private final int leverUsageLimit;
private final long leverCooldownInMillis;
private final boolean shouldKickPlayer, sendActionBar;

public LeverSpam() {
shouldEnable();
Config config = AnarchyExploitFixes.getConfiguration();
config.addComment("lag-preventions.prevent-lever-spam.enable", "Rate Limit levers to prevent a lag exploit.");
this.sendActionBar = config.getBoolean("lag-preventions.prevent-lever-spam.show-actionbar", true);
this.shouldKickPlayer = config.getBoolean("lag-preventions.prevent-lever-spam.kick-player", false);
this.leverUsageLimit = config.getInt("lag-preventions.prevent-lever-spam.max-lever-usages-per-time", 10);
this.leverCooldownInMillis = config.getInt("lag-preventions.prevent-lever-spam.lever-time-in-ticks", 180) * 50L;
this.leverUsageLimit = config.getInt("lag-preventions.prevent-lever-spam.max-lever-usages-per-time", 15);
final long cacheTimeMillis = config.getInt("lag-preventions.prevent-lever-spam.lever-time-in-ticks", 40) * 50L;
this.leverLocationCooldowns = Caffeine.newBuilder().expireAfterWrite(Duration.ofMillis(cacheTimeMillis)).build();
this.playersUsingLeversCooldowns = Caffeine.newBuilder().expireAfterWrite(Duration.ofMillis(cacheTimeMillis)).build();
}

@Override
Expand Down Expand Up @@ -60,46 +64,53 @@ public void disable() {
HandlerList.unregisterAll(this);
}

@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
private void onInteract(PlayerInteractEvent event) {
if (!event.getAction().equals(Action.RIGHT_CLICK_BLOCK)) return;
if (!event.getClickedBlock().getType().equals(Material.LEVER)) return;
Block clicked = event.getClickedBlock();
if (!clicked.getType().equals(Material.LEVER)) return;

Player player = event.getPlayer();
final UUID playerUniqueId = player.getUniqueId();
final long currentTime = System.currentTimeMillis();

if (!leverCounts.containsKey(playerUniqueId) || !cooldowns.containsKey(playerUniqueId)) {
leverCounts.put(playerUniqueId, 1);
cooldowns.put(playerUniqueId, currentTime);
return;
}
final Player player = event.getPlayer();

int leverUseCount = leverCounts.get(playerUniqueId);
final Location leverLoc = clicked.getLocation();
Integer activationCount = leverLocationCooldowns.getIfPresent(leverLoc);

if (leverUseCount >= leverUsageLimit) {
if (currentTime - cooldowns.get(playerUniqueId) < leverCooldownInMillis) {
if (activationCount == null) {
leverLocationCooldowns.put(leverLoc, 1);
} else {
activationCount++;
leverLocationCooldowns.put(leverLoc, activationCount);
if (activationCount > leverUsageLimit) {
event.setCancelled(true);
if (sendActionBar)
player.sendActionBar(AnarchyExploitFixes.getLang(player.locale()).lagpreventions_stopSpammingLevers);
if (shouldKickPlayer)
if (shouldKickPlayer) {
player.kick(AnarchyExploitFixes.getLang(player.locale()).lagpreventions_stopSpammingLevers);
return;
}
if (sendActionBar) {
player.sendActionBar(AnarchyExploitFixes.getLang(player.locale()).lagpreventions_stopSpammingLevers);
}
return;
}

leverUseCount = 1;
}

leverUseCount++;

leverCounts.put(playerUniqueId, leverUseCount);
cooldowns.put(playerUniqueId, currentTime);
}

@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
private void onPlayerQuit(PlayerQuitEvent event) {
final UUID playerUniqueId = event.getPlayer().getUniqueId();
cooldowns.remove(playerUniqueId);
leverCounts.remove(playerUniqueId);
final UUID playerUniqueId = player.getUniqueId();
Integer leverFlickCount = playersUsingLeversCooldowns.getIfPresent(playerUniqueId);

if (leverFlickCount == null) {
playersUsingLeversCooldowns.put(playerUniqueId, 1);
} else {
leverFlickCount++;
playersUsingLeversCooldowns.put(playerUniqueId, leverFlickCount);
if (leverFlickCount > leverUsageLimit) {
event.setCancelled(true);
if (shouldKickPlayer) {
player.kick(AnarchyExploitFixes.getLang(player.locale()).lagpreventions_stopSpammingLevers);
return;
}
if (sendActionBar) {
player.sendActionBar(AnarchyExploitFixes.getLang(player.locale()).lagpreventions_stopSpammingLevers);
}
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package me.moomoo.anarchyexploitfixes.modules.lagpreventions;

import io.papermc.paper.threadedregions.scheduler.ScheduledTask;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import me.moomoo.anarchyexploitfixes.AnarchyExploitFixes;
import me.moomoo.anarchyexploitfixes.config.Config;
import me.moomoo.anarchyexploitfixes.modules.AnarchyExploitFixesModule;
Expand All @@ -13,25 +14,25 @@
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockFromToEvent;
import org.bukkit.event.world.ChunkUnloadEvent;

import java.util.HashMap;
import java.time.Duration;
import java.util.logging.Level;

public class LiquidUpdateLag implements AnarchyExploitFixesModule, Listener {

private ScheduledTask scheduledTask;
private final HashMap<String, Long> cooldowns = new HashMap<>();
private final HashMap<String, Integer> liquidEventCounts = new HashMap<>();
private final Cache<Chunk, Integer> liquidSpreadEventCountCache;
private final boolean logIsEnabled;
private final int maxLiquidSpreadEventsPerChunk;
private final long liquidSpreadCooldownInMillis;

public LiquidUpdateLag() {
shouldEnable();
Config config = AnarchyExploitFixes.getConfiguration();
this.maxLiquidSpreadEventsPerChunk = config.getInt("lag-preventions.prevent-liquid-update-lag.max-liquid-events-in-same-chunk-per-time", 1200, "amount of liquid source blocks * sides it can spread to * block spread length");
this.liquidSpreadCooldownInMillis = config.getInt("lag-preventions.prevent-liquid-update-lag.time-in-ticks", 100, "Record time after first liquid spread. \nWhen this time runs out, the spread counter resets") * 50L;
this.maxLiquidSpreadEventsPerChunk = config.getInt("lag-preventions.prevent-liquid-update-lag.max-liquid-events-in-same-chunk-per-time", 1200,
"amount of liquid source blocks * sides it can spread to * block spread length");
this.liquidSpreadEventCountCache = Caffeine.newBuilder().expireAfterWrite(Duration.ofMillis(
config.getInt("lag-preventions.prevent-liquid-update-lag.time-in-ticks", 100,
"Record time after first liquid spread. \nWhen this time runs out, the spread counter resets") * 50L
)).build();
this.logIsEnabled = config.getBoolean("lag-preventions.prevent-liquid-update-lag.log", false, "very spammy, use for testing only");
}

Expand All @@ -49,10 +50,6 @@ public String category() {
public void enable() {
AnarchyExploitFixes plugin = AnarchyExploitFixes.getInstance();
plugin.getServer().getPluginManager().registerEvents(this, plugin);
this.scheduledTask = plugin.getServer().getGlobalRegionScheduler().runAtFixedRate(plugin, task -> {
cooldowns.clear();
liquidEventCounts.clear();
}, 2000L, 2000L);
}

@Override
Expand All @@ -62,48 +59,32 @@ public boolean shouldEnable() {

@Override
public void disable() {
if (scheduledTask != null) scheduledTask.cancel();
HandlerList.unregisterAll(this);
}

@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
private void onLiquidSpread(BlockFromToEvent event) {
Block sourceBlock = event.getBlock();
final Block sourceBlock = event.getBlock();
if (sourceBlock.getType().equals(Material.DRAGON_EGG)) return; // Event fires only for liquids and the dragon egg

final Chunk chunk = sourceBlock.getChunk();
final String chunkId = String.valueOf(chunk.getWorld().getUID()) + chunk.getChunkKey();
final long currentTime = System.currentTimeMillis();

if (!liquidEventCounts.containsKey(chunkId) || !cooldowns.containsKey(chunkId)) {
liquidEventCounts.put(chunkId, 1);
cooldowns.put(chunkId, currentTime);
return;
}

int liquidEventCount = liquidEventCounts.get(chunkId);

if (liquidEventCount >= maxLiquidSpreadEventsPerChunk) {
if (currentTime - cooldowns.get(chunkId) < liquidSpreadCooldownInMillis) {
Integer liquidSpreadCount = liquidSpreadEventCountCache.getIfPresent(chunk);

if (liquidSpreadCount == null) {
liquidSpreadEventCountCache.put(chunk, 1);
} else {
liquidSpreadCount++;
liquidSpreadEventCountCache.put(chunk, liquidSpreadCount);
if (liquidSpreadCount > maxLiquidSpreadEventsPerChunk) {
event.setCancelled(true);
if (logIsEnabled) LogUtils.moduleLog(Level.INFO, name(), "Cancelled liquid events for chunk x:" + chunk.getX() + ", z:" + chunk.getZ() + "in world: " + chunk.getWorld().getName());
if (logIsEnabled) LogUtils.moduleLog(Level.INFO, name(),
"Cancelled liquid events for chunk x:" + chunk.getX() + ", z:" + chunk.getZ() +
"in world: " + chunk.getWorld().getName());
return;
}

liquidEventCount = 1;
}

liquidEventCount++;
if (logIsEnabled) LogUtils.moduleLog(Level.INFO, name(), " Recorded " + liquidEventCount + " liquid updates in chunk x:" + chunk.getX() + ", z:" + chunk.getZ() + ", world: " + chunk.getWorld().getName());

liquidEventCounts.put(chunkId, liquidEventCount);
cooldowns.put(chunkId, currentTime);
}

@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
private void onChunkUnload(ChunkUnloadEvent event) {
final String chunkId = String.valueOf(event.getChunk().getWorld().getUID()) + event.getChunk().getChunkKey();
liquidEventCounts.remove(chunkId);
cooldowns.remove(chunkId);
if (logIsEnabled) LogUtils.moduleLog(Level.INFO, name(), " Recorded " + liquidSpreadCount +
" liquid updates in chunk x:" + chunk.getX() + ", z:" + chunk.getZ() + ", world: " + chunk.getWorld().getName());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public boolean shouldEnable() {
return AnarchyExploitFixes.getConfiguration().getBoolean("lag-preventions.prevent-flooding-machines.enable", false);
}

@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
private void onPistonExtend(BlockPistonExtendEvent event) {
for (Block block : event.getBlocks()) {
if (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,18 +51,17 @@ public void disable() {
private void run() {
for (World world : plugin.getServer().getWorlds()) {
for (Projectile projectile : world.getEntitiesByClass(Projectile.class)) {
projectile.getScheduler().run(plugin, removeOld -> {
if (
projectile instanceof EnderPearl
|| projectile instanceof WitherSkull
|| projectile instanceof FishHook
|| projectile instanceof EnderSignal
) return;
final EntityType projectileType = projectile.getType();
if (
projectileType.equals(EntityType.ENDER_PEARL)
|| projectileType.equals(EntityType.WITHER_SKULL)
|| projectileType.equals(EntityType.FISHING_HOOK)
|| projectileType.equals(EntityType.ENDER_SIGNAL)
) continue;

if (projectile.getTicksLived() > max_alive_time) {
projectile.remove();
}
}, null);
if (projectile.getTicksLived() > max_alive_time) {
projectile.getScheduler().run(plugin, removeOld -> projectile.remove(), null);
}
}
}
}
Expand Down
Loading

0 comments on commit 3b4b159

Please sign in to comment.