1910 lines
107 KiB
Java
1910 lines
107 KiB
Java
package net.coreprotect.database;
|
|
|
|
import java.io.ByteArrayInputStream;
|
|
import java.math.BigDecimal;
|
|
import java.math.RoundingMode;
|
|
import java.sql.Statement;
|
|
import java.text.NumberFormat;
|
|
import java.util.ArrayList;
|
|
import java.util.Collections;
|
|
import java.util.HashMap;
|
|
import java.util.Iterator;
|
|
import java.util.LinkedHashMap;
|
|
import java.util.List;
|
|
import java.util.Locale;
|
|
import java.util.Map;
|
|
import java.util.Map.Entry;
|
|
import java.util.TreeMap;
|
|
import java.util.UUID;
|
|
|
|
import org.bukkit.Bukkit;
|
|
import org.bukkit.DyeColor;
|
|
import org.bukkit.FireworkEffect;
|
|
import org.bukkit.FireworkEffect.Builder;
|
|
import org.bukkit.Location;
|
|
import org.bukkit.Material;
|
|
import org.bukkit.Tag;
|
|
import org.bukkit.World;
|
|
import org.bukkit.attribute.Attribute;
|
|
import org.bukkit.attribute.AttributeModifier;
|
|
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.ShulkerBox;
|
|
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.command.CommandSender;
|
|
import org.bukkit.entity.ArmorStand;
|
|
import org.bukkit.entity.EnderCrystal;
|
|
import org.bukkit.entity.Entity;
|
|
import org.bukkit.entity.EntityType;
|
|
import org.bukkit.entity.ItemFrame;
|
|
import org.bukkit.entity.LivingEntity;
|
|
import org.bukkit.entity.Player;
|
|
import org.bukkit.inventory.EntityEquipment;
|
|
import org.bukkit.inventory.Inventory;
|
|
import org.bukkit.inventory.ItemStack;
|
|
import org.bukkit.inventory.meta.BannerMeta;
|
|
import org.bukkit.inventory.meta.BlockStateMeta;
|
|
import org.bukkit.inventory.meta.CrossbowMeta;
|
|
import org.bukkit.inventory.meta.FireworkEffectMeta;
|
|
import org.bukkit.inventory.meta.FireworkMeta;
|
|
import org.bukkit.inventory.meta.ItemMeta;
|
|
import org.bukkit.inventory.meta.LeatherArmorMeta;
|
|
import org.bukkit.inventory.meta.MapMeta;
|
|
import org.bukkit.inventory.meta.PotionMeta;
|
|
import org.bukkit.potion.PotionEffect;
|
|
import org.bukkit.util.io.BukkitObjectInputStream;
|
|
|
|
import net.coreprotect.CoreProtect;
|
|
import net.coreprotect.bukkit.BukkitAdapter;
|
|
import net.coreprotect.config.Config;
|
|
import net.coreprotect.config.ConfigHandler;
|
|
import net.coreprotect.consumer.Queue;
|
|
import net.coreprotect.consumer.process.Process;
|
|
import net.coreprotect.database.logger.ItemLogger;
|
|
import net.coreprotect.database.statement.UserStatement;
|
|
import net.coreprotect.language.Phrase;
|
|
import net.coreprotect.language.Selector;
|
|
import net.coreprotect.model.BlockGroup;
|
|
import net.coreprotect.thread.CacheHandler;
|
|
import net.coreprotect.utility.Chat;
|
|
import net.coreprotect.utility.ChestTool;
|
|
import net.coreprotect.utility.Color;
|
|
import net.coreprotect.utility.Teleport;
|
|
import net.coreprotect.utility.Util;
|
|
import net.coreprotect.utility.entity.HangingUtil;
|
|
|
|
public class Rollback extends Queue {
|
|
|
|
public static List<String[]> performRollbackRestore(Statement statement, CommandSender user, List<String> checkUuids, List<String> checkUsers, String timeString, List<Object> restrictList, Map<Object, Boolean> excludeList, List<String> excludeUserList, List<Integer> actionList, Location location, Integer[] radius, long startTime, long endTime, boolean restrictWorld, boolean lookup, boolean verbose, final int rollbackType, final int preview) {
|
|
List<String[]> list = new ArrayList<>();
|
|
|
|
try {
|
|
long timeStart = System.currentTimeMillis();
|
|
List<Object[]> lookupList = new ArrayList<>();
|
|
|
|
if (!actionList.contains(4) && !actionList.contains(5) && !checkUsers.contains("#container")) {
|
|
lookupList = Lookup.performLookupRaw(statement, user, checkUuids, checkUsers, restrictList, excludeList, excludeUserList, actionList, location, radius, null, startTime, endTime, -1, -1, restrictWorld, lookup);
|
|
}
|
|
|
|
if (lookupList == null) {
|
|
return null;
|
|
}
|
|
|
|
boolean ROLLBACK_ITEMS = false;
|
|
List<Object> itemRestrictList = new ArrayList<>(restrictList);
|
|
Map<Object, Boolean> itemExcludeList = new HashMap<>(excludeList);
|
|
|
|
if (actionList.contains(1)) {
|
|
for (Object target : restrictList) {
|
|
if (target instanceof Material) {
|
|
if (!excludeList.containsKey(target)) {
|
|
if (BlockGroup.CONTAINERS.contains(target)) {
|
|
ROLLBACK_ITEMS = true;
|
|
itemRestrictList.clear();
|
|
itemExcludeList.clear();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
List<Object[]> itemList = new ArrayList<>();
|
|
if (Config.getGlobal().ROLLBACK_ITEMS && !checkUsers.contains("#container") && (actionList.size() == 0 || actionList.contains(4) || ROLLBACK_ITEMS) && preview == 0) {
|
|
List<Integer> itemActionList = new ArrayList<>(actionList);
|
|
|
|
if (!itemActionList.contains(4)) {
|
|
itemActionList.add(4);
|
|
}
|
|
|
|
itemExcludeList.entrySet().removeIf(entry -> Boolean.TRUE.equals(entry.getValue()));
|
|
itemList = Lookup.performLookupRaw(statement, user, checkUuids, checkUsers, itemRestrictList, itemExcludeList, excludeUserList, itemActionList, location, radius, null, startTime, endTime, -1, -1, restrictWorld, lookup);
|
|
}
|
|
|
|
TreeMap<Long, Integer> chunkList = new TreeMap<>();
|
|
HashMap<Long, ArrayList<Object[]>> dataList = new HashMap<>();
|
|
HashMap<Long, ArrayList<Object[]>> itemDataList = new HashMap<>();
|
|
boolean inventoryRollback = actionList.contains(11);
|
|
|
|
/*
|
|
int worldMin = BukkitAdapter.ADAPTER.getMinHeight(world);
|
|
int worldHeight = world.getMaxHeight() - worldMin;
|
|
int negativeOffset = (-(worldMin) >> 4);
|
|
Integer[] chunkSections = new Integer[worldHeight >> 4];
|
|
|
|
int y = -2044;
|
|
if (y < worldMin) {
|
|
return;
|
|
}
|
|
int chunkSection = ((y >> 4) + negativeOffset);
|
|
*/
|
|
|
|
int worldId = -1;
|
|
int worldMin = 0;
|
|
int worldMax = 2032;
|
|
|
|
int listC = 0;
|
|
while (listC < 2) {
|
|
List<Object[]> scanList = lookupList;
|
|
|
|
if (listC == 1) {
|
|
scanList = itemList;
|
|
}
|
|
|
|
for (Object[] result : scanList) {
|
|
int userId = (Integer) result[2];
|
|
int rowX = (Integer) result[3];
|
|
int rowY = (Integer) result[4];
|
|
int rowZ = (Integer) result[5];
|
|
int rowWorldId = (Integer) result[10];
|
|
int chunkX = rowX >> 4;
|
|
int chunkZ = rowZ >> 4;
|
|
long chunkKey = inventoryRollback ? 0 : (chunkX & 0xffffffffL | (chunkZ & 0xffffffffL) << 32);
|
|
// int rowAction = result[8];
|
|
// if (rowAction==10) result[8] = 0;
|
|
// if (rowAction==11) result[8] = 1;
|
|
|
|
if (rowWorldId != worldId) {
|
|
String world = Util.getWorldName(rowWorldId);
|
|
World bukkitWorld = Bukkit.getServer().getWorld(world);
|
|
if (bukkitWorld != null) {
|
|
worldMin = BukkitAdapter.ADAPTER.getMinHeight(bukkitWorld);
|
|
worldMax = bukkitWorld.getMaxHeight();
|
|
}
|
|
}
|
|
|
|
/*
|
|
if (rowY < worldMin) {
|
|
continue;
|
|
}
|
|
*/
|
|
|
|
if (chunkList.get(chunkKey) == null) {
|
|
int distance = 0;
|
|
if (location != null) {
|
|
distance = (int) Math.sqrt(Math.pow((Integer) result[3] - location.getBlockX(), 2) + Math.pow((Integer) result[5] - location.getBlockZ(), 2));
|
|
}
|
|
|
|
chunkList.put(chunkKey, distance);
|
|
}
|
|
|
|
if (ConfigHandler.playerIdCacheReversed.get(userId) == null) {
|
|
UserStatement.loadName(statement.getConnection(), userId);
|
|
}
|
|
|
|
HashMap<Long, ArrayList<Object[]>> 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<>());
|
|
}
|
|
|
|
modifyList.get(chunkKey).add(result);
|
|
}
|
|
|
|
listC++;
|
|
}
|
|
|
|
if (rollbackType == 1) { // Restore
|
|
Iterator<Map.Entry<Long, ArrayList<Object[]>>> dlIterator = dataList.entrySet().iterator();
|
|
while (dlIterator.hasNext()) {
|
|
Collections.reverse(dlIterator.next().getValue());
|
|
}
|
|
|
|
dlIterator = itemDataList.entrySet().iterator();
|
|
while (dlIterator.hasNext()) {
|
|
Collections.reverse(dlIterator.next().getValue());
|
|
}
|
|
}
|
|
|
|
Integer chunkCount = 0;
|
|
String userString = "#server";
|
|
if (user != null) {
|
|
userString = user.getName();
|
|
if (verbose && preview == 0 && !actionList.contains(11)) {
|
|
Integer chunks = chunkList.size();
|
|
Chat.sendMessage(user, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.ROLLBACK_CHUNKS_FOUND, chunks.toString(), (chunks == 1 ? Selector.FIRST : Selector.SECOND)));
|
|
}
|
|
}
|
|
|
|
// Perform update transaction(s) in consumer
|
|
if (preview == 0) {
|
|
if (actionList.contains(11)) {
|
|
List<Object[]> blockList = new ArrayList<>();
|
|
List<Object[]> inventoryList = new ArrayList<>();
|
|
List<Object[]> containerList = new ArrayList<>();
|
|
for (Object[] data : itemList) {
|
|
int table = (Integer) data[14];
|
|
if (table == 2) { // item
|
|
inventoryList.add(data);
|
|
}
|
|
else if (table == 1) { // container
|
|
containerList.add(data);
|
|
}
|
|
else { // block
|
|
blockList.add(data);
|
|
}
|
|
}
|
|
Queue.queueRollbackUpdate(userString, location, inventoryList, Process.INVENTORY_ROLLBACK_UPDATE, rollbackType);
|
|
Queue.queueRollbackUpdate(userString, location, containerList, Process.INVENTORY_CONTAINER_ROLLBACK_UPDATE, rollbackType);
|
|
Queue.queueRollbackUpdate(userString, location, blockList, Process.BLOCK_INVENTORY_ROLLBACK_UPDATE, rollbackType);
|
|
}
|
|
else {
|
|
Queue.queueRollbackUpdate(userString, location, lookupList, Process.ROLLBACK_UPDATE, rollbackType);
|
|
Queue.queueRollbackUpdate(userString, location, itemList, Process.CONTAINER_ROLLBACK_UPDATE, rollbackType);
|
|
}
|
|
}
|
|
|
|
ConfigHandler.rollbackHash.put(userString, new int[] { 0, 0, 0, 0 });
|
|
|
|
final String finalUserString = userString;
|
|
for (Entry<Long, Integer> entry : Util.entriesSortedByValues(chunkList)) {
|
|
chunkCount++;
|
|
|
|
int itemCount = 0;
|
|
int blockCount = 0;
|
|
int entityCount = 0;
|
|
int[] rollbackHashData = ConfigHandler.rollbackHash.get(finalUserString);
|
|
itemCount = rollbackHashData[0];
|
|
blockCount = rollbackHashData[1];
|
|
entityCount = rollbackHashData[2];
|
|
|
|
final long chunkKey = entry.getKey();
|
|
final CommandSender finalUser = user;
|
|
final HashMap<Long, ArrayList<Object[]>> finalBlockList = dataList;
|
|
final HashMap<Long, ArrayList<Object[]>> 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;
|
|
}
|
|
|
|
ArrayList<Object[]> data = finalBlockList.get(chunkKey);
|
|
ArrayList<Object[]> itemData = finalItemList.get(chunkKey);
|
|
Map<Block, BlockData> chunkChanges = new LinkedHashMap<>();
|
|
|
|
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);
|
|
|
|
List<Object> meta = null;
|
|
if (rowMeta != null) {
|
|
ByteArrayInputStream metaByteStream = new ByteArrayInputStream(rowMeta);
|
|
BukkitObjectInputStream metaObjectStream = new BukkitObjectInputStream(metaByteStream);
|
|
@SuppressWarnings("unchecked")
|
|
List<Object> metaList = (List<Object>) metaObjectStream.readObject();
|
|
metaObjectStream.close();
|
|
metaByteStream.close();
|
|
meta = metaList;
|
|
}
|
|
|
|
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 = 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;
|
|
}
|
|
|
|
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();
|
|
|
|
if (rowRolledBack == 1 && rollbackType == 0) { // rollback
|
|
countBlock = false;
|
|
}
|
|
|
|
if ((rowType == changeType) && ((!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(changeBlockData.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 ((changeType != Material.AIR) && (changeType != Material.CAVE_AIR)) {
|
|
countBlock = true;
|
|
}
|
|
|
|
if ((changeType == 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))) {
|
|
BlockData existingBlockData = block.getBlockData();
|
|
if (existingBlockData instanceof Waterlogged) {
|
|
Waterlogged waterlogged = (Waterlogged) existingBlockData;
|
|
waterlogged.setWaterlogged(false);
|
|
block.setBlockData(waterlogged);
|
|
}
|
|
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)) {
|
|
BlockData currentBlockData = block.getBlockData();
|
|
if (currentBlockData instanceof Waterlogged) {
|
|
Waterlogged waterlogged = (Waterlogged) currentBlockData;
|
|
if (waterlogged.isWaterlogged()) {
|
|
boolean physics = (changeBlockData instanceof Chest);
|
|
Util.prepareTypeAndData(chunkChanges, block, Material.WATER, Material.WATER.createBlockData(), physics);
|
|
remove = false;
|
|
}
|
|
}
|
|
else if ((changeType == 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)) {
|
|
BlockData existingBlockData = block.getBlockData();
|
|
if (existingBlockData instanceof Waterlogged) {
|
|
Waterlogged waterlogged = (Waterlogged) existingBlockData;
|
|
waterlogged.setWaterlogged(true);
|
|
block.setBlockData(waterlogged);
|
|
}
|
|
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<String, Object>) 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 });
|
|
}
|
|
data.clear();
|
|
|
|
// Apply cached changes
|
|
for (Entry<Block, BlockData> 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();
|
|
|
|
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, (short) rowData);
|
|
Object[] populatedStack = populateItemStack(itemstack, rowMetadata);
|
|
if (rowAction == ItemLogger.ITEM_REMOVE_ENDER || rowAction == ItemLogger.ITEM_ADD_ENDER) {
|
|
modifyContainerItems(containerType, player.getEnderChest(), (Integer) populatedStack[0], ((ItemStack) populatedStack[2]).clone(), action ^ 1);
|
|
}
|
|
modifyContainerItems(containerType, player.getInventory(), (Integer) populatedStack[0], (ItemStack) populatedStack[2], action);
|
|
|
|
itemCount1 = itemCount1 + rowAmount;
|
|
ConfigHandler.rollbackHash.put(finalUserString, new int[] { itemCount1, blockCount1, entityCount1, 0 });
|
|
continue; // remove this for merged rollbacks in future? (be sure to re-enable chunk sorting)
|
|
}
|
|
|
|
if (inventoryRollback || rowAction > 1) {
|
|
continue; // skip inventory & ender chest transactions
|
|
}
|
|
|
|
if ((rollbackType == 0 && rowRolledBack == 0) || (rollbackType == 1 && rowRolledBack == 1)) {
|
|
ItemStack itemstack = new ItemStack(rowType, rowAmount, (short) rowData);
|
|
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())) {
|
|
container = Util.getContainerInventory(block.getState(), 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;
|
|
}
|
|
}
|
|
|
|
ConfigHandler.rollbackHash.put(finalUserString, new int[] { itemCount1, blockCount1, entityCount1, 0 });
|
|
}
|
|
itemData.clear();
|
|
|
|
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;
|
|
|
|
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];
|
|
|
|
ConfigHandler.rollbackHash.put(finalUserString, new int[] { itemCount1, blockCount1, entityCount1, 2 });
|
|
}
|
|
}, 0);
|
|
|
|
rollbackHashData = ConfigHandler.rollbackHash.get(finalUserString);
|
|
int next = rollbackHashData[3];
|
|
int sleepTime = 0;
|
|
int abort = 0;
|
|
|
|
while (next == 0) {
|
|
if (preview == 1) {
|
|
// Not actually changing blocks, so less intensive.
|
|
sleepTime = sleepTime + 1;
|
|
Thread.sleep(1);
|
|
}
|
|
else {
|
|
sleepTime = sleepTime + 5;
|
|
Thread.sleep(5);
|
|
}
|
|
|
|
rollbackHashData = ConfigHandler.rollbackHash.get(finalUserString);
|
|
next = rollbackHashData[3];
|
|
|
|
if (sleepTime > 300000) {
|
|
abort = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (abort == 1 || next == 2) {
|
|
Chat.console(Phrase.build(Phrase.ROLLBACK_ABORTED));
|
|
break;
|
|
}
|
|
|
|
rollbackHashData = ConfigHandler.rollbackHash.get(finalUserString);
|
|
itemCount = rollbackHashData[0];
|
|
blockCount = rollbackHashData[1];
|
|
entityCount = rollbackHashData[2];
|
|
ConfigHandler.rollbackHash.put(finalUserString, new int[] { itemCount, blockCount, entityCount, 0 });
|
|
|
|
if (verbose && user != null && preview == 0 && !actionList.contains(11)) {
|
|
Integer chunks = chunkList.size();
|
|
Chat.sendMessage(user, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.ROLLBACK_CHUNKS_MODIFIED, chunkCount.toString(), chunks.toString(), (chunks == 1 ? Selector.FIRST : Selector.SECOND)));
|
|
}
|
|
}
|
|
|
|
chunkList.clear();
|
|
dataList.clear();
|
|
itemDataList.clear();
|
|
|
|
int[] rollbackHashData = ConfigHandler.rollbackHash.get(finalUserString);
|
|
int itemCount = rollbackHashData[0];
|
|
int blockCount = rollbackHashData[1];
|
|
int entityCount = rollbackHashData[2];
|
|
long timeFinish = System.currentTimeMillis();
|
|
double totalSeconds = (timeFinish - timeStart) / 1000.0;
|
|
|
|
if (user != null) {
|
|
finishRollbackRestore(user, location, checkUsers, restrictList, excludeList, excludeUserList, actionList, timeString, chunkCount, totalSeconds, itemCount, blockCount, entityCount, rollbackType, radius, verbose, restrictWorld, preview);
|
|
}
|
|
|
|
list = Lookup.convertRawLookup(statement, lookupList);
|
|
return list;
|
|
}
|
|
catch (Exception e) {
|
|
e.printStackTrace();
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
static void finishRollbackRestore(CommandSender user, Location location, List<String> checkUsers, List<Object> restrictList, Map<Object, Boolean> excludeList, List<String> excludeUserList, List<Integer> actionList, String timeString, Integer chunkCount, Double seconds, Integer itemCount, Integer blockCount, Integer entityCount, int rollbackType, Integer[] radius, boolean verbose, boolean restrictWorld, int preview) {
|
|
try {
|
|
if (preview == 2) {
|
|
Chat.sendMessage(user, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.PREVIEW_CANCELLED));
|
|
return;
|
|
}
|
|
|
|
Chat.sendMessage(user, "-----");
|
|
|
|
StringBuilder usersBuilder = new StringBuilder();
|
|
for (String value : checkUsers) {
|
|
if (usersBuilder.length() == 0) {
|
|
usersBuilder = usersBuilder.append("" + value + "");
|
|
}
|
|
else {
|
|
usersBuilder.append(", ").append(value);
|
|
}
|
|
}
|
|
String users = usersBuilder.toString();
|
|
|
|
if (users.equals("#global") && restrictWorld) {
|
|
users = "#" + location.getWorld().getName();
|
|
}
|
|
|
|
if (preview > 0) {
|
|
Chat.sendMessage(user, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.ROLLBACK_COMPLETED, users, Selector.THIRD)); // preview
|
|
}
|
|
else if (rollbackType == 0) {
|
|
Chat.sendMessage(user, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.ROLLBACK_COMPLETED, users, Selector.FIRST)); // rollback
|
|
}
|
|
else if (rollbackType == 1) {
|
|
Chat.sendMessage(user, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.ROLLBACK_COMPLETED, users, Selector.SECOND)); // restore
|
|
}
|
|
|
|
if (preview == 1 || rollbackType == 0 || rollbackType == 1) {
|
|
Chat.sendMessage(user, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.ROLLBACK_TIME, timeString));
|
|
}
|
|
|
|
if (radius != null) {
|
|
int worldedit = radius[7];
|
|
if (worldedit == 0) {
|
|
Integer rollbackRadius = radius[0];
|
|
Chat.sendMessage(user, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.ROLLBACK_RADIUS, rollbackRadius.toString(), (rollbackRadius == 1 ? Selector.FIRST : Selector.SECOND)));
|
|
}
|
|
else {
|
|
Chat.sendMessage(user, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.ROLLBACK_SELECTION, "#worldedit"));
|
|
}
|
|
}
|
|
|
|
if (restrictWorld && radius == null) {
|
|
if (location != null) {
|
|
Chat.sendMessage(user, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.ROLLBACK_WORLD_ACTION, location.getWorld().getName(), Selector.FIRST));
|
|
}
|
|
}
|
|
|
|
if (actionList.contains(4) && actionList.contains(11)) {
|
|
if (actionList.contains(0)) {
|
|
Chat.sendMessage(user, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.ROLLBACK_WORLD_ACTION, "+inventory", Selector.SECOND));
|
|
}
|
|
else if (actionList.contains(1)) {
|
|
Chat.sendMessage(user, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.ROLLBACK_WORLD_ACTION, "-inventory", Selector.SECOND));
|
|
}
|
|
else {
|
|
Chat.sendMessage(user, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.ROLLBACK_WORLD_ACTION, "inventory", Selector.SECOND));
|
|
}
|
|
}
|
|
else if (actionList.contains(4)) {
|
|
if (actionList.contains(0)) {
|
|
Chat.sendMessage(user, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.ROLLBACK_WORLD_ACTION, "-container", Selector.SECOND));
|
|
}
|
|
else if (actionList.contains(1)) {
|
|
Chat.sendMessage(user, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.ROLLBACK_WORLD_ACTION, "+container", Selector.SECOND));
|
|
}
|
|
else {
|
|
Chat.sendMessage(user, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.ROLLBACK_WORLD_ACTION, "container", Selector.SECOND));
|
|
}
|
|
}
|
|
else if (actionList.contains(0) && actionList.contains(1)) {
|
|
Chat.sendMessage(user, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.ROLLBACK_WORLD_ACTION, "block", Selector.SECOND));
|
|
}
|
|
else if (actionList.contains(0)) {
|
|
Chat.sendMessage(user, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.ROLLBACK_WORLD_ACTION, "-block", Selector.SECOND));
|
|
}
|
|
else if (actionList.contains(1)) {
|
|
Chat.sendMessage(user, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.ROLLBACK_WORLD_ACTION, "+block", Selector.SECOND));
|
|
}
|
|
else if (actionList.contains(3)) {
|
|
Chat.sendMessage(user, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.ROLLBACK_WORLD_ACTION, "kill", Selector.SECOND));
|
|
}
|
|
|
|
if (restrictList.size() > 0) {
|
|
StringBuilder restrictTargets = new StringBuilder();
|
|
boolean material = false;
|
|
boolean item = false;
|
|
boolean entity = false;
|
|
|
|
int targetCount = 0;
|
|
for (Object restrictTarget : restrictList) {
|
|
String targetName = "";
|
|
|
|
if (restrictTarget instanceof Material) {
|
|
targetName = ((Material) restrictTarget).name().toLowerCase(Locale.ROOT);
|
|
item = (!item ? !(((Material) restrictTarget).isBlock()) : item);
|
|
material = true;
|
|
}
|
|
else if (restrictTarget instanceof EntityType) {
|
|
targetName = ((EntityType) restrictTarget).name().toLowerCase(Locale.ROOT);
|
|
entity = true;
|
|
}
|
|
|
|
if (targetCount == 0) {
|
|
restrictTargets = restrictTargets.append("" + targetName + "");
|
|
}
|
|
else {
|
|
restrictTargets.append(", ").append(targetName);
|
|
}
|
|
|
|
targetCount++;
|
|
}
|
|
|
|
String targetType = Selector.THIRD;
|
|
if (material && !item && !entity) {
|
|
targetType = Selector.FIRST;
|
|
}
|
|
else if (material && item && !entity) {
|
|
targetType = Selector.THIRD;
|
|
}
|
|
else if (entity && !material) {
|
|
targetType = Selector.SECOND;
|
|
}
|
|
|
|
Chat.sendMessage(user, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.ROLLBACK_INCLUDE, restrictTargets.toString(), Selector.FIRST, targetType, (targetCount == 1 ? Selector.FIRST : Selector.SECOND))); // include
|
|
}
|
|
|
|
if (excludeList.size() > 0) {
|
|
StringBuilder excludeTargets = new StringBuilder();
|
|
boolean material = false;
|
|
boolean item = false;
|
|
boolean entity = false;
|
|
|
|
int excludeCount = 0;
|
|
for (Map.Entry<Object, Boolean> entry : excludeList.entrySet()) {
|
|
Object excludeTarget = entry.getKey();
|
|
Boolean excludeTargetInternal = entry.getValue();
|
|
|
|
// don't display default block excludes
|
|
if (Boolean.TRUE.equals(excludeTargetInternal)) {
|
|
continue;
|
|
}
|
|
|
|
// don't display that excluded water/fire/farmland in inventory rollbacks
|
|
if (actionList.contains(4) && actionList.contains(11)) {
|
|
if (excludeTarget.equals(Material.FIRE) || excludeTarget.equals(Material.WATER) || excludeTarget.equals(Material.FARMLAND)) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
String targetName = "";
|
|
if (excludeTarget instanceof Material) {
|
|
targetName = ((Material) excludeTarget).name().toLowerCase(Locale.ROOT);
|
|
item = (!item ? !(((Material) excludeTarget).isBlock()) : item);
|
|
material = true;
|
|
}
|
|
else if (excludeTarget instanceof EntityType) {
|
|
targetName = ((EntityType) excludeTarget).name().toLowerCase(Locale.ROOT);
|
|
entity = true;
|
|
}
|
|
|
|
if (excludeCount == 0) {
|
|
excludeTargets = excludeTargets.append("" + targetName + "");
|
|
}
|
|
else {
|
|
excludeTargets.append(", ").append(targetName);
|
|
}
|
|
|
|
excludeCount++;
|
|
}
|
|
|
|
String targetType = Selector.THIRD;
|
|
if (material && !item && !entity) {
|
|
targetType = Selector.FIRST;
|
|
}
|
|
else if (material && item && !entity) {
|
|
targetType = Selector.THIRD;
|
|
}
|
|
else if (entity && !material) {
|
|
targetType = Selector.SECOND;
|
|
}
|
|
|
|
if (excludeCount > 0) {
|
|
Chat.sendMessage(user, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.ROLLBACK_INCLUDE, excludeTargets.toString(), Selector.SECOND, targetType, (excludeCount == 1 ? Selector.FIRST : Selector.SECOND))); // exclude
|
|
}
|
|
}
|
|
|
|
if (excludeUserList.size() > 0) {
|
|
StringBuilder excludeUsers = new StringBuilder();
|
|
|
|
int excludeCount = 0;
|
|
for (String excludeUser : excludeUserList) {
|
|
// don't display that excluded #hopper in inventory rollbacks
|
|
if (actionList.contains(4) && actionList.contains(11)) {
|
|
if (excludeUser.equals("#hopper")) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (excludeCount == 0) {
|
|
excludeUsers = excludeUsers.append("" + excludeUser + "");
|
|
}
|
|
else {
|
|
excludeUsers.append(", ").append(excludeUser);
|
|
}
|
|
|
|
excludeCount++;
|
|
}
|
|
|
|
if (excludeCount > 0) {
|
|
Chat.sendMessage(user, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.ROLLBACK_EXCLUDED_USERS, excludeUsers.toString(), (excludeCount == 1 ? Selector.FIRST : Selector.SECOND)));
|
|
}
|
|
}
|
|
|
|
StringBuilder modifiedData = new StringBuilder();
|
|
Integer modifyCount = 0;
|
|
if (actionList.contains(5)) {
|
|
modifiedData = modifiedData.append(Phrase.build(Phrase.AMOUNT_ITEM, NumberFormat.getInstance().format(blockCount), (blockCount == 1 ? Selector.FIRST : Selector.SECOND)));
|
|
modifyCount++;
|
|
}
|
|
else {
|
|
if (itemCount > 0 || actionList.contains(4)) {
|
|
modifiedData = modifiedData.append(Phrase.build(Phrase.AMOUNT_ITEM, NumberFormat.getInstance().format(itemCount), (itemCount == 1 ? Selector.FIRST : Selector.SECOND)));
|
|
modifyCount++;
|
|
}
|
|
|
|
if (entityCount > 0) {
|
|
if (modifyCount > 0) {
|
|
modifiedData.append(", ");
|
|
}
|
|
modifiedData.append(Phrase.build(Phrase.AMOUNT_ENTITY, NumberFormat.getInstance().format(entityCount), (entityCount == 1 ? Selector.FIRST : Selector.SECOND)));
|
|
modifyCount++;
|
|
}
|
|
|
|
if (blockCount > 0 || !actionList.contains(4) || preview > 0) {
|
|
if (modifyCount > 0) {
|
|
modifiedData.append(", ");
|
|
}
|
|
modifiedData.append(Phrase.build(Phrase.AMOUNT_BLOCK, NumberFormat.getInstance().format(blockCount), (blockCount == 1 ? Selector.FIRST : Selector.SECOND)));
|
|
modifyCount++;
|
|
}
|
|
}
|
|
|
|
StringBuilder modifiedDataVerbose = new StringBuilder();
|
|
if (verbose && preview == 0 && !actionList.contains(11)) {
|
|
if (chunkCount > -1 && modifyCount < 3) {
|
|
if (modifyCount > 0) {
|
|
modifiedData.append(", ");
|
|
}
|
|
modifiedData.append(Phrase.build(Phrase.AMOUNT_CHUNK, NumberFormat.getInstance().format(chunkCount), (chunkCount == 1 ? Selector.FIRST : Selector.SECOND)));
|
|
modifyCount++;
|
|
}
|
|
else if (chunkCount > 1) {
|
|
modifiedDataVerbose.append(Phrase.build(Phrase.AMOUNT_CHUNK, NumberFormat.getInstance().format(chunkCount), (chunkCount == 1 ? Selector.FIRST : Selector.SECOND)));
|
|
}
|
|
}
|
|
|
|
Chat.sendMessage(user, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.ROLLBACK_MODIFIED, modifiedData.toString(), (preview == 0 ? Selector.FIRST : Selector.SECOND)));
|
|
if (modifiedDataVerbose.length() > 0) {
|
|
Chat.sendMessage(user, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.ROLLBACK_MODIFIED, modifiedDataVerbose.toString(), (preview == 0 ? Selector.FIRST : Selector.SECOND)));
|
|
}
|
|
|
|
if (preview == 0) {
|
|
BigDecimal decimalSeconds = new BigDecimal(seconds).setScale(1, RoundingMode.HALF_EVEN);
|
|
Chat.sendMessage(user, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.ROLLBACK_LENGTH, decimalSeconds.stripTrailingZeros().toPlainString(), (decimalSeconds.doubleValue() == 1 ? Selector.FIRST : Selector.SECOND)));
|
|
}
|
|
|
|
Chat.sendMessage(user, "-----");
|
|
if (preview > 0) {
|
|
Chat.sendMessage(user, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.PLEASE_SELECT, "/co apply", "/co cancel"));
|
|
}
|
|
}
|
|
catch (Exception e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
static void modifyContainerItems(Material type, Object container, int slot, ItemStack itemstack, int action) {
|
|
try {
|
|
ItemStack[] contents = null;
|
|
|
|
if (type != null && type.equals(Material.ARMOR_STAND)) {
|
|
EntityEquipment equipment = (EntityEquipment) container;
|
|
if (equipment != null) {
|
|
if (action == 1) {
|
|
itemstack.setAmount(1);
|
|
}
|
|
else {
|
|
itemstack.setType(Material.AIR);
|
|
itemstack.setAmount(0);
|
|
}
|
|
|
|
if (slot < 4) {
|
|
contents = equipment.getArmorContents();
|
|
if (slot >= 0) {
|
|
contents[slot] = itemstack;
|
|
}
|
|
equipment.setArmorContents(contents);
|
|
}
|
|
else {
|
|
ArmorStand armorStand = (ArmorStand) equipment.getHolder();
|
|
armorStand.setArms(true);
|
|
switch (slot) {
|
|
case 4:
|
|
equipment.setItemInMainHand(itemstack);
|
|
break;
|
|
case 5:
|
|
equipment.setItemInOffHand(itemstack);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (type != null && type.equals(Material.ITEM_FRAME)) {
|
|
ItemFrame frame = (ItemFrame) container;
|
|
if (frame != null) {
|
|
if (action == 1) {
|
|
itemstack.setAmount(1);
|
|
}
|
|
else {
|
|
itemstack.setType(Material.AIR);
|
|
itemstack.setAmount(0);
|
|
}
|
|
|
|
frame.setItem(itemstack);
|
|
}
|
|
}
|
|
else {
|
|
Inventory inventory = (Inventory) container;
|
|
if (inventory != null) {
|
|
if (action == 1) {
|
|
int count = 0;
|
|
int amount = itemstack.getAmount();
|
|
itemstack.setAmount(1);
|
|
|
|
while (count < amount) {
|
|
inventory.addItem(itemstack);
|
|
count++;
|
|
}
|
|
}
|
|
else {
|
|
int removeAmount = itemstack.getAmount();
|
|
ItemStack removeMatch = itemstack.clone();
|
|
removeMatch.setAmount(1);
|
|
|
|
ItemStack[] inventoryContents = inventory.getStorageContents().clone();
|
|
for (int i = inventoryContents.length - 1; i >= 0; i--) {
|
|
if (inventoryContents[i] != null) {
|
|
ItemStack itemStack = inventoryContents[i].clone();
|
|
int maxAmount = itemStack.getAmount();
|
|
int currentAmount = maxAmount;
|
|
itemStack.setAmount(1);
|
|
|
|
if (itemStack.toString().equals(removeMatch.toString())) {
|
|
for (int scan = 0; scan < maxAmount; scan++) {
|
|
if (removeAmount > 0) {
|
|
currentAmount--;
|
|
itemStack.setAmount(currentAmount);
|
|
removeAmount--;
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
itemStack.setAmount(maxAmount);
|
|
}
|
|
|
|
if (itemStack.getAmount() == 0) {
|
|
inventoryContents[i] = null;
|
|
}
|
|
else {
|
|
inventoryContents[i] = itemStack;
|
|
}
|
|
}
|
|
|
|
if (removeAmount == 0) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
inventory.setStorageContents(inventoryContents);
|
|
|
|
int count = 0;
|
|
while (count < removeAmount) {
|
|
inventory.removeItem(removeMatch);
|
|
count++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (Exception e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
private static void buildFireworkEffect(Builder effectBuilder, Material rowType, ItemStack itemstack) {
|
|
try {
|
|
FireworkEffect effect = effectBuilder.build();
|
|
if ((rowType == Material.FIREWORK_ROCKET)) {
|
|
FireworkMeta meta = (FireworkMeta) itemstack.getItemMeta();
|
|
meta.addEffect(effect);
|
|
itemstack.setItemMeta(meta);
|
|
}
|
|
else if ((rowType == Material.FIREWORK_STAR)) {
|
|
FireworkEffectMeta meta = (FireworkEffectMeta) itemstack.getItemMeta();
|
|
meta.setEffect(effect);
|
|
itemstack.setItemMeta(meta);
|
|
}
|
|
}
|
|
catch (Exception e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
@SuppressWarnings("unchecked")
|
|
public static Object[] populateItemStack(ItemStack itemstack, Object list) {
|
|
int slot = 0;
|
|
String faceData = "";
|
|
|
|
try {
|
|
/*
|
|
if (list instanceof Object[]) {
|
|
slot = (int) ((Object[]) list)[0];
|
|
ItemMeta itemMeta = (ItemMeta) ((Object[]) list)[1];
|
|
itemstack.setItemMeta(itemMeta);
|
|
return new Object[] { slot, itemstack };
|
|
}
|
|
*/
|
|
|
|
Material rowType = itemstack.getType();
|
|
List<Object> metaList = (List<Object>) list;
|
|
if (metaList.size() > 0 && !(metaList.get(0) instanceof List<?>)) {
|
|
if (rowType.name().endsWith("_BANNER")) {
|
|
BannerMeta meta = (BannerMeta) itemstack.getItemMeta();
|
|
for (Object value : metaList) {
|
|
if (value instanceof Map) {
|
|
Pattern pattern = new Pattern((Map<String, Object>) value);
|
|
meta.addPattern(pattern);
|
|
}
|
|
}
|
|
itemstack.setItemMeta(meta);
|
|
}
|
|
else if (BlockGroup.SHULKER_BOXES.contains(rowType)) {
|
|
BlockStateMeta meta = (BlockStateMeta) itemstack.getItemMeta();
|
|
ShulkerBox shulkerBox = (ShulkerBox) meta.getBlockState();
|
|
for (Object value : metaList) {
|
|
ItemStack item = Util.unserializeItemStackLegacy(value);
|
|
if (item != null) {
|
|
shulkerBox.getInventory().addItem(item);
|
|
}
|
|
}
|
|
meta.setBlockState(shulkerBox);
|
|
itemstack.setItemMeta(meta);
|
|
}
|
|
|
|
return new Object[] { slot, faceData, itemstack };
|
|
}
|
|
|
|
int itemCount = 0;
|
|
Builder effectBuilder = FireworkEffect.builder();
|
|
for (List<Map<String, Object>> map : (List<List<Map<String, Object>>>) list) {
|
|
if (map.size() == 0) {
|
|
if (itemCount == 3 && (rowType == Material.FIREWORK_ROCKET || rowType == Material.FIREWORK_STAR)) {
|
|
buildFireworkEffect(effectBuilder, rowType, itemstack);
|
|
itemCount = 0;
|
|
}
|
|
|
|
itemCount++;
|
|
continue;
|
|
}
|
|
Map<String, Object> mapData = map.get(0);
|
|
|
|
if (mapData.get("slot") != null) {
|
|
slot = (Integer) mapData.get("slot");
|
|
}
|
|
else if (mapData.get("facing") != null) {
|
|
faceData = (String) mapData.get("facing");
|
|
}
|
|
else if (mapData.get("modifiers") != null) {
|
|
ItemMeta itemMeta = itemstack.getItemMeta();
|
|
if (itemMeta.hasAttributeModifiers()) {
|
|
for (Map.Entry<Attribute, AttributeModifier> entry : itemMeta.getAttributeModifiers().entries()) {
|
|
itemMeta.removeAttributeModifier(entry.getKey(), entry.getValue());
|
|
}
|
|
}
|
|
|
|
List<Object> modifiers = (List<Object>) mapData.get("modifiers");
|
|
|
|
for (Object item : modifiers) {
|
|
Map<Attribute, Map<String, Object>> modifiersMap = (Map<Attribute, Map<String, Object>>) item;
|
|
for (Map.Entry<Attribute, Map<String, Object>> entry : modifiersMap.entrySet()) {
|
|
try {
|
|
Attribute attribute = entry.getKey();
|
|
AttributeModifier modifier = AttributeModifier.deserialize(entry.getValue());
|
|
itemMeta.addAttributeModifier(attribute, modifier);
|
|
}
|
|
catch (IllegalArgumentException e) {
|
|
// AttributeModifier already exists
|
|
}
|
|
}
|
|
}
|
|
|
|
itemstack.setItemMeta(itemMeta);
|
|
}
|
|
else if (itemCount == 0) {
|
|
ItemMeta meta = Util.deserializeItemMeta(itemstack.getItemMeta().getClass(), map.get(0));
|
|
itemstack.setItemMeta(meta);
|
|
|
|
if (map.size() > 1 && (rowType == Material.POTION)) {
|
|
PotionMeta subMeta = (PotionMeta) itemstack.getItemMeta();
|
|
org.bukkit.Color color = org.bukkit.Color.deserialize(map.get(1));
|
|
subMeta.setColor(color);
|
|
itemstack.setItemMeta(subMeta);
|
|
}
|
|
}
|
|
else {
|
|
if ((rowType == Material.LEATHER_HORSE_ARMOR) || (rowType == Material.LEATHER_HELMET) || (rowType == Material.LEATHER_CHESTPLATE) || (rowType == Material.LEATHER_LEGGINGS) || (rowType == Material.LEATHER_BOOTS)) { // leather armor
|
|
for (Map<String, Object> colorData : map) {
|
|
LeatherArmorMeta meta = (LeatherArmorMeta) itemstack.getItemMeta();
|
|
org.bukkit.Color color = org.bukkit.Color.deserialize(colorData);
|
|
meta.setColor(color);
|
|
itemstack.setItemMeta(meta);
|
|
}
|
|
}
|
|
else if ((rowType == Material.POTION)) { // potion
|
|
for (Map<String, Object> potionData : map) {
|
|
PotionMeta meta = (PotionMeta) itemstack.getItemMeta();
|
|
PotionEffect effect = new PotionEffect(potionData);
|
|
meta.addCustomEffect(effect, true);
|
|
itemstack.setItemMeta(meta);
|
|
}
|
|
}
|
|
else if (rowType.name().endsWith("_BANNER")) {
|
|
for (Map<String, Object> patternData : map) {
|
|
BannerMeta meta = (BannerMeta) itemstack.getItemMeta();
|
|
Pattern pattern = new Pattern(patternData);
|
|
meta.addPattern(pattern);
|
|
itemstack.setItemMeta(meta);
|
|
}
|
|
}
|
|
else if ((rowType == Material.CROSSBOW)) {
|
|
CrossbowMeta meta = (CrossbowMeta) itemstack.getItemMeta();
|
|
for (Map<String, Object> itemData : map) {
|
|
ItemStack crossbowItem = Util.unserializeItemStack(itemData);
|
|
if (crossbowItem != null) {
|
|
meta.addChargedProjectile(crossbowItem);
|
|
}
|
|
}
|
|
itemstack.setItemMeta(meta);
|
|
}
|
|
else if ((rowType == Material.MAP)) {
|
|
for (Map<String, Object> colorData : map) {
|
|
MapMeta meta = (MapMeta) itemstack.getItemMeta();
|
|
org.bukkit.Color color = org.bukkit.Color.deserialize(colorData);
|
|
meta.setColor(color);
|
|
itemstack.setItemMeta(meta);
|
|
}
|
|
}
|
|
else if ((rowType == Material.FIREWORK_ROCKET) || (rowType == Material.FIREWORK_STAR)) {
|
|
if (itemCount == 1) {
|
|
effectBuilder = FireworkEffect.builder();
|
|
for (Map<String, Object> fireworkData : map) {
|
|
org.bukkit.FireworkEffect.Type type = (org.bukkit.FireworkEffect.Type) fireworkData.getOrDefault("type", org.bukkit.FireworkEffect.Type.BALL);
|
|
boolean hasFlicker = (Boolean) fireworkData.get("flicker");
|
|
boolean hasTrail = (Boolean) fireworkData.get("trail");
|
|
effectBuilder.with(type);
|
|
effectBuilder.flicker(hasFlicker);
|
|
effectBuilder.trail(hasTrail);
|
|
}
|
|
}
|
|
else if (itemCount == 2) {
|
|
for (Map<String, Object> colorData : map) {
|
|
org.bukkit.Color color = org.bukkit.Color.deserialize(colorData);
|
|
effectBuilder.withColor(color);
|
|
}
|
|
}
|
|
else if (itemCount == 3) {
|
|
for (Map<String, Object> colorData : map) {
|
|
org.bukkit.Color color = org.bukkit.Color.deserialize(colorData);
|
|
effectBuilder.withFade(color);
|
|
}
|
|
buildFireworkEffect(effectBuilder, rowType, itemstack);
|
|
itemCount = 0;
|
|
}
|
|
}
|
|
else {
|
|
BukkitAdapter.ADAPTER.setItemMeta(rowType, itemstack, map);
|
|
}
|
|
}
|
|
|
|
itemCount++;
|
|
}
|
|
}
|
|
catch (Exception e) {
|
|
e.printStackTrace();
|
|
}
|
|
return new Object[] { slot, faceData, itemstack };
|
|
}
|
|
|
|
public static Object[] populateItemStack(ItemStack itemstack, byte[] metadata) {
|
|
if (metadata != null) {
|
|
try {
|
|
ByteArrayInputStream metaByteStream = new ByteArrayInputStream(metadata);
|
|
BukkitObjectInputStream metaObjectStream = new BukkitObjectInputStream(metaByteStream);
|
|
Object metaList = metaObjectStream.readObject();
|
|
metaObjectStream.close();
|
|
metaByteStream.close();
|
|
|
|
return populateItemStack(itemstack, metaList);
|
|
}
|
|
catch (Exception e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
return new Object[] { 0, "", itemstack };
|
|
}
|
|
|
|
}
|