Extended de-dupe logic to include droppers/dispensers/fluids/kills

This commit is contained in:
Intelli committed 2026-04-23 12:36:14 -06:00
1 parent c706cf5b18
commit bd449d1b89
10 files changed
+270 -53

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,10 +67,12 @@ 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;
}
CacheHandler.pistonCache.put(cords, new Object[] { unixtimestamp });
if (duplicateSuppression) {
CacheHandler.pistonCache.put(cords, new Object[] { unixtimestamp });
}
}
l++;
}
@@ -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,47 +48,35 @@ 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();
if (config.DUPLICATE_SUPPRESSION) {
String eventKey = event.getSlot() + "." + item.getType().name() + ":" + item.getAmount();
// Create a detailed event key that includes item details
String eventKey = event.getSlot() + "." + item.getType().name() + ":" + item.getAmount();
// Add metadata hash if available
if (item.hasItemMeta()) {
try {
eventKey += ":" + item.getItemMeta().hashCode();
if (item.hasItemMeta()) {
try {
eventKey += ":" + item.getItemMeta().hashCode();
}
catch (Exception e) {
}
}
catch (Exception e) {
// If we can't get metadata hash, just use the basic key
ConcurrentHashMap<String, Long> locationMap = ConfigHandler.dispenserNoChange.computeIfAbsent(locationKey, k -> new ConcurrentHashMap<>());
Long lastNoChangeTime = locationMap.get(eventKey);
long currentTime = System.currentTimeMillis();
if (lastNoChangeTime != null && (currentTime - lastNoChangeTime) < CACHE_EXPIRY_TIME) {
locationMap.put(eventKey, currentTime);
return;
}
ConfigHandler.dispenserNoChange.remove(locationKey);
ConfigHandler.dispenserPending.put(locationKey, new Object[] { eventKey, currentTime, event.getSlot(), item.clone() });
}
// 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;
else {
ConfigHandler.dispenserNoChange.remove(locationKey);
ConfigHandler.dispenserPending.remove(locationKey);
}
// 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)
});
// Process the inventory transaction
String user = "#dispenser";
ItemStack[] inventory = ((InventoryHolder) block.getState()).getInventory().getStorageContents();
@@ -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;