forked from github-mirror/CoreProtect
Extended de-dupe logic to include droppers/dispensers/fluids/kills
This commit is contained in:
1 parent
c706cf5b18
commit
bd449d1b89
10 files changed
+254
-37
No files matched your search
@@ -46,6 +46,7 @@ public class Config extends Language {
|
||||
public boolean DATABASE_LOCK;
|
||||
public boolean LOG_CANCELLED_CHAT;
|
||||
public boolean HOPPER_FILTER_META;
|
||||
public boolean DUPLICATE_SUPPRESSION;
|
||||
public boolean EXCLUDE_TNT;
|
||||
public boolean NETWORK_DEBUG;
|
||||
public boolean MYSQL;
|
||||
@@ -193,6 +194,7 @@ public class Config extends Language {
|
||||
this.DATABASE_LOCK = this.getBoolean("database-lock", true);
|
||||
this.LOG_CANCELLED_CHAT = this.getBoolean("log-cancelled-chat", true);
|
||||
this.HOPPER_FILTER_META = this.getBoolean("hopper-filter-meta", false);
|
||||
this.DUPLICATE_SUPPRESSION = this.getBoolean("duplicate-suppression", true);
|
||||
this.EXCLUDE_TNT = this.getBoolean("exclude-tnt", false);
|
||||
this.NETWORK_DEBUG = this.getBoolean("network-debug", false);
|
||||
this.UNKNOWN_LOGGING = this.getBoolean("unknown-logging", false);
|
||||
|
||||
@@ -3,9 +3,11 @@ package net.coreprotect.database.logger;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
@@ -22,6 +24,7 @@ import net.coreprotect.consumer.Queue;
|
||||
import net.coreprotect.database.statement.ContainerStatement;
|
||||
import net.coreprotect.database.statement.UserStatement;
|
||||
import net.coreprotect.event.CoreProtectPreLogEvent;
|
||||
import net.coreprotect.thread.CacheHandler;
|
||||
import net.coreprotect.utility.BlockUtils;
|
||||
import net.coreprotect.utility.ItemUtils;
|
||||
import net.coreprotect.utility.MaterialUtils;
|
||||
@@ -30,6 +33,10 @@ import net.coreprotect.utility.serialize.ItemMetaHandler;
|
||||
|
||||
public class ContainerLogger extends Queue {
|
||||
|
||||
private static final int CONTAINER_DUPLICATE_THRESHOLD_DROPPER = 512;
|
||||
private static final int CONTAINER_DUPLICATE_THRESHOLD_DISPENSER = 256;
|
||||
private static final int CONTAINER_DUPLICATE_WINDOW_SECONDS = 1200;
|
||||
|
||||
private ContainerLogger() {
|
||||
throw new IllegalStateException("Database class");
|
||||
}
|
||||
@@ -65,9 +72,10 @@ public class ContainerLogger extends Queue {
|
||||
if (oldInventory == null || newInventory == null) {
|
||||
return;
|
||||
}
|
||||
boolean duplicateSuppression = Config.getConfig(location.getWorld()).DUPLICATE_SUPPRESSION;
|
||||
|
||||
// Check if this is a dispenser with no actual changes
|
||||
if ("#dispenser".equals(player) && ItemUtils.compareContainers(oldInventory, newInventory)) {
|
||||
if (duplicateSuppression && "#dispenser".equals(player) && ItemUtils.compareContainers(oldInventory, newInventory)) {
|
||||
// No changes detected, mark this dispenser in the dispenserNoChange map
|
||||
// Extract the location key from the loggingContainerId
|
||||
// Format: #dispenser.x.y.z
|
||||
@@ -95,7 +103,7 @@ public class ContainerLogger extends Queue {
|
||||
|
||||
// If we reach here, the dispenser event resulted in changes
|
||||
// Remove any pending event for this dispenser
|
||||
if ("#dispenser".equals(player)) {
|
||||
if (duplicateSuppression && "#dispenser".equals(player)) {
|
||||
String[] parts = loggingContainerId.split("\\.");
|
||||
if (parts.length >= 4) {
|
||||
int x = Integer.parseInt(parts[1]);
|
||||
@@ -166,6 +174,17 @@ public class ContainerLogger extends Queue {
|
||||
}
|
||||
}
|
||||
|
||||
if (duplicateSuppression && shouldSuppressContainerDuplicate(player, location, oldInventory, newInventory)) {
|
||||
oldList.remove(0);
|
||||
ConfigHandler.oldContainer.put(loggingContainerId, oldList);
|
||||
if ("#hopper".equals(player)) {
|
||||
String hopperPush = "#hopper-push." + location.getBlockX() + "." + location.getBlockY() + "." + location.getBlockZ();
|
||||
ConfigHandler.hopperSuccess.remove(hopperPush);
|
||||
ConfigHandler.hopperAbort.remove(hopperPush);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for (ItemStack oldi : oldInventory) {
|
||||
for (ItemStack newi : newInventory) {
|
||||
if (oldi != null && newi != null) {
|
||||
@@ -265,4 +284,65 @@ public class ContainerLogger extends Queue {
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean shouldSuppressContainerDuplicate(String user, Location location, ItemStack[] oldInventory, ItemStack[] newInventory) {
|
||||
if (!"#dropper".equals(user) && !"#dispenser".equals(user)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String oldSignature = getContainerStateSignature(oldInventory);
|
||||
String newSignature = getContainerStateSignature(newInventory);
|
||||
String transitionSignature = oldSignature + ">" + newSignature;
|
||||
if ("#dispenser".equals(user)) {
|
||||
if (oldSignature.compareTo(newSignature) <= 0) {
|
||||
transitionSignature = oldSignature + "<>" + newSignature;
|
||||
}
|
||||
else {
|
||||
transitionSignature = newSignature + "<>" + oldSignature;
|
||||
}
|
||||
}
|
||||
|
||||
int threshold = getContainerDuplicateThreshold(user);
|
||||
String signature = location.getWorld().getUID().toString() + "." + location.getBlockX() + "." + location.getBlockY() + "." + location.getBlockZ() + "." + user + "." + transitionSignature;
|
||||
return CacheHandler.shouldSuppressRepeat(CacheHandler.containerDuplicateCache, signature, threshold, CONTAINER_DUPLICATE_WINDOW_SECONDS);
|
||||
}
|
||||
|
||||
private static int getContainerDuplicateThreshold(String user) {
|
||||
if ("#dispenser".equals(user)) {
|
||||
return CONTAINER_DUPLICATE_THRESHOLD_DISPENSER;
|
||||
}
|
||||
return CONTAINER_DUPLICATE_THRESHOLD_DROPPER;
|
||||
}
|
||||
|
||||
private static String getContainerStateSignature(ItemStack[] inventory) {
|
||||
if (inventory == null || inventory.length == 0) {
|
||||
return "-";
|
||||
}
|
||||
|
||||
Map<String, Integer> itemCounts = new HashMap<>();
|
||||
for (ItemStack itemStack : inventory) {
|
||||
if (itemStack == null || itemStack.getAmount() <= 0 || BlockUtils.isAir(itemStack.getType())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ItemStack normalized = itemStack.clone();
|
||||
normalized.setAmount(1);
|
||||
String key = normalized.toString();
|
||||
itemCounts.merge(key, itemStack.getAmount(), Integer::sum);
|
||||
}
|
||||
|
||||
if (itemCounts.isEmpty()) {
|
||||
return "-";
|
||||
}
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (Map.Entry<String, Integer> entry : new TreeMap<>(itemCounts).entrySet()) {
|
||||
if (builder.length() > 0) {
|
||||
builder.append('|');
|
||||
}
|
||||
builder.append(entry.getKey()).append('*').append(entry.getValue());
|
||||
}
|
||||
|
||||
return Integer.toHexString(builder.toString().hashCode());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -28,11 +28,15 @@ import net.coreprotect.thread.CacheHandler;
|
||||
|
||||
public final class BlockDispenseListener extends Queue implements Listener {
|
||||
|
||||
private static final int DISPENSER_LIQUID_DUPLICATE_THRESHOLD = 256;
|
||||
private static final int DISPENSER_LIQUID_DUPLICATE_WINDOW_SECONDS = 1200;
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
protected void onBlockDispense(BlockDispenseEvent event) {
|
||||
Block block = event.getBlock();
|
||||
World world = block.getWorld();
|
||||
if (!event.isCancelled() && Config.getConfig(world).BLOCK_PLACE) {
|
||||
Config config = Config.getConfig(world);
|
||||
if (!event.isCancelled() && config.BLOCK_PLACE) {
|
||||
BlockData blockData = block.getBlockData();
|
||||
ItemStack item = event.getItem();
|
||||
if (item != null && blockData instanceof Dispenser) {
|
||||
@@ -67,11 +71,9 @@ public final class BlockDispenseListener extends Queue implements Listener {
|
||||
|
||||
if (material.equals(Material.WATER_BUCKET)) {
|
||||
type = Material.WATER;
|
||||
user = "#water";
|
||||
}
|
||||
else if (material.equals(Material.LAVA_BUCKET)) {
|
||||
type = Material.LAVA;
|
||||
user = "#lava";
|
||||
}
|
||||
else if (material.equals(Material.FLINT_AND_STEEL)) {
|
||||
type = Material.FIRE;
|
||||
@@ -85,10 +87,10 @@ public final class BlockDispenseListener extends Queue implements Listener {
|
||||
CacheHandler.redstoneCache.put(newBlock.getLocation(), new Object[] { System.currentTimeMillis(), user });
|
||||
}
|
||||
|
||||
if (type == Material.FIRE && (!Config.getConfig(world).BLOCK_IGNITE || !(newBlockData instanceof Lightable))) {
|
||||
if (type == Material.FIRE && (!config.BLOCK_IGNITE || !(newBlockData instanceof Lightable))) {
|
||||
return;
|
||||
}
|
||||
else if (type != Material.FIRE && (!Config.getConfig(world).BUCKETS || (!Config.getConfig(world).WATER_FLOW && type.equals(Material.WATER)) || (!Config.getConfig(world).LAVA_FLOW && type.equals(Material.LAVA)))) {
|
||||
else if (type != Material.FIRE && (!config.BUCKETS || (!config.WATER_FLOW && type.equals(Material.WATER)) || (!config.LAVA_FLOW && type.equals(Material.LAVA)))) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -104,6 +106,9 @@ public final class BlockDispenseListener extends Queue implements Listener {
|
||||
}
|
||||
else if (dispenseRelative) {
|
||||
BlockState blockState = newBlock.getState();
|
||||
if (config.DUPLICATE_SUPPRESSION && shouldSuppressDispenseLiquidDuplicate(user, newBlock, type)) {
|
||||
return;
|
||||
}
|
||||
if (!type.equals(Material.AIR)) {
|
||||
queueBlockPlaceValidate(user, blockState, newBlock, blockState, type, 1, 1, null, 0);
|
||||
}
|
||||
@@ -116,4 +121,32 @@ public final class BlockDispenseListener extends Queue implements Listener {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean shouldSuppressDispenseLiquidDuplicate(String user, Block targetBlock, Material newType) {
|
||||
Material oldType = targetBlock.getType();
|
||||
boolean placeLiquid = ("#water".equals(user) || "#lava".equals(user) || "#dispenser".equals(user)) && (newType == Material.WATER || newType == Material.LAVA);
|
||||
boolean removeLiquid = "#dispenser".equals(user) && newType == Material.AIR && (oldType == Material.WATER || oldType == Material.LAVA);
|
||||
if (!placeLiquid && !removeLiquid) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isLiquidToggleType(oldType) || !isLiquidToggleType(newType) || oldType == newType) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String left = oldType.name();
|
||||
String right = newType.name();
|
||||
if (left.compareTo(right) > 0) {
|
||||
String swap = left;
|
||||
left = right;
|
||||
right = swap;
|
||||
}
|
||||
|
||||
String signature = targetBlock.getWorld().getUID().toString() + "." + targetBlock.getX() + "." + targetBlock.getY() + "." + targetBlock.getZ() + ".#dispense-liquid." + left + "<>" + right;
|
||||
return CacheHandler.shouldSuppressRepeat(CacheHandler.flowDuplicateCache, signature, DISPENSER_LIQUID_DUPLICATE_THRESHOLD, DISPENSER_LIQUID_DUPLICATE_WINDOW_SECONDS);
|
||||
}
|
||||
|
||||
private boolean isLiquidToggleType(Material type) {
|
||||
return type == Material.AIR || type == Material.WATER || type == Material.LAVA;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
package net.coreprotect.listener.block;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
@@ -20,6 +22,9 @@ import net.coreprotect.thread.CacheHandler;
|
||||
|
||||
public final class BlockFertilizeListener extends Queue implements Listener {
|
||||
|
||||
private static final int BONEMEAL_DUPLICATE_THRESHOLD = 256;
|
||||
private static final int BONEMEAL_DUPLICATE_WINDOW_SECONDS = 900;
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
protected void onBlockFertilize(BlockFertilizeEvent event) {
|
||||
if (event.isCancelled()) {
|
||||
@@ -66,6 +71,10 @@ public final class BlockFertilizeListener extends Queue implements Listener {
|
||||
}
|
||||
}
|
||||
|
||||
if (config.DUPLICATE_SUPPRESSION && "#dispenser".equals(user) && shouldSuppressBonemealDuplicate(location, blocks)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (BlockState newBlock : blocks) {
|
||||
Queue.queueBlockPlace(user, newBlock, newBlock.getType(), newBlock.getBlock().getState(), newBlock.getType(), -1, 0, newBlock.getBlockData().getAsString());
|
||||
}
|
||||
@@ -75,4 +84,19 @@ public final class BlockFertilizeListener extends Queue implements Listener {
|
||||
return blockType == Material.CRIMSON_FUNGUS || blockType == Material.WARPED_FUNGUS || blockType.name().toLowerCase(Locale.ROOT).contains("mushroom");
|
||||
}
|
||||
|
||||
private boolean shouldSuppressBonemealDuplicate(Location location, List<BlockState> blocks) {
|
||||
if (blocks == null || blocks.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
List<String> states = new ArrayList<>();
|
||||
for (BlockState newBlock : blocks) {
|
||||
Location newLocation = newBlock.getLocation();
|
||||
states.add(newLocation.getBlockX() + "." + newLocation.getBlockY() + "." + newLocation.getBlockZ() + "." + newBlock.getType().name() + "." + newBlock.getBlockData().getAsString());
|
||||
}
|
||||
Collections.sort(states);
|
||||
String signature = location.getWorld().getUID().toString() + "." + location.getBlockX() + "." + location.getBlockY() + "." + location.getBlockZ() + "." + Integer.toHexString(String.join("|", states).hashCode());
|
||||
return CacheHandler.shouldSuppressRepeat(CacheHandler.bonemealDuplicateCache, signature, BONEMEAL_DUPLICATE_THRESHOLD, BONEMEAL_DUPLICATE_WINDOW_SECONDS);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -21,6 +21,9 @@ import net.coreprotect.utility.WorldUtils;
|
||||
|
||||
public final class BlockFromToListener extends Queue implements Listener {
|
||||
|
||||
private static final int FLOW_DUPLICATE_THRESHOLD = 512;
|
||||
private static final int FLOW_DUPLICATE_WINDOW_SECONDS = 900;
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
protected void onBlockFromTo(BlockFromToEvent event) {
|
||||
Block block = event.getBlock();
|
||||
@@ -36,7 +39,8 @@ public final class BlockFromToListener extends Queue implements Listener {
|
||||
}
|
||||
|
||||
World world = event.getBlock().getWorld();
|
||||
if ((Config.getConfig(world).WATER_FLOW && type.equals(Material.WATER)) || (Config.getConfig(world).LAVA_FLOW && type.equals(Material.LAVA))) {
|
||||
Config config = Config.getConfig(world);
|
||||
if ((config.WATER_FLOW && type.equals(Material.WATER)) || (config.LAVA_FLOW && type.equals(Material.LAVA))) {
|
||||
Block toBlock = event.getToBlock();
|
||||
BlockState toBlockState = toBlock.getState();
|
||||
|
||||
@@ -67,14 +71,14 @@ public final class BlockFromToListener extends Queue implements Listener {
|
||||
int y = toBlock.getY();
|
||||
int z = toBlock.getZ();
|
||||
int wid = WorldUtils.getWorldId(block.getWorld().getName());
|
||||
if (Config.getConfig(world).LIQUID_TRACKING) {
|
||||
if (config.LIQUID_TRACKING) {
|
||||
String p = Lookup.whoPlacedCache(block);
|
||||
if (p.length() > 0) {
|
||||
f = p;
|
||||
}
|
||||
}
|
||||
|
||||
if (f.startsWith("#")) {
|
||||
if (config.DUPLICATE_SUPPRESSION && f.startsWith("#")) {
|
||||
String cacheId = toBlock.getX() + "." + toBlock.getY() + "." + toBlock.getZ() + "." + WorldUtils.getWorldId(toBlock.getWorld().getName());
|
||||
int timestamp = (int) (System.currentTimeMillis() / 1000L);
|
||||
Object[] cacheData = CacheHandler.spreadCache.get(cacheId);
|
||||
@@ -84,6 +88,10 @@ public final class BlockFromToListener extends Queue implements Listener {
|
||||
}
|
||||
}
|
||||
|
||||
if (config.DUPLICATE_SUPPRESSION && shouldSuppressFlowDuplicate(f, block, toBlock, type, blockData, toBlockState)) {
|
||||
return;
|
||||
}
|
||||
|
||||
CacheHandler.lookupCache.put("" + x + "." + y + "." + z + "." + wid + "", new Object[] { unixtimestamp, f, type });
|
||||
Queue.queueBlockPlace(f, toBlock.getState(), block.getType(), toBlockState, type, -1, 0, blockData.getAsString());
|
||||
}
|
||||
@@ -123,4 +131,14 @@ public final class BlockFromToListener extends Queue implements Listener {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean shouldSuppressFlowDuplicate(String user, Block sourceBlock, Block targetBlock, Material liquidType, BlockData blockData, BlockState replacedState) {
|
||||
String replacedSignature = "-";
|
||||
if (replacedState != null) {
|
||||
replacedSignature = replacedState.getType().name() + ":" + replacedState.getBlockData().getAsString();
|
||||
}
|
||||
|
||||
String signature = sourceBlock.getWorld().getUID().toString() + "." + sourceBlock.getX() + "." + sourceBlock.getY() + "." + sourceBlock.getZ() + ">" + targetBlock.getX() + "." + targetBlock.getY() + "." + targetBlock.getZ() + "." + user + "." + liquidType.name() + "." + blockData.getAsString() + "." + replacedSignature;
|
||||
return CacheHandler.shouldSuppressRepeat(CacheHandler.flowDuplicateCache, signature, FLOW_DUPLICATE_THRESHOLD, FLOW_DUPLICATE_WINDOW_SECONDS);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -32,7 +32,8 @@ public final class BlockPistonListener extends Queue implements Listener {
|
||||
}
|
||||
|
||||
World world = event.getBlock().getWorld();
|
||||
if (Config.getConfig(world).PISTONS && !event.isCancelled()) {
|
||||
Config config = Config.getConfig(world);
|
||||
if (config.PISTONS && !event.isCancelled()) {
|
||||
List<Block> nblocks = new ArrayList<>();
|
||||
List<Block> blocks = new ArrayList<>();
|
||||
|
||||
@@ -48,6 +49,7 @@ public final class BlockPistonListener extends Queue implements Listener {
|
||||
int wid = WorldUtils.getWorldId(bm.getWorld().getName());
|
||||
|
||||
int unixtimestamp = (int) (System.currentTimeMillis() / 1000L);
|
||||
boolean duplicateSuppression = config.DUPLICATE_SUPPRESSION;
|
||||
int log = 0;
|
||||
int l = 0;
|
||||
while (l <= nblocks.size()) {
|
||||
@@ -65,11 +67,13 @@ public final class BlockPistonListener extends Queue implements Listener {
|
||||
int z = n.getZ();
|
||||
Material t = n.getType();
|
||||
String cords = "" + x + "." + y + "." + z + "." + wid + "." + t.name() + "";
|
||||
if (CacheHandler.pistonCache.get(cords) == null) {
|
||||
if (!duplicateSuppression || CacheHandler.pistonCache.get(cords) == null) {
|
||||
log = 1;
|
||||
}
|
||||
if (duplicateSuppression) {
|
||||
CacheHandler.pistonCache.put(cords, new Object[] { unixtimestamp });
|
||||
}
|
||||
}
|
||||
l++;
|
||||
}
|
||||
if (log == 1) {
|
||||
|
||||
@@ -35,10 +35,11 @@ public final class BlockSpreadListener extends Queue implements Listener {
|
||||
|
||||
BlockState blockstate = event.getNewState();
|
||||
Material type = blockstate.getType();
|
||||
Config config = Config.getConfig(event.getBlock().getWorld());
|
||||
|
||||
if (Config.getConfig(event.getBlock().getWorld()).VINE_GROWTH && (BlockGroup.VINES.contains(type) || BlockGroup.AMETHYST.contains(type) || type == Material.CHORUS_FLOWER || type == Material.BAMBOO)) {
|
||||
if (config.VINE_GROWTH && (BlockGroup.VINES.contains(type) || BlockGroup.AMETHYST.contains(type) || type == Material.CHORUS_FLOWER || type == Material.BAMBOO)) {
|
||||
Block block = event.getBlock();
|
||||
if (checkCacheData(block, type)) {
|
||||
if (config.DUPLICATE_SUPPRESSION && checkCacheData(block, type)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -72,9 +73,9 @@ public final class BlockSpreadListener extends Queue implements Listener {
|
||||
Queue.queueBlockPlaceDelayed("#bamboo", block.getLocation(), type, null, block.getState(), 0);
|
||||
}
|
||||
}
|
||||
else if (Config.getConfig(event.getBlock().getWorld()).SCULK_SPREAD && BlockGroup.SCULK.contains(type)) {
|
||||
else if (config.SCULK_SPREAD && BlockGroup.SCULK.contains(type)) {
|
||||
Block block = event.getBlock();
|
||||
if (checkCacheData(block, type)) {
|
||||
if (config.DUPLICATE_SUPPRESSION && checkCacheData(block, type)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -73,11 +73,15 @@ import net.coreprotect.CoreProtect;
|
||||
import net.coreprotect.bukkit.BukkitAdapter;
|
||||
import net.coreprotect.config.Config;
|
||||
import net.coreprotect.consumer.Queue;
|
||||
import net.coreprotect.thread.CacheHandler;
|
||||
import net.coreprotect.thread.Scheduler;
|
||||
import net.coreprotect.utility.serialize.ItemMetaHandler;
|
||||
|
||||
public final class EntityDeathListener extends Queue implements Listener {
|
||||
|
||||
private static final int ENTITY_KILL_DUPLICATE_THRESHOLD = 256;
|
||||
private static final int ENTITY_KILL_DUPLICATE_WINDOW_SECONDS = 900;
|
||||
|
||||
public static void parseEntityKills(String message) {
|
||||
message = message.trim().toLowerCase(Locale.ROOT);
|
||||
if (!message.contains(" ")) {
|
||||
@@ -258,6 +262,10 @@ public final class EntityDeathListener extends Queue implements Listener {
|
||||
e = "#lightning";
|
||||
}
|
||||
|
||||
if (Config.getConfig(entity.getWorld()).DUPLICATE_SUPPRESSION && shouldSuppressEntityKill(e, entity, cause)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.length() > 0) {
|
||||
List<Object> data = new ArrayList<>();
|
||||
List<Object> age = new ArrayList<>();
|
||||
@@ -552,6 +560,18 @@ public final class EntityDeathListener extends Queue implements Listener {
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean shouldSuppressEntityKill(String user, LivingEntity entity, DamageCause cause) {
|
||||
if (user == null || !user.startsWith("#") || "#command".equals(user)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Location location = entity.getLocation();
|
||||
String causeName = cause == null ? "UNKNOWN" : cause.name();
|
||||
String customName = entity.getCustomName();
|
||||
String signature = location.getWorld().getUID().toString() + "." + location.getBlockX() + "." + location.getBlockY() + "." + location.getBlockZ() + "." + user + "." + entity.getType().name() + "." + causeName + "." + (customName == null ? "-" : customName);
|
||||
return CacheHandler.shouldSuppressRepeat(CacheHandler.entityKillDuplicateCache, signature, ENTITY_KILL_DUPLICATE_THRESHOLD, ENTITY_KILL_DUPLICATE_WINDOW_SECONDS);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
public void onEntityDeath(EntityDeathEvent event) {
|
||||
/*
|
||||
|
||||
@@ -31,7 +31,8 @@ public final class BlockPreDispenseListener extends Queue implements Listener {
|
||||
public void onBlockPreDispense(BlockPreDispenseEvent event) {
|
||||
Block block = event.getBlock();
|
||||
World world = block.getWorld();
|
||||
if (!Config.getConfig(world).BLOCK_PLACE) {
|
||||
Config config = Config.getConfig(world);
|
||||
if (!config.BLOCK_PLACE) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -47,46 +48,34 @@ public final class BlockPreDispenseListener extends Queue implements Listener {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a basic location key for this dispenser
|
||||
String locationKey = block.getWorld().getUID().toString() + "." + block.getX() + "." + block.getY() + "." + block.getZ();
|
||||
|
||||
// Create a detailed event key that includes item details
|
||||
if (config.DUPLICATE_SUPPRESSION) {
|
||||
String eventKey = event.getSlot() + "." + item.getType().name() + ":" + item.getAmount();
|
||||
|
||||
// Add metadata hash if available
|
||||
if (item.hasItemMeta()) {
|
||||
try {
|
||||
eventKey += ":" + item.getItemMeta().hashCode();
|
||||
}
|
||||
catch (Exception e) {
|
||||
// If we can't get metadata hash, just use the basic key
|
||||
}
|
||||
}
|
||||
|
||||
// Get or create the inner map for this location
|
||||
ConcurrentHashMap<String, Long> locationMap = ConfigHandler.dispenserNoChange.computeIfAbsent(locationKey, k -> new ConcurrentHashMap<>());
|
||||
|
||||
// Check if this specific dispenser event has been marked as having no changes recently
|
||||
Long lastNoChangeTime = locationMap.get(eventKey);
|
||||
|
||||
long currentTime = System.currentTimeMillis();
|
||||
if (lastNoChangeTime != null && (currentTime - lastNoChangeTime) < CACHE_EXPIRY_TIME) {
|
||||
// This specific dispenser event was recently processed and had no changes
|
||||
// Update the timestamp to extend the skip period
|
||||
locationMap.put(eventKey, currentTime);
|
||||
return;
|
||||
}
|
||||
|
||||
// This is a new or changed event
|
||||
// Clear any existing dispenserNoChange entries for this location
|
||||
ConfigHandler.dispenserNoChange.remove(locationKey);
|
||||
|
||||
// Store the event details for ContainerLogger to use
|
||||
ConfigHandler.dispenserPending.put(locationKey, new Object[] { eventKey, // The detailed event key
|
||||
currentTime, // Timestamp
|
||||
event.getSlot(), // Slot
|
||||
item.clone() // Item (cloned to prevent modification)
|
||||
});
|
||||
ConfigHandler.dispenserPending.put(locationKey, new Object[] { eventKey, currentTime, event.getSlot(), item.clone() });
|
||||
}
|
||||
else {
|
||||
ConfigHandler.dispenserNoChange.remove(locationKey);
|
||||
ConfigHandler.dispenserPending.remove(locationKey);
|
||||
}
|
||||
|
||||
// Process the inventory transaction
|
||||
String user = "#dispenser";
|
||||
|
||||
@@ -19,14 +19,44 @@ public class CacheHandler implements Runnable {
|
||||
public static Map<String, Object[]> entityCache = Collections.synchronizedMap(new HashMap<>());
|
||||
public static ConcurrentHashMap<String, Object[]> pistonCache = new ConcurrentHashMap<>(16, 0.75f, 2);
|
||||
public static ConcurrentHashMap<String, Object[]> spreadCache = new ConcurrentHashMap<>(16, 0.75f, 2);
|
||||
public static ConcurrentHashMap<String, Object[]> containerDuplicateCache = new ConcurrentHashMap<>(16, 0.75f, 2);
|
||||
public static ConcurrentHashMap<String, Object[]> flowDuplicateCache = new ConcurrentHashMap<>(16, 0.75f, 2);
|
||||
public static ConcurrentHashMap<String, Object[]> bonemealDuplicateCache = new ConcurrentHashMap<>(16, 0.75f, 2);
|
||||
public static ConcurrentHashMap<String, Object[]> entityKillDuplicateCache = new ConcurrentHashMap<>(16, 0.75f, 2);
|
||||
public static ConcurrentHashMap<Location, Object[]> redstoneCache = new ConcurrentHashMap<>(16, 0.75f, 2);
|
||||
|
||||
public static boolean shouldSuppressRepeat(ConcurrentHashMap<String, Object[]> cache, String cacheId, int threshold, int resetWindowSeconds) {
|
||||
if (cache == null || cacheId == null || cacheId.isEmpty() || threshold <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final int timestamp = (int) (System.currentTimeMillis() / 1000L);
|
||||
Object[] result = cache.compute(cacheId, (key, value) -> {
|
||||
int count = 1;
|
||||
if (value != null) {
|
||||
int previousTime = (int) value[0];
|
||||
int previousCount = (int) value[1];
|
||||
if (timestamp - previousTime <= resetWindowSeconds) {
|
||||
count = previousCount + 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (count > (threshold + 1)) {
|
||||
count = threshold + 1;
|
||||
}
|
||||
|
||||
return new Object[] { timestamp, count };
|
||||
});
|
||||
|
||||
return ((int) result[1]) > threshold;
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
@Override
|
||||
public void run() {
|
||||
while (ConfigHandler.serverRunning) {
|
||||
try {
|
||||
for (int id = 0; id < 9; id++) {
|
||||
for (int id = 0; id < 13; id++) {
|
||||
Thread.sleep(1000);
|
||||
int scanTime = 30;
|
||||
Map cache = CacheHandler.lookupCache;
|
||||
@@ -63,6 +93,22 @@ public class CacheHandler implements Runnable {
|
||||
// Clean up dispenserNoChange cache
|
||||
cleanupDispenserCache();
|
||||
continue;
|
||||
case 9:
|
||||
cache = CacheHandler.containerDuplicateCache;
|
||||
scanTime = 1800; // 30 minutes
|
||||
break;
|
||||
case 10:
|
||||
cache = CacheHandler.flowDuplicateCache;
|
||||
scanTime = 900; // 15 minutes
|
||||
break;
|
||||
case 11:
|
||||
cache = CacheHandler.bonemealDuplicateCache;
|
||||
scanTime = 900; // 15 minutes
|
||||
break;
|
||||
case 12:
|
||||
cache = CacheHandler.entityKillDuplicateCache;
|
||||
scanTime = 900; // 15 minutes
|
||||
break;
|
||||
}
|
||||
|
||||
int timestamp = (int) (System.currentTimeMillis() / 1000L) - scanTime;
|
||||
|
||||
Reference in new issue
Block a user