From a2353afd0a60f0a94985069fb2cf41051756eabd Mon Sep 17 00:00:00 2001 From: Intelli Date: Fri, 11 Feb 2022 21:45:33 -0700 Subject: [PATCH] Added inventory rollback support for online players --- .../coreprotect/command/LookupCommand.java | 2 +- .../command/RollbackRestoreCommand.java | 28 +++- .../java/net/coreprotect/consumer/Queue.java | 17 +-- .../coreprotect/consumer/process/Process.java | 8 ++ .../database/ContainerRollback.java | 3 +- .../net/coreprotect/database/Database.java | 18 ++- .../java/net/coreprotect/database/Lookup.java | 35 +++-- .../net/coreprotect/database/Rollback.java | 121 +++++++++++++++--- .../statement/ContainerStatement.java | 1 + .../database/statement/ItemStatement.java | 1 + .../net/coreprotect/language/Language.java | 3 +- .../java/net/coreprotect/language/Phrase.java | 3 +- .../coreprotect/patch/script/__2_21_0.java | 50 ++++++++ 13 files changed, 230 insertions(+), 60 deletions(-) diff --git a/src/main/java/net/coreprotect/command/LookupCommand.java b/src/main/java/net/coreprotect/command/LookupCommand.java index 21b6ed3..879b21c 100755 --- a/src/main/java/net/coreprotect/command/LookupCommand.java +++ b/src/main/java/net/coreprotect/command/LookupCommand.java @@ -612,7 +612,7 @@ public class LookupCommand { } if (rollbackusers.contains("#container")) { - if (argAction.contains(6) || argAction.contains(7) || argAction.contains(8) || argAction.contains(9) || argAction.contains(10)) { + if (argAction.contains(6) || argAction.contains(7) || argAction.contains(8) || argAction.contains(9) || argAction.contains(10) || argAction.contains(11)) { Chat.sendMessage(player, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.INVALID_USERNAME, "#container")); return; } diff --git a/src/main/java/net/coreprotect/command/RollbackRestoreCommand.java b/src/main/java/net/coreprotect/command/RollbackRestoreCommand.java index 96b62a2..39b8890 100755 --- a/src/main/java/net/coreprotect/command/RollbackRestoreCommand.java +++ b/src/main/java/net/coreprotect/command/RollbackRestoreCommand.java @@ -151,7 +151,7 @@ public class RollbackRestoreCommand { final int finalAction = a; int DEFAULT_RADIUS = Config.getGlobal().DEFAULT_RADIUS; - if ((player instanceof Player || player instanceof BlockCommandSender) && argRadius == null && DEFAULT_RADIUS > 0 && !forceglobal) { + if ((player instanceof Player || player instanceof BlockCommandSender) && argRadius == null && DEFAULT_RADIUS > 0 && !forceglobal && !argAction.contains(11)) { Location location = lo; int xmin = location.getBlockX() - DEFAULT_RADIUS; int xmax = location.getBlockX() + DEFAULT_RADIUS; @@ -195,11 +195,11 @@ public class RollbackRestoreCommand { return; } else if (preview > 0) { - Chat.sendMessage(player, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.PREVIEW_CONTAINER)); + Chat.sendMessage(player, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.PREVIEW_TRANSACTION, !argAction.contains(11) ? Selector.FIRST : Selector.SECOND)); return; } } - if (argAction.contains(8) || argAction.contains(11) || (!argAction.contains(0) && !argAction.contains(1) && !argAction.contains(3) && !argAction.contains(4))) { + if (argAction.contains(8) || (argAction.contains(11) && !argAction.contains(4)) || (!argAction.contains(0) && !argAction.contains(1) && !argAction.contains(3) && !argAction.contains(4))) { if (finalAction == 0) { Chat.sendMessage(player, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.ACTION_NOT_SUPPORTED)); } @@ -210,7 +210,7 @@ public class RollbackRestoreCommand { } } - if (argAction.contains(4) && argAction.contains(11)) { // a:inventory + if (argAction.contains(4) && argAction.contains(11) && !argExcludeUsers.contains("#hopper")) { // a:inventory argExcludeUsers.add("#hopper"); } @@ -220,13 +220,22 @@ public class RollbackRestoreCommand { List rollbackusers = argUsers; int c = 0; for (String ruser : rollbackusers) { - List players = Bukkit.getServer().matchPlayer(ruser); + List players = Bukkit.getServer().matchPlayer(ruser); // here for (Player p : players) { if (p.getName().equalsIgnoreCase(ruser)) { - rollbackusers.set(c, p.getName()); + ruser = p.getName(); + rollbackusers.set(c, ruser); } } c++; + + if (argAction.contains(4) && argAction.contains(11)) { + Player onlineUser = Bukkit.getServer().getPlayer(ruser); + if (onlineUser == null || !onlineUser.isOnline()) { + Chat.sendMessage(player, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.USER_OFFLINE, ruser)); + return; + } + } } int wid = 0; @@ -234,6 +243,11 @@ public class RollbackRestoreCommand { int y = 0; int z = 0; if (rollbackusers.contains("#container")) { + if (argAction.contains(11)) { + Chat.sendMessage(player, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.INVALID_USERNAME, "#container")); + return; + } + boolean valid = false; if (ConfigHandler.lookupType.get(player.getName()) != null) { int lookupType = ConfigHandler.lookupType.get(player.getName()); @@ -248,7 +262,7 @@ public class RollbackRestoreCommand { } if (valid) { if (preview > 0) { - Chat.sendMessage(player, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.PREVIEW_CONTAINER)); + Chat.sendMessage(player, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.PREVIEW_TRANSACTION, Selector.FIRST)); return; } else { diff --git a/src/main/java/net/coreprotect/consumer/Queue.java b/src/main/java/net/coreprotect/consumer/Queue.java index 13648f5..ce88f0d 100755 --- a/src/main/java/net/coreprotect/consumer/Queue.java +++ b/src/main/java/net/coreprotect/consumer/Queue.java @@ -216,18 +216,6 @@ public class Queue { queueStandardData(consumerId, currentConsumer, new String[] { user, null }, location); } - protected static void queueContainerRollbackUpdate(String user, Location location, List list, int action) { - if (location == null) { - location = new Location(Bukkit.getServer().getWorlds().get(0), 0, 0, 0); - } - - int currentConsumer = Consumer.currentConsumer; - int consumerId = Consumer.newConsumerId(currentConsumer); - addConsumer(currentConsumer, new Object[] { consumerId, Process.CONTAINER_ROLLBACK_UPDATE, null, 0, null, 0, action, null }); - Consumer.consumerObjectArrayList.get(currentConsumer).put(consumerId, list); - queueStandardData(consumerId, currentConsumer, new String[] { user, null }, location); - } - protected static synchronized void queueContainerTransaction(String user, Location location, Material type, Object inventory, int chestId) { int currentConsumer = Consumer.currentConsumer; int consumerId = Consumer.newConsumerId(currentConsumer); @@ -352,13 +340,14 @@ public class Queue { queueStandardData(consumerId, currentConsumer, new String[] { player.getName(), null }, player.getLocation().clone()); } - protected static void queueRollbackUpdate(String user, Location location, List list, int action) { + protected static void queueRollbackUpdate(String user, Location location, List list, int table, int action) { if (location == null) { location = new Location(Bukkit.getServer().getWorlds().get(0), 0, 0, 0); } + int currentConsumer = Consumer.currentConsumer; int consumerId = Consumer.newConsumerId(currentConsumer); - addConsumer(currentConsumer, new Object[] { consumerId, Process.ROLLBACK_UPDATE, null, 0, null, 0, action, null }); + addConsumer(currentConsumer, new Object[] { consumerId, table, null, 0, null, 0, action, null }); Consumer.consumerObjectArrayList.get(currentConsumer).put(consumerId, list); queueStandardData(consumerId, currentConsumer, new String[] { user, null }, location); } diff --git a/src/main/java/net/coreprotect/consumer/process/Process.java b/src/main/java/net/coreprotect/consumer/process/Process.java index 4085ab6..8627053 100755 --- a/src/main/java/net/coreprotect/consumer/process/Process.java +++ b/src/main/java/net/coreprotect/consumer/process/Process.java @@ -44,6 +44,8 @@ public class Process { public static final int PLAYER_KILL = 24; public static final int BLOCKDATA_INSERT = 25; public static final int ITEM_TRANSACTION = 26; + public static final int INVENTORY_ROLLBACK_UPDATE = 27; + public static final int INVENTORY_CONTAINER_ROLLBACK_UPDATE = 28; public static int lastLockUpdate = 0; private static volatile int currentConsumerSize = 0; @@ -160,6 +162,12 @@ public class Process { case Process.CONTAINER_ROLLBACK_UPDATE: RollbackUpdateProcess.process(statement, processId, id, forceData, 1); break; + case Process.INVENTORY_ROLLBACK_UPDATE: + RollbackUpdateProcess.process(statement, processId, id, forceData, 2); + break; + case Process.INVENTORY_CONTAINER_ROLLBACK_UPDATE: + RollbackUpdateProcess.process(statement, processId, id, forceData, 3); + break; case Process.WORLD_INSERT: WorldInsertProcess.process(preparedStmtWorlds, i, statement, object, forceData); break; diff --git a/src/main/java/net/coreprotect/database/ContainerRollback.java b/src/main/java/net/coreprotect/database/ContainerRollback.java index 0ef3808..42c1963 100644 --- a/src/main/java/net/coreprotect/database/ContainerRollback.java +++ b/src/main/java/net/coreprotect/database/ContainerRollback.java @@ -17,6 +17,7 @@ import org.bukkit.inventory.ItemStack; import net.coreprotect.CoreProtect; import net.coreprotect.config.ConfigHandler; import net.coreprotect.consumer.Queue; +import net.coreprotect.consumer.process.Process; import net.coreprotect.language.Phrase; import net.coreprotect.model.BlockGroup; import net.coreprotect.utility.Chat; @@ -38,7 +39,7 @@ public class ContainerRollback extends Queue { userString = user.getName(); } - Queue.queueContainerRollbackUpdate(userString, location, lookupList, rollbackType); // Perform update transaction in consumer + Queue.queueRollbackUpdate(userString, location, lookupList, Process.CONTAINER_ROLLBACK_UPDATE, rollbackType); // Perform update transaction in consumer final String finalUserString = userString; ConfigHandler.rollbackHash.put(userString, new int[] { 0, 0, 0, 0 }); diff --git a/src/main/java/net/coreprotect/database/Database.java b/src/main/java/net/coreprotect/database/Database.java index 2e5c2b5..8c51477 100755 --- a/src/main/java/net/coreprotect/database/Database.java +++ b/src/main/java/net/coreprotect/database/Database.java @@ -197,6 +197,12 @@ public class Database extends Queue { if (table == 1) { statement.executeUpdate("UPDATE " + ConfigHandler.prefix + "container SET rolled_back='" + rolledBack + "' WHERE rowid='" + id + "'"); } + else if (table == 2) { + statement.executeUpdate("UPDATE " + ConfigHandler.prefix + "item SET rolled_back='" + rolledBack + "' WHERE rowid='" + id + "'"); + } + else if (table == 3) { + statement.executeUpdate("UPDATE " + ConfigHandler.prefix + "container SET rolled_back_inventory='" + rolledBack + "' WHERE rowid='" + id + "'"); + } else { statement.executeUpdate("UPDATE " + ConfigHandler.prefix + "block SET rolled_back='" + rolledBack + "' WHERE rowid='" + id + "'"); } @@ -212,8 +218,8 @@ public class Database extends Queue { String signInsert = "INSERT INTO " + ConfigHandler.prefix + "sign (time, user, wid, x, y, z, action, color, data, line_1, line_2, line_3, line_4) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; String blockInsert = "INSERT INTO " + ConfigHandler.prefix + "block (time, user, wid, x, y, z, type, data, meta, blockdata, action, rolled_back) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; String skullInsert = "INSERT INTO " + ConfigHandler.prefix + "skull (time, owner) VALUES (?, ?)"; - String containerInsert = "INSERT INTO " + ConfigHandler.prefix + "container (time, user, wid, x, y, z, type, data, amount, metadata, action, rolled_back) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; - String itemInsert = "INSERT INTO " + ConfigHandler.prefix + "item (time, user, wid, x, y, z, type, data, amount, action) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; + String containerInsert = "INSERT INTO " + ConfigHandler.prefix + "container (time, user, wid, x, y, z, type, data, amount, metadata, action, rolled_back, rolled_back_inventory) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; + String itemInsert = "INSERT INTO " + ConfigHandler.prefix + "item (time, user, wid, x, y, z, type, data, amount, action, rolled_back) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; String worldInsert = "INSERT INTO " + ConfigHandler.prefix + "world (id, world) VALUES (?, ?)"; String chatInsert = "INSERT INTO " + ConfigHandler.prefix + "chat (time, user, wid, x, y, z, message) VALUES (?, ?, ?, ?, ?, ?, ?)"; String commandInsert = "INSERT INTO " + ConfigHandler.prefix + "command (time, user, wid, x, y, z, message) VALUES (?, ?, ?, ?, ?, ?, ?)"; @@ -333,9 +339,9 @@ public class Database extends Queue { index = ", INDEX(time), INDEX(user,time), INDEX(wid,x,z,time)"; statement.executeUpdate("CREATE TABLE IF NOT EXISTS " + prefix + "command(rowid int(8) NOT NULL AUTO_INCREMENT,PRIMARY KEY(rowid),time int(10), user int(8), wid int(4), x int(8), y int (3), z int(8), message varchar(16000)" + index + ") ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4"); index = ", INDEX(wid,x,z,time), INDEX(user,time), INDEX(type,time)"; - statement.executeUpdate("CREATE TABLE IF NOT EXISTS " + prefix + "container(rowid int(10) NOT NULL AUTO_INCREMENT,PRIMARY KEY(rowid), time int(10), user int(8), wid int(4), x int(8), y int(3), z int(8), type int(6), data int(6), amount int(4), metadata blob, action int(2), rolled_back tinyint(1)" + index + ") ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4"); + statement.executeUpdate("CREATE TABLE IF NOT EXISTS " + prefix + "container(rowid int(10) NOT NULL AUTO_INCREMENT,PRIMARY KEY(rowid), time int(10), user int(8), wid int(4), x int(8), y int(3), z int(8), type int(6), data int(6), amount int(4), metadata blob, action int(2), rolled_back tinyint(1), rolled_back_inventory tinyint(1)" + index + ") ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4"); index = ", INDEX(wid,x,z,time), INDEX(user,time), INDEX(type,time)"; - statement.executeUpdate("CREATE TABLE IF NOT EXISTS " + prefix + "item(rowid int(10) NOT NULL AUTO_INCREMENT,PRIMARY KEY(rowid), time int(10), user int(8), wid int(4), x int(8), y int(3), z int(8), type int(6), data blob, amount int(4), action tinyint(1)" + index + ") ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4"); + statement.executeUpdate("CREATE TABLE IF NOT EXISTS " + prefix + "item(rowid int(10) NOT NULL AUTO_INCREMENT,PRIMARY KEY(rowid), time int(10), user int(8), wid int(4), x int(8), y int(3), z int(8), type int(6), data blob, amount int(4), action tinyint(1), rolled_back tinyint(1)" + index + ") ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4"); statement.executeUpdate("CREATE TABLE IF NOT EXISTS " + prefix + "database_lock(rowid int(8) NOT NULL AUTO_INCREMENT,PRIMARY KEY(rowid),status tinyint(1),time int(10)) ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4"); statement.executeUpdate("CREATE TABLE IF NOT EXISTS " + prefix + "entity(rowid int(8) NOT NULL AUTO_INCREMENT,PRIMARY KEY(rowid), time int(10), data blob) ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4"); index = ", INDEX(id)"; @@ -411,10 +417,10 @@ public class Database extends Queue { statement.executeUpdate("CREATE TABLE IF NOT EXISTS " + prefix + "command (time INTEGER, user INTEGER, wid INTEGER, x INTEGER, y INTEGER, z INTEGER, message TEXT);"); } if (!tableData.contains(prefix + "container")) { - statement.executeUpdate("CREATE TABLE IF NOT EXISTS " + prefix + "container (time INTEGER, user INTEGER, wid INTEGER, x INTEGER, y INTEGER, z INTEGER, type INTEGER, data INTEGER, amount INTEGER, metadata BLOB, action INTEGER, rolled_back INTEGER);"); + statement.executeUpdate("CREATE TABLE IF NOT EXISTS " + prefix + "container (time INTEGER, user INTEGER, wid INTEGER, x INTEGER, y INTEGER, z INTEGER, type INTEGER, data INTEGER, amount INTEGER, metadata BLOB, action INTEGER, rolled_back INTEGER, rolled_back_inventory INTEGER);"); } if (!tableData.contains(prefix + "item")) { - statement.executeUpdate("CREATE TABLE IF NOT EXISTS " + prefix + "item (time INTEGER, user INTEGER, wid INTEGER, x INTEGER, y INTEGER, z INTEGER, type INTEGER, data BLOB, amount INTEGER, action INTEGER);"); + statement.executeUpdate("CREATE TABLE IF NOT EXISTS " + prefix + "item (time INTEGER, user INTEGER, wid INTEGER, x INTEGER, y INTEGER, z INTEGER, type INTEGER, data BLOB, amount INTEGER, action INTEGER, rolled_back INTEGER);"); } if (!tableData.contains(prefix + "database_lock")) { statement.executeUpdate("CREATE TABLE IF NOT EXISTS " + prefix + "database_lock (status INTEGER, time INTEGER);"); diff --git a/src/main/java/net/coreprotect/database/Lookup.java b/src/main/java/net/coreprotect/database/Lookup.java index 1813c98..3a41998 100755 --- a/src/main/java/net/coreprotect/database/Lookup.java +++ b/src/main/java/net/coreprotect/database/Lookup.java @@ -56,7 +56,7 @@ public class Lookup extends Queue { else if (i == 13 && map[i] instanceof Byte[]) { results[newId] = Util.byteDataToString((byte[]) map[i], (int) map[6]); } - else if (i > 0) { + else if (i > 0) { // skip rowid if (map[i] instanceof Integer) { results[newId] = map[i].toString(); } @@ -125,6 +125,10 @@ public class Lookup extends Queue { invalidRollbackActions.add(3); } + if (actionList.contains(4) && actionList.contains(11)) { + invalidRollbackActions.clear(); + } + try { while (Consumer.isPaused) { Thread.sleep(1); @@ -211,6 +215,7 @@ public class Lookup extends Queue { else { int resultData = 0; int resultAmount = -1; + int resultTable = 0; byte[] resultMeta = null; byte[] resultBlockData = null; long resultId = results.getLong("id"); @@ -224,10 +229,15 @@ public class Lookup extends Queue { int resultZ = results.getInt("z"); int resultWorldId = results.getInt("wid"); + boolean hasTbl = false; if ((lookup && actionList.size() == 0) || actionList.contains(4) || actionList.contains(5) || actionList.contains(11)) { resultData = results.getInt("data"); resultAmount = results.getInt("amount"); resultMeta = results.getBytes("metadata"); + if (!lookup) { + resultTable = results.getInt("tbl"); + hasTbl = true; + } } else { resultData = results.getInt("data"); @@ -236,7 +246,6 @@ public class Lookup extends Queue { } boolean valid = true; - if (!lookup) { if (invalidRollbackActions.contains(resultAction)) { valid = false; @@ -244,8 +253,14 @@ public class Lookup extends Queue { } if (valid) { - Object[] dataArray = new Object[] { resultId, resultTime, resultUserId, resultX, resultY, resultZ, resultType, resultData, resultAction, resultRolledBack, resultWorldId, resultAmount, resultMeta, resultBlockData }; - list.add(dataArray); + if (hasTbl) { + Object[] dataArray = new Object[] { resultId, resultTime, resultUserId, resultX, resultY, resultZ, resultType, resultData, resultAction, resultRolledBack, resultWorldId, resultAmount, resultMeta, resultBlockData, resultTable }; + list.add(dataArray); + } + else { + Object[] dataArray = new Object[] { resultId, resultTime, resultUserId, resultX, resultY, resultZ, resultType, resultData, resultAction, resultRolledBack, resultWorldId, resultAmount, resultMeta, resultBlockData }; + list.add(dataArray); + } } } } @@ -661,25 +676,25 @@ public class Lookup extends Queue { baseQuery = baseQuery.replace("action NOT IN(-1)", "action NOT IN(3)"); // if block specified for include/exclude, filter out entity data } - query = unionSelect + "SELECT " + (count ? "'0' as tbl," : "") + rows + " FROM " + ConfigHandler.prefix + queryTable + " " + index + "WHERE" + baseQuery + unionLimit + ") UNION ALL "; + query = unionSelect + "SELECT " + "'0' as tbl," + rows + " FROM " + ConfigHandler.prefix + queryTable + " " + index + "WHERE" + baseQuery + unionLimit + ") UNION ALL "; itemLookup = true; } if (itemLookup) { if (!count) { - rows = "rowid as id,time,user,wid,x,y,z,type,metadata,data,amount,action,rolled_back"; + rows = "rowid as id,time,user,wid,x,y,z,type,metadata,data,amount,action,rolled_back_inventory as rolled_back"; } - query = query + unionSelect + "SELECT " + (count ? "'1' as tbl," : "") + rows + " FROM " + ConfigHandler.prefix + "container WHERE" + queryBlock + unionLimit + ") UNION ALL "; + query = query + unionSelect + "SELECT " + "'1' as tbl," + rows + " FROM " + ConfigHandler.prefix + "container WHERE" + queryBlock + unionLimit + ") UNION ALL "; if (!count) { - rows = "rowid as id,time,user,wid,x,y,z,type,data as metadata,0 as data,amount,action,0 as rolled_back"; + 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, id DESC"; } - query = query + unionSelect + "SELECT " + (count ? "'2' as tbl," : "") + rows + " FROM " + ConfigHandler.prefix + "item WHERE" + queryBlock + unionLimit + ")"; + query = query + unionSelect + "SELECT " + "'2' as tbl," + rows + " FROM " + ConfigHandler.prefix + "item WHERE" + queryBlock + unionLimit + ")"; } if (query.length() == 0) { - query = "SELECT " + (count ? "'0' as tbl," : "") + rows + " FROM " + ConfigHandler.prefix + queryTable + " " + index + "WHERE" + baseQuery; + query = "SELECT " + "'0' as tbl," + rows + " FROM " + ConfigHandler.prefix + queryTable + " " + index + "WHERE" + baseQuery; } query = query + queryOrder + queryLimit + ""; diff --git a/src/main/java/net/coreprotect/database/Rollback.java b/src/main/java/net/coreprotect/database/Rollback.java index a8fdfcb..4d02d16 100644 --- a/src/main/java/net/coreprotect/database/Rollback.java +++ b/src/main/java/net/coreprotect/database/Rollback.java @@ -15,6 +15,7 @@ import java.util.Locale; import java.util.Map; import java.util.Map.Entry; import java.util.TreeMap; +import java.util.UUID; import org.bukkit.Bukkit; import org.bukkit.DyeColor; @@ -75,6 +76,8 @@ import net.coreprotect.bukkit.BukkitAdapter; import net.coreprotect.config.Config; import net.coreprotect.config.ConfigHandler; import net.coreprotect.consumer.Queue; +import net.coreprotect.consumer.process.Process; +import net.coreprotect.database.logger.ItemLogger; import net.coreprotect.database.statement.UserStatement; import net.coreprotect.language.Phrase; import net.coreprotect.language.Selector; @@ -136,6 +139,7 @@ public class Rollback extends Queue { TreeMap chunkList = new TreeMap<>(); HashMap> dataList = new HashMap<>(); HashMap> itemDataList = new HashMap<>(); + boolean inventoryRollback = actionList.contains(11); /* int worldMin = BukkitAdapter.ADAPTER.getMinHeight(world); @@ -170,7 +174,7 @@ public class Rollback extends Queue { int rowWorldId = (Integer) result[10]; int chunkX = rowX >> 4; int chunkZ = rowZ >> 4; - long chunkKey = chunkX & 0xffffffffL | (chunkZ & 0xffffffffL) << 32; + long chunkKey = inventoryRollback ? 0 : (chunkX & 0xffffffffL | (chunkZ & 0xffffffffL) << 32); // int rowAction = result[8]; // if (rowAction==10) result[8] = 0; // if (rowAction==11) result[8] = 1; @@ -237,7 +241,7 @@ public class Rollback extends Queue { String userString = "#server"; if (user != null) { userString = user.getName(); - if (verbose && preview == 0) { + if (verbose && preview == 0 && !actionList.contains(11)) { Integer chunks = chunkList.size(); Chat.sendMessage(user, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.ROLLBACK_CHUNKS_FOUND, chunks.toString(), (chunks == 1 ? Selector.FIRST : Selector.SECOND))); } @@ -245,8 +249,25 @@ public class Rollback extends Queue { // Perform update transaction(s) in consumer if (preview == 0) { - Queue.queueRollbackUpdate(userString, location, lookupList, rollbackType); - Queue.queueContainerRollbackUpdate(userString, location, itemList, rollbackType); + if (actionList.contains(11)) { + List inventoryList = new ArrayList<>(); + List containerList = new ArrayList<>(); + for (Object[] data : itemList) { + int table = (Integer) data[14]; + if (table == 2) { // item + inventoryList.add(data); + } + else { + containerList.add(data); + } + } + Queue.queueRollbackUpdate(userString, location, inventoryList, Process.INVENTORY_ROLLBACK_UPDATE, rollbackType); + Queue.queueRollbackUpdate(userString, location, containerList, Process.INVENTORY_CONTAINER_ROLLBACK_UPDATE, rollbackType); + } + else { + Queue.queueRollbackUpdate(userString, location, lookupList, Process.ROLLBACK_UPDATE, rollbackType); + Queue.queueRollbackUpdate(userString, location, itemList, Process.CONTAINER_ROLLBACK_UPDATE, rollbackType); + } } ConfigHandler.rollbackHash.put(userString, new int[] { 0, 0, 0, 0 }); @@ -272,7 +293,6 @@ public class Rollback extends Queue { Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(CoreProtect.getInstance(), () -> { try { boolean clearInventories = false; - if (Config.getGlobal().ROLLBACK_ITEMS) { clearInventories = true; } @@ -1010,11 +1030,52 @@ public class Rollback extends Queue { byte[] rowMetadata = (byte[]) row[12]; Material rowType = Util.getType(rowTypeRaw); - if (rowAction > 1) { - continue; // skip inventory & ender chest transactions - } - if ((rollbackType == 0 && rowRolledBack == 0) || (rollbackType == 1 && rowRolledBack == 1)) { + if (inventoryRollback) { + int rowUserId = (Integer) row[2]; + String rowUser = ConfigHandler.playerIdCacheReversed.get(rowUserId); + if (rowUser == null) { + continue; + } + + String uuid = ConfigHandler.uuidCache.get(rowUser.toLowerCase(Locale.ROOT)); + if (uuid == null) { + continue; + } + + Player player = Bukkit.getServer().getPlayer(UUID.fromString(uuid)); + if (player == null) { + continue; + } + + int inventoryAction = 0; + if (rowAction == ItemLogger.ITEM_DROP || rowAction == ItemLogger.ITEM_PICKUP) { + inventoryAction = (rowAction == ItemLogger.ITEM_PICKUP ? 1 : 0); + } + else if (rowAction == ItemLogger.ITEM_REMOVE_ENDER || rowAction == ItemLogger.ITEM_ADD_ENDER) { + inventoryAction = (rowAction == ItemLogger.ITEM_REMOVE_ENDER ? 1 : 0); + } + else { + inventoryAction = (rowAction == ItemLogger.ITEM_REMOVE ? 1 : 0); + } + + int action = rollbackType == 0 ? (inventoryAction ^ 1) : inventoryAction; + ItemStack itemstack = new ItemStack(rowType, rowAmount, (short) rowData); + Object[] populatedStack = populateItemStack(itemstack, rowMetadata); + if (rowAction == ItemLogger.ITEM_REMOVE_ENDER || rowAction == ItemLogger.ITEM_ADD_ENDER) { + modifyContainerItems(containerType, player.getEnderChest(), (Integer) populatedStack[0], ((ItemStack) populatedStack[1]).clone(), action ^ 1); + } + modifyContainerItems(containerType, player.getInventory(), (Integer) populatedStack[0], (ItemStack) populatedStack[1], action); + + itemCount1 = itemCount1 + rowAmount; + ConfigHandler.rollbackHash.put(finalUserString, new int[] { itemCount1, blockCount1, entityCount1, 0 }); + continue; // remove this for merged rollbacks in future? (be sure to re-enable chunk sorting) + } + + if (rowAction > 1) { + continue; // skip inventory & ender chest transactions + } + if (!containerInit || rowX != lastX || rowY != lastY || rowZ != lastZ || rowWorldId != lastWorldId) { container = null; // container patch 2.14.0 String world = Util.getWorldName(rowWorldId); @@ -1143,7 +1204,7 @@ public class Rollback extends Queue { entityCount = rollbackHashData[2]; ConfigHandler.rollbackHash.put(finalUserString, new int[] { itemCount, blockCount, entityCount, 0 }); - if (verbose && user != null && preview == 0) { + if (verbose && user != null && preview == 0 && !actionList.contains(11)) { Integer chunks = chunkList.size(); Chat.sendMessage(user, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.ROLLBACK_CHUNKS_MODIFIED, chunkCount.toString(), chunks.toString(), (chunks == 1 ? Selector.FIRST : Selector.SECOND))); } @@ -1229,7 +1290,18 @@ public class Rollback extends Queue { } } - if (actionList.contains(4)) { + if (actionList.contains(4) && actionList.contains(11)) { + if (actionList.contains(0)) { + Chat.sendMessage(user, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.ROLLBACK_WORLD_ACTION, "+inventory", Selector.SECOND)); + } + else if (actionList.contains(1)) { + Chat.sendMessage(user, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.ROLLBACK_WORLD_ACTION, "-inventory", Selector.SECOND)); + } + else { + Chat.sendMessage(user, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.ROLLBACK_WORLD_ACTION, "inventory", Selector.SECOND)); + } + } + else if (actionList.contains(4)) { if (actionList.contains(0)) { Chat.sendMessage(user, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.ROLLBACK_WORLD_ACTION, "-container", Selector.SECOND)); } @@ -1346,6 +1418,13 @@ public class Rollback extends Queue { int excludeCount = 0; for (String excludeUser : excludeUserList) { + // don't display that excluded #hopper in inventory rollbacks + if (actionList.contains(4) && actionList.contains(11)) { + if (excludeUser.equals("#hopper")) { + continue; + } + } + if (excludeCount == 0) { excludeUsers = excludeUsers.append("" + excludeUser + ""); } @@ -1356,7 +1435,9 @@ public class Rollback extends Queue { excludeCount++; } - Chat.sendMessage(user, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.ROLLBACK_EXCLUDED_USERS, excludeUsers.toString(), (excludeCount == 1 ? Selector.FIRST : Selector.SECOND))); + if (excludeCount > 0) { + Chat.sendMessage(user, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.ROLLBACK_EXCLUDED_USERS, excludeUsers.toString(), (excludeCount == 1 ? Selector.FIRST : Selector.SECOND))); + } } StringBuilder modifiedData = new StringBuilder(); @@ -1366,7 +1447,7 @@ public class Rollback extends Queue { modifyCount++; } else if (preview == 0) { - if (itemCount > 0) { + if (itemCount > 0 || actionList.contains(4)) { modifiedData = modifiedData.append(Phrase.build(Phrase.AMOUNT_ITEM, NumberFormat.getInstance().format(itemCount), (itemCount == 1 ? Selector.FIRST : Selector.SECOND))); modifyCount++; } @@ -1379,11 +1460,13 @@ public class Rollback extends Queue { modifyCount++; } - if (modifyCount > 0) { - modifiedData.append(", "); + if (blockCount > 0 || !actionList.contains(4)) { + if (modifyCount > 0) { + modifiedData.append(", "); + } + modifiedData.append(Phrase.build(Phrase.AMOUNT_BLOCK, NumberFormat.getInstance().format(blockCount), (blockCount == 1 ? Selector.FIRST : Selector.SECOND))); + modifyCount++; } - modifiedData.append(Phrase.build(Phrase.AMOUNT_BLOCK, NumberFormat.getInstance().format(blockCount), (blockCount == 1 ? Selector.FIRST : Selector.SECOND))); - modifyCount++; } else if (preview > 0) { modifiedData = modifiedData.append(Phrase.build(Phrase.AMOUNT_BLOCK, NumberFormat.getInstance().format(blockCount), (blockCount == 1 ? Selector.FIRST : Selector.SECOND))); @@ -1391,7 +1474,7 @@ public class Rollback extends Queue { } StringBuilder modifiedDataVerbose = new StringBuilder(); - if (verbose && preview == 0) { + if (verbose && preview == 0 && !actionList.contains(11)) { if (chunkCount > -1 && modifyCount < 3) { if (modifyCount > 0) { modifiedData.append(", "); @@ -1428,7 +1511,7 @@ public class Rollback extends Queue { try { ItemStack[] contents = null; - if (type.equals(Material.ARMOR_STAND)) { + if (type != null && type.equals(Material.ARMOR_STAND)) { EntityEquipment equipment = (EntityEquipment) container; if (equipment != null) { if (action == 1) { diff --git a/src/main/java/net/coreprotect/database/statement/ContainerStatement.java b/src/main/java/net/coreprotect/database/statement/ContainerStatement.java index 45e9c64..26c1083 100644 --- a/src/main/java/net/coreprotect/database/statement/ContainerStatement.java +++ b/src/main/java/net/coreprotect/database/statement/ContainerStatement.java @@ -25,6 +25,7 @@ public class ContainerStatement { preparedStmt.setObject(10, byteData); preparedStmt.setInt(11, action); preparedStmt.setInt(12, rolledBack); + preparedStmt.setInt(13, 0); // rolled_back_inventory preparedStmt.addBatch(); if (batchCount > 0 && batchCount % 1000 == 0) { diff --git a/src/main/java/net/coreprotect/database/statement/ItemStatement.java b/src/main/java/net/coreprotect/database/statement/ItemStatement.java index 2bdd113..1aef72a 100644 --- a/src/main/java/net/coreprotect/database/statement/ItemStatement.java +++ b/src/main/java/net/coreprotect/database/statement/ItemStatement.java @@ -23,6 +23,7 @@ public class ItemStatement { preparedStmt.setObject(8, byteData); preparedStmt.setInt(9, amount); preparedStmt.setInt(10, action); + preparedStmt.setInt(11, 0); // rolled_back preparedStmt.addBatch(); if (batchCount > 0 && batchCount % 1000 == 0) { diff --git a/src/main/java/net/coreprotect/language/Language.java b/src/main/java/net/coreprotect/language/Language.java index 8fc1281..1690ed4 100644 --- a/src/main/java/net/coreprotect/language/Language.java +++ b/src/main/java/net/coreprotect/language/Language.java @@ -167,8 +167,8 @@ public class Language { phrases.put(Phrase.PLEASE_SELECT, "Please select: \"{0}\" or \"{1}\"."); phrases.put(Phrase.PREVIEW_CANCELLED, "Preview cancelled."); phrases.put(Phrase.PREVIEW_CANCELLING, "Cancelling preview..."); - phrases.put(Phrase.PREVIEW_CONTAINER, "You can't preview container transactions."); phrases.put(Phrase.PREVIEW_IN_GAME, "You can only preview rollbacks in-game."); + phrases.put(Phrase.PREVIEW_TRANSACTION, "You can't preview {container|inventory} transactions."); phrases.put(Phrase.PURGE_ABORTED, "Purge failed. Database may be corrupt."); phrases.put(Phrase.PURGE_ERROR, "Unable to process {0} data!"); phrases.put(Phrase.PURGE_FAILED, "Purge failed. Please try again later."); @@ -217,6 +217,7 @@ public class Language { phrases.put(Phrase.UPDATE_NOTICE, "Notice: {0} is now available."); phrases.put(Phrase.UPGRADE_IN_PROGRESS, "Upgrade in progress. Please try again later."); phrases.put(Phrase.USER_NOT_FOUND, "User \"{0}\" not found."); + phrases.put(Phrase.USER_OFFLINE, "The user \"{0}\" is not online."); phrases.put(Phrase.USING_MYSQL, "Using MySQL for data storage."); phrases.put(Phrase.USING_SQLITE, "Using SQLite for data storage."); phrases.put(Phrase.VALID_DONATION_KEY, "Valid donation key."); diff --git a/src/main/java/net/coreprotect/language/Phrase.java b/src/main/java/net/coreprotect/language/Phrase.java index 2d5f9ee..32f78cf 100644 --- a/src/main/java/net/coreprotect/language/Phrase.java +++ b/src/main/java/net/coreprotect/language/Phrase.java @@ -148,8 +148,8 @@ public enum Phrase { PLEASE_SELECT, PREVIEW_CANCELLED, PREVIEW_CANCELLING, - PREVIEW_CONTAINER, PREVIEW_IN_GAME, + PREVIEW_TRANSACTION, PURGE_ABORTED, PURGE_ERROR, PURGE_FAILED, @@ -198,6 +198,7 @@ public enum Phrase { UPDATE_NOTICE, UPGRADE_IN_PROGRESS, USER_NOT_FOUND, + USER_OFFLINE, USING_MYSQL, USING_SQLITE, VALID_DONATION_KEY, diff --git a/src/main/java/net/coreprotect/patch/script/__2_21_0.java b/src/main/java/net/coreprotect/patch/script/__2_21_0.java index 65f7c74..ff8b839 100644 --- a/src/main/java/net/coreprotect/patch/script/__2_21_0.java +++ b/src/main/java/net/coreprotect/patch/script/__2_21_0.java @@ -2,13 +2,63 @@ package net.coreprotect.patch.script; import java.sql.Statement; +import net.coreprotect.config.Config; import net.coreprotect.config.ConfigFile; +import net.coreprotect.config.ConfigHandler; +import net.coreprotect.language.Phrase; +import net.coreprotect.language.Selector; +import net.coreprotect.patch.Patch; +import net.coreprotect.utility.Chat; public class __2_21_0 { protected static boolean patch(Statement statement) { try { + if (Config.getGlobal().MYSQL) { + try { + statement.executeUpdate("ALTER TABLE " + ConfigHandler.prefix + "item ADD COLUMN rolled_back tinyint(1) DEFAULT 0;"); + } + catch (Exception e) { + Chat.console(Phrase.build(Phrase.PATCH_SKIP_UPDATE, ConfigHandler.prefix + "item", Selector.FIRST, Selector.FIRST)); + } + + if (!Patch.continuePatch()) { + return false; + } + + try { + statement.executeUpdate("ALTER TABLE " + ConfigHandler.prefix + "container ADD COLUMN rolled_back_inventory tinyint(1) DEFAULT 0;"); + } + catch (Exception e) { + Chat.console(Phrase.build(Phrase.PATCH_SKIP_UPDATE, ConfigHandler.prefix + "container", Selector.FIRST, Selector.FIRST)); + } + } + else { + try { + statement.executeUpdate("ALTER TABLE " + ConfigHandler.prefix + "item ADD COLUMN rolled_back INTEGER DEFAULT 0;"); + } + catch (Exception e) { + Chat.console(Phrase.build(Phrase.PATCH_SKIP_UPDATE, ConfigHandler.prefix + "item", Selector.FIRST, Selector.FIRST)); + } + + if (!Patch.continuePatch()) { + return false; + } + + try { + statement.executeUpdate("ALTER TABLE " + ConfigHandler.prefix + "container ADD COLUMN rolled_back_inventory INTEGER DEFAULT 0;"); + } + catch (Exception e) { + Chat.console(Phrase.build(Phrase.PATCH_SKIP_UPDATE, ConfigHandler.prefix + "container", Selector.FIRST, Selector.FIRST)); + } + } + + if (!Patch.continuePatch()) { + return false; + } + ConfigFile.modifyLine("language.yml", "LOOKUP_VIEW_PAGE: \"To view a page, type \\\"{0}\\\".\"", null); + ConfigFile.modifyLine("language.yml", "PREVIEW_CONTAINER: \"You can't preview container transactions.\"", null); } catch (Exception e) { e.printStackTrace();