Improved performance of BlockPreDispenseEvent

This commit is contained in:
Intelli 2025-03-10 19:39:33 -06:00
parent 4803b1af23
commit 484614f71e
4 changed files with 161 additions and 2 deletions

View file

@ -109,6 +109,8 @@ public class ConfigHandler extends Queue {
public static ConcurrentHashMap<String, List<ItemStack>> itemsBuy = new ConcurrentHashMap<>(); public static ConcurrentHashMap<String, List<ItemStack>> itemsBuy = new ConcurrentHashMap<>();
public static ConcurrentHashMap<String, Object[]> hopperAbort = new ConcurrentHashMap<>(); public static ConcurrentHashMap<String, Object[]> hopperAbort = new ConcurrentHashMap<>();
public static ConcurrentHashMap<String, Object[]> hopperSuccess = new ConcurrentHashMap<>(); public static ConcurrentHashMap<String, Object[]> hopperSuccess = new ConcurrentHashMap<>();
public static ConcurrentHashMap<String, ConcurrentHashMap<String, Long>> dispenserNoChange = new ConcurrentHashMap<>();
public static ConcurrentHashMap<String, Object[]> dispenserPending = new ConcurrentHashMap<>();
public static Map<String, List<ItemStack[]>> forceContainer = syncMap(); public static Map<String, List<ItemStack[]>> forceContainer = syncMap();
public static Map<String, Integer> lookupType = syncMap(); public static Map<String, Integer> lookupType = syncMap();
public static Map<String, Object[]> lookupThrottle = syncMap(); public static Map<String, Object[]> lookupThrottle = syncMap();

View file

@ -6,6 +6,7 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
@ -65,6 +66,52 @@ public class ContainerLogger extends Queue {
return; return;
} }
// Check if this is a dispenser with no actual changes
if (player.equals("#dispenser") && 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
String[] parts = loggingContainerId.split("\\.");
if (parts.length >= 4) {
int x = Integer.parseInt(parts[1]);
int y = Integer.parseInt(parts[2]);
int z = Integer.parseInt(parts[3]);
// Create the location key
String locationKey = location.getWorld().getUID().toString() + "." + x + "." + y + "." + z;
// Check if we have pending event details for this dispenser
Object[] pendingEvent = ConfigHandler.dispenserPending.remove(locationKey);
if (pendingEvent != null) {
// We have the exact event details, use them to mark this event as unchanged
String eventKey = (String) pendingEvent[0];
// Get or create the inner map for this location
ConfigHandler.dispenserNoChange.computeIfAbsent(locationKey, k -> new ConcurrentHashMap<>()).put(eventKey, System.currentTimeMillis());
}
}
return;
}
// If we reach here, the dispenser event resulted in changes
// Remove any pending event for this dispenser
if (player.equals("#dispenser")) {
String[] parts = loggingContainerId.split("\\.");
if (parts.length >= 4) {
int x = Integer.parseInt(parts[1]);
int y = Integer.parseInt(parts[2]);
int z = Integer.parseInt(parts[3]);
String locationKey = location.getWorld().getUID().toString() + "." + x + "." + y + "." + z;
// Remove the pending event since it resulted in changes
ConfigHandler.dispenserPending.remove(locationKey);
// Clear any existing dispenserNoChange entries for this location
ConfigHandler.dispenserNoChange.remove(locationKey);
}
}
List<ItemStack[]> forceList = ConfigHandler.forceContainer.get(loggingContainerId); List<ItemStack[]> forceList = ConfigHandler.forceContainer.get(loggingContainerId);
if (forceList != null) { if (forceList != null) {
int forceSize = 0; int forceSize = 0;

View file

@ -1,5 +1,7 @@
package net.coreprotect.paper.listener; package net.coreprotect.paper.listener;
import java.util.concurrent.ConcurrentHashMap;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.block.Block; import org.bukkit.block.Block;
@ -13,6 +15,7 @@ import org.bukkit.inventory.ItemStack;
import io.papermc.paper.event.block.BlockPreDispenseEvent; import io.papermc.paper.event.block.BlockPreDispenseEvent;
import net.coreprotect.config.Config; import net.coreprotect.config.Config;
import net.coreprotect.config.ConfigHandler;
import net.coreprotect.consumer.Queue; import net.coreprotect.consumer.Queue;
import net.coreprotect.listener.player.InventoryChangeListener; import net.coreprotect.listener.player.InventoryChangeListener;
@ -21,6 +24,9 @@ public final class BlockPreDispenseListener extends Queue implements Listener {
public static boolean useBlockPreDispenseEvent = true; public static boolean useBlockPreDispenseEvent = true;
public static boolean useForDroppers = false; public static boolean useForDroppers = false;
// Maximum time to keep entries in the cache (in milliseconds)
private static final long CACHE_EXPIRY_TIME = 5000; // 5 seconds
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onBlockPreDispense(BlockPreDispenseEvent event) { public void onBlockPreDispense(BlockPreDispenseEvent event) {
Block block = event.getBlock(); Block block = event.getBlock();
@ -35,10 +41,57 @@ public final class BlockPreDispenseListener extends Queue implements Listener {
useForDroppers = true; useForDroppers = true;
} }
// Safeguard against null items
ItemStack item = event.getItemStack();
if (item == null) {
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
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)
});
// Process the inventory transaction
String user = "#dispenser"; String user = "#dispenser";
ItemStack[] inventory = ((InventoryHolder) block.getState()).getInventory().getStorageContents(); ItemStack[] inventory = ((InventoryHolder) block.getState()).getInventory().getStorageContents();
InventoryChangeListener.inventoryTransaction(user, block.getLocation(), inventory); InventoryChangeListener.inventoryTransaction(user, block.getLocation(), inventory);
} }
} }
} }

