Split up Rollback class
This commit is contained in:
parent
e2c2505b2b
commit
5bb0080ca3
6 changed files with 1482 additions and 1010 deletions
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,593 @@
|
||||||
|
package net.coreprotect.database.rollback;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
import org.bukkit.DyeColor;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.block.Banner;
|
||||||
|
import org.bukkit.block.Block;
|
||||||
|
import org.bukkit.block.BlockFace;
|
||||||
|
import org.bukkit.block.CommandBlock;
|
||||||
|
import org.bukkit.block.CreatureSpawner;
|
||||||
|
import org.bukkit.block.banner.Pattern;
|
||||||
|
import org.bukkit.block.data.Bisected;
|
||||||
|
import org.bukkit.block.data.Bisected.Half;
|
||||||
|
import org.bukkit.block.data.BlockData;
|
||||||
|
import org.bukkit.block.data.MultipleFacing;
|
||||||
|
import org.bukkit.block.data.Waterlogged;
|
||||||
|
import org.bukkit.block.data.type.Bed;
|
||||||
|
import org.bukkit.block.data.type.Bed.Part;
|
||||||
|
import org.bukkit.block.data.type.Chest;
|
||||||
|
import org.bukkit.block.data.type.Door;
|
||||||
|
import org.bukkit.block.data.type.Door.Hinge;
|
||||||
|
import org.bukkit.block.data.type.Piston;
|
||||||
|
import org.bukkit.block.data.type.PistonHead;
|
||||||
|
import org.bukkit.block.data.type.RedstoneWire;
|
||||||
|
import org.bukkit.block.data.type.Snow;
|
||||||
|
import org.bukkit.block.data.type.Stairs;
|
||||||
|
import org.bukkit.block.data.type.TechnicalPiston;
|
||||||
|
import org.bukkit.block.data.type.TrapDoor;
|
||||||
|
import org.bukkit.entity.ArmorStand;
|
||||||
|
import org.bukkit.entity.EnderCrystal;
|
||||||
|
import org.bukkit.entity.Entity;
|
||||||
|
import org.bukkit.entity.EntityType;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.inventory.Inventory;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
|
||||||
|
import net.coreprotect.bukkit.BukkitAdapter;
|
||||||
|
import net.coreprotect.config.ConfigHandler;
|
||||||
|
import net.coreprotect.consumer.Queue;
|
||||||
|
import net.coreprotect.model.BlockGroup;
|
||||||
|
import net.coreprotect.paper.PaperAdapter;
|
||||||
|
import net.coreprotect.thread.CacheHandler;
|
||||||
|
import net.coreprotect.utility.BlockUtils;
|
||||||
|
import net.coreprotect.utility.ChestTool;
|
||||||
|
import net.coreprotect.utility.EntityUtils;
|
||||||
|
import net.coreprotect.utility.ItemUtils;
|
||||||
|
import net.coreprotect.utility.Util;
|
||||||
|
import net.coreprotect.utility.entity.HangingUtil;
|
||||||
|
|
||||||
|
public class RollbackBlockHandler extends Queue {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle block-related rollback operations
|
||||||
|
*
|
||||||
|
* @param block
|
||||||
|
* The block to modify
|
||||||
|
* @param row
|
||||||
|
* Block data from the database (used only for specific operations)
|
||||||
|
* @param rollbackType
|
||||||
|
* The type of rollback (0=rollback, 1=restore)
|
||||||
|
* @param clearInventories
|
||||||
|
* Whether to clear container inventories
|
||||||
|
* @param chunkChanges
|
||||||
|
* Map of block changes to apply
|
||||||
|
* @param countBlock
|
||||||
|
* Whether to count this block in stats
|
||||||
|
* @param oldTypeMaterial
|
||||||
|
* The previous material type
|
||||||
|
* @param pendingChangeType
|
||||||
|
* The pending change material type
|
||||||
|
* @param pendingChangeData
|
||||||
|
* The pending change block data
|
||||||
|
* @param finalUserString
|
||||||
|
* The username for this rollback
|
||||||
|
* @param rawBlockData
|
||||||
|
* The raw block data
|
||||||
|
* @param changeType
|
||||||
|
* The current block type
|
||||||
|
* @param changeBlockData
|
||||||
|
* The current block data
|
||||||
|
* @param meta
|
||||||
|
* Block metadata
|
||||||
|
* @param blockData
|
||||||
|
* The processed block data
|
||||||
|
* @param rowUser
|
||||||
|
* The username associated with this block change
|
||||||
|
* @param rowType
|
||||||
|
* The material type for this block change
|
||||||
|
* @param rowX
|
||||||
|
* The X coordinate
|
||||||
|
* @param rowY
|
||||||
|
* The Y coordinate
|
||||||
|
* @param rowZ
|
||||||
|
* The Z coordinate
|
||||||
|
* @param rowTypeRaw
|
||||||
|
* The raw type value
|
||||||
|
* @param rowData
|
||||||
|
* The data value
|
||||||
|
* @param rowAction
|
||||||
|
* The action value
|
||||||
|
* @param rowWorldId
|
||||||
|
* The world ID
|
||||||
|
* @param blockDataString
|
||||||
|
* The block data as a string
|
||||||
|
* @return Updated count status
|
||||||
|
*/
|
||||||
|
public static boolean processBlockChange(Block block, Object[] row, int rollbackType, boolean clearInventories, Map<Block, BlockData> chunkChanges, boolean countBlock, Material oldTypeMaterial, Material pendingChangeType, BlockData pendingChangeData, String finalUserString, BlockData rawBlockData, Material changeType, BlockData changeBlockData, ArrayList<Object> meta, BlockData blockData, String rowUser, Material rowType, int rowX, int rowY, int rowZ, int rowTypeRaw, int rowData, int rowAction, int rowWorldId, String blockDataString) {
|
||||||
|
|
||||||
|
boolean changeBlock = true;
|
||||||
|
World bukkitWorld = block.getWorld();
|
||||||
|
int unixtimestamp = (int) (System.currentTimeMillis() / 1000L);
|
||||||
|
|
||||||
|
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);
|
||||||
|
PaperAdapter.ADAPTER.teleportAsync(entity, 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, BukkitAdapter.ADAPTER.getEntityType(Material.END_CRYSTAL));
|
||||||
|
EnderCrystal enderCrystal = (EnderCrystal) entity;
|
||||||
|
enderCrystal.setShowingBottom((rowData != 0));
|
||||||
|
PaperAdapter.ADAPTER.teleportAsync(entity, location1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ((rowType == Material.AIR) && ((oldTypeMaterial == Material.WATER))) {
|
||||||
|
if (pendingChangeData instanceof Waterlogged) {
|
||||||
|
Waterlogged waterlogged = (Waterlogged) pendingChangeData;
|
||||||
|
waterlogged.setWaterlogged(false);
|
||||||
|
BlockUtils.prepareTypeAndData(chunkChanges, block, null, waterlogged, false);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
BlockUtils.prepareTypeAndData(chunkChanges, block, rowType, blockData, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return countBlock;
|
||||||
|
}
|
||||||
|
else if ((rowType == Material.AIR) && ((oldTypeMaterial == Material.SNOW))) {
|
||||||
|
BlockUtils.prepareTypeAndData(chunkChanges, block, rowType, blockData, true);
|
||||||
|
return countBlock;
|
||||||
|
}
|
||||||
|
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 = BlockUtils.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) {
|
||||||
|
ItemUtils.getEntityEquipment((ArmorStand) entity).clear();
|
||||||
|
|
||||||
|
entityLocation.setY(entityLocation.getY() - 1.99);
|
||||||
|
PaperAdapter.ADAPTER.teleportAsync(entity, entityLocation);
|
||||||
|
entity.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean remove = true;
|
||||||
|
if ((rowType == Material.AIR)) {
|
||||||
|
if (pendingChangeData instanceof Waterlogged) {
|
||||||
|
Waterlogged waterlogged = (Waterlogged) pendingChangeData;
|
||||||
|
if (waterlogged.isWaterlogged()) {
|
||||||
|
BlockUtils.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);
|
||||||
|
BlockUtils.prepareTypeAndData(chunkChanges, bisectBlock, rowType, null, false);
|
||||||
|
|
||||||
|
if (countBlock) {
|
||||||
|
updateBlockCount(finalUserString, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (changeBlockData instanceof Bed) {
|
||||||
|
Bed bed = (Bed) changeBlockData;
|
||||||
|
if (bed.getPart() == Part.FOOT) {
|
||||||
|
Block adjacentBlock = block.getRelative(bed.getFacing());
|
||||||
|
BlockUtils.prepareTypeAndData(chunkChanges, adjacentBlock, rowType, null, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockUtils.prepareTypeAndData(chunkChanges, block, rowType, null, physics);
|
||||||
|
}
|
||||||
|
|
||||||
|
return countBlock;
|
||||||
|
}
|
||||||
|
else if ((rowType == Material.SPAWNER)) {
|
||||||
|
try {
|
||||||
|
BlockUtils.prepareTypeAndData(chunkChanges, block, rowType, blockData, false);
|
||||||
|
CreatureSpawner mobSpawner = (CreatureSpawner) block.getState();
|
||||||
|
mobSpawner.setSpawnedType(EntityUtils.getSpawnerType(rowData));
|
||||||
|
mobSpawner.update();
|
||||||
|
|
||||||
|
return countBlock;
|
||||||
|
}
|
||||||
|
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
|
||||||
|
BlockUtils.prepareTypeAndData(chunkChanges, block, rowType, blockData, false);
|
||||||
|
if (rowData > 0) {
|
||||||
|
Queue.queueSkullUpdate(rowUser, block.getState(), rowData);
|
||||||
|
}
|
||||||
|
|
||||||
|
return countBlock;
|
||||||
|
}
|
||||||
|
else if (BukkitAdapter.ADAPTER.isSign(rowType)) {// sign
|
||||||
|
BlockUtils.prepareTypeAndData(chunkChanges, block, rowType, blockData, false);
|
||||||
|
Queue.queueSignUpdate(rowUser, block.getState(), rollbackType, (Integer) row[1]);
|
||||||
|
|
||||||
|
return countBlock;
|
||||||
|
}
|
||||||
|
else if (BlockGroup.SHULKER_BOXES.contains(rowType)) {
|
||||||
|
BlockUtils.prepareTypeAndData(chunkChanges, block, rowType, blockData, false);
|
||||||
|
if (countBlock) {
|
||||||
|
updateBlockCount(finalUserString, 1);
|
||||||
|
}
|
||||||
|
if (meta != null) {
|
||||||
|
Inventory inventory = BlockUtils.getContainerInventory(block.getState(), false);
|
||||||
|
for (Object value : meta) {
|
||||||
|
ItemStack item = ItemUtils.unserializeItemStackLegacy(value);
|
||||||
|
if (item != null) {
|
||||||
|
RollbackUtil.modifyContainerItems(rowType, inventory, 0, item, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (rowType == Material.COMMAND_BLOCK || rowType == Material.REPEATING_COMMAND_BLOCK || rowType == Material.CHAIN_COMMAND_BLOCK) { // command block
|
||||||
|
BlockUtils.prepareTypeAndData(chunkChanges, block, rowType, blockData, false);
|
||||||
|
if (countBlock) {
|
||||||
|
updateBlockCount(finalUserString, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if ((rowType == Material.WATER)) {
|
||||||
|
if (pendingChangeData instanceof Waterlogged) {
|
||||||
|
Waterlogged waterlogged = (Waterlogged) pendingChangeData;
|
||||||
|
waterlogged.setWaterlogged(true);
|
||||||
|
BlockUtils.prepareTypeAndData(chunkChanges, block, null, waterlogged, false);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
BlockUtils.prepareTypeAndData(chunkChanges, block, rowType, blockData, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return countBlock;
|
||||||
|
}
|
||||||
|
else if ((rowType == Material.NETHER_PORTAL) && rowAction == 0) {
|
||||||
|
BlockUtils.prepareTypeAndData(chunkChanges, block, Material.FIRE, null, true);
|
||||||
|
}
|
||||||
|
else if (blockData == null && rowData > 0 && (rowType == Material.IRON_DOOR || BlockGroup.DOORS.contains(rowType))) {
|
||||||
|
if (countBlock) {
|
||||||
|
updateBlockCount(finalUserString, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (blockData == null && rowData > 0 && (rowType.name().endsWith("_BED"))) {
|
||||||
|
if (countBlock) {
|
||||||
|
updateBlockCount(finalUserString, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (rowType.name().endsWith("_BANNER")) {
|
||||||
|
BlockUtils.prepareTypeAndData(chunkChanges, block, rowType, blockData, false);
|
||||||
|
if (countBlock) {
|
||||||
|
updateBlockCount(finalUserString, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
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<String, Object>) value);
|
||||||
|
banner.addPattern(pattern);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
banner.update();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
BlockUtils.prepareTypeAndData(chunkChanges, block, rowType, blockData, (isChest));
|
||||||
|
if (isChest) {
|
||||||
|
ChestTool.updateDoubleChest(block, blockData, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return countBlock;
|
||||||
|
}
|
||||||
|
else if (BlockGroup.UPDATE_STATE.contains(rowType) || rowType.name().contains("CANDLE")) {
|
||||||
|
BlockUtils.prepareTypeAndData(chunkChanges, block, rowType, blockData, true);
|
||||||
|
ChestTool.updateDoubleChest(block, blockData, true);
|
||||||
|
return countBlock;
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
BlockUtils.prepareTypeAndData(chunkChanges, bisectBlock, rowType, bisectData, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockUtils.prepareTypeAndData(chunkChanges, block, rowType, blockData, false);
|
||||||
|
if (countBlock) {
|
||||||
|
updateBlockCount(finalUserString, 2);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
BlockUtils.prepareTypeAndData(chunkChanges, adjacentBlock, rowType, bedData, false);
|
||||||
|
if (countBlock) {
|
||||||
|
updateBlockCount(finalUserString, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockUtils.prepareTypeAndData(chunkChanges, block, rowType, blockData, true);
|
||||||
|
return countBlock;
|
||||||
|
}
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
BlockUtils.prepareTypeAndData(chunkChanges, block, rowType, blockData, physics);
|
||||||
|
return countBlock;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return countBlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the block count in the rollback hash
|
||||||
|
*
|
||||||
|
* @param userString
|
||||||
|
* The username for this rollback
|
||||||
|
* @param increment
|
||||||
|
* The amount to increment the block count by
|
||||||
|
*/
|
||||||
|
protected static void updateBlockCount(String userString, int increment) {
|
||||||
|
int[] rollbackHashData = ConfigHandler.rollbackHash.get(userString);
|
||||||
|
int itemCount = rollbackHashData[0];
|
||||||
|
int blockCount = rollbackHashData[1];
|
||||||
|
int entityCount = rollbackHashData[2];
|
||||||
|
int scannedWorlds = rollbackHashData[4];
|
||||||
|
|
||||||
|
blockCount += increment;
|
||||||
|
ConfigHandler.rollbackHash.put(userString, new int[] { itemCount, blockCount, entityCount, 0, scannedWorlds });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply all pending block changes to the world
|
||||||
|
*
|
||||||
|
* @param chunkChanges
|
||||||
|
* Map of blocks to change
|
||||||
|
* @param preview
|
||||||
|
* Whether this is a preview
|
||||||
|
* @param user
|
||||||
|
* The user performing the rollback
|
||||||
|
*/
|
||||||
|
public static void applyBlockChanges(Map<Block, BlockData> chunkChanges, int preview, Player user) {
|
||||||
|
for (Entry<Block, BlockData> chunkChange : chunkChanges.entrySet()) {
|
||||||
|
Block changeBlock = chunkChange.getKey();
|
||||||
|
BlockData changeBlockData = chunkChange.getValue();
|
||||||
|
if (preview > 0 && user != null) {
|
||||||
|
Util.sendBlockChange(user, changeBlock.getLocation(), changeBlockData);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
BlockUtils.setTypeAndData(changeBlock, null, changeBlockData, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
chunkChanges.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,202 @@
|
||||||
|
package net.coreprotect.database.rollback;
|
||||||
|
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.block.Block;
|
||||||
|
import org.bukkit.block.BlockState;
|
||||||
|
import org.bukkit.entity.Entity;
|
||||||
|
import org.bukkit.entity.EntityType;
|
||||||
|
|
||||||
|
import net.coreprotect.config.ConfigHandler;
|
||||||
|
import net.coreprotect.thread.CacheHandler;
|
||||||
|
import net.coreprotect.utility.EntityUtils;
|
||||||
|
import net.coreprotect.utility.WorldUtils;
|
||||||
|
|
||||||
|
public class RollbackEntityHandler {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processes an entity-related rollback operation.
|
||||||
|
*
|
||||||
|
* @param row
|
||||||
|
* The database row containing entity data (used only for specific operations)
|
||||||
|
* @param rollbackType
|
||||||
|
* The type of rollback (0 for rollback, 1 for restore)
|
||||||
|
* @param finalUserString
|
||||||
|
* The user string for tracking operations
|
||||||
|
* @param rowTypeRaw
|
||||||
|
* The raw type value
|
||||||
|
* @param rowData
|
||||||
|
* The data value
|
||||||
|
* @param rowAction
|
||||||
|
* The action value
|
||||||
|
* @param rowRolledBack
|
||||||
|
* Whether the entity was already rolled back
|
||||||
|
* @param rowX
|
||||||
|
* The X coordinate
|
||||||
|
* @param rowY
|
||||||
|
* The Y coordinate
|
||||||
|
* @param rowZ
|
||||||
|
* The Z coordinate
|
||||||
|
* @param rowWorldId
|
||||||
|
* The world ID
|
||||||
|
* @param rowUserId
|
||||||
|
* The user ID
|
||||||
|
* @param rowUser
|
||||||
|
* The username associated with this entity change
|
||||||
|
* @return The number of entities affected (1 if successful, 0 otherwise)
|
||||||
|
*/
|
||||||
|
public static int processEntity(Object[] row, int rollbackType, String finalUserString, int rowTypeRaw, int rowData, int rowAction, int rowRolledBack, int rowX, int rowY, int rowZ, int rowWorldId, int rowUserId, String rowUser) {
|
||||||
|
try {
|
||||||
|
// Entity kill
|
||||||
|
if (rowAction == 3) {
|
||||||
|
String world = getWorldName(rowWorldId);
|
||||||
|
if (world.isEmpty()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
World bukkitWorld = Bukkit.getServer().getWorld(world);
|
||||||
|
if (bukkitWorld == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 entityType = EntityUtils.getEntityType(rowTypeRaw);
|
||||||
|
// Use the spawnEntity method from the RollbackUtil class instead of Queue
|
||||||
|
spawnEntity(rowUser, block.getState(), entityType, rowData);
|
||||||
|
updateEntityCount(finalUserString, 1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (rowTypeRaw <= 0) {
|
||||||
|
int oldTypeRaw = rowTypeRaw;
|
||||||
|
// Attempt to remove entity
|
||||||
|
if (rowRolledBack == 1) {
|
||||||
|
boolean removed = false;
|
||||||
|
int entityId = -1;
|
||||||
|
String entityName = EntityUtils.getEntityType(Math.abs(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) {
|
||||||
|
updateEntityCount(finalUserString, 1);
|
||||||
|
removed = true;
|
||||||
|
entity.remove();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (entity.getType().equals(EntityUtils.getEntityType(Math.abs(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) {
|
||||||
|
updateEntityCount(finalUserString, 1);
|
||||||
|
removed = true;
|
||||||
|
entity.remove();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!removed && entityId > -1) {
|
||||||
|
for (Entity entity : block.getWorld().getLivingEntities()) {
|
||||||
|
int id = entity.getEntityId();
|
||||||
|
if (id == entityId) {
|
||||||
|
updateEntityCount(finalUserString, 1);
|
||||||
|
removed = true;
|
||||||
|
entity.remove();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (removed) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the world name from a world ID.
|
||||||
|
*
|
||||||
|
* @param worldId
|
||||||
|
* The world ID
|
||||||
|
* @return The world name
|
||||||
|
*/
|
||||||
|
private static String getWorldName(int worldId) {
|
||||||
|
return WorldUtils.getWorldName(worldId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the entity count in the rollback hash for a specific user.
|
||||||
|
*
|
||||||
|
* @param userString
|
||||||
|
* The user string identifier
|
||||||
|
* @param increment
|
||||||
|
* The amount to increment the entity count by
|
||||||
|
*/
|
||||||
|
public static void updateEntityCount(String userString, int increment) {
|
||||||
|
int[] rollbackHashData = ConfigHandler.rollbackHash.get(userString);
|
||||||
|
if (rollbackHashData != null) {
|
||||||
|
int itemCount = rollbackHashData[0];
|
||||||
|
int blockCount = rollbackHashData[1];
|
||||||
|
int entityCount = rollbackHashData[2];
|
||||||
|
int next = rollbackHashData[3];
|
||||||
|
int scannedWorlds = rollbackHashData[4];
|
||||||
|
|
||||||
|
entityCount += increment;
|
||||||
|
|
||||||
|
ConfigHandler.rollbackHash.put(userString, new int[] { itemCount, blockCount, entityCount, next, scannedWorlds });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spawns an entity at the given block location.
|
||||||
|
*
|
||||||
|
* @param user
|
||||||
|
* The username of the player
|
||||||
|
* @param block
|
||||||
|
* The block state where the entity should be spawned
|
||||||
|
* @param type
|
||||||
|
* The type of entity to spawn
|
||||||
|
* @param data
|
||||||
|
* Additional data for the entity
|
||||||
|
*/
|
||||||
|
public static void spawnEntity(String user, BlockState block, EntityType type, int data) {
|
||||||
|
// Create a new helper method that will delegate to Queue
|
||||||
|
RollbackUtil.queueEntitySpawn(user, block, type, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,127 @@
|
||||||
|
package net.coreprotect.database.rollback;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import org.bukkit.inventory.PlayerInventory;
|
||||||
|
import org.bukkit.util.io.BukkitObjectInputStream;
|
||||||
|
|
||||||
|
import net.coreprotect.config.ConfigHandler;
|
||||||
|
|
||||||
|
public class RollbackItemHandler {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Populates an ItemStack with metadata from the database
|
||||||
|
*
|
||||||
|
* @param itemstack
|
||||||
|
* The ItemStack to populate
|
||||||
|
* @param metadata
|
||||||
|
* The metadata as a byte array
|
||||||
|
* @return Object array containing [slot, facing, itemstack]
|
||||||
|
*/
|
||||||
|
public static Object[] populateItemStack(ItemStack itemstack, byte[] metadata) {
|
||||||
|
int slot = 0;
|
||||||
|
String face = "";
|
||||||
|
|
||||||
|
if (metadata != null) {
|
||||||
|
try {
|
||||||
|
ByteArrayInputStream metaByteStream = new ByteArrayInputStream(metadata);
|
||||||
|
BukkitObjectInputStream metaObjectStream = new BukkitObjectInputStream(metaByteStream);
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
List<Object> meta = (List<Object>) metaObjectStream.readObject();
|
||||||
|
metaObjectStream.close();
|
||||||
|
metaByteStream.close();
|
||||||
|
|
||||||
|
for (Object value : meta) {
|
||||||
|
if (value instanceof Integer) {
|
||||||
|
slot = (Integer) value;
|
||||||
|
}
|
||||||
|
else if (value instanceof ItemStack) {
|
||||||
|
itemstack = (ItemStack) value;
|
||||||
|
}
|
||||||
|
else if (value instanceof String) {
|
||||||
|
face = (String) value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Object[] { slot, face, itemstack };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sorts inventory items for better display
|
||||||
|
*
|
||||||
|
* @param inventory
|
||||||
|
* The inventory to sort
|
||||||
|
* @param slots
|
||||||
|
* The slots to sort
|
||||||
|
*/
|
||||||
|
public static void sortContainerItems(PlayerInventory inventory, List<Integer> slots) {
|
||||||
|
if (slots.contains(0)) {
|
||||||
|
ItemStack boots = inventory.getBoots();
|
||||||
|
if (boots != null && !boots.getType().equals(Material.AIR)) {
|
||||||
|
if (!boots.getType().name().contains("BOOTS")) {
|
||||||
|
inventory.setBoots(new ItemStack(Material.AIR));
|
||||||
|
inventory.addItem(boots);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (slots.contains(1)) {
|
||||||
|
ItemStack leggings = inventory.getLeggings();
|
||||||
|
if (leggings != null && !leggings.getType().equals(Material.AIR)) {
|
||||||
|
if (!leggings.getType().name().contains("LEGGINGS")) {
|
||||||
|
inventory.setLeggings(new ItemStack(Material.AIR));
|
||||||
|
inventory.addItem(leggings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (slots.contains(2)) {
|
||||||
|
ItemStack chestplate = inventory.getChestplate();
|
||||||
|
if (chestplate != null && !chestplate.getType().equals(Material.AIR)) {
|
||||||
|
if (!chestplate.getType().name().contains("CHESTPLATE")) {
|
||||||
|
inventory.setChestplate(new ItemStack(Material.AIR));
|
||||||
|
inventory.addItem(chestplate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (slots.contains(3)) {
|
||||||
|
ItemStack helmet = inventory.getHelmet();
|
||||||
|
if (helmet != null && !helmet.getType().equals(Material.AIR)) {
|
||||||
|
String materialName = helmet.getType().name();
|
||||||
|
if (!materialName.contains("HELMET") && !materialName.contains("SKULL") && !materialName.endsWith("_HEAD")) {
|
||||||
|
inventory.setHelmet(new ItemStack(Material.AIR));
|
||||||
|
inventory.addItem(helmet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the item count in the rollback hash
|
||||||
|
*
|
||||||
|
* @param userString
|
||||||
|
* The username for this rollback
|
||||||
|
* @param increment
|
||||||
|
* The amount to increment the item count by
|
||||||
|
*/
|
||||||
|
public static void updateItemCount(String userString, int increment) {
|
||||||
|
int[] rollbackHashData = ConfigHandler.rollbackHash.get(userString);
|
||||||
|
int itemCount = rollbackHashData[0];
|
||||||
|
int blockCount = rollbackHashData[1];
|
||||||
|
int entityCount = rollbackHashData[2];
|
||||||
|
int scannedWorlds = rollbackHashData[4];
|
||||||
|
|
||||||
|
itemCount += increment;
|
||||||
|
ConfigHandler.rollbackHash.put(userString, new int[] { itemCount, blockCount, entityCount, 0, scannedWorlds });
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,454 @@
|
||||||
|
package net.coreprotect.database.rollback;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.block.Block;
|
||||||
|
import org.bukkit.block.BlockFace;
|
||||||
|
import org.bukkit.block.BlockState;
|
||||||
|
import org.bukkit.block.data.BlockData;
|
||||||
|
import org.bukkit.block.data.type.Jukebox;
|
||||||
|
import org.bukkit.entity.ArmorStand;
|
||||||
|
import org.bukkit.entity.Entity;
|
||||||
|
import org.bukkit.entity.ItemFrame;
|
||||||
|
import org.bukkit.entity.LivingEntity;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
|
||||||
|
import net.coreprotect.bukkit.BukkitAdapter;
|
||||||
|
import net.coreprotect.config.Config;
|
||||||
|
import net.coreprotect.config.ConfigHandler;
|
||||||
|
import net.coreprotect.database.logger.ItemLogger;
|
||||||
|
import net.coreprotect.model.BlockGroup;
|
||||||
|
import net.coreprotect.utility.BlockUtils;
|
||||||
|
import net.coreprotect.utility.ItemUtils;
|
||||||
|
import net.coreprotect.utility.MaterialUtils;
|
||||||
|
import net.coreprotect.utility.Teleport;
|
||||||
|
import net.coreprotect.utility.WorldUtils;
|
||||||
|
|
||||||
|
public class RollbackProcessor {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process data for a specific chunk
|
||||||
|
*
|
||||||
|
* @param finalChunkX
|
||||||
|
* The chunk X coordinate
|
||||||
|
* @param finalChunkZ
|
||||||
|
* The chunk Z coordinate
|
||||||
|
* @param chunkKey
|
||||||
|
* The chunk lookup key
|
||||||
|
* @param blockList
|
||||||
|
* The list of block data to process
|
||||||
|
* @param itemList
|
||||||
|
* The list of item data to process
|
||||||
|
* @param rollbackType
|
||||||
|
* The rollback type (0=rollback, 1=restore)
|
||||||
|
* @param preview
|
||||||
|
* Whether this is a preview (0=no, 1=yes-non-destructive, 2=yes-destructive)
|
||||||
|
* @param finalUserString
|
||||||
|
* The username performing the rollback
|
||||||
|
* @param finalUser
|
||||||
|
* The user performing the rollback
|
||||||
|
* @param bukkitRollbackWorld
|
||||||
|
* The world to process
|
||||||
|
* @return True if successful, false if there was an error
|
||||||
|
*/
|
||||||
|
public static boolean processChunk(int finalChunkX, int finalChunkZ, long chunkKey, ArrayList<Object[]> blockList, ArrayList<Object[]> itemList, int rollbackType, int preview, String finalUserString, Player finalUser, World bukkitRollbackWorld, boolean inventoryRollback) {
|
||||||
|
try {
|
||||||
|
boolean clearInventories = Config.getGlobal().ROLLBACK_ITEMS;
|
||||||
|
ArrayList<Object[]> data = blockList != null ? blockList : new ArrayList<>();
|
||||||
|
ArrayList<Object[]> itemData = itemList != null ? itemList : new ArrayList<>();
|
||||||
|
Map<Block, BlockData> chunkChanges = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
// Process blocks
|
||||||
|
for (Object[] row : data) {
|
||||||
|
int unixtimestamp = (int) (System.currentTimeMillis() / 1000L);
|
||||||
|
int[] rollbackHashData = ConfigHandler.rollbackHash.get(finalUserString);
|
||||||
|
int itemCount = rollbackHashData[0];
|
||||||
|
int blockCount = rollbackHashData[1];
|
||||||
|
int entityCount = rollbackHashData[2];
|
||||||
|
int scannedWorlds = rollbackHashData[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 = MaterialUtils.rolledBack((Integer) row[9], false);
|
||||||
|
int rowWorldId = (Integer) row[10];
|
||||||
|
byte[] rowMeta = (byte[]) row[12];
|
||||||
|
byte[] rowBlockData = (byte[]) row[13];
|
||||||
|
String blockDataString = BlockUtils.byteDataToString(rowBlockData, rowTypeRaw);
|
||||||
|
Material rowType = MaterialUtils.getType(rowTypeRaw);
|
||||||
|
|
||||||
|
List<Object> meta = null;
|
||||||
|
if (rowMeta != null) {
|
||||||
|
meta = Rollback.deserializeMetadata(rowMeta);
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockData blockData = null;
|
||||||
|
if (blockDataString != null && blockDataString.contains(":")) {
|
||||||
|
try {
|
||||||
|
blockData = Bukkit.getServer().createBlockData(blockDataString);
|
||||||
|
}
|
||||||
|
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 = BlockUtils.createBlockData(rowType);
|
||||||
|
}
|
||||||
|
|
||||||
|
String rowUser = ConfigHandler.playerIdCacheReversed.get((Integer) row[2]);
|
||||||
|
int oldTypeRaw = rowTypeRaw;
|
||||||
|
Material oldTypeMaterial = MaterialUtils.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 = WorldUtils.getWorldName(rowWorldId);
|
||||||
|
if (world.length() == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
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)) {
|
||||||
|
BlockUtils.prepareTypeAndData(chunkChanges, block, blockType, block.getBlockData(), true);
|
||||||
|
blockCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if ((!BukkitAdapter.ADAPTER.isItemFrame(rowType)) && (rowType != Material.PAINTING) && (rowType != Material.ARMOR_STAND) && (rowType != Material.END_CRYSTAL)) {
|
||||||
|
BlockUtils.prepareTypeAndData(chunkChanges, block, rowType, blockData, true);
|
||||||
|
blockCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
entityCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (rowAction == 3) { // entity kill
|
||||||
|
entityCount += RollbackEntityHandler.processEntity(row, rollbackType, finalUserString, rowTypeRaw, rowData, rowAction, MaterialUtils.rolledBack((Integer) row[9], false), rowX, rowY, rowZ, rowWorldId, (Integer) row[2], rowUser);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
String world = WorldUtils.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 org.bukkit.block.data.MultipleFacing || checkData instanceof org.bukkit.block.data.type.Stairs || checkData instanceof org.bukkit.block.data.type.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 org.bukkit.block.data.Waterlogged) {
|
||||||
|
if (Material.WATER.createBlockData().equals(block.getBlockData())) {
|
||||||
|
org.bukkit.block.data.Waterlogged waterlogged = (org.bukkit.block.data.Waterlogged) blockData;
|
||||||
|
waterlogged.setWaterlogged(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (countBlock && RollbackBlockHandler.processBlockChange(block, row, rollbackType, clearInventories, chunkChanges, countBlock, oldTypeMaterial, pendingChangeType, pendingChangeData, finalUserString, rawBlockData, changeType, changeBlockData, meta != null ? new ArrayList<>(meta) : null, blockData, rowUser, rowType, rowX, rowY, rowZ, rowTypeRaw, rowData, rowAction, rowWorldId, BlockUtils.byteDataToString((byte[]) row[13], rowTypeRaw))) {
|
||||||
|
blockCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigHandler.rollbackHash.put(finalUserString, new int[] { itemCount, blockCount, entityCount, 0, scannedWorlds });
|
||||||
|
}
|
||||||
|
data.clear();
|
||||||
|
|
||||||
|
// Apply cached block changes
|
||||||
|
RollbackBlockHandler.applyBlockChanges(chunkChanges, preview, finalUser instanceof Player ? (Player) finalUser : null);
|
||||||
|
|
||||||
|
// Process container items
|
||||||
|
Map<Player, List<Integer>> 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 = MaterialUtils.rolledBack((Integer) row[9], false);
|
||||||
|
int rowWorldId = (Integer) row[10];
|
||||||
|
int rowAmount = (Integer) row[11];
|
||||||
|
byte[] rowMetadata = (byte[]) row[12];
|
||||||
|
Material rowType = MaterialUtils.getType(rowTypeRaw);
|
||||||
|
|
||||||
|
int rolledBackInventory = MaterialUtils.rolledBack((Integer) row[9], true);
|
||||||
|
if (rowType != null) {
|
||||||
|
if (inventoryRollback && ((rollbackType == 0 && rolledBackInventory == 0) || (rollbackType == 1 && rolledBackInventory == 1))) {
|
||||||
|
Material inventoryItem = ItemUtils.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 = RollbackItemHandler.populateItemStack(itemstack, rowMetadata);
|
||||||
|
if (rowAction == ItemLogger.ITEM_REMOVE_ENDER || rowAction == ItemLogger.ITEM_ADD_ENDER) {
|
||||||
|
RollbackUtil.modifyContainerItems(containerType, player.getEnderChest(), (Integer) populatedStack[0], ((ItemStack) populatedStack[2]).clone(), action ^ 1);
|
||||||
|
}
|
||||||
|
int modifiedArmor = RollbackUtil.modifyContainerItems(containerType, player.getInventory(), (Integer) populatedStack[0], (ItemStack) populatedStack[2], action);
|
||||||
|
if (modifiedArmor > -1) {
|
||||||
|
List<Integer> 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, 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 = RollbackItemHandler.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 = WorldUtils.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 = BlockUtils.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 = ItemUtils.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];
|
||||||
|
|
||||||
|
RollbackUtil.modifyContainerItems(containerType, container, slot, itemstack, action);
|
||||||
|
itemCount1 = itemCount1 + rowAmount;
|
||||||
|
}
|
||||||
|
containerInit = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigHandler.rollbackHash.put(finalUserString, new int[] { itemCount1, blockCount1, entityCount1, 0, scannedWorlds });
|
||||||
|
}
|
||||||
|
itemData.clear();
|
||||||
|
|
||||||
|
for (Entry<Player, List<Integer>> sortEntry : sortPlayers.entrySet()) {
|
||||||
|
RollbackItemHandler.sortContainerItems(sortEntry.getKey().getInventory(), sortEntry.getValue());
|
||||||
|
}
|
||||||
|
sortPlayers.clear();
|
||||||
|
|
||||||
|
int[] rollbackHashData = ConfigHandler.rollbackHash.get(finalUserString);
|
||||||
|
int itemCount = rollbackHashData[0];
|
||||||
|
int blockCount = rollbackHashData[1];
|
||||||
|
int entityCount = rollbackHashData[2];
|
||||||
|
int scannedWorlds = rollbackHashData[4];
|
||||||
|
ConfigHandler.rollbackHash.put(finalUserString, new int[] { itemCount, blockCount, entityCount, 1, (scannedWorlds + 1) });
|
||||||
|
|
||||||
|
// Teleport players out of danger if they're within this chunk
|
||||||
|
if (preview == 0) {
|
||||||
|
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||||
|
Location playerLocation = player.getLocation();
|
||||||
|
String playerWorld = playerLocation.getWorld().getName();
|
||||||
|
int chunkX = playerLocation.getBlockX() >> 4;
|
||||||
|
int chunkZ = playerLocation.getBlockZ() >> 4;
|
||||||
|
|
||||||
|
if (bukkitRollbackWorld.getName().equals(playerWorld) && chunkX == finalChunkX && chunkZ == finalChunkZ) {
|
||||||
|
Teleport.performSafeTeleport(player, playerLocation, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
int[] rollbackHashData = ConfigHandler.rollbackHash.get(finalUserString);
|
||||||
|
int itemCount = rollbackHashData[0];
|
||||||
|
int blockCount = rollbackHashData[1];
|
||||||
|
int entityCount = rollbackHashData[2];
|
||||||
|
int scannedWorlds = rollbackHashData[4];
|
||||||
|
|
||||||
|
ConfigHandler.rollbackHash.put(finalUserString, new int[] { itemCount, blockCount, entityCount, 2, (scannedWorlds + 1) });
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -9,10 +9,12 @@ import org.bukkit.FireworkEffect.Builder;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
import org.bukkit.attribute.Attribute;
|
import org.bukkit.attribute.Attribute;
|
||||||
import org.bukkit.attribute.AttributeModifier;
|
import org.bukkit.attribute.AttributeModifier;
|
||||||
|
import org.bukkit.block.BlockState;
|
||||||
import org.bukkit.block.Jukebox;
|
import org.bukkit.block.Jukebox;
|
||||||
import org.bukkit.block.ShulkerBox;
|
import org.bukkit.block.ShulkerBox;
|
||||||
import org.bukkit.block.banner.Pattern;
|
import org.bukkit.block.banner.Pattern;
|
||||||
import org.bukkit.entity.ArmorStand;
|
import org.bukkit.entity.ArmorStand;
|
||||||
|
import org.bukkit.entity.EntityType;
|
||||||
import org.bukkit.entity.ItemFrame;
|
import org.bukkit.entity.ItemFrame;
|
||||||
import org.bukkit.inventory.EntityEquipment;
|
import org.bukkit.inventory.EntityEquipment;
|
||||||
import org.bukkit.inventory.Inventory;
|
import org.bukkit.inventory.Inventory;
|
||||||
|
|
@ -32,6 +34,7 @@ import org.bukkit.potion.PotionEffect;
|
||||||
import org.bukkit.util.io.BukkitObjectInputStream;
|
import org.bukkit.util.io.BukkitObjectInputStream;
|
||||||
|
|
||||||
import net.coreprotect.bukkit.BukkitAdapter;
|
import net.coreprotect.bukkit.BukkitAdapter;
|
||||||
|
import net.coreprotect.consumer.Queue;
|
||||||
import net.coreprotect.database.Lookup;
|
import net.coreprotect.database.Lookup;
|
||||||
import net.coreprotect.model.BlockGroup;
|
import net.coreprotect.model.BlockGroup;
|
||||||
import net.coreprotect.utility.ItemUtils;
|
import net.coreprotect.utility.ItemUtils;
|
||||||
|
|
@ -490,4 +493,103 @@ public class RollbackUtil extends Lookup {
|
||||||
return new Object[] { 0, "", itemstack };
|
return new Object[] { 0, "", itemstack };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deserializes metadata from a byte array into a list of objects.
|
||||||
|
*
|
||||||
|
* @param metadata
|
||||||
|
* The byte array containing serialized metadata
|
||||||
|
* @return The deserialized list of objects or null if deserialization fails
|
||||||
|
*/
|
||||||
|
public static List<Object> deserializeMetadata(byte[] metadata) {
|
||||||
|
if (metadata == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
ByteArrayInputStream metaByteStream = new ByteArrayInputStream(metadata);
|
||||||
|
BukkitObjectInputStream metaObjectStream = new BukkitObjectInputStream(metaByteStream);
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
List<Object> metaList = (List<Object>) metaObjectStream.readObject();
|
||||||
|
metaObjectStream.close();
|
||||||
|
metaByteStream.close();
|
||||||
|
return metaList;
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Queues an entity spawn operation for processing.
|
||||||
|
*
|
||||||
|
* @param user
|
||||||
|
* The username of the player
|
||||||
|
* @param block
|
||||||
|
* The block state where the entity should be spawned
|
||||||
|
* @param type
|
||||||
|
* The type of entity to spawn
|
||||||
|
* @param data
|
||||||
|
* Additional data for the entity
|
||||||
|
*/
|
||||||
|
public static void queueEntitySpawn(String user, BlockState block, EntityType type, int data) {
|
||||||
|
if (Queue.class.getDeclaredMethods() != null) {
|
||||||
|
try {
|
||||||
|
java.lang.reflect.Method method = Queue.class.getDeclaredMethod("queueEntitySpawn", String.class, BlockState.class, EntityType.class, int.class);
|
||||||
|
method.setAccessible(true);
|
||||||
|
method.invoke(null, user, block, type, data);
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Queues a skull update operation for processing.
|
||||||
|
*
|
||||||
|
* @param user
|
||||||
|
* The username of the player
|
||||||
|
* @param block
|
||||||
|
* The block state to update
|
||||||
|
* @param rowId
|
||||||
|
* The row ID for the skull data
|
||||||
|
*/
|
||||||
|
public static void queueSkullUpdate(String user, BlockState block, int rowId) {
|
||||||
|
if (Queue.class.getDeclaredMethods() != null) {
|
||||||
|
try {
|
||||||
|
java.lang.reflect.Method method = Queue.class.getDeclaredMethod("queueSkullUpdate", String.class, BlockState.class, int.class);
|
||||||
|
method.setAccessible(true);
|
||||||
|
method.invoke(null, user, block, rowId);
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Queues a sign update operation for processing.
|
||||||
|
*
|
||||||
|
* @param user
|
||||||
|
* The username of the player
|
||||||
|
* @param block
|
||||||
|
* The block state to update
|
||||||
|
* @param action
|
||||||
|
* The action type
|
||||||
|
* @param time
|
||||||
|
* The time of the update
|
||||||
|
*/
|
||||||
|
public static void queueSignUpdate(String user, BlockState block, int action, int time) {
|
||||||
|
if (Queue.class.getDeclaredMethods() != null) {
|
||||||
|
try {
|
||||||
|
java.lang.reflect.Method method = Queue.class.getDeclaredMethod("queueSignUpdate", String.class, BlockState.class, int.class, int.class);
|
||||||
|
method.setAccessible(true);
|
||||||
|
method.invoke(null, user, block, action, time);
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue