From 7170e2945269f6aa7f6a8468cad8e2ae473357eb Mon Sep 17 00:00:00 2001 From: Intelli Date: Thu, 23 Mar 2023 19:45:44 -0600 Subject: [PATCH] Added support for Folia --- pom.xml | 8 +- .../java/net/coreprotect/CoreProtect.java | 3 +- .../java/net/coreprotect/config/Config.java | 3 +- .../net/coreprotect/config/ConfigHandler.java | 1 + .../java/net/coreprotect/consumer/Queue.java | 17 +- .../database/ContainerRollback.java | 10 +- .../net/coreprotect/database/Rollback.java | 1834 +++++++++-------- .../entity/EntityDamageByEntityListener.java | 6 +- .../listener/entity/EntityDeathListener.java | 9 +- .../player/FoodLevelChangeListener.java | 6 +- .../listener/player/HopperPullListener.java | 4 +- .../listener/player/HopperPushListener.java | 4 +- .../player/InventoryChangeListener.java | 4 +- .../player/PlayerInteractListener.java | 6 +- .../net/coreprotect/thread/Scheduler.java | 91 + .../net/coreprotect/utility/ChestTool.java | 6 +- .../java/net/coreprotect/utility/Util.java | 16 +- .../utility/entity/EntityUtil.java | 5 +- .../CoreProtectEditSessionEvent.java | 3 +- src/main/resources/plugin.yml | 1 + 20 files changed, 1095 insertions(+), 942 deletions(-) create mode 100644 src/main/java/net/coreprotect/thread/Scheduler.java diff --git a/pom.xml b/pom.xml index 0e2f36e..b23ab2d 100755 --- a/pom.xml +++ b/pom.xml @@ -82,7 +82,7 @@ paper-repo - https://papermc.io/repo/repository/maven-public/ + https://repo.papermc.io/repository/maven-public/ codemc-repo @@ -100,9 +100,9 @@ bukkit: org.bukkit --> - io.papermc.paper - paper-api - 1.19-R0.1-SNAPSHOT + dev.folia + folia-api + 1.19.4-R0.1-SNAPSHOT provided diff --git a/src/main/java/net/coreprotect/CoreProtect.java b/src/main/java/net/coreprotect/CoreProtect.java index c7300dc..ba9d9ea 100755 --- a/src/main/java/net/coreprotect/CoreProtect.java +++ b/src/main/java/net/coreprotect/CoreProtect.java @@ -21,6 +21,7 @@ import net.coreprotect.listener.player.PlayerQuitListener; import net.coreprotect.paper.PaperAdapter; import net.coreprotect.thread.CacheHandler; import net.coreprotect.thread.NetworkHandler; +import net.coreprotect.thread.Scheduler; import net.coreprotect.utility.Chat; import net.coreprotect.utility.Color; import net.coreprotect.utility.Util; @@ -94,7 +95,7 @@ public final class CoreProtect extends JavaPlugin { Chat.console(Phrase.build(Phrase.LINK_DISCORD, "www.coreprotect.net/discord/")); Chat.console("--------------------"); - getServer().getScheduler().scheduleSyncDelayedTask(this, () -> { + Scheduler.scheduleSyncDelayedTask(this, () -> { try { Thread networkHandler = new Thread(new NetworkHandler(true, true)); networkHandler.start(); diff --git a/src/main/java/net/coreprotect/config/Config.java b/src/main/java/net/coreprotect/config/Config.java index 3c67fd3..d0500a7 100644 --- a/src/main/java/net/coreprotect/config/Config.java +++ b/src/main/java/net/coreprotect/config/Config.java @@ -20,6 +20,7 @@ import org.bukkit.World; import net.coreprotect.CoreProtect; import net.coreprotect.language.Language; +import net.coreprotect.thread.Scheduler; public class Config extends Language { @@ -383,7 +384,7 @@ public class Config extends Language { // for now this solution is good enough to ensure we only modify on the main thread final CompletableFuture complete = new CompletableFuture<>(); - Bukkit.getScheduler().runTask(CoreProtect.getInstance(), () -> { + Scheduler.runTask(CoreProtect.getInstance(), () -> { try { parseConfig(data); } diff --git a/src/main/java/net/coreprotect/config/ConfigHandler.java b/src/main/java/net/coreprotect/config/ConfigHandler.java index 3d60e8e..4d1f7a7 100644 --- a/src/main/java/net/coreprotect/config/ConfigHandler.java +++ b/src/main/java/net/coreprotect/config/ConfigHandler.java @@ -55,6 +55,7 @@ public class ConfigHandler extends Queue { public static HikariDataSource hikariDataSource = null; public static final boolean isSpigot = Util.isSpigot(); public static final boolean isPaper = Util.isPaper(); + public static final boolean isFolia = Util.isFolia(); public static volatile boolean serverRunning = false; public static volatile boolean converterRunning = false; public static volatile boolean purgeRunning = false; diff --git a/src/main/java/net/coreprotect/consumer/Queue.java b/src/main/java/net/coreprotect/consumer/Queue.java index caed20c..660a058 100755 --- a/src/main/java/net/coreprotect/consumer/Queue.java +++ b/src/main/java/net/coreprotect/consumer/Queue.java @@ -24,6 +24,7 @@ import net.coreprotect.config.ConfigHandler; import net.coreprotect.consumer.process.Process; import net.coreprotect.listener.block.BlockUtil; import net.coreprotect.model.BlockGroup; +import net.coreprotect.thread.Scheduler; import net.coreprotect.utility.Util; public class Queue { @@ -85,7 +86,7 @@ public class Queue { } protected static void queueBlockBreakValidate(final String user, final Block block, final BlockState blockState, final Material type, final String blockData, final int extraData, int ticks) { - Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(CoreProtect.getInstance(), () -> { + Scheduler.scheduleSyncDelayedTask(CoreProtect.getInstance(), () -> { try { if (!block.getType().equals(type)) { queueBlockBreak(user, blockState, type, blockData, null, extraData, 0); @@ -94,7 +95,7 @@ public class Queue { catch (Exception e) { e.printStackTrace(); } - }, ticks); + }, block.getLocation(), ticks); } protected static void queueBlockBreak(String user, BlockState block, Material type, String blockData, Material breakType, int extraData, int blockNumber) { @@ -178,18 +179,18 @@ public class Queue { } protected static void queueBlockPlaceDelayed(final String user, final Location placed, final Material type, final String blockData, final BlockState replaced, int ticks) { - Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(CoreProtect.getInstance(), () -> { + Scheduler.scheduleSyncDelayedTask(CoreProtect.getInstance(), () -> { try { queueBlockPlace(user, placed.getBlock().getState(), type, replaced, null, -1, 0, blockData); } catch (Exception e) { e.printStackTrace(); } - }, ticks); + }, placed, ticks); } protected static void queueBlockPlaceValidate(final String user, final BlockState blockLocation, final Block block, final BlockState blockReplaced, final Material forceT, final int forceD, final int forceData, final String blockData, int ticks) { - Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(CoreProtect.getInstance(), () -> { + Scheduler.scheduleSyncDelayedTask(CoreProtect.getInstance(), () -> { try { Material blockType = block.getType(); if (blockType.equals(forceT)) { @@ -204,11 +205,11 @@ public class Queue { catch (Exception e) { e.printStackTrace(); } - }, ticks); + }, blockLocation.getLocation(), ticks); } protected static void queueBlockGravityValidate(final String user, final Location location, final Block block, final Material blockType, int ticks) { - Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(CoreProtect.getInstance(), () -> { + Scheduler.scheduleSyncDelayedTask(CoreProtect.getInstance(), () -> { try { Block placementBlock = BlockUtil.gravityScan(location, blockType, user); if (!block.equals(placementBlock)) { @@ -218,7 +219,7 @@ public class Queue { catch (Exception e) { e.printStackTrace(); } - }, ticks); + }, location, ticks); } protected static void queueContainerBreak(String user, Location location, Material type, ItemStack[] oldInventory) { diff --git a/src/main/java/net/coreprotect/database/ContainerRollback.java b/src/main/java/net/coreprotect/database/ContainerRollback.java index 14e1e7b..61ea742 100644 --- a/src/main/java/net/coreprotect/database/ContainerRollback.java +++ b/src/main/java/net/coreprotect/database/ContainerRollback.java @@ -6,7 +6,6 @@ import java.util.Collections; import java.util.List; import java.util.Map; -import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.block.Block; @@ -24,6 +23,7 @@ import net.coreprotect.consumer.Queue; import net.coreprotect.consumer.process.Process; import net.coreprotect.language.Phrase; import net.coreprotect.model.BlockGroup; +import net.coreprotect.thread.Scheduler; import net.coreprotect.utility.Chat; import net.coreprotect.utility.Util; @@ -46,9 +46,9 @@ public class ContainerRollback extends Queue { 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 }); + ConfigHandler.rollbackHash.put(userString, new int[] { 0, 0, 0, 0, 0 }); - Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(CoreProtect.getInstance(), new Runnable() { + Scheduler.scheduleSyncDelayedTask(CoreProtect.getInstance(), new Runnable() { @Override public void run() { try { @@ -140,13 +140,13 @@ public class ContainerRollback extends Queue { } matchingFrames.clear(); - ConfigHandler.rollbackHash.put(finalUserString, new int[] { itemCount, modifyCount, entityCount, 1 }); + ConfigHandler.rollbackHash.put(finalUserString, new int[] { itemCount, modifyCount, entityCount, 1, 1 }); } catch (Exception e) { e.printStackTrace(); } } - }, 0); + }, location, 0); int[] rollbackHashData = ConfigHandler.rollbackHash.get(finalUserString); int next = rollbackHashData[3]; diff --git a/src/main/java/net/coreprotect/database/Rollback.java b/src/main/java/net/coreprotect/database/Rollback.java index 84b599c..b5b76c2 100644 --- a/src/main/java/net/coreprotect/database/Rollback.java +++ b/src/main/java/net/coreprotect/database/Rollback.java @@ -10,6 +10,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Locale; import java.util.Map; @@ -90,6 +91,7 @@ import net.coreprotect.language.Phrase; import net.coreprotect.language.Selector; import net.coreprotect.model.BlockGroup; import net.coreprotect.thread.CacheHandler; +import net.coreprotect.thread.Scheduler; import net.coreprotect.utility.Chat; import net.coreprotect.utility.ChestTool; import net.coreprotect.utility.Color; @@ -145,9 +147,10 @@ public class Rollback extends Queue { itemList = Lookup.performLookupRaw(statement, user, checkUuids, checkUsers, itemRestrictList, itemExcludeList, excludeUserList, itemActionList, location, radius, null, startTime, endTime, -1, -1, restrictWorld, lookup); } + LinkedHashSet worldList = new LinkedHashSet<>(); TreeMap chunkList = new TreeMap<>(); - HashMap> dataList = new HashMap<>(); - HashMap> itemDataList = new HashMap<>(); + HashMap>> dataList = new HashMap<>(); + HashMap>> itemDataList = new HashMap<>(); boolean inventoryRollback = actionList.contains(11); /* @@ -216,33 +219,43 @@ public class Rollback extends Queue { UserStatement.loadName(statement.getConnection(), userId); } - HashMap> modifyList = dataList; + HashMap>> modifyList = dataList; if (listC == 1) { modifyList = itemDataList; } - if (modifyList.get(chunkKey) == null) { - // Integer[][] chunkSections = new Integer[((worldMax - worldMin) >> 4)][]; - // adjacentDataList.put(chunkKey, chunkSections); - dataList.put(chunkKey, new ArrayList<>()); - itemDataList.put(chunkKey, new ArrayList<>()); + if (modifyList.get(rowWorldId) == null) { + dataList.put(rowWorldId, new HashMap<>()); + itemDataList.put(rowWorldId, new HashMap<>()); + worldList.add(rowWorldId); } - modifyList.get(chunkKey).add(result); + if (modifyList.get(rowWorldId).get(chunkKey) == null) { + // Integer[][] chunkSections = new Integer[((worldMax - worldMin) >> 4)][]; + // adjacentDataList.put(chunkKey, chunkSections); + dataList.get(rowWorldId).put(chunkKey, new ArrayList<>()); + itemDataList.get(rowWorldId).put(chunkKey, new ArrayList<>()); + } + + modifyList.get(rowWorldId).get(chunkKey).add(result); } listC++; } if (rollbackType == 1) { // Restore - Iterator>> dlIterator = dataList.entrySet().iterator(); + Iterator>>> dlIterator = dataList.entrySet().iterator(); while (dlIterator.hasNext()) { - Collections.reverse(dlIterator.next().getValue()); + for (ArrayList map : dlIterator.next().getValue().values()) { + Collections.reverse(map); + } } dlIterator = itemDataList.entrySet().iterator(); while (dlIterator.hasNext()) { - Collections.reverse(dlIterator.next().getValue()); + for (ArrayList map : dlIterator.next().getValue().values()) { + Collections.reverse(map); + } } } @@ -284,7 +297,7 @@ public class Rollback extends Queue { } } - ConfigHandler.rollbackHash.put(userString, new int[] { 0, 0, 0, 0 }); + ConfigHandler.rollbackHash.put(userString, new int[] { 0, 0, 0, 0, 0 }); final String finalUserString = userString; for (Entry entry : Util.entriesSortedByValues(chunkList)) { @@ -293,843 +306,129 @@ public class Rollback extends Queue { int itemCount = 0; int blockCount = 0; int entityCount = 0; + int scannedWorldData = 0; int[] rollbackHashData = ConfigHandler.rollbackHash.get(finalUserString); itemCount = rollbackHashData[0]; blockCount = rollbackHashData[1]; entityCount = rollbackHashData[2]; + scannedWorldData = rollbackHashData[4]; - final long chunkKey = entry.getKey(); + long chunkKey = entry.getKey(); + final int finalChunkX = (int) chunkKey; + final int finalChunkZ = (int) (chunkKey >> 32); final CommandSender finalUser = user; - final HashMap> finalBlockList = dataList; - final HashMap> finalItemList = itemDataList; - ConfigHandler.rollbackHash.put(finalUserString, new int[] { itemCount, blockCount, entityCount, 0 }); - Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(CoreProtect.getInstance(), () -> { - try { - boolean clearInventories = false; - if (Config.getGlobal().ROLLBACK_ITEMS) { - clearInventories = true; - } + HashMap worldMap = new HashMap<>(); + for (int rollbackWorldId : worldList) { + String rollbackWorld = Util.getWorldName(rollbackWorldId); + if (rollbackWorld.length() == 0) { + continue; + } - ArrayList data = finalBlockList.get(chunkKey); - ArrayList itemData = finalItemList.get(chunkKey); - Map chunkChanges = new LinkedHashMap<>(); + World bukkitRollbackWorld = Bukkit.getServer().getWorld(rollbackWorld); + if (bukkitRollbackWorld == null) { + continue; + } - int finalChunkX = (int) chunkKey; - int finalChunkZ = (int) (chunkKey >> 32); - for (Object[] row : data) { - int unixtimestamp = (int) (System.currentTimeMillis() / 1000L); - int[] rollbackHashData1 = ConfigHandler.rollbackHash.get(finalUserString); - int itemCount1 = rollbackHashData1[0]; - int blockCount1 = rollbackHashData1[1]; - int entityCount1 = rollbackHashData1[2]; - // int rowId = row[0]; - int rowTime = (Integer) row[1]; - int rowUserId = (Integer) row[2]; - int rowX = (Integer) row[3]; - int rowY = (Integer) row[4]; - int rowZ = (Integer) row[5]; - int rowTypeRaw = (Integer) row[6]; - int rowData = (Integer) row[7]; - int rowAction = (Integer) row[8]; - int rowRolledBack = Util.rolledBack((Integer) row[9], false); - int rowWorldId = (Integer) row[10]; - byte[] rowMeta = (byte[]) row[12]; - byte[] rowBlockData = (byte[]) row[13]; - String blockDataString = Util.byteDataToString(rowBlockData, rowTypeRaw); - Material rowType = Util.getType(rowTypeRaw); + worldMap.put(rollbackWorldId, bukkitRollbackWorld); + } - List meta = null; - if (rowMeta != null) { - ByteArrayInputStream metaByteStream = new ByteArrayInputStream(rowMeta); - BukkitObjectInputStream metaObjectStream = new BukkitObjectInputStream(metaByteStream); - @SuppressWarnings("unchecked") - List metaList = (List) metaObjectStream.readObject(); - metaObjectStream.close(); - metaByteStream.close(); - meta = metaList; + ConfigHandler.rollbackHash.put(finalUserString, new int[] { itemCount, blockCount, entityCount, 0, scannedWorldData }); + for (Entry rollbackWorlds : worldMap.entrySet()) { + Integer rollbackWorldId = rollbackWorlds.getKey(); + World bukkitRollbackWorld = rollbackWorlds.getValue(); + Location chunkLocation = new Location(bukkitRollbackWorld, (finalChunkX << 4), 0, (finalChunkZ << 4)); + final HashMap> finalBlockList = dataList.get(rollbackWorldId); + final HashMap> finalItemList = itemDataList.get(rollbackWorldId); + + Scheduler.scheduleSyncDelayedTask(CoreProtect.getInstance(), () -> { + try { + boolean clearInventories = false; + if (Config.getGlobal().ROLLBACK_ITEMS) { + clearInventories = true; } - BlockData blockData = null; - if (blockDataString != null && blockDataString.contains(":")) { - try { - blockData = Bukkit.getServer().createBlockData(blockDataString); + ArrayList data = finalBlockList.get(chunkKey); + ArrayList itemData = finalItemList.get(chunkKey); + Map chunkChanges = new LinkedHashMap<>(); + + for (Object[] row : data) { + int unixtimestamp = (int) (System.currentTimeMillis() / 1000L); + int[] rollbackHashData1 = ConfigHandler.rollbackHash.get(finalUserString); + int itemCount1 = rollbackHashData1[0]; + int blockCount1 = rollbackHashData1[1]; + int entityCount1 = rollbackHashData1[2]; + int scannedWorlds = rollbackHashData1[4]; + // int rowId = row[0]; + int rowTime = (Integer) row[1]; + int rowUserId = (Integer) row[2]; + int rowX = (Integer) row[3]; + int rowY = (Integer) row[4]; + int rowZ = (Integer) row[5]; + int rowTypeRaw = (Integer) row[6]; + int rowData = (Integer) row[7]; + int rowAction = (Integer) row[8]; + int rowRolledBack = Util.rolledBack((Integer) row[9], false); + int rowWorldId = (Integer) row[10]; + byte[] rowMeta = (byte[]) row[12]; + byte[] rowBlockData = (byte[]) row[13]; + String blockDataString = Util.byteDataToString(rowBlockData, rowTypeRaw); + Material rowType = Util.getType(rowTypeRaw); + + List meta = null; + if (rowMeta != null) { + ByteArrayInputStream metaByteStream = new ByteArrayInputStream(rowMeta); + BukkitObjectInputStream metaObjectStream = new BukkitObjectInputStream(metaByteStream); + @SuppressWarnings("unchecked") + List metaList = (List) metaObjectStream.readObject(); + metaObjectStream.close(); + metaByteStream.close(); + meta = metaList; } - catch (Exception e) { - // corrupt BlockData, let the server automatically set the BlockData instead - } - } - BlockData rawBlockData = null; - if (blockData != null) { - rawBlockData = blockData.clone(); - } - if (rawBlockData == null && rowType != null && rowType.isBlock()) { - rawBlockData = Util.createBlockData(rowType); - } - - String rowUser = ConfigHandler.playerIdCacheReversed.get(rowUserId); - int oldTypeRaw = rowTypeRaw; - Material oldTypeMaterial = Util.getType(oldTypeRaw); - - if (rowAction == 1 && rollbackType == 0) { // block placement - rowType = Material.AIR; - blockData = null; - rowTypeRaw = 0; - } - else if (rowAction == 0 && rollbackType == 1) { // block removal - rowType = Material.AIR; - blockData = null; - rowTypeRaw = 0; - } - else if (rowAction == 4 && rollbackType == 0) { // entity placement - rowType = null; - rowTypeRaw = 0; - } - else if (rowAction == 3 && rollbackType == 1) { // entity removal - rowType = null; - rowTypeRaw = 0; - } - - if (preview > 0) { - if (rowAction != 3) { // entity kill - String world = Util.getWorldName(rowWorldId); - if (world.length() == 0) { - continue; + BlockData blockData = null; + if (blockDataString != null && blockDataString.contains(":")) { + try { + blockData = Bukkit.getServer().createBlockData(blockDataString); } - - World bukkitWorld = Bukkit.getServer().getWorld(world); - if (bukkitWorld == null) { - continue; - } - - Block block = new Location(bukkitWorld, rowX, rowY, rowZ).getBlock(); - if (preview == 2) { - Material blockType = block.getType(); - if (!BukkitAdapter.ADAPTER.isItemFrame(blockType) && !blockType.equals(Material.PAINTING) && !blockType.equals(Material.ARMOR_STAND) && !blockType.equals(Material.END_CRYSTAL)) { - Util.prepareTypeAndData(chunkChanges, block, blockType, block.getBlockData(), true); - blockCount1++; - } - } - else { - if ((!BukkitAdapter.ADAPTER.isItemFrame(rowType)) && (rowType != Material.PAINTING) && (rowType != Material.ARMOR_STAND) && (rowType != Material.END_CRYSTAL)) { - Util.prepareTypeAndData(chunkChanges, block, rowType, blockData, true); - blockCount1++; - } - } - } - else { - entityCount1++; - } - } - else if (rowAction == 3) { // entity kill - String world = Util.getWorldName(rowWorldId); - if (world.length() == 0) { - continue; - } - - World bukkitWorld = Bukkit.getServer().getWorld(world); - if (bukkitWorld == null) { - continue; - } - Block block = bukkitWorld.getBlockAt(rowX, rowY, rowZ); - if (!bukkitWorld.isChunkLoaded(block.getChunk())) { - bukkitWorld.getChunkAt(block.getLocation()); - } - - if (rowTypeRaw > 0) { - // spawn in entity - if (rowRolledBack == 0) { - EntityType entity_type = Util.getEntityType(rowTypeRaw); - Queue.queueEntitySpawn(rowUser, block.getState(), entity_type, rowData); - entityCount1++; - } - } - else if (oldTypeRaw > 0) { - // attempt to remove entity - if (rowRolledBack == 1) { - boolean removed = false; - int entityId = -1; - String entityName = Util.getEntityType(oldTypeRaw).name(); - String token = "" + rowX + "." + rowY + "." + rowZ + "." + rowWorldId + "." + entityName + ""; - Object[] cachedEntity = CacheHandler.entityCache.get(token); - - if (cachedEntity != null) { - entityId = (Integer) cachedEntity[1]; - } - - int xmin = rowX - 5; - int xmax = rowX + 5; - int ymin = rowY - 1; - int ymax = rowY + 1; - int zmin = rowZ - 5; - int zmax = rowZ + 5; - - for (Entity entity : block.getChunk().getEntities()) { - if (entityId > -1) { - int id = entity.getEntityId(); - if (id == entityId) { - entityCount1++; - removed = true; - entity.remove(); - break; - } - } - else { - if (entity.getType().equals(Util.getEntityType(oldTypeRaw))) { - Location entityLocation = entity.getLocation(); - int entityx = entityLocation.getBlockX(); - int entityY = entityLocation.getBlockY(); - int entityZ = entityLocation.getBlockZ(); - - if (entityx >= xmin && entityx <= xmax && entityY >= ymin && entityY <= ymax && entityZ >= zmin && entityZ <= zmax) { - entityCount1++; - removed = true; - entity.remove(); - break; - } - } - } - } - - if (!removed && entityId > -1) { - for (Entity entity : block.getWorld().getLivingEntities()) { - int id = entity.getEntityId(); - if (id == entityId) { - entityCount1++; - removed = true; - entity.remove(); - break; - } - } - } - } - } - } - else { - if (rowType == null) { - continue; - } - - String world = Util.getWorldName(rowWorldId); - if (world.length() == 0) { - continue; - } - - World bukkitWorld = Bukkit.getServer().getWorld(world); - if (bukkitWorld == null) { - continue; - } - Block block = bukkitWorld.getBlockAt(rowX, rowY, rowZ); - if (!bukkitWorld.isChunkLoaded(block.getChunk())) { - bukkitWorld.getChunkAt(block.getLocation()); - } - - boolean changeBlock = true; - boolean countBlock = true; - Material changeType = block.getType(); - BlockData changeBlockData = block.getBlockData(); - BlockData pendingChangeData = chunkChanges.get(block); - Material pendingChangeType = changeType; - if (pendingChangeData != null) { - pendingChangeType = pendingChangeData.getMaterial(); - } - else { - pendingChangeData = changeBlockData; - } - - if (rowRolledBack == 1 && rollbackType == 0) { // rollback - countBlock = false; - } - - if ((rowType == pendingChangeType) && ((!BukkitAdapter.ADAPTER.isItemFrame(oldTypeMaterial)) && (oldTypeMaterial != Material.PAINTING) && (oldTypeMaterial != Material.ARMOR_STAND)) && (oldTypeMaterial != Material.END_CRYSTAL)) { - // block is already changed! - BlockData checkData = rowType == Material.AIR ? blockData : rawBlockData; - if (checkData != null) { - if (checkData.getAsString().equals(pendingChangeData.getAsString()) || checkData instanceof MultipleFacing || checkData instanceof Stairs || checkData instanceof RedstoneWire) { - if (rowType != Material.CHEST && rowType != Material.TRAPPED_CHEST) { // always update double chests - changeBlock = false; - } - } - } - else if (rowType == Material.AIR) { - changeBlock = false; - } - - countBlock = false; - } - else if ((pendingChangeType != Material.AIR) && (pendingChangeType != Material.CAVE_AIR)) { - countBlock = true; - } - - if ((pendingChangeType == Material.WATER) && (rowType != Material.AIR) && (rowType != Material.CAVE_AIR) && blockData != null) { - if (blockData instanceof Waterlogged) { - if (Material.WATER.createBlockData().equals(block.getBlockData())) { - Waterlogged waterlogged = (Waterlogged) blockData; - waterlogged.setWaterlogged(true); - } + catch (Exception e) { + // corrupt BlockData, let the server automatically set the BlockData instead } } - try { - if (changeBlock) { - /* If modifying the head of a piston, update the base piston block to prevent it from being destroyed */ - if (changeBlockData instanceof PistonHead) { - PistonHead pistonHead = (PistonHead) changeBlockData; - Block pistonBlock = block.getRelative(pistonHead.getFacing().getOppositeFace()); - BlockData pistonData = pistonBlock.getBlockData(); - if (pistonData instanceof Piston) { - Piston piston = (Piston) pistonData; - piston.setExtended(false); - pistonBlock.setBlockData(piston, false); - } - } - else if (rowType == Material.MOVING_PISTON && blockData instanceof TechnicalPiston && !(blockData instanceof PistonHead)) { - TechnicalPiston technicalPiston = (TechnicalPiston) blockData; - rowType = (technicalPiston.getType() == org.bukkit.block.data.type.TechnicalPiston.Type.STICKY ? Material.STICKY_PISTON : Material.PISTON); - blockData = rowType.createBlockData(); - ((Piston) blockData).setFacing(technicalPiston.getFacing()); - } - - if ((rowType == Material.AIR) && ((BukkitAdapter.ADAPTER.isItemFrame(oldTypeMaterial)) || (oldTypeMaterial == Material.PAINTING))) { - HangingUtil.removeHanging(block.getState(), blockDataString); - } - else if ((BukkitAdapter.ADAPTER.isItemFrame(rowType)) || (rowType == Material.PAINTING)) { - HangingUtil.spawnHanging(block.getState(), rowType, blockDataString, rowData); - } - else if ((rowType == Material.ARMOR_STAND)) { - Location location1 = block.getLocation(); - location1.setX(location1.getX() + 0.50); - location1.setZ(location1.getZ() + 0.50); - location1.setYaw(rowData); - boolean exists = false; - - for (Entity entity : block.getChunk().getEntities()) { - if (entity instanceof ArmorStand) { - if (entity.getLocation().getBlockX() == location1.getBlockX() && entity.getLocation().getBlockY() == location1.getBlockY() && entity.getLocation().getBlockZ() == location1.getBlockZ()) { - exists = true; - } - } - } - - if (!exists) { - Entity entity = block.getLocation().getWorld().spawnEntity(location1, EntityType.ARMOR_STAND); - entity.teleport(location1); - } - } - else if ((rowType == Material.END_CRYSTAL)) { - Location location1 = block.getLocation(); - location1.setX(location1.getX() + 0.50); - location1.setZ(location1.getZ() + 0.50); - boolean exists = false; - - for (Entity entity : block.getChunk().getEntities()) { - if (entity instanceof EnderCrystal) { - if (entity.getLocation().getBlockX() == location1.getBlockX() && entity.getLocation().getBlockY() == location1.getBlockY() && entity.getLocation().getBlockZ() == location1.getBlockZ()) { - exists = true; - } - } - } - - if (!exists) { - Entity entity = block.getLocation().getWorld().spawnEntity(location1, EntityType.ENDER_CRYSTAL); - EnderCrystal enderCrystal = (EnderCrystal) entity; - enderCrystal.setShowingBottom((rowData != 0)); - entity.teleport(location1); - } - } - else if ((rowType == Material.AIR) && ((oldTypeMaterial == Material.WATER))) { - if (pendingChangeData instanceof Waterlogged) { - Waterlogged waterlogged = (Waterlogged) pendingChangeData; - waterlogged.setWaterlogged(false); - Util.prepareTypeAndData(chunkChanges, block, null, waterlogged, false); - } - else { - Util.prepareTypeAndData(chunkChanges, block, rowType, blockData, true); - } - - if (countBlock) { - blockCount1++; - } - } - else if ((rowType == Material.AIR) && ((oldTypeMaterial == Material.SNOW))) { - Util.prepareTypeAndData(chunkChanges, block, rowType, blockData, true); - if (countBlock) { - blockCount1++; - } - } - else if ((rowType == Material.AIR) && ((oldTypeMaterial == Material.END_CRYSTAL))) { - for (Entity entity : block.getChunk().getEntities()) { - if (entity instanceof EnderCrystal) { - if (entity.getLocation().getBlockX() == rowX && entity.getLocation().getBlockY() == rowY && entity.getLocation().getBlockZ() == rowZ) { - entity.remove(); - } - } - } - } - else if (rollbackType == 0 && rowAction == 0 && (rowType == Material.AIR)) { - // broke block ID #0 - } - else if ((rowType == Material.AIR) || (rowType == Material.TNT)) { - if (clearInventories) { - if (BlockGroup.CONTAINERS.contains(changeType)) { - Inventory inventory = Util.getContainerInventory(block.getState(), false); - if (inventory != null) { - inventory.clear(); - } - } - else if (BlockGroup.CONTAINERS.contains(Material.ARMOR_STAND)) { - if ((oldTypeMaterial == Material.ARMOR_STAND)) { - for (Entity entity : block.getChunk().getEntities()) { - if (entity instanceof ArmorStand) { - Location entityLocation = entity.getLocation(); - entityLocation.setY(entityLocation.getY() + 0.99); - - if (entityLocation.getBlockX() == rowX && entityLocation.getBlockY() == rowY && entityLocation.getBlockZ() == rowZ) { - EntityEquipment equipment = Util.getEntityEquipment((LivingEntity) entity); - if (equipment != null) { - equipment.clear(); - } - - entityLocation.setY(entityLocation.getY() - 1.99); - entity.teleport(entityLocation); - entity.remove(); - } - } - } - } - } - } - - boolean remove = true; - if ((rowType == Material.AIR)) { - if (pendingChangeData instanceof Waterlogged) { - Waterlogged waterlogged = (Waterlogged) pendingChangeData; - if (waterlogged.isWaterlogged()) { - Util.prepareTypeAndData(chunkChanges, block, Material.WATER, Material.WATER.createBlockData(), true); - remove = false; - } - } - else if ((pendingChangeType == Material.WATER)) { - if (rawBlockData instanceof Waterlogged) { - Waterlogged waterlogged = (Waterlogged) rawBlockData; - if (waterlogged.isWaterlogged()) { - remove = false; - } - } - } - } - - if (remove) { - boolean physics = true; - if ((changeType == Material.NETHER_PORTAL) || changeBlockData instanceof MultipleFacing || changeBlockData instanceof Snow || changeBlockData instanceof Stairs || changeBlockData instanceof RedstoneWire || changeBlockData instanceof Chest) { - physics = true; - } - else if (changeBlockData instanceof Bisected && !(changeBlockData instanceof TrapDoor)) { - Bisected bisected = (Bisected) changeBlockData; - Location bisectLocation = block.getLocation().clone(); - if (bisected.getHalf() == Half.TOP) { - bisectLocation.setY(bisectLocation.getY() - 1); - } - else { - bisectLocation.setY(bisectLocation.getY() + 1); - } - - int worldMaxHeight = bukkitWorld.getMaxHeight(); - int worldMinHeight = BukkitAdapter.ADAPTER.getMinHeight(bukkitWorld); - if (bisectLocation.getBlockY() >= worldMinHeight && bisectLocation.getBlockY() < worldMaxHeight) { - Block bisectBlock = block.getWorld().getBlockAt(bisectLocation); - Util.prepareTypeAndData(chunkChanges, bisectBlock, rowType, null, false); - - if (countBlock) { - blockCount1++; - } - } - } - else if (changeBlockData instanceof Bed) { - Bed bed = (Bed) changeBlockData; - if (bed.getPart() == Part.FOOT) { - Block adjacentBlock = block.getRelative(bed.getFacing()); - Util.prepareTypeAndData(chunkChanges, adjacentBlock, rowType, null, false); - } - } - - Util.prepareTypeAndData(chunkChanges, block, rowType, null, physics); - } - - if (countBlock) { - blockCount1++; - } - } - else if ((rowType == Material.SPAWNER)) { - try { - Util.prepareTypeAndData(chunkChanges, block, rowType, blockData, false); - CreatureSpawner mobSpawner = (CreatureSpawner) block.getState(); - mobSpawner.setSpawnedType(Util.getSpawnerType(rowData)); - mobSpawner.update(); - - if (countBlock) { - blockCount1++; - } - } - catch (Exception e) { - // e.printStackTrace(); - } - } - else if ((rowType == Material.SKELETON_SKULL) || (rowType == Material.SKELETON_WALL_SKULL) || (rowType == Material.WITHER_SKELETON_SKULL) || (rowType == Material.WITHER_SKELETON_WALL_SKULL) || (rowType == Material.ZOMBIE_HEAD) || (rowType == Material.ZOMBIE_WALL_HEAD) || (rowType == Material.PLAYER_HEAD) || (rowType == Material.PLAYER_WALL_HEAD) || (rowType == Material.CREEPER_HEAD) || (rowType == Material.CREEPER_WALL_HEAD) || (rowType == Material.DRAGON_HEAD) || (rowType == Material.DRAGON_WALL_HEAD)) { // skull - Util.prepareTypeAndData(chunkChanges, block, rowType, blockData, false); - if (rowData > 0) { - Queue.queueSkullUpdate(rowUser, block.getState(), rowData); - } - - if (countBlock) { - blockCount1++; - } - } - else if (Tag.SIGNS.isTagged(rowType)) {// sign - Util.prepareTypeAndData(chunkChanges, block, rowType, blockData, false); - Queue.queueSignUpdate(rowUser, block.getState(), rollbackType, rowTime); - - if (countBlock) { - blockCount1++; - } - } - else if (BlockGroup.SHULKER_BOXES.contains(rowType)) { - Util.prepareTypeAndData(chunkChanges, block, rowType, blockData, false); - if (countBlock) { - blockCount1++; - } - if (meta != null) { - Inventory inventory = Util.getContainerInventory(block.getState(), false); - for (Object value : meta) { - ItemStack item = Util.unserializeItemStackLegacy(value); - if (item != null) { - modifyContainerItems(rowType, inventory, 0, item, 1); - } - } - } - } - else if (rowType == Material.COMMAND_BLOCK || rowType == Material.REPEATING_COMMAND_BLOCK || rowType == Material.CHAIN_COMMAND_BLOCK) { // command block - Util.prepareTypeAndData(chunkChanges, block, rowType, blockData, false); - if (countBlock) { - blockCount1++; - } - - if (meta != null) { - CommandBlock commandBlock = (CommandBlock) block.getState(); - for (Object value : meta) { - if (value instanceof String) { - String string = (String) value; - commandBlock.setCommand(string); - commandBlock.update(); - } - } - } - } - else if ((rowType == Material.WATER)) { - if (pendingChangeData instanceof Waterlogged) { - Waterlogged waterlogged = (Waterlogged) pendingChangeData; - waterlogged.setWaterlogged(true); - Util.prepareTypeAndData(chunkChanges, block, null, waterlogged, false); - } - else { - Util.prepareTypeAndData(chunkChanges, block, rowType, blockData, false); - } - - if (countBlock) { - blockCount1++; - } - } - else if ((rowType == Material.NETHER_PORTAL) && rowAction == 0) { - Util.prepareTypeAndData(chunkChanges, block, Material.FIRE, null, true); - } - else if (blockData == null && rowData > 0 && (rowType == Material.IRON_DOOR || BlockGroup.DOORS.contains(rowType))) { - if (countBlock) { - blockCount1++; - } - - block.setType(rowType, false); - Door door = (Door) block.getBlockData(); - if (rowData >= 8) { - door.setHalf(Half.TOP); - rowData = rowData - 8; - } - else { - door.setHalf(Half.BOTTOM); - } - if (rowData >= 4) { - door.setHinge(Hinge.RIGHT); - rowData = rowData - 4; - } - else { - door.setHinge(Hinge.LEFT); - } - BlockFace face = BlockFace.NORTH; - - switch (rowData) { - case 0: - face = BlockFace.EAST; - break; - case 1: - face = BlockFace.SOUTH; - break; - case 2: - face = BlockFace.WEST; - break; - } - - door.setFacing(face); - door.setOpen(false); - block.setBlockData(door, false); - } - else if (blockData == null && rowData > 0 && (rowType.name().endsWith("_BED"))) { - if (countBlock) { - blockCount1++; - } - - block.setType(rowType, false); - Bed bed = (Bed) block.getBlockData(); - BlockFace face = BlockFace.NORTH; - - if (rowData > 4) { - bed.setPart(Part.HEAD); - rowData = rowData - 4; - } - - switch (rowData) { - case 2: - face = BlockFace.WEST; - break; - case 3: - face = BlockFace.EAST; - break; - case 4: - face = BlockFace.SOUTH; - break; - } - - bed.setFacing(face); - block.setBlockData(bed, false); - } - else if (rowType.name().endsWith("_BANNER")) { - Util.prepareTypeAndData(chunkChanges, block, rowType, blockData, false); - if (countBlock) { - blockCount1++; - } - - if (meta != null) { - Banner banner = (Banner) block.getState(); - - for (Object value : meta) { - if (value instanceof DyeColor) { - banner.setBaseColor((DyeColor) value); - } - else if (value instanceof Map) { - @SuppressWarnings("unchecked") - Pattern pattern = new Pattern((Map) value); - banner.addPattern(pattern); - } - } - - banner.update(); - } - } - else if (rowType != changeType && (BlockGroup.CONTAINERS.contains(rowType) || BlockGroup.CONTAINERS.contains(changeType))) { - block.setType(Material.AIR); // Clear existing container to prevent errors - - boolean isChest = (blockData instanceof Chest); - Util.prepareTypeAndData(chunkChanges, block, rowType, blockData, (isChest)); - if (isChest) { - ChestTool.updateDoubleChest(block, blockData, false); - } - - if (countBlock) { - blockCount1++; - } - } - else if (BlockGroup.UPDATE_STATE.contains(rowType) || rowType.name().contains("CANDLE")) { - Util.prepareTypeAndData(chunkChanges, block, rowType, blockData, true); - ChestTool.updateDoubleChest(block, blockData, true); - if (countBlock) { - blockCount1++; - } - } - else if (rowType != Material.AIR && rawBlockData instanceof Bisected && !(rawBlockData instanceof Stairs || rawBlockData instanceof TrapDoor)) { - Bisected bisected = (Bisected) rawBlockData; - Bisected bisectData = (Bisected) rawBlockData.clone(); - Location bisectLocation = block.getLocation().clone(); - if (bisected.getHalf() == Half.TOP) { - bisectData.setHalf(Half.BOTTOM); - bisectLocation.setY(bisectLocation.getY() - 1); - } - else { - bisectData.setHalf(Half.TOP); - bisectLocation.setY(bisectLocation.getY() + 1); - } - - int worldMaxHeight = bukkitWorld.getMaxHeight(); - int worldMinHeight = BukkitAdapter.ADAPTER.getMinHeight(bukkitWorld); - if (bisectLocation.getBlockY() >= worldMinHeight && bisectLocation.getBlockY() < worldMaxHeight) { - Block bisectBlock = block.getWorld().getBlockAt(bisectLocation); - Util.prepareTypeAndData(chunkChanges, bisectBlock, rowType, bisectData, false); - } - - Util.prepareTypeAndData(chunkChanges, block, rowType, blockData, false); - if (countBlock) { - blockCount1++; - blockCount1++; - } - } - else if (rowType != Material.AIR && rawBlockData instanceof Bed) { - Bed bed = (Bed) rawBlockData; - if (bed.getPart() == Part.FOOT) { - Block adjacentBlock = block.getRelative(bed.getFacing()); - Bed bedData = (Bed) rawBlockData.clone(); - bedData.setPart(Part.HEAD); - Util.prepareTypeAndData(chunkChanges, adjacentBlock, rowType, bedData, false); - if (countBlock) { - blockCount1++; - } - } - - Util.prepareTypeAndData(chunkChanges, block, rowType, blockData, true); - if (countBlock) { - blockCount1++; - } - } - else { - boolean physics = true; - /* - if (blockData instanceof MultipleFacing || BukkitAdapter.ADAPTER.isWall(blockData) || blockData instanceof Snow || blockData instanceof Stairs || blockData instanceof RedstoneWire || blockData instanceof Chest) { - physics = !(blockData instanceof Snow) || block.getY() <= BukkitAdapter.ADAPTER.getMinHeight(block.getWorld()) || (block.getWorld().getBlockAt(block.getX(), block.getY() - 1, block.getZ()).getType().equals(Material.GRASS_BLOCK)); - } - */ - - Util.prepareTypeAndData(chunkChanges, block, rowType, blockData, physics); - if (countBlock) { - blockCount1++; - } - } - } + BlockData rawBlockData = null; + if (blockData != null) { + rawBlockData = blockData.clone(); } - catch (Exception e) { - e.printStackTrace(); + if (rawBlockData == null && rowType != null && rowType.isBlock()) { + rawBlockData = Util.createBlockData(rowType); } - if ((rowType != Material.AIR) && changeBlock) { - if (rowUser.length() > 0) { - CacheHandler.lookupCache.put(rowX + "." + rowY + "." + rowZ + "." + rowWorldId, new Object[] { unixtimestamp, rowUser, rowType }); - } + String rowUser = ConfigHandler.playerIdCacheReversed.get(rowUserId); + int oldTypeRaw = rowTypeRaw; + Material oldTypeMaterial = Util.getType(oldTypeRaw); + + if (rowAction == 1 && rollbackType == 0) { // block placement + rowType = Material.AIR; + blockData = null; + rowTypeRaw = 0; } - } - - // count++; - ConfigHandler.rollbackHash.put(finalUserString, new int[] { itemCount1, blockCount1, entityCount1, 0 }); - } - data.clear(); - - // Apply cached changes - for (Entry chunkChange : chunkChanges.entrySet()) { - Block changeBlock = chunkChange.getKey(); - BlockData changeBlockData = chunkChange.getValue(); - if (preview > 0) { - Util.sendBlockChange((Player) finalUser, changeBlock.getLocation(), changeBlockData); - } - else { - Util.setTypeAndData(changeBlock, null, changeBlockData, true); - } - } - chunkChanges.clear(); - - Map> sortPlayers = new HashMap<>(); - Object container = null; - Material containerType = null; - boolean containerInit = false; - int lastX = 0; - int lastY = 0; - int lastZ = 0; - int lastWorldId = 0; - String lastFace = ""; - - for (Object[] row : itemData) { - int[] rollbackHashData1 = ConfigHandler.rollbackHash.get(finalUserString); - int itemCount1 = rollbackHashData1[0]; - int blockCount1 = rollbackHashData1[1]; - int entityCount1 = rollbackHashData1[2]; - int rowX = (Integer) row[3]; - int rowY = (Integer) row[4]; - int rowZ = (Integer) row[5]; - int rowTypeRaw = (Integer) row[6]; - int rowData = (Integer) row[7]; - int rowAction = (Integer) row[8]; - int rowRolledBack = Util.rolledBack((Integer) row[9], false); - int rowWorldId = (Integer) row[10]; - int rowAmount = (Integer) row[11]; - byte[] rowMetadata = (byte[]) row[12]; - Material rowType = Util.getType(rowTypeRaw); - - int rolledBackInventory = Util.rolledBack((Integer) row[9], true); - if (rowType != null) { - if (inventoryRollback && ((rollbackType == 0 && rolledBackInventory == 0) || (rollbackType == 1 && rolledBackInventory == 1))) { - Material inventoryItem = Util.itemFilter(rowType, ((Integer) row[14] == 0)); - 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 || rowAction == ItemLogger.ITEM_THROW || rowAction == ItemLogger.ITEM_SHOOT || rowAction == ItemLogger.ITEM_BREAK || rowAction == ItemLogger.ITEM_DESTROY || rowAction == ItemLogger.ITEM_CREATE || rowAction == ItemLogger.ITEM_SELL || rowAction == ItemLogger.ITEM_BUY) { - inventoryAction = ((rowAction == ItemLogger.ITEM_PICKUP || rowAction == ItemLogger.ITEM_CREATE || rowAction == ItemLogger.ITEM_BUY) ? 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(inventoryItem, rowAmount); - 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[2]).clone(), action ^ 1); - } - int modifiedArmor = modifyContainerItems(containerType, player.getInventory(), (Integer) populatedStack[0], (ItemStack) populatedStack[2], action); - if (modifiedArmor > -1) { - List currentSortList = sortPlayers.getOrDefault(player, new ArrayList<>()); - if (!currentSortList.contains(modifiedArmor)) { - currentSortList.add(modifiedArmor); - } - sortPlayers.put(player, currentSortList); - } - - 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) + else if (rowAction == 0 && rollbackType == 1) { // block removal + rowType = Material.AIR; + blockData = null; + rowTypeRaw = 0; + } + else if (rowAction == 4 && rollbackType == 0) { // entity placement + rowType = null; + rowTypeRaw = 0; + } + else if (rowAction == 3 && rollbackType == 1) { // entity removal + rowType = null; + rowTypeRaw = 0; } - if (inventoryRollback || rowAction > 1) { - continue; // skip inventory & ender chest transactions - } - - if ((rollbackType == 0 && rowRolledBack == 0) || (rollbackType == 1 && rowRolledBack == 1)) { - ItemStack itemstack = new ItemStack(rowType, rowAmount); - Object[] populatedStack = populateItemStack(itemstack, rowMetadata); - String faceData = (String) populatedStack[1]; - - if (!containerInit || rowX != lastX || rowY != lastY || rowZ != lastZ || rowWorldId != lastWorldId || !faceData.equals(lastFace)) { - container = null; // container patch 2.14.0 + if (preview > 0) { + if (rowAction != 3) { // entity kill String world = Util.getWorldName(rowWorldId); if (world.length() == 0) { continue; @@ -1139,112 +438,854 @@ public class Rollback extends Queue { if (bukkitWorld == null) { continue; } - Block block = bukkitWorld.getBlockAt(rowX, rowY, rowZ); - if (!bukkitWorld.isChunkLoaded(block.getChunk())) { - bukkitWorld.getChunkAt(block.getLocation()); - } - if (BlockGroup.CONTAINERS.contains(block.getType())) { - BlockState blockState = block.getState(); - if (blockState instanceof Jukebox) { - container = blockState; + Block block = new Location(bukkitWorld, rowX, rowY, rowZ).getBlock(); + if (preview == 2) { + Material blockType = block.getType(); + if (!BukkitAdapter.ADAPTER.isItemFrame(blockType) && !blockType.equals(Material.PAINTING) && !blockType.equals(Material.ARMOR_STAND) && !blockType.equals(Material.END_CRYSTAL)) { + Util.prepareTypeAndData(chunkChanges, block, blockType, block.getBlockData(), true); + blockCount1++; } - else { - container = Util.getContainerInventory(blockState, false); + } + else { + if ((!BukkitAdapter.ADAPTER.isItemFrame(rowType)) && (rowType != Material.PAINTING) && (rowType != Material.ARMOR_STAND) && (rowType != Material.END_CRYSTAL)) { + Util.prepareTypeAndData(chunkChanges, block, rowType, blockData, true); + blockCount1++; + } + } + } + else { + entityCount1++; + } + } + else if (rowAction == 3) { // entity kill + String world = Util.getWorldName(rowWorldId); + if (world.length() == 0) { + continue; + } + + World bukkitWorld = Bukkit.getServer().getWorld(world); + if (bukkitWorld == null) { + continue; + } + Block block = bukkitWorld.getBlockAt(rowX, rowY, rowZ); + if (!bukkitWorld.isChunkLoaded(block.getChunk())) { + bukkitWorld.getChunkAt(block.getLocation()); + } + + if (rowTypeRaw > 0) { + // spawn in entity + if (rowRolledBack == 0) { + EntityType entity_type = Util.getEntityType(rowTypeRaw); + Queue.queueEntitySpawn(rowUser, block.getState(), entity_type, rowData); + entityCount1++; + } + } + else if (oldTypeRaw > 0) { + // attempt to remove entity + if (rowRolledBack == 1) { + boolean removed = false; + int entityId = -1; + String entityName = Util.getEntityType(oldTypeRaw).name(); + String token = "" + rowX + "." + rowY + "." + rowZ + "." + rowWorldId + "." + entityName + ""; + Object[] cachedEntity = CacheHandler.entityCache.get(token); + + if (cachedEntity != null) { + entityId = (Integer) cachedEntity[1]; } - containerType = block.getType(); - } - else if (BlockGroup.CONTAINERS.contains(Material.ARMOR_STAND) || BlockGroup.CONTAINERS.contains(Material.ITEM_FRAME)) { + int xmin = rowX - 5; + int xmax = rowX + 5; + int ymin = rowY - 1; + int ymax = rowY + 1; + int zmin = rowZ - 5; + int zmax = rowZ + 5; + for (Entity entity : block.getChunk().getEntities()) { - if (entity.getLocation().getBlockX() == rowX && entity.getLocation().getBlockY() == rowY && entity.getLocation().getBlockZ() == rowZ) { - if (entity instanceof ArmorStand) { - container = Util.getEntityEquipment((LivingEntity) entity); - containerType = Material.ARMOR_STAND; + if (entityId > -1) { + int id = entity.getEntityId(); + if (id == entityId) { + entityCount1++; + removed = true; + entity.remove(); + break; } - else if (entity instanceof ItemFrame) { - container = entity; - containerType = Material.ITEM_FRAME; - if (faceData.length() > 0 && (BlockFace.valueOf(faceData) == ((ItemFrame) entity).getFacing())) { + } + else { + if (entity.getType().equals(Util.getEntityType(oldTypeRaw))) { + Location entityLocation = entity.getLocation(); + int entityx = entityLocation.getBlockX(); + int entityY = entityLocation.getBlockY(); + int entityZ = entityLocation.getBlockZ(); + + if (entityx >= xmin && entityx <= xmax && entityY >= ymin && entityY <= ymax && entityZ >= zmin && entityZ <= zmax) { + entityCount1++; + removed = true; + entity.remove(); break; } } } } - } - lastX = rowX; - lastY = rowY; - lastZ = rowZ; - lastWorldId = rowWorldId; - lastFace = faceData; + if (!removed && entityId > -1) { + for (Entity entity : block.getWorld().getLivingEntities()) { + int id = entity.getEntityId(); + if (id == entityId) { + entityCount1++; + removed = true; + entity.remove(); + break; + } + } + } + } + } + } + else { + if (rowType == null) { + continue; } - if (container != null) { - int action = 0; - if (rollbackType == 0 && rowAction == 0) { - action = 1; + String world = Util.getWorldName(rowWorldId); + if (world.length() == 0) { + continue; + } + + World bukkitWorld = Bukkit.getServer().getWorld(world); + if (bukkitWorld == null) { + continue; + } + Block block = bukkitWorld.getBlockAt(rowX, rowY, rowZ); + if (!bukkitWorld.isChunkLoaded(block.getChunk())) { + bukkitWorld.getChunkAt(block.getLocation()); + } + + boolean changeBlock = true; + boolean countBlock = true; + Material changeType = block.getType(); + BlockData changeBlockData = block.getBlockData(); + BlockData pendingChangeData = chunkChanges.get(block); + Material pendingChangeType = changeType; + if (pendingChangeData != null) { + pendingChangeType = pendingChangeData.getMaterial(); + } + else { + pendingChangeData = changeBlockData; + } + + if (rowRolledBack == 1 && rollbackType == 0) { // rollback + countBlock = false; + } + + if ((rowType == pendingChangeType) && ((!BukkitAdapter.ADAPTER.isItemFrame(oldTypeMaterial)) && (oldTypeMaterial != Material.PAINTING) && (oldTypeMaterial != Material.ARMOR_STAND)) && (oldTypeMaterial != Material.END_CRYSTAL)) { + // block is already changed! + BlockData checkData = rowType == Material.AIR ? blockData : rawBlockData; + if (checkData != null) { + if (checkData.getAsString().equals(pendingChangeData.getAsString()) || checkData instanceof MultipleFacing || checkData instanceof Stairs || checkData instanceof RedstoneWire) { + if (rowType != Material.CHEST && rowType != Material.TRAPPED_CHEST) { // always update double chests + changeBlock = false; + } + } + } + else if (rowType == Material.AIR) { + changeBlock = false; } - if (rollbackType == 1 && rowAction == 1) { - action = 1; + countBlock = false; + } + else if ((pendingChangeType != Material.AIR) && (pendingChangeType != Material.CAVE_AIR)) { + countBlock = true; + } + + if ((pendingChangeType == Material.WATER) && (rowType != Material.AIR) && (rowType != Material.CAVE_AIR) && blockData != null) { + if (blockData instanceof Waterlogged) { + if (Material.WATER.createBlockData().equals(block.getBlockData())) { + Waterlogged waterlogged = (Waterlogged) blockData; + waterlogged.setWaterlogged(true); + } + } + } + + try { + if (changeBlock) { + /* If modifying the head of a piston, update the base piston block to prevent it from being destroyed */ + if (changeBlockData instanceof PistonHead) { + PistonHead pistonHead = (PistonHead) changeBlockData; + Block pistonBlock = block.getRelative(pistonHead.getFacing().getOppositeFace()); + BlockData pistonData = pistonBlock.getBlockData(); + if (pistonData instanceof Piston) { + Piston piston = (Piston) pistonData; + piston.setExtended(false); + pistonBlock.setBlockData(piston, false); + } + } + else if (rowType == Material.MOVING_PISTON && blockData instanceof TechnicalPiston && !(blockData instanceof PistonHead)) { + TechnicalPiston technicalPiston = (TechnicalPiston) blockData; + rowType = (technicalPiston.getType() == org.bukkit.block.data.type.TechnicalPiston.Type.STICKY ? Material.STICKY_PISTON : Material.PISTON); + blockData = rowType.createBlockData(); + ((Piston) blockData).setFacing(technicalPiston.getFacing()); + } + + if ((rowType == Material.AIR) && ((BukkitAdapter.ADAPTER.isItemFrame(oldTypeMaterial)) || (oldTypeMaterial == Material.PAINTING))) { + HangingUtil.removeHanging(block.getState(), blockDataString); + } + else if ((BukkitAdapter.ADAPTER.isItemFrame(rowType)) || (rowType == Material.PAINTING)) { + HangingUtil.spawnHanging(block.getState(), rowType, blockDataString, rowData); + } + else if ((rowType == Material.ARMOR_STAND)) { + Location location1 = block.getLocation(); + location1.setX(location1.getX() + 0.50); + location1.setZ(location1.getZ() + 0.50); + location1.setYaw(rowData); + boolean exists = false; + + for (Entity entity : block.getChunk().getEntities()) { + if (entity instanceof ArmorStand) { + if (entity.getLocation().getBlockX() == location1.getBlockX() && entity.getLocation().getBlockY() == location1.getBlockY() && entity.getLocation().getBlockZ() == location1.getBlockZ()) { + exists = true; + } + } + } + + if (!exists) { + Entity entity = block.getLocation().getWorld().spawnEntity(location1, EntityType.ARMOR_STAND); + entity.teleport(location1); + } + } + else if ((rowType == Material.END_CRYSTAL)) { + Location location1 = block.getLocation(); + location1.setX(location1.getX() + 0.50); + location1.setZ(location1.getZ() + 0.50); + boolean exists = false; + + for (Entity entity : block.getChunk().getEntities()) { + if (entity instanceof EnderCrystal) { + if (entity.getLocation().getBlockX() == location1.getBlockX() && entity.getLocation().getBlockY() == location1.getBlockY() && entity.getLocation().getBlockZ() == location1.getBlockZ()) { + exists = true; + } + } + } + + if (!exists) { + Entity entity = block.getLocation().getWorld().spawnEntity(location1, EntityType.ENDER_CRYSTAL); + EnderCrystal enderCrystal = (EnderCrystal) entity; + enderCrystal.setShowingBottom((rowData != 0)); + entity.teleport(location1); + } + } + else if ((rowType == Material.AIR) && ((oldTypeMaterial == Material.WATER))) { + if (pendingChangeData instanceof Waterlogged) { + Waterlogged waterlogged = (Waterlogged) pendingChangeData; + waterlogged.setWaterlogged(false); + Util.prepareTypeAndData(chunkChanges, block, null, waterlogged, false); + } + else { + Util.prepareTypeAndData(chunkChanges, block, rowType, blockData, true); + } + + if (countBlock) { + blockCount1++; + } + } + else if ((rowType == Material.AIR) && ((oldTypeMaterial == Material.SNOW))) { + Util.prepareTypeAndData(chunkChanges, block, rowType, blockData, true); + if (countBlock) { + blockCount1++; + } + } + else if ((rowType == Material.AIR) && ((oldTypeMaterial == Material.END_CRYSTAL))) { + for (Entity entity : block.getChunk().getEntities()) { + if (entity instanceof EnderCrystal) { + if (entity.getLocation().getBlockX() == rowX && entity.getLocation().getBlockY() == rowY && entity.getLocation().getBlockZ() == rowZ) { + entity.remove(); + } + } + } + } + else if (rollbackType == 0 && rowAction == 0 && (rowType == Material.AIR)) { + // broke block ID #0 + } + else if ((rowType == Material.AIR) || (rowType == Material.TNT)) { + if (clearInventories) { + if (BlockGroup.CONTAINERS.contains(changeType)) { + Inventory inventory = Util.getContainerInventory(block.getState(), false); + if (inventory != null) { + inventory.clear(); + } + } + else if (BlockGroup.CONTAINERS.contains(Material.ARMOR_STAND)) { + if ((oldTypeMaterial == Material.ARMOR_STAND)) { + for (Entity entity : block.getChunk().getEntities()) { + if (entity instanceof ArmorStand) { + Location entityLocation = entity.getLocation(); + entityLocation.setY(entityLocation.getY() + 0.99); + + if (entityLocation.getBlockX() == rowX && entityLocation.getBlockY() == rowY && entityLocation.getBlockZ() == rowZ) { + EntityEquipment equipment = Util.getEntityEquipment((LivingEntity) entity); + if (equipment != null) { + equipment.clear(); + } + + entityLocation.setY(entityLocation.getY() - 1.99); + entity.teleport(entityLocation); + entity.remove(); + } + } + } + } + } + } + + boolean remove = true; + if ((rowType == Material.AIR)) { + if (pendingChangeData instanceof Waterlogged) { + Waterlogged waterlogged = (Waterlogged) pendingChangeData; + if (waterlogged.isWaterlogged()) { + Util.prepareTypeAndData(chunkChanges, block, Material.WATER, Material.WATER.createBlockData(), true); + remove = false; + } + } + else if ((pendingChangeType == Material.WATER)) { + if (rawBlockData instanceof Waterlogged) { + Waterlogged waterlogged = (Waterlogged) rawBlockData; + if (waterlogged.isWaterlogged()) { + remove = false; + } + } + } + } + + if (remove) { + boolean physics = true; + if ((changeType == Material.NETHER_PORTAL) || changeBlockData instanceof MultipleFacing || changeBlockData instanceof Snow || changeBlockData instanceof Stairs || changeBlockData instanceof RedstoneWire || changeBlockData instanceof Chest) { + physics = true; + } + else if (changeBlockData instanceof Bisected && !(changeBlockData instanceof TrapDoor)) { + Bisected bisected = (Bisected) changeBlockData; + Location bisectLocation = block.getLocation().clone(); + if (bisected.getHalf() == Half.TOP) { + bisectLocation.setY(bisectLocation.getY() - 1); + } + else { + bisectLocation.setY(bisectLocation.getY() + 1); + } + + int worldMaxHeight = bukkitWorld.getMaxHeight(); + int worldMinHeight = BukkitAdapter.ADAPTER.getMinHeight(bukkitWorld); + if (bisectLocation.getBlockY() >= worldMinHeight && bisectLocation.getBlockY() < worldMaxHeight) { + Block bisectBlock = block.getWorld().getBlockAt(bisectLocation); + Util.prepareTypeAndData(chunkChanges, bisectBlock, rowType, null, false); + + if (countBlock) { + blockCount1++; + } + } + } + else if (changeBlockData instanceof Bed) { + Bed bed = (Bed) changeBlockData; + if (bed.getPart() == Part.FOOT) { + Block adjacentBlock = block.getRelative(bed.getFacing()); + Util.prepareTypeAndData(chunkChanges, adjacentBlock, rowType, null, false); + } + } + + Util.prepareTypeAndData(chunkChanges, block, rowType, null, physics); + } + + if (countBlock) { + blockCount1++; + } + } + else if ((rowType == Material.SPAWNER)) { + try { + Util.prepareTypeAndData(chunkChanges, block, rowType, blockData, false); + CreatureSpawner mobSpawner = (CreatureSpawner) block.getState(); + mobSpawner.setSpawnedType(Util.getSpawnerType(rowData)); + mobSpawner.update(); + + if (countBlock) { + blockCount1++; + } + } + catch (Exception e) { + // e.printStackTrace(); + } + } + else if ((rowType == Material.SKELETON_SKULL) || (rowType == Material.SKELETON_WALL_SKULL) || (rowType == Material.WITHER_SKELETON_SKULL) || (rowType == Material.WITHER_SKELETON_WALL_SKULL) || (rowType == Material.ZOMBIE_HEAD) || (rowType == Material.ZOMBIE_WALL_HEAD) || (rowType == Material.PLAYER_HEAD) || (rowType == Material.PLAYER_WALL_HEAD) || (rowType == Material.CREEPER_HEAD) || (rowType == Material.CREEPER_WALL_HEAD) || (rowType == Material.DRAGON_HEAD) || (rowType == Material.DRAGON_WALL_HEAD)) { // skull + Util.prepareTypeAndData(chunkChanges, block, rowType, blockData, false); + if (rowData > 0) { + Queue.queueSkullUpdate(rowUser, block.getState(), rowData); + } + + if (countBlock) { + blockCount1++; + } + } + else if (Tag.SIGNS.isTagged(rowType)) {// sign + Util.prepareTypeAndData(chunkChanges, block, rowType, blockData, false); + Queue.queueSignUpdate(rowUser, block.getState(), rollbackType, rowTime); + + if (countBlock) { + blockCount1++; + } + } + else if (BlockGroup.SHULKER_BOXES.contains(rowType)) { + Util.prepareTypeAndData(chunkChanges, block, rowType, blockData, false); + if (countBlock) { + blockCount1++; + } + if (meta != null) { + Inventory inventory = Util.getContainerInventory(block.getState(), false); + for (Object value : meta) { + ItemStack item = Util.unserializeItemStackLegacy(value); + if (item != null) { + modifyContainerItems(rowType, inventory, 0, item, 1); + } + } + } + } + else if (rowType == Material.COMMAND_BLOCK || rowType == Material.REPEATING_COMMAND_BLOCK || rowType == Material.CHAIN_COMMAND_BLOCK) { // command block + Util.prepareTypeAndData(chunkChanges, block, rowType, blockData, false); + if (countBlock) { + blockCount1++; + } + + if (meta != null) { + CommandBlock commandBlock = (CommandBlock) block.getState(); + for (Object value : meta) { + if (value instanceof String) { + String string = (String) value; + commandBlock.setCommand(string); + commandBlock.update(); + } + } + } + } + else if ((rowType == Material.WATER)) { + if (pendingChangeData instanceof Waterlogged) { + Waterlogged waterlogged = (Waterlogged) pendingChangeData; + waterlogged.setWaterlogged(true); + Util.prepareTypeAndData(chunkChanges, block, null, waterlogged, false); + } + else { + Util.prepareTypeAndData(chunkChanges, block, rowType, blockData, false); + } + + if (countBlock) { + blockCount1++; + } + } + else if ((rowType == Material.NETHER_PORTAL) && rowAction == 0) { + Util.prepareTypeAndData(chunkChanges, block, Material.FIRE, null, true); + } + else if (blockData == null && rowData > 0 && (rowType == Material.IRON_DOOR || BlockGroup.DOORS.contains(rowType))) { + if (countBlock) { + blockCount1++; + } + + block.setType(rowType, false); + Door door = (Door) block.getBlockData(); + if (rowData >= 8) { + door.setHalf(Half.TOP); + rowData = rowData - 8; + } + else { + door.setHalf(Half.BOTTOM); + } + if (rowData >= 4) { + door.setHinge(Hinge.RIGHT); + rowData = rowData - 4; + } + else { + door.setHinge(Hinge.LEFT); + } + BlockFace face = BlockFace.NORTH; + + switch (rowData) { + case 0: + face = BlockFace.EAST; + break; + case 1: + face = BlockFace.SOUTH; + break; + case 2: + face = BlockFace.WEST; + break; + } + + door.setFacing(face); + door.setOpen(false); + block.setBlockData(door, false); + } + else if (blockData == null && rowData > 0 && (rowType.name().endsWith("_BED"))) { + if (countBlock) { + blockCount1++; + } + + block.setType(rowType, false); + Bed bed = (Bed) block.getBlockData(); + BlockFace face = BlockFace.NORTH; + + if (rowData > 4) { + bed.setPart(Part.HEAD); + rowData = rowData - 4; + } + + switch (rowData) { + case 2: + face = BlockFace.WEST; + break; + case 3: + face = BlockFace.EAST; + break; + case 4: + face = BlockFace.SOUTH; + break; + } + + bed.setFacing(face); + block.setBlockData(bed, false); + } + else if (rowType.name().endsWith("_BANNER")) { + Util.prepareTypeAndData(chunkChanges, block, rowType, blockData, false); + if (countBlock) { + blockCount1++; + } + + if (meta != null) { + Banner banner = (Banner) block.getState(); + + for (Object value : meta) { + if (value instanceof DyeColor) { + banner.setBaseColor((DyeColor) value); + } + else if (value instanceof Map) { + @SuppressWarnings("unchecked") + Pattern pattern = new Pattern((Map) value); + banner.addPattern(pattern); + } + } + + banner.update(); + } + } + else if (rowType != changeType && (BlockGroup.CONTAINERS.contains(rowType) || BlockGroup.CONTAINERS.contains(changeType))) { + block.setType(Material.AIR); // Clear existing container to prevent errors + + boolean isChest = (blockData instanceof Chest); + Util.prepareTypeAndData(chunkChanges, block, rowType, blockData, (isChest)); + if (isChest) { + ChestTool.updateDoubleChest(block, blockData, false); + } + + if (countBlock) { + blockCount1++; + } + } + else if (BlockGroup.UPDATE_STATE.contains(rowType) || rowType.name().contains("CANDLE")) { + Util.prepareTypeAndData(chunkChanges, block, rowType, blockData, true); + ChestTool.updateDoubleChest(block, blockData, true); + if (countBlock) { + blockCount1++; + } + } + else if (rowType != Material.AIR && rawBlockData instanceof Bisected && !(rawBlockData instanceof Stairs || rawBlockData instanceof TrapDoor)) { + Bisected bisected = (Bisected) rawBlockData; + Bisected bisectData = (Bisected) rawBlockData.clone(); + Location bisectLocation = block.getLocation().clone(); + if (bisected.getHalf() == Half.TOP) { + bisectData.setHalf(Half.BOTTOM); + bisectLocation.setY(bisectLocation.getY() - 1); + } + else { + bisectData.setHalf(Half.TOP); + bisectLocation.setY(bisectLocation.getY() + 1); + } + + int worldMaxHeight = bukkitWorld.getMaxHeight(); + int worldMinHeight = BukkitAdapter.ADAPTER.getMinHeight(bukkitWorld); + if (bisectLocation.getBlockY() >= worldMinHeight && bisectLocation.getBlockY() < worldMaxHeight) { + Block bisectBlock = block.getWorld().getBlockAt(bisectLocation); + Util.prepareTypeAndData(chunkChanges, bisectBlock, rowType, bisectData, false); + } + + Util.prepareTypeAndData(chunkChanges, block, rowType, blockData, false); + if (countBlock) { + blockCount1++; + blockCount1++; + } + } + else if (rowType != Material.AIR && rawBlockData instanceof Bed) { + Bed bed = (Bed) rawBlockData; + if (bed.getPart() == Part.FOOT) { + Block adjacentBlock = block.getRelative(bed.getFacing()); + Bed bedData = (Bed) rawBlockData.clone(); + bedData.setPart(Part.HEAD); + Util.prepareTypeAndData(chunkChanges, adjacentBlock, rowType, bedData, false); + if (countBlock) { + blockCount1++; + } + } + + Util.prepareTypeAndData(chunkChanges, block, rowType, blockData, true); + if (countBlock) { + blockCount1++; + } + } + else { + boolean physics = true; + /* + if (blockData instanceof MultipleFacing || BukkitAdapter.ADAPTER.isWall(blockData) || blockData instanceof Snow || blockData instanceof Stairs || blockData instanceof RedstoneWire || blockData instanceof Chest) { + physics = !(blockData instanceof Snow) || block.getY() <= BukkitAdapter.ADAPTER.getMinHeight(block.getWorld()) || (block.getWorld().getBlockAt(block.getX(), block.getY() - 1, block.getZ()).getType().equals(Material.GRASS_BLOCK)); + } + */ + + Util.prepareTypeAndData(chunkChanges, block, rowType, blockData, physics); + if (countBlock) { + blockCount1++; + } + } + } + } + catch (Exception e) { + e.printStackTrace(); + } + + if ((rowType != Material.AIR) && changeBlock) { + if (rowUser.length() > 0) { + CacheHandler.lookupCache.put(rowX + "." + rowY + "." + rowZ + "." + rowWorldId, new Object[] { unixtimestamp, rowUser, rowType }); + } + } + } + + // count++; + ConfigHandler.rollbackHash.put(finalUserString, new int[] { itemCount1, blockCount1, entityCount1, 0, scannedWorlds }); + } + data.clear(); + + // Apply cached changes + for (Entry chunkChange : chunkChanges.entrySet()) { + Block changeBlock = chunkChange.getKey(); + BlockData changeBlockData = chunkChange.getValue(); + if (preview > 0) { + Util.sendBlockChange((Player) finalUser, changeBlock.getLocation(), changeBlockData); + } + else { + Util.setTypeAndData(changeBlock, null, changeBlockData, true); + } + } + chunkChanges.clear(); + + Map> sortPlayers = new HashMap<>(); + Object container = null; + Material containerType = null; + boolean containerInit = false; + int lastX = 0; + int lastY = 0; + int lastZ = 0; + int lastWorldId = 0; + String lastFace = ""; + + for (Object[] row : itemData) { + int[] rollbackHashData1 = ConfigHandler.rollbackHash.get(finalUserString); + int itemCount1 = rollbackHashData1[0]; + int blockCount1 = rollbackHashData1[1]; + int entityCount1 = rollbackHashData1[2]; + int scannedWorlds = rollbackHashData1[4]; + int rowX = (Integer) row[3]; + int rowY = (Integer) row[4]; + int rowZ = (Integer) row[5]; + int rowTypeRaw = (Integer) row[6]; + int rowData = (Integer) row[7]; + int rowAction = (Integer) row[8]; + int rowRolledBack = Util.rolledBack((Integer) row[9], false); + int rowWorldId = (Integer) row[10]; + int rowAmount = (Integer) row[11]; + byte[] rowMetadata = (byte[]) row[12]; + Material rowType = Util.getType(rowTypeRaw); + + int rolledBackInventory = Util.rolledBack((Integer) row[9], true); + if (rowType != null) { + if (inventoryRollback && ((rollbackType == 0 && rolledBackInventory == 0) || (rollbackType == 1 && rolledBackInventory == 1))) { + Material inventoryItem = Util.itemFilter(rowType, ((Integer) row[14] == 0)); + int rowUserId = (Integer) row[2]; + String rowUser = ConfigHandler.playerIdCacheReversed.get(rowUserId); + if (rowUser == null) { + continue; } - int slot = (Integer) populatedStack[0]; - itemstack = (ItemStack) populatedStack[2]; + 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 || rowAction == ItemLogger.ITEM_THROW || rowAction == ItemLogger.ITEM_SHOOT || rowAction == ItemLogger.ITEM_BREAK || rowAction == ItemLogger.ITEM_DESTROY || rowAction == ItemLogger.ITEM_CREATE || rowAction == ItemLogger.ITEM_SELL || rowAction == ItemLogger.ITEM_BUY) { + inventoryAction = ((rowAction == ItemLogger.ITEM_PICKUP || rowAction == ItemLogger.ITEM_CREATE || rowAction == ItemLogger.ITEM_BUY) ? 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(inventoryItem, rowAmount); + 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[2]).clone(), action ^ 1); + } + int modifiedArmor = modifyContainerItems(containerType, player.getInventory(), (Integer) populatedStack[0], (ItemStack) populatedStack[2], action); + if (modifiedArmor > -1) { + List currentSortList = sortPlayers.getOrDefault(player, new ArrayList<>()); + if (!currentSortList.contains(modifiedArmor)) { + currentSortList.add(modifiedArmor); + } + sortPlayers.put(player, currentSortList); + } - modifyContainerItems(containerType, container, slot, itemstack, action); itemCount1 = itemCount1 + rowAmount; + ConfigHandler.rollbackHash.put(finalUserString, new int[] { itemCount1, blockCount1, entityCount1, 0, scannedWorlds }); + continue; // remove this for merged rollbacks in future? (be sure to re-enable chunk sorting) + } + + if (inventoryRollback || rowAction > 1) { + continue; // skip inventory & ender chest transactions + } + + if ((rollbackType == 0 && rowRolledBack == 0) || (rollbackType == 1 && rowRolledBack == 1)) { + ItemStack itemstack = new ItemStack(rowType, rowAmount); + Object[] populatedStack = populateItemStack(itemstack, rowMetadata); + String faceData = (String) populatedStack[1]; + + if (!containerInit || rowX != lastX || rowY != lastY || rowZ != lastZ || rowWorldId != lastWorldId || !faceData.equals(lastFace)) { + container = null; // container patch 2.14.0 + String world = Util.getWorldName(rowWorldId); + if (world.length() == 0) { + continue; + } + + World bukkitWorld = Bukkit.getServer().getWorld(world); + if (bukkitWorld == null) { + continue; + } + Block block = bukkitWorld.getBlockAt(rowX, rowY, rowZ); + if (!bukkitWorld.isChunkLoaded(block.getChunk())) { + bukkitWorld.getChunkAt(block.getLocation()); + } + + if (BlockGroup.CONTAINERS.contains(block.getType())) { + BlockState blockState = block.getState(); + if (blockState instanceof Jukebox) { + container = blockState; + } + else { + container = Util.getContainerInventory(blockState, false); + } + + containerType = block.getType(); + } + else if (BlockGroup.CONTAINERS.contains(Material.ARMOR_STAND) || BlockGroup.CONTAINERS.contains(Material.ITEM_FRAME)) { + for (Entity entity : block.getChunk().getEntities()) { + if (entity.getLocation().getBlockX() == rowX && entity.getLocation().getBlockY() == rowY && entity.getLocation().getBlockZ() == rowZ) { + if (entity instanceof ArmorStand) { + container = Util.getEntityEquipment((LivingEntity) entity); + containerType = Material.ARMOR_STAND; + } + else if (entity instanceof ItemFrame) { + container = entity; + containerType = Material.ITEM_FRAME; + if (faceData.length() > 0 && (BlockFace.valueOf(faceData) == ((ItemFrame) entity).getFacing())) { + break; + } + } + } + } + } + + lastX = rowX; + lastY = rowY; + lastZ = rowZ; + lastWorldId = rowWorldId; + lastFace = faceData; + } + + if (container != null) { + int action = 0; + if (rollbackType == 0 && rowAction == 0) { + action = 1; + } + + if (rollbackType == 1 && rowAction == 1) { + action = 1; + } + + int slot = (Integer) populatedStack[0]; + itemstack = (ItemStack) populatedStack[2]; + + modifyContainerItems(containerType, container, slot, itemstack, action); + itemCount1 = itemCount1 + rowAmount; + } + containerInit = true; } - containerInit = true; } + + ConfigHandler.rollbackHash.put(finalUserString, new int[] { itemCount1, blockCount1, entityCount1, 0, scannedWorlds }); } + itemData.clear(); - ConfigHandler.rollbackHash.put(finalUserString, new int[] { itemCount1, blockCount1, entityCount1, 0 }); - } - itemData.clear(); + for (Entry> sortEntry : sortPlayers.entrySet()) { + sortContainerItems(sortEntry.getKey().getInventory(), sortEntry.getValue()); + } + sortPlayers.clear(); - for (Entry> sortEntry : sortPlayers.entrySet()) { - sortContainerItems(sortEntry.getKey().getInventory(), sortEntry.getValue()); - } - sortPlayers.clear(); + int[] rollbackHashData1 = ConfigHandler.rollbackHash.get(finalUserString); + int itemCount1 = rollbackHashData1[0]; + int blockCount1 = rollbackHashData1[1]; + int entityCount1 = rollbackHashData1[2]; + int scannedWorlds = rollbackHashData1[4]; + ConfigHandler.rollbackHash.put(finalUserString, new int[] { itemCount1, blockCount1, entityCount1, 1, (scannedWorlds + 1) }); - int[] rollbackHashData1 = ConfigHandler.rollbackHash.get(finalUserString); - int itemCount1 = rollbackHashData1[0]; - int blockCount1 = rollbackHashData1[1]; - int entityCount1 = rollbackHashData1[2]; - ConfigHandler.rollbackHash.put(finalUserString, new int[] { itemCount1, blockCount1, entityCount1, 1 }); + // Teleport players out of danger if they're within this chunk + if (preview == 0) { + for (Player player : Bukkit.getOnlinePlayers()) { + Location playerLocation = player.getLocation(); + int chunkX = playerLocation.getBlockX() >> 4; + int chunkZ = playerLocation.getBlockZ() >> 4; - // Teleport players out of danger if they're within this chunk - if (preview == 0) { - for (Player player : Bukkit.getOnlinePlayers()) { - Location playerLocation = player.getLocation(); - int chunkX = playerLocation.getBlockX() >> 4; - int chunkZ = playerLocation.getBlockZ() >> 4; - - if (chunkX == finalChunkX && chunkZ == finalChunkZ) { - Teleport.performSafeTeleport(player, playerLocation, false); + if (chunkX == finalChunkX && chunkZ == finalChunkZ) { + Teleport.performSafeTeleport(player, playerLocation, false); + } } } } - } - catch (Exception e) { - e.printStackTrace(); - int[] rollbackHashData1 = ConfigHandler.rollbackHash.get(finalUserString); - int itemCount1 = rollbackHashData1[0]; - int blockCount1 = rollbackHashData1[1]; - int entityCount1 = rollbackHashData1[2]; + catch (Exception e) { + e.printStackTrace(); + int[] rollbackHashData1 = ConfigHandler.rollbackHash.get(finalUserString); + int itemCount1 = rollbackHashData1[0]; + int blockCount1 = rollbackHashData1[1]; + int entityCount1 = rollbackHashData1[2]; + int scannedWorlds = rollbackHashData1[4]; - ConfigHandler.rollbackHash.put(finalUserString, new int[] { itemCount1, blockCount1, entityCount1, 2 }); - } - }, 0); + ConfigHandler.rollbackHash.put(finalUserString, new int[] { itemCount1, blockCount1, entityCount1, 2, (scannedWorlds + 1) }); + } + }, chunkLocation, 0); + } rollbackHashData = ConfigHandler.rollbackHash.get(finalUserString); int next = rollbackHashData[3]; + int scannedWorlds = rollbackHashData[4]; int sleepTime = 0; int abort = 0; - while (next == 0) { + while (next == 0 || scannedWorlds < worldMap.size()) { if (preview == 1) { // Not actually changing blocks, so less intensive. sleepTime = sleepTime + 1; @@ -1257,6 +1298,7 @@ public class Rollback extends Queue { rollbackHashData = ConfigHandler.rollbackHash.get(finalUserString); next = rollbackHashData[3]; + scannedWorlds = rollbackHashData[4]; if (sleepTime > 300000) { abort = 1; @@ -1273,7 +1315,7 @@ public class Rollback extends Queue { itemCount = rollbackHashData[0]; blockCount = rollbackHashData[1]; entityCount = rollbackHashData[2]; - ConfigHandler.rollbackHash.put(finalUserString, new int[] { itemCount, blockCount, entityCount, 0 }); + ConfigHandler.rollbackHash.put(finalUserString, new int[] { itemCount, blockCount, entityCount, 0, 0 }); if (verbose && user != null && preview == 0 && !actionList.contains(11)) { Integer chunks = chunkList.size(); diff --git a/src/main/java/net/coreprotect/listener/entity/EntityDamageByEntityListener.java b/src/main/java/net/coreprotect/listener/entity/EntityDamageByEntityListener.java index 2912e6e..41648c2 100644 --- a/src/main/java/net/coreprotect/listener/entity/EntityDamageByEntityListener.java +++ b/src/main/java/net/coreprotect/listener/entity/EntityDamageByEntityListener.java @@ -2,7 +2,6 @@ package net.coreprotect.listener.entity; import java.util.Locale; -import org.bukkit.Bukkit; import org.bukkit.GameMode; import org.bukkit.Location; import org.bukkit.Material; @@ -35,6 +34,7 @@ import net.coreprotect.config.ConfigHandler; import net.coreprotect.consumer.Queue; import net.coreprotect.database.Database; import net.coreprotect.listener.player.PlayerInteractEntityListener; +import net.coreprotect.thread.Scheduler; import net.coreprotect.utility.Util; public final class EntityDamageByEntityListener extends Queue implements Listener { @@ -125,13 +125,13 @@ public final class EntityDamageByEntityListener extends Queue implements Listene if (Config.getConfig(entityLocation.getWorld()).ITEM_TRANSACTIONS) { String killer = user; ItemStack[] contents = Util.getContainerContents(Material.ARMOR_STAND, entity, block.getLocation()); - Bukkit.getScheduler().runTask(CoreProtect.getInstance(), () -> { + Scheduler.runTask(CoreProtect.getInstance(), () -> { if (entity != null && entity.isDead()) { entityLocation.setY(entityLocation.getY() + 0.99); Database.containerBreakCheck(killer, Material.ARMOR_STAND, entity, contents, block.getLocation()); Queue.queueBlockBreak(killer, block.getState(), Material.ARMOR_STAND, null, (int) entityLocation.getYaw()); } - }); + }, entity); } } } diff --git a/src/main/java/net/coreprotect/listener/entity/EntityDeathListener.java b/src/main/java/net/coreprotect/listener/entity/EntityDeathListener.java index 8671f67..53d2d03 100644 --- a/src/main/java/net/coreprotect/listener/entity/EntityDeathListener.java +++ b/src/main/java/net/coreprotect/listener/entity/EntityDeathListener.java @@ -67,6 +67,7 @@ import net.coreprotect.CoreProtect; import net.coreprotect.bukkit.BukkitAdapter; import net.coreprotect.config.Config; import net.coreprotect.consumer.Queue; +import net.coreprotect.thread.Scheduler; import net.coreprotect.utility.serialize.ItemMetaHandler; public final class EntityDeathListener extends Queue implements Listener { @@ -96,13 +97,13 @@ public final class EntityDeathListener extends Queue implements Listener { } } - Bukkit.getScheduler().runTask(CoreProtect.getInstance(), () -> { - for (LivingEntity entity : entityList) { + for (LivingEntity entity : entityList) { + Scheduler.runTask(CoreProtect.getInstance(), () -> { if (entity != null && entity.isDead()) { logEntityDeath(entity, "#command"); } - } - }); + }, entity); + } } protected static void logEntityDeath(LivingEntity entity, String e) { diff --git a/src/main/java/net/coreprotect/listener/player/FoodLevelChangeListener.java b/src/main/java/net/coreprotect/listener/player/FoodLevelChangeListener.java index ca1b277..c923cff 100644 --- a/src/main/java/net/coreprotect/listener/player/FoodLevelChangeListener.java +++ b/src/main/java/net/coreprotect/listener/player/FoodLevelChangeListener.java @@ -1,6 +1,5 @@ package net.coreprotect.listener.player; -import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.block.Block; @@ -15,6 +14,7 @@ import org.bukkit.event.entity.FoodLevelChangeEvent; import net.coreprotect.CoreProtect; import net.coreprotect.consumer.Queue; import net.coreprotect.thread.CacheHandler; +import net.coreprotect.thread.Scheduler; import net.coreprotect.utility.Util; public final class FoodLevelChangeListener extends Queue implements Listener { @@ -50,7 +50,7 @@ public final class FoodLevelChangeListener extends Queue implements Listener { } final Material oldBlockType = oldType; - Bukkit.getServer().getScheduler().runTask(CoreProtect.getInstance(), () -> { + Scheduler.runTask(CoreProtect.getInstance(), () -> { try { Block newBlock = oldBlockState.getBlock(); BlockState newBlockState = newBlock.getState(); @@ -66,7 +66,7 @@ public final class FoodLevelChangeListener extends Queue implements Listener { catch (Exception e) { e.printStackTrace(); } - }); + }, oldBlockState.getLocation()); } CacheHandler.interactCache.remove(coordinates); diff --git a/src/main/java/net/coreprotect/listener/player/HopperPullListener.java b/src/main/java/net/coreprotect/listener/player/HopperPullListener.java index 4ec547a..d1dedda 100644 --- a/src/main/java/net/coreprotect/listener/player/HopperPullListener.java +++ b/src/main/java/net/coreprotect/listener/player/HopperPullListener.java @@ -5,7 +5,6 @@ import java.util.HashSet; import java.util.List; import java.util.Set; -import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.inventory.BrewerInventory; import org.bukkit.inventory.FurnaceInventory; @@ -16,6 +15,7 @@ import org.bukkit.inventory.ItemStack; import net.coreprotect.CoreProtect; import net.coreprotect.config.Config; import net.coreprotect.config.ConfigHandler; +import net.coreprotect.thread.Scheduler; import net.coreprotect.utility.Util; public final class HopperPullListener { @@ -38,7 +38,7 @@ public final class HopperPullListener { ItemStack movedItem = item.clone(); final long taskStarted = InventoryChangeListener.tasksStarted.incrementAndGet(); - Bukkit.getServer().getScheduler().runTaskAsynchronously(CoreProtect.getInstance(), () -> { + Scheduler.runTaskAsynchronously(CoreProtect.getInstance(), () -> { try { if (sourceHolder == null || destinationHolder == null) { return; diff --git a/src/main/java/net/coreprotect/listener/player/HopperPushListener.java b/src/main/java/net/coreprotect/listener/player/HopperPushListener.java index ddf388c..0acaa4c 100644 --- a/src/main/java/net/coreprotect/listener/player/HopperPushListener.java +++ b/src/main/java/net/coreprotect/listener/player/HopperPushListener.java @@ -5,7 +5,6 @@ import java.util.HashSet; import java.util.List; import java.util.Set; -import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.inventory.BrewerInventory; import org.bukkit.inventory.FurnaceInventory; @@ -16,6 +15,7 @@ import org.bukkit.inventory.ItemStack; import net.coreprotect.CoreProtect; import net.coreprotect.config.Config; import net.coreprotect.config.ConfigHandler; +import net.coreprotect.thread.Scheduler; import net.coreprotect.utility.Util; public final class HopperPushListener { @@ -38,7 +38,7 @@ public final class HopperPushListener { ItemStack movedItem = item.clone(); final long taskStarted = InventoryChangeListener.tasksStarted.incrementAndGet(); - Bukkit.getServer().getScheduler().runTaskAsynchronously(CoreProtect.getInstance(), () -> { + Scheduler.runTaskAsynchronously(CoreProtect.getInstance(), () -> { try { if (sourceHolder == null || destinationHolder == null) { return; diff --git a/src/main/java/net/coreprotect/listener/player/InventoryChangeListener.java b/src/main/java/net/coreprotect/listener/player/InventoryChangeListener.java index bc54754..b89124c 100644 --- a/src/main/java/net/coreprotect/listener/player/InventoryChangeListener.java +++ b/src/main/java/net/coreprotect/listener/player/InventoryChangeListener.java @@ -6,7 +6,6 @@ import java.util.List; import java.util.Locale; import java.util.concurrent.atomic.AtomicLong; -import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.World; @@ -32,6 +31,7 @@ import net.coreprotect.config.ConfigHandler; import net.coreprotect.consumer.Queue; import net.coreprotect.model.BlockGroup; import net.coreprotect.paper.PaperAdapter; +import net.coreprotect.thread.Scheduler; import net.coreprotect.utility.Util; import net.coreprotect.utility.Validate; @@ -227,7 +227,7 @@ public final class InventoryChangeListener extends Queue implements Listener { ItemStack[] containerState = Util.getContainerState(inventory.getContents()); final long taskStarted = InventoryChangeListener.tasksStarted.incrementAndGet(); - Bukkit.getServer().getScheduler().runTaskAsynchronously(CoreProtect.getInstance(), () -> { + Scheduler.runTaskAsynchronously(CoreProtect.getInstance(), () -> { try { Material containerType = (enderChest != true ? null : Material.ENDER_CHEST); InventoryChangeListener.checkTasks(taskStarted); diff --git a/src/main/java/net/coreprotect/listener/player/PlayerInteractListener.java b/src/main/java/net/coreprotect/listener/player/PlayerInteractListener.java index a5b0084..218b7db 100755 --- a/src/main/java/net/coreprotect/listener/player/PlayerInteractListener.java +++ b/src/main/java/net/coreprotect/listener/player/PlayerInteractListener.java @@ -8,7 +8,6 @@ import java.util.List; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import org.bukkit.Bukkit; import org.bukkit.DyeColor; import org.bukkit.GameMode; import org.bukkit.Location; @@ -55,6 +54,7 @@ import net.coreprotect.database.lookup.SignMessageLookup; import net.coreprotect.language.Phrase; import net.coreprotect.model.BlockGroup; import net.coreprotect.thread.CacheHandler; +import net.coreprotect.thread.Scheduler; import net.coreprotect.utility.Chat; import net.coreprotect.utility.Color; import net.coreprotect.utility.Util; @@ -735,7 +735,7 @@ public final class PlayerInteractListener extends Queue implements Listener { if (!exists) { final Player playerFinal = player; final Location locationFinal = crystalLocation; - Bukkit.getServer().getScheduler().runTask(CoreProtect.getInstance(), () -> { + Scheduler.runTask(CoreProtect.getInstance(), () -> { try { boolean blockExists = false; int showingBottom = 0; @@ -755,7 +755,7 @@ public final class PlayerInteractListener extends Queue implements Listener { catch (Exception e) { e.printStackTrace(); } - }); + }, locationFinal); } } } diff --git a/src/main/java/net/coreprotect/thread/Scheduler.java b/src/main/java/net/coreprotect/thread/Scheduler.java new file mode 100644 index 0000000..83f7f45 --- /dev/null +++ b/src/main/java/net/coreprotect/thread/Scheduler.java @@ -0,0 +1,91 @@ +package net.coreprotect.thread; + +import java.util.concurrent.TimeUnit; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.entity.Entity; + +import net.coreprotect.CoreProtect; +import net.coreprotect.config.ConfigHandler; + +public class Scheduler { + + private Scheduler() { + throw new IllegalStateException("Scheduler class"); + } + + public static void scheduleSyncDelayedTask(CoreProtect plugin, Runnable task, Object regionData, int delay) { + if (ConfigHandler.isFolia) { + if (regionData instanceof Location) { // REGION + Location location = (Location) regionData; + if (delay == 0) { + Bukkit.getServer().getRegionScheduler().run(plugin, location, value -> task.run()); + } + else { + Bukkit.getServer().getRegionScheduler().runDelayed(plugin, location, value -> task.run(), delay); + } + } + else if (regionData instanceof Entity) { // ENTITY + Entity entity = (Entity) regionData; + if (delay == 0) { + entity.getScheduler().run(plugin, value -> task.run(), null); + } + else { + entity.getScheduler().runDelayed(plugin, value -> task.run(), null, delay); + } + } + else { // GLOBAL + if (delay == 0) { + Bukkit.getServer().getGlobalRegionScheduler().run(plugin, value -> task.run()); + } + else { + Bukkit.getServer().getGlobalRegionScheduler().runDelayed(plugin, value -> task.run(), delay); + } + } + } + else { // BUKKIT + if (delay == 0) { + Bukkit.getServer().getScheduler().runTask(plugin, task); + } + else { + Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, task, delay); + } + } + } + + public static void scheduleAsyncDelayedTask(CoreProtect plugin, Runnable task, int delay) { + if (ConfigHandler.isFolia) { + if (delay == 0) { + Bukkit.getServer().getAsyncScheduler().runNow(plugin, value -> task.run()); + } + else { + Bukkit.getServer().getAsyncScheduler().runDelayed(plugin, value -> task.run(), (delay * 50L), TimeUnit.MILLISECONDS); + } + } + else { // BUKKIT + if (delay == 0) { + Bukkit.getServer().getScheduler().runTaskAsynchronously(plugin, task); + } + else { + Bukkit.getServer().getScheduler().runTaskLaterAsynchronously(plugin, task, delay); + } + } + } + + public static void scheduleSyncDelayedTask(CoreProtect plugin, Runnable task, int delay) { + scheduleSyncDelayedTask(plugin, task, null, delay); + } + + public static void runTask(CoreProtect plugin, Runnable task) { + scheduleSyncDelayedTask(plugin, task, null, 0); + } + + public static void runTask(CoreProtect plugin, Runnable task, Object regionData) { + scheduleSyncDelayedTask(plugin, task, regionData, 0); + } + + public static void runTaskAsynchronously(CoreProtect plugin, Runnable task) { + scheduleAsyncDelayedTask(plugin, task, 0); + } +} diff --git a/src/main/java/net/coreprotect/utility/ChestTool.java b/src/main/java/net/coreprotect/utility/ChestTool.java index 99263de..2e7fe98 100644 --- a/src/main/java/net/coreprotect/utility/ChestTool.java +++ b/src/main/java/net/coreprotect/utility/ChestTool.java @@ -1,6 +1,5 @@ package net.coreprotect.utility; -import org.bukkit.Bukkit; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.block.data.BlockData; @@ -9,6 +8,7 @@ import org.bukkit.block.data.type.Chest; import org.bukkit.block.data.type.Chest.Type; import net.coreprotect.CoreProtect; +import net.coreprotect.thread.Scheduler; public class ChestTool { @@ -75,7 +75,7 @@ public class ChestTool { } private static void validateContainer(BlockData blockData, Type newType, Block block, Block relativeBlock) { - Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(CoreProtect.getInstance(), () -> { + Scheduler.scheduleSyncDelayedTask(CoreProtect.getInstance(), () -> { try { BlockData relativeBlockData = relativeBlock.getBlockData(); if (!blockData.getAsString().equals(block.getBlockData().getAsString()) || !(relativeBlockData instanceof Chest) || ((Chest) relativeBlockData).getType() == newType) { @@ -89,7 +89,7 @@ public class ChestTool { catch (Exception e) { e.printStackTrace(); } - }, 2); + }, relativeBlock.getLocation(), 2); } } diff --git a/src/main/java/net/coreprotect/utility/Util.java b/src/main/java/net/coreprotect/utility/Util.java index 7379697..cd1d2b5 100755 --- a/src/main/java/net/coreprotect/utility/Util.java +++ b/src/main/java/net/coreprotect/utility/Util.java @@ -59,6 +59,7 @@ import net.coreprotect.database.Rollback; import net.coreprotect.language.Phrase; import net.coreprotect.model.BlockGroup; import net.coreprotect.thread.CacheHandler; +import net.coreprotect.thread.Scheduler; import net.coreprotect.utility.serialize.ItemMetaHandler; import net.coreprotect.worldedit.CoreProtectEditSessionEvent; @@ -1281,6 +1282,17 @@ public class Util extends Queue { return true; } + public static boolean isFolia() { + try { + Class.forName("io.papermc.paper.threadedregions.scheduler.GlobalRegionScheduler"); + } + catch (Exception e) { + return false; + } + + return true; + } + public static String getBranch() { String branch = ""; try { @@ -1521,7 +1533,7 @@ public class Util extends Queue { } public static void updateBlock(final BlockState block) { - Bukkit.getServer().getScheduler().runTask(CoreProtect.getInstance(), () -> { + Scheduler.runTask(CoreProtect.getInstance(), () -> { try { if (block.getBlockData() instanceof Waterlogged) { Block currentBlock = block.getBlock(); @@ -1534,7 +1546,7 @@ public class Util extends Queue { catch (Exception e) { e.printStackTrace(); } - }); + }, block.getLocation()); } public static void updateInventory(Player player) { diff --git a/src/main/java/net/coreprotect/utility/entity/EntityUtil.java b/src/main/java/net/coreprotect/utility/entity/EntityUtil.java index b50b83f..a8f4b16 100644 --- a/src/main/java/net/coreprotect/utility/entity/EntityUtil.java +++ b/src/main/java/net/coreprotect/utility/entity/EntityUtil.java @@ -59,6 +59,7 @@ import net.coreprotect.CoreProtect; import net.coreprotect.bukkit.BukkitAdapter; import net.coreprotect.database.Rollback; import net.coreprotect.thread.CacheHandler; +import net.coreprotect.thread.Scheduler; import net.coreprotect.utility.Util; public class EntityUtil { @@ -71,7 +72,7 @@ public class EntityUtil { if (type == null) { return; } - Bukkit.getServer().getScheduler().runTask(CoreProtect.getInstance(), () -> { + Scheduler.runTask(CoreProtect.getInstance(), () -> { try { Location location = block.getLocation(); location.setX(location.getX() + 0.50); @@ -557,7 +558,7 @@ public class EntityUtil { catch (Exception e) { e.printStackTrace(); } - }); + }, block.getLocation()); } } diff --git a/src/main/java/net/coreprotect/worldedit/CoreProtectEditSessionEvent.java b/src/main/java/net/coreprotect/worldedit/CoreProtectEditSessionEvent.java index e170641..fe9b394 100755 --- a/src/main/java/net/coreprotect/worldedit/CoreProtectEditSessionEvent.java +++ b/src/main/java/net/coreprotect/worldedit/CoreProtectEditSessionEvent.java @@ -14,6 +14,7 @@ import net.coreprotect.config.Config; import net.coreprotect.config.ConfigHandler; import net.coreprotect.language.Phrase; import net.coreprotect.language.Selector; +import net.coreprotect.thread.Scheduler; import net.coreprotect.utility.Chat; public class CoreProtectEditSessionEvent { @@ -47,7 +48,7 @@ public class CoreProtectEditSessionEvent { // Failed to initialize WorldEdit logging } - Bukkit.getServer().getScheduler().runTask(CoreProtect.getInstance(), () -> { + Scheduler.runTask(CoreProtect.getInstance(), () -> { try { if (isInitialized()) { Chat.console(Phrase.build(Phrase.INTEGRATION_SUCCESS, "WorldEdit", Selector.FIRST)); diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index c5c5a62..fbb1c9d 100755 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -3,6 +3,7 @@ main: net.coreprotect.CoreProtect version: ${project.version} branch: ${project.branch} api-version: 1.13 +folia-supported: true website: http://coreprotect.net author: Intelli softdepend: [WorldEdit]