Skip to content
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 17 additions & 10 deletions src/main/java/net/coreprotect/command/TeleportCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,98 +19,105 @@

public class TeleportCommand {

protected static void runCommand(CommandSender player, boolean permission, String[] args) {
int resultc = args.length;

if (!permission) {
Chat.sendMessage(player, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.NO_PERMISSION));
return;
}

if (!(player instanceof Player)) {
Chat.sendMessage(player, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.TELEPORT_PLAYERS));
return;
}

if (ConfigHandler.teleportThrottle.get(player.getName()) != null) {
Object[] lookupThrottle = ConfigHandler.teleportThrottle.get(player.getName());
if ((boolean) lookupThrottle[0] || ((System.currentTimeMillis() - (long) lookupThrottle[1])) < 500) {
Chat.sendMessage(player, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.COMMAND_THROTTLED));
return;
}
}

if (resultc < 3) {
Chat.sendMessage(player, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.MISSING_PARAMETERS, "/co teleport <world> <x> <y> <z>"));
return;
}

String worldName = args[1];
int wid = WorldUtils.matchWorld(worldName);
if (wid == -1 && resultc >= 5) {
Chat.sendMessage(player, new ChatMessage(Phrase.build(Phrase.WORLD_NOT_FOUND, worldName)).build());
return;
}

Location location = ((Player) player).getLocation().clone();
World world = location.getWorld();
if (wid > -1) {
world = Bukkit.getServer().getWorld(WorldUtils.getWorldName(wid));
}

String x = null;
String y = null;
String z = null;

for (int i = 1; i < args.length; i++) {
if (i == 1 && wid > -1) {
continue;
}

if (x == null) {
x = args[i].replaceAll("[^0-9.\\-]", "");
}
else if (z == null) {
z = args[i].replaceAll("[^0-9.\\-]", "");
}
else if (y == null) {
y = z;
z = args[i].replaceAll("[^0-9.\\-]", "");
}
}

if (y == null) {
if (location.getBlockY() > 63) {
location.setY(63);
}
y = Double.toString(location.getY());
}

String xValidate = x.replaceAll("[^.\\-]", "");
String yValidate = y.replaceAll("[^.\\-]", "");
String zValidate = z.replaceAll("[^.\\-]", "");

if ((x.length() == 0 || x.length() >= 12 || x.equals(xValidate)) || (y.length() == 0 || y.length() >= 12 || y.equals(yValidate)) || (z.length() == 0 || z.length() >= 12 || z.equals(zValidate))) {
Chat.sendMessage(player, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.MISSING_PARAMETERS, "/co teleport <world> <x> <y> <z>"));
return;
}

location.setWorld(world);
location.setX(Double.parseDouble(x));
location.setY(Double.parseDouble(y));
location.setZ(Double.parseDouble(z));

int chunkX = location.getBlockX() >> 4;
int chunkZ = location.getBlockZ() >> 4;
Scheduler.runTask(CoreProtect.getInstance(), () -> {
if (!location.getWorld().isChunkLoaded(chunkX, chunkZ)) {
location.getWorld().getChunkAt(location);
}

// Teleport the player to a safe location
Teleport.performSafeTeleport(((Player) player), location, true);
}, location);
if (ConfigHandler.isFolia) {
location.getWorld().getChunkAtAsync(location).thenAccept(chunk -> {
Scheduler.runTask(CoreProtect.getInstance(), () -> {
Teleport.performSafeTeleport(((Player) player), location, true);
}, location);
});
}
else {
int chunkX = location.getBlockX() >> 4;
int chunkZ = location.getBlockZ() >> 4;
Scheduler.runTask(CoreProtect.getInstance(), () -> {
if (!location.getWorld().isChunkLoaded(chunkX, chunkZ)) {
location.getWorld().getChunkAt(location);
}
Teleport.performSafeTeleport(((Player) player), location, true);
}, location);
}

ConfigHandler.teleportThrottle.put(player.getName(), new Object[] { false, System.currentTimeMillis() });
}

Check notice on line 122 in src/main/java/net/coreprotect/command/TeleportCommand.java

View check run for this annotation

codefactor.io / CodeFactor

src/main/java/net/coreprotect/command/TeleportCommand.java#L22-L122

Complex Method
}
90 changes: 17 additions & 73 deletions src/main/java/net/coreprotect/database/rollback/Rollback.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,15 @@
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
import java.util.concurrent.CompletableFuture;

import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.command.BlockCommandSender;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;

import net.coreprotect.CoreProtect;
Expand All @@ -38,297 +41,238 @@

public class Rollback extends RollbackUtil {

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);
}

LinkedHashSet<Integer> worldList = new LinkedHashSet<>();
TreeMap<Long, Integer> chunkList = new TreeMap<>();
HashMap<Integer, HashMap<Long, ArrayList<Object[]>>> dataList = new HashMap<>();
HashMap<Integer, HashMap<Long, ArrayList<Object[]>>> itemDataList = new HashMap<>();
boolean inventoryRollback = actionList.contains(11);

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);

if (rowWorldId != worldId) {
String world = WorldUtils.getWorldName(rowWorldId);
World bukkitWorld = Bukkit.getServer().getWorld(world);
if (bukkitWorld != null) {
worldMin = BukkitAdapter.ADAPTER.getMinHeight(bukkitWorld);
worldMax = bukkitWorld.getMaxHeight();
}
}

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<Integer, HashMap<Long, ArrayList<Object[]>>> modifyList = dataList;
if (listC == 1) {
modifyList = itemDataList;
}

if (modifyList.get(rowWorldId) == null) {
dataList.put(rowWorldId, new HashMap<>());
itemDataList.put(rowWorldId, new HashMap<>());
worldList.add(rowWorldId);
}

if (modifyList.get(rowWorldId).get(chunkKey) == null) {
dataList.get(rowWorldId).put(chunkKey, new ArrayList<>());
itemDataList.get(rowWorldId).put(chunkKey, new ArrayList<>());
}

modifyList.get(rowWorldId).get(chunkKey).add(result);
}

listC++;
}

if (rollbackType == 1) { // Restore
Iterator<Map.Entry<Integer, HashMap<Long, ArrayList<Object[]>>>> dlIterator = dataList.entrySet().iterator();
while (dlIterator.hasNext()) {
for (ArrayList<Object[]> map : dlIterator.next().getValue().values()) {
Collections.reverse(map);
}
}

dlIterator = itemDataList.entrySet().iterator();
while (dlIterator.hasNext()) {
for (ArrayList<Object[]> map : dlIterator.next().getValue().values()) {
Collections.reverse(map);
}
}
}

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, 0 });

final String finalUserString = userString;
List<CompletableFuture<Boolean>> futures = new ArrayList<>();

for (Entry<Long, Integer> entry : DatabaseUtils.entriesSortedByValues(chunkList)) {
chunkCount++;

int itemCount = 0;
int blockCount = 0;
int entityCount = 0;
int scannedWorldData = 0;
int[] rollbackHashData = ConfigHandler.rollbackHash.get(finalUserString);
itemCount = rollbackHashData[0];
blockCount = rollbackHashData[1];
entityCount = rollbackHashData[2];
scannedWorldData = rollbackHashData[4];

long chunkKey = entry.getKey();
final int finalChunkX = (int) chunkKey;
final int finalChunkZ = (int) (chunkKey >> 32);
final CommandSender finalUser = user;
final Integer finalChunkCount = chunkCount;
final Integer totalChunks = chunkList.size();

HashMap<Integer, World> worldMap = new HashMap<>();
for (int rollbackWorldId : worldList) {
String rollbackWorld = WorldUtils.getWorldName(rollbackWorldId);
if (rollbackWorld.length() == 0) {
continue;
}

World bukkitRollbackWorld = Bukkit.getServer().getWorld(rollbackWorld);
if (bukkitRollbackWorld == null) {
continue;
}

worldMap.put(rollbackWorldId, bukkitRollbackWorld);
}
final ArrayList<Object[]> finalBlockList = dataList.get(rollbackWorldId).getOrDefault(chunkKey, new ArrayList<>());
final ArrayList<Object[]> finalItemList = itemDataList.get(rollbackWorldId).getOrDefault(chunkKey, new ArrayList<>());

ConfigHandler.rollbackHash.put(finalUserString, new int[] { itemCount, blockCount, entityCount, 0, scannedWorldData });
for (Entry<Integer, World> rollbackWorlds : worldMap.entrySet()) {
Integer rollbackWorldId = rollbackWorlds.getKey();
World bukkitRollbackWorld = rollbackWorlds.getValue();
Location chunkLocation = new Location(bukkitRollbackWorld, (finalChunkX << 4), 0, (finalChunkZ << 4));
final HashMap<Long, ArrayList<Object[]>> finalBlockList = dataList.get(rollbackWorldId);
final HashMap<Long, ArrayList<Object[]>> finalItemList = itemDataList.get(rollbackWorldId);

Scheduler.scheduleSyncDelayedTask(CoreProtect.getInstance(), () -> {
// Process this chunk using our new RollbackProcessor class
ArrayList<Object[]> blockData = finalBlockList != null ? finalBlockList.getOrDefault(chunkKey, new ArrayList<>()) : new ArrayList<>();
ArrayList<Object[]> itemData = finalItemList != null ? finalItemList.getOrDefault(chunkKey, new ArrayList<>()) : new ArrayList<>();
RollbackProcessor.processChunk(finalChunkX, finalChunkZ, chunkKey, blockData, itemData, rollbackType, preview, finalUserString, finalUser instanceof Player ? (Player) finalUser : null, bukkitRollbackWorld, inventoryRollback);
}, chunkLocation, 0);
}
CompletableFuture<Boolean> future = RollbackProcessor.processChunk(finalChunkX, finalChunkZ, chunkKey, finalBlockList, finalItemList, rollbackType, preview, finalUserString, finalUser instanceof Player ? (Player) finalUser : null, bukkitRollbackWorld, inventoryRollback);
futures.add(future);

rollbackHashData = ConfigHandler.rollbackHash.get(finalUserString);
int next = rollbackHashData[3];
int scannedWorlds = rollbackHashData[4];
int sleepTime = 0;
int abort = 0;

while (next == 0 || scannedWorlds < worldMap.size()) {
if (preview == 1) {
// Not actually changing blocks, so less intensive.
sleepTime = sleepTime + 1;
Thread.sleep(1);
}
else {
sleepTime = sleepTime + 5;
Thread.sleep(5);
if (verbose && user != null && preview == 0 && !actionList.contains(11)) {
future.thenRun(() -> Chat.sendMessage(user, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.ROLLBACK_CHUNKS_MODIFIED, finalChunkCount.toString(), totalChunks.toString(), (totalChunks == 1 ? Selector.FIRST : Selector.SECOND))));
}

rollbackHashData = ConfigHandler.rollbackHash.get(finalUserString);
next = rollbackHashData[3];
scannedWorlds = rollbackHashData[4];

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, 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();
// wait for all chunk processing tasks to complete
CompletableFuture<Void> allFutures = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
allFutures.get();

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) {
RollbackComplete.output(user, location, checkUsers, restrictList, excludeList, excludeUserList, actionList, timeString, chunkCount, totalSeconds, itemCount, blockCount, entityCount, rollbackType, radius, verbose, restrictWorld, preview);
RollbackComplete.output(user, location, checkUsers, restrictList, excludeList, excludeUserList, actionList, timeString, chunkList.size(), totalSeconds, itemCount, blockCount, entityCount, rollbackType, radius, verbose, restrictWorld, preview);
}

list = LookupConverter.convertRawLookup(statement, lookupList);
return list;
}
catch (Exception e) {
e.printStackTrace();
}

return null;
}

Check notice on line 277 in src/main/java/net/coreprotect/database/rollback/Rollback.java

View check run for this annotation

codefactor.io / CodeFactor

src/main/java/net/coreprotect/database/rollback/Rollback.java#L44-L277

Complex Method
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
* The type of rollback (0 for rollback, 1 for restore)
* @param finalUserString
* The user string for tracking operations
* @param oldTypeRaw
* The old raw type value
* @param rowTypeRaw
* The raw type value
* @param rowData
Expand All @@ -47,114 +49,121 @@
* @return The number of entities affected (1 if successful, 0 otherwise)
*/
public static int processEntity(Object[] row, int rollbackType, String finalUserString, int oldTypeRaw, int rowTypeRaw, int rowData, int rowAction, int rowRolledBack, int rowX, int rowY, int rowZ, int rowWorldId, int rowUserId, String rowUser) {
World bukkitWorld = Bukkit.getServer().getWorld(WorldUtils.getWorldName(rowWorldId));
if (bukkitWorld == null) {
return 0;
}

if (ConfigHandler.isFolia) {
// Folia - load chunk async before processing
bukkitWorld.getChunkAtAsync(rowX >> 4, rowZ >> 4, true).thenAccept(chunk -> {
processEntityLogic(row, rollbackType, finalUserString, oldTypeRaw, rowTypeRaw, rowData, rowAction, rowRolledBack, rowX, rowY, rowZ, rowWorldId, rowUserId, rowUser, bukkitWorld);
});
return 1; // assume task is queued successfully
}
else {
if (!bukkitWorld.isChunkLoaded(rowX >> 4, rowZ >> 4)) {
bukkitWorld.getChunkAt(rowX >> 4, rowZ >> 4);
}
return processEntityLogic(row, rollbackType, finalUserString, oldTypeRaw, rowTypeRaw, rowData, rowAction, rowRolledBack, rowX, rowY, rowZ, rowWorldId, rowUserId, rowUser, bukkitWorld);
}
}

private static int processEntityLogic(Object[] row, int rollbackType, String finalUserString, int oldTypeRaw, int rowTypeRaw, int rowData, int rowAction, int rowRolledBack, int rowX, int rowY, int rowZ, int rowWorldId, int rowUserId, String rowUser, World bukkitWorld) {
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) {
// Attempt to remove entity
if (rowRolledBack == 1) {
boolean removed = false;
int entityId = -1;
String entityName = EntityUtils.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) {
updateEntityCount(finalUserString, 1);
removed = true;
entity.remove();
break;
}
}
else {
if (entity.getType().equals(EntityUtils.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) {
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
*/

Check notice on line 166 in src/main/java/net/coreprotect/database/rollback/RollbackEntityHandler.java

View check run for this annotation

codefactor.io / CodeFactor

src/main/java/net/coreprotect/database/rollback/RollbackEntityHandler.java#L72-L166

Complex Method
private static String getWorldName(int worldId) {
return WorldUtils.getWorldName(worldId);
}
Expand Down Expand Up @@ -184,7 +193,7 @@

/**
* Spawns an entity at the given block location.
*
*
* @param user
* The username of the player
* @param block
Expand Down
Loading