From 5494bce0fcde26384efba46d0c00a8fbec61f1ca Mon Sep 17 00:00:00 2001 From: Intelli Date: Thu, 24 Feb 2022 19:52:19 -0700 Subject: [PATCH] Added logging and rollback support for players crafting items --- .../coreprotect/command/LookupCommand.java | 5 + .../net/coreprotect/config/ConfigHandler.java | 3 + .../process/ItemTransactionProcess.java | 5 +- .../java/net/coreprotect/database/Lookup.java | 24 +++- .../net/coreprotect/database/Rollback.java | 4 +- .../database/logger/ItemLogger.java | 24 ++++ .../coreprotect/listener/ListenerHandler.java | 4 +- .../listener/player/CraftItemListener.java | 131 ++++++++++++++++++ 8 files changed, 195 insertions(+), 5 deletions(-) create mode 100644 src/main/java/net/coreprotect/listener/player/CraftItemListener.java diff --git a/src/main/java/net/coreprotect/command/LookupCommand.java b/src/main/java/net/coreprotect/command/LookupCommand.java index 7d81d50..474ea19 100755 --- a/src/main/java/net/coreprotect/command/LookupCommand.java +++ b/src/main/java/net/coreprotect/command/LookupCommand.java @@ -24,6 +24,7 @@ import net.coreprotect.config.Config; import net.coreprotect.config.ConfigHandler; import net.coreprotect.database.Database; import net.coreprotect.database.Lookup; +import net.coreprotect.database.logger.ItemLogger; import net.coreprotect.database.lookup.BlockLookup; import net.coreprotect.database.lookup.ChestTransactionLookup; import net.coreprotect.database.lookup.InteractionLookup; @@ -890,6 +891,10 @@ public class LookupCommand { selector = Selector.SECOND; tag = Color.RED + "-"; } + else if (daction == ItemLogger.ITEM_BREAK || daction == ItemLogger.ITEM_DESTROY || daction == ItemLogger.ITEM_CREATE) { + selector = (daction == ItemLogger.ITEM_CREATE ? Selector.FIRST : Selector.SECOND); + tag = (daction == ItemLogger.ITEM_CREATE ? Color.GREEN + "+" : Color.RED + "-"); + } else { // LOOKUP_CONTAINER selector = (daction == 0 ? Selector.FIRST : Selector.SECOND); tag = (daction == 0 ? Color.GREEN + "+" : Color.RED + "-"); diff --git a/src/main/java/net/coreprotect/config/ConfigHandler.java b/src/main/java/net/coreprotect/config/ConfigHandler.java index ddb60bb..d050ef1 100644 --- a/src/main/java/net/coreprotect/config/ConfigHandler.java +++ b/src/main/java/net/coreprotect/config/ConfigHandler.java @@ -92,6 +92,9 @@ public class ConfigHandler extends Queue { public static ConcurrentHashMap> itemsDrop = new ConcurrentHashMap<>(); public static ConcurrentHashMap> itemsThrown = new ConcurrentHashMap<>(); public static ConcurrentHashMap> itemsShot = new ConcurrentHashMap<>(); + public static ConcurrentHashMap> itemsBreak = new ConcurrentHashMap<>(); + public static ConcurrentHashMap> itemsDestroy = new ConcurrentHashMap<>(); + public static ConcurrentHashMap> itemsCreate = new ConcurrentHashMap<>(); public static ConcurrentHashMap hopperAbort = new ConcurrentHashMap<>(); public static Map> forceContainer = syncMap(); public static Map lookupType = syncMap(); diff --git a/src/main/java/net/coreprotect/consumer/process/ItemTransactionProcess.java b/src/main/java/net/coreprotect/consumer/process/ItemTransactionProcess.java index 5729add..f8a0e42 100644 --- a/src/main/java/net/coreprotect/consumer/process/ItemTransactionProcess.java +++ b/src/main/java/net/coreprotect/consumer/process/ItemTransactionProcess.java @@ -18,7 +18,7 @@ class ItemTransactionProcess extends Queue { if (ConfigHandler.loggingItem.get(loggingItemId) != null) { int current_chest = ConfigHandler.loggingItem.get(loggingItemId); - if (ConfigHandler.itemsPickup.get(loggingItemId) == null && ConfigHandler.itemsDrop.get(loggingItemId) == null && ConfigHandler.itemsThrown.get(loggingItemId) == null && ConfigHandler.itemsShot.get(loggingItemId) == null) { + if (ConfigHandler.itemsPickup.get(loggingItemId) == null && ConfigHandler.itemsDrop.get(loggingItemId) == null && ConfigHandler.itemsThrown.get(loggingItemId) == null && ConfigHandler.itemsShot.get(loggingItemId) == null && ConfigHandler.itemsBreak.get(loggingItemId) == null && ConfigHandler.itemsDestroy.get(loggingItemId) == null && ConfigHandler.itemsCreate.get(loggingItemId) == null) { return; } if (current_chest == forceData) { @@ -29,6 +29,9 @@ class ItemTransactionProcess extends Queue { ConfigHandler.itemsDrop.remove(loggingItemId); ConfigHandler.itemsThrown.remove(loggingItemId); ConfigHandler.itemsShot.remove(loggingItemId); + ConfigHandler.itemsBreak.remove(loggingItemId); + ConfigHandler.itemsDestroy.remove(loggingItemId); + ConfigHandler.itemsCreate.remove(loggingItemId); ConfigHandler.loggingItem.remove(loggingItemId); } else { diff --git a/src/main/java/net/coreprotect/database/Lookup.java b/src/main/java/net/coreprotect/database/Lookup.java index 71a6f1b..94b23fb 100755 --- a/src/main/java/net/coreprotect/database/Lookup.java +++ b/src/main/java/net/coreprotect/database/Lookup.java @@ -304,6 +304,7 @@ public class Lookup extends Queue { String queryLimit = ""; String queryTable = "block"; String action = ""; + String actionExclude = ""; String includeBlock = ""; String includeEntity = ""; String excludeBlock = ""; @@ -445,6 +446,15 @@ public class Lookup extends Queue { excludeUsers = excludeUserText.toString(); } + // Specify actions to exclude from a:item + if ((lookup && actionList.size() == 0) || (actionList.contains(11) && actionList.size() == 1)) { + StringBuilder actionText = new StringBuilder(); + actionText = actionText.append(ItemLogger.ITEM_BREAK); + actionText.append(",").append(ItemLogger.ITEM_DESTROY); + actionText.append(",").append(ItemLogger.ITEM_CREATE); + actionExclude = actionText.toString(); + } + if (!actionList.isEmpty()) { StringBuilder actionText = new StringBuilder(); for (Integer actionTarget : actionList) { @@ -471,12 +481,15 @@ public class Lookup extends Queue { if (actionTarget == ItemLogger.ITEM_REMOVE) { actionText.append(",").append(ItemLogger.ITEM_PICKUP); actionText.append(",").append(ItemLogger.ITEM_REMOVE_ENDER); + actionText.append(",").append(ItemLogger.ITEM_CREATE); } if (actionTarget == ItemLogger.ITEM_ADD) { actionText.append(",").append(ItemLogger.ITEM_DROP); actionText.append(",").append(ItemLogger.ITEM_ADD_ENDER); actionText.append(",").append(ItemLogger.ITEM_THROW); actionText.append(",").append(ItemLogger.ITEM_SHOOT); + actionText.append(",").append(ItemLogger.ITEM_BREAK); + actionText.append(",").append(ItemLogger.ITEM_DESTROY); } } // If just looking up drops/pickups, include ender chest transactions @@ -535,7 +548,7 @@ public class Lookup extends Queue { if (validAction) { queryBlock = queryBlock + " action IN(" + action + ") AND"; } - else if (inventoryQuery || includeBlock.length() > 0 || includeEntity.length() > 0 || excludeBlock.length() > 0 || excludeEntity.length() > 0) { + else if (inventoryQuery || actionExclude.length() > 0 || includeBlock.length() > 0 || includeEntity.length() > 0 || excludeBlock.length() > 0 || excludeEntity.length() > 0) { queryBlock = queryBlock + " action NOT IN(-1) AND"; } @@ -708,10 +721,19 @@ public class Lookup extends Queue { rows = "rowid as id,time,user,wid,x,y,z,type,data as metadata,0 as data,amount,action,rolled_back"; queryOrder = " ORDER BY time DESC, tbl DESC, id DESC"; } + + if (actionExclude.length() > 0) { + queryBlock = queryBlock.replace("action NOT IN(-1)", "action NOT IN(" + actionExclude + ")"); + } + query = query + unionSelect + "SELECT " + "'2' as tbl," + rows + " FROM " + ConfigHandler.prefix + "item WHERE" + queryBlock + unionLimit + ")"; } if (query.length() == 0) { + if (actionExclude.length() > 0) { + baseQuery = baseQuery.replace("action NOT IN(-1)", "action NOT IN(" + actionExclude + ")"); + } + query = "SELECT " + "'0' as tbl," + rows + " FROM " + ConfigHandler.prefix + queryTable + " " + index + "WHERE" + baseQuery; } diff --git a/src/main/java/net/coreprotect/database/Rollback.java b/src/main/java/net/coreprotect/database/Rollback.java index b1228e1..db46c28 100644 --- a/src/main/java/net/coreprotect/database/Rollback.java +++ b/src/main/java/net/coreprotect/database/Rollback.java @@ -1071,8 +1071,8 @@ public class Rollback extends Queue { } int inventoryAction = 0; - if (rowAction == ItemLogger.ITEM_DROP || rowAction == ItemLogger.ITEM_PICKUP || rowAction == ItemLogger.ITEM_THROW || rowAction == ItemLogger.ITEM_SHOOT) { - inventoryAction = (rowAction == ItemLogger.ITEM_PICKUP ? 1 : 0); + if (rowAction == ItemLogger.ITEM_DROP || rowAction == ItemLogger.ITEM_PICKUP || rowAction == ItemLogger.ITEM_THROW || rowAction == ItemLogger.ITEM_SHOOT || rowAction == ItemLogger.ITEM_BREAK || rowAction == ItemLogger.ITEM_DESTROY || rowAction == ItemLogger.ITEM_CREATE) { + inventoryAction = ((rowAction == ItemLogger.ITEM_PICKUP || rowAction == ItemLogger.ITEM_CREATE) ? 1 : 0); } else if (rowAction == ItemLogger.ITEM_REMOVE_ENDER || rowAction == ItemLogger.ITEM_ADD_ENDER) { inventoryAction = (rowAction == ItemLogger.ITEM_REMOVE_ENDER ? 1 : 0); diff --git a/src/main/java/net/coreprotect/database/logger/ItemLogger.java b/src/main/java/net/coreprotect/database/logger/ItemLogger.java index 900db50..727bba7 100644 --- a/src/main/java/net/coreprotect/database/logger/ItemLogger.java +++ b/src/main/java/net/coreprotect/database/logger/ItemLogger.java @@ -27,6 +27,9 @@ public class ItemLogger { public static final int ITEM_ADD_ENDER = 5; public static final int ITEM_THROW = 6; public static final int ITEM_SHOOT = 7; + public static final int ITEM_BREAK = 8; + public static final int ITEM_DESTROY = 9; + public static final int ITEM_CREATE = 10; private ItemLogger() { throw new IllegalStateException("Database class"); @@ -60,14 +63,35 @@ public class ItemLogger { itemShots = shotList.toArray(itemShots); shotList.clear(); + List breakList = ConfigHandler.itemsBreak.getOrDefault(loggingItemId, new ArrayList<>()); + ItemStack[] itemBreaks = new ItemStack[breakList.size()]; + itemBreaks = breakList.toArray(itemBreaks); + breakList.clear(); + + List destroyList = ConfigHandler.itemsDestroy.getOrDefault(loggingItemId, new ArrayList<>()); + ItemStack[] itemDestroys = new ItemStack[destroyList.size()]; + itemDestroys = destroyList.toArray(itemDestroys); + destroyList.clear(); + + List createList = ConfigHandler.itemsCreate.getOrDefault(loggingItemId, new ArrayList<>()); + ItemStack[] itemCreates = new ItemStack[createList.size()]; + itemCreates = createList.toArray(itemCreates); + createList.clear(); + Util.mergeItems(null, itemPickups); Util.mergeItems(null, itemDrops); Util.mergeItems(null, itemThrows); Util.mergeItems(null, itemShots); + Util.mergeItems(null, itemBreaks); + Util.mergeItems(null, itemDestroys); + Util.mergeItems(null, itemCreates); logTransaction(preparedStmt, batchCount, offset, user, location, itemPickups, ITEM_PICKUP); logTransaction(preparedStmt, batchCount, offset, user, location, itemDrops, ITEM_DROP); logTransaction(preparedStmt, batchCount, offset, user, location, itemThrows, ITEM_THROW); logTransaction(preparedStmt, batchCount, offset, user, location, itemShots, ITEM_SHOOT); + logTransaction(preparedStmt, batchCount, offset, user, location, itemBreaks, ITEM_BREAK); + logTransaction(preparedStmt, batchCount, offset, user, location, itemDestroys, ITEM_DESTROY); + logTransaction(preparedStmt, batchCount, offset, user, location, itemCreates, ITEM_CREATE); } catch (Exception e) { e.printStackTrace(); diff --git a/src/main/java/net/coreprotect/listener/ListenerHandler.java b/src/main/java/net/coreprotect/listener/ListenerHandler.java index 10ff133..b83e856 100644 --- a/src/main/java/net/coreprotect/listener/ListenerHandler.java +++ b/src/main/java/net/coreprotect/listener/ListenerHandler.java @@ -28,6 +28,7 @@ import net.coreprotect.listener.entity.HangingBreakByEntityListener; import net.coreprotect.listener.entity.HangingBreakListener; import net.coreprotect.listener.entity.HangingPlaceListener; import net.coreprotect.listener.player.ArmorStandManipulateListener; +import net.coreprotect.listener.player.CraftItemListener; import net.coreprotect.listener.player.FoodLevelChangeListener; import net.coreprotect.listener.player.InventoryChangeListener; import net.coreprotect.listener.player.PlayerBucketEmptyListener; @@ -83,8 +84,9 @@ public final class ListenerHandler { pluginManager.registerEvents(new HangingBreakByEntityListener(), plugin); // Player Listeners - pluginManager.registerEvents(new InventoryChangeListener(), plugin); pluginManager.registerEvents(new ArmorStandManipulateListener(), plugin); + pluginManager.registerEvents(new CraftItemListener(), plugin); + pluginManager.registerEvents(new InventoryChangeListener(), plugin); pluginManager.registerEvents(new PlayerBucketEmptyListener(), plugin); pluginManager.registerEvents(new PlayerBucketFillListener(), plugin); pluginManager.registerEvents(new PlayerCommandListener(), plugin); diff --git a/src/main/java/net/coreprotect/listener/player/CraftItemListener.java b/src/main/java/net/coreprotect/listener/player/CraftItemListener.java new file mode 100644 index 0000000..e22ada3 --- /dev/null +++ b/src/main/java/net/coreprotect/listener/player/CraftItemListener.java @@ -0,0 +1,131 @@ +package net.coreprotect.listener.player; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.Event.Result; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.ClickType; +import org.bukkit.event.inventory.CraftItemEvent; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.CraftingInventory; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.ShapedRecipe; +import org.bukkit.inventory.ShapelessRecipe; + +import net.coreprotect.config.Config; +import net.coreprotect.config.ConfigHandler; +import net.coreprotect.consumer.Queue; +import net.coreprotect.database.logger.ItemLogger; + +public final class CraftItemListener extends Queue implements Listener { + + protected static void playerCraftItem(Location location, String user, ItemStack itemStack, int action) { + if (!Config.getConfig(location.getWorld()).ITEM_TRANSACTIONS || itemStack == null) { + return; + } + + String loggingItemId = user.toLowerCase(Locale.ROOT) + "." + location.getBlockX() + "." + location.getBlockY() + "." + location.getBlockZ(); + int itemId = getItemId(loggingItemId); + + if (action == ItemLogger.ITEM_CREATE) { + List list = ConfigHandler.itemsCreate.getOrDefault(loggingItemId, new ArrayList<>()); + list.add(itemStack); + ConfigHandler.itemsCreate.put(loggingItemId, list); + } + else { + List list = ConfigHandler.itemsDestroy.getOrDefault(loggingItemId, new ArrayList<>()); + list.add(itemStack); + ConfigHandler.itemsDestroy.put(loggingItemId, list); + } + + int time = (int) (System.currentTimeMillis() / 1000L) + 1; + Queue.queueItemTransaction(user, location.clone(), time, 0, itemId); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + protected void onCraftItem(CraftItemEvent event) { + if (event.getResult() == Result.DENY) { + return; + } + + Player player = (Player) event.getWhoClicked(); + if (event.getClick() == ClickType.NUMBER_KEY && player.getInventory().getItem(event.getHotbarButton()) != null) { + return; + } + + if ((event.getClick() == ClickType.DROP || event.getClick() == ClickType.CONTROL_DROP) && event.getCursor().getType() != Material.AIR) { + return; + } + + CraftingInventory craftingInventory = event.getInventory(); + if (craftingInventory.getResult() == null) { + return; + } + + Inventory bottomInventory = event.getView().getBottomInventory(); + if (bottomInventory.getType() != InventoryType.PLAYER) { + return; + } + + ItemStack addItem = event.getRecipe().getResult().clone(); + int amount = addItem.getAmount(); + if (amount == 0) { + return; + } + + int amountMultiplier = 1; + if (event.getClick() == ClickType.SHIFT_LEFT || event.getClick() == ClickType.SHIFT_RIGHT) { + int newMultiplier = Integer.MIN_VALUE; + for (ItemStack item : craftingInventory.getMatrix()) { + if (item != null && (newMultiplier == Integer.MIN_VALUE || item.getAmount() < newMultiplier)) { + newMultiplier = item.getAmount(); + } + } + amountMultiplier = (newMultiplier == Integer.MIN_VALUE ? 1 : newMultiplier); + + int addAmount = amount * amountMultiplier; + Inventory virtualInventory = Bukkit.createInventory(null, 36); + virtualInventory.setStorageContents(bottomInventory.getStorageContents()); + addItem.setAmount(addAmount); + + HashMap result = virtualInventory.addItem(addItem); + for (ItemStack itemFailed : result.values()) { + if (itemFailed.isSimilar(addItem)) { + addAmount = (int) (Math.ceil((addAmount - itemFailed.getAmount()) / (double) amount) * amount); + amountMultiplier = addAmount / amount; + } + } + virtualInventory.clear(); + addItem.setAmount(addAmount); + } + + List oldItems = new ArrayList<>(); + if (event.getRecipe() instanceof ShapelessRecipe) { + oldItems.addAll(((ShapelessRecipe) event.getRecipe()).getIngredientList()); + } + if (event.getRecipe() instanceof ShapedRecipe) { + oldItems.addAll(((ShapedRecipe) event.getRecipe()).getIngredientMap().values()); + } + + if (addItem.getAmount() > 0) { + for (ItemStack oldItem : oldItems) { + ItemStack removedItem = oldItem.clone(); + removedItem.setAmount(oldItem.getAmount() * amountMultiplier); + playerCraftItem(player.getLocation(), player.getName(), removedItem, ItemLogger.ITEM_DESTROY); + } + + playerCraftItem(player.getLocation(), player.getName(), addItem, ItemLogger.ITEM_CREATE); + } + } + +}