View file

@ -26,7 +26,7 @@ public class CacheHandler implements Runnable {
public void run() { public void run() {
while (ConfigHandler.serverRunning) { while (ConfigHandler.serverRunning) {
try { try {
for (int id = 0; id < 8; id++) { for (int id = 0; id < 9; id++) {
Thread.sleep(1000); Thread.sleep(1000);
int scanTime = 30; int scanTime = 30;
Map cache = CacheHandler.lookupCache; Map cache = CacheHandler.lookupCache;
@ -59,6 +59,10 @@ public class CacheHandler implements Runnable {
cache = ConfigHandler.entityBlockMapper; cache = ConfigHandler.entityBlockMapper;
scanTime = 5; scanTime = 5;
break; break;
case 8:
// Clean up dispenserNoChange cache
cleanupDispenserCache();
continue;
} }
int timestamp = (int) (System.currentTimeMillis() / 1000L) - scanTime; int timestamp = (int) (System.currentTimeMillis() / 1000L) - scanTime;
@ -88,4 +92,57 @@ public class CacheHandler implements Runnable {
} }
} }
} }
/**
* Cleans up the dispenserNoChange cache by removing entries older than 5 seconds
*/
private void cleanupDispenserCache() {
try {
long currentTime = System.currentTimeMillis();
long expiryTime = 5000; // 5 seconds
// Clean up dispenserNoChange map (now a nested map)
Iterator<Entry<String, ConcurrentHashMap<String, Long>>> locationIterator = ConfigHandler.dispenserNoChange.entrySet().iterator();
while (locationIterator.hasNext()) {
Entry<String, ConcurrentHashMap<String, Long>> locationEntry = locationIterator.next();
ConcurrentHashMap<String, Long> eventMap = locationEntry.getValue();
if (eventMap.isEmpty()) {
// Remove empty location entries
locationIterator.remove();
continue;
}
// Clean up expired events within this location
Iterator<Entry<String, Long>> eventIterator = eventMap.entrySet().iterator();
while (eventIterator.hasNext()) {
Entry<String, Long> eventEntry = eventIterator.next();
if ((currentTime - eventEntry.getValue()) > expiryTime) {
eventIterator.remove();
}
}
// If all events were removed, remove the location entry
if (eventMap.isEmpty()) {
locationIterator.remove();
}
}
// Clean up dispenserPending map
Iterator<Entry<String, Object[]>> pendingIterator = ConfigHandler.dispenserPending.entrySet().iterator();
while (pendingIterator.hasNext()) {
Entry<String, Object[]> entry = pendingIterator.next();
Object[] data = entry.getValue();
if (data != null && data.length > 1) {
long timestamp = (long) data[1];
if ((currentTime - timestamp) > expiryTime) {
pendingIterator.remove();
}
}
}
}
catch (Exception e) {
e.printStackTrace();
}
}
} }