Rollback optimizations / features

This commit is contained in:
Jesse Boyd 2016-04-22 05:59:24 +10:00
parent 63ad22a021
commit d38db03600
7 changed files with 154 additions and 45 deletions

View File

@ -23,7 +23,7 @@ public class BukkitCommand implements CommandExecutor {
BBC.NO_PERM.send(plr, this.cmd.getPerm()); BBC.NO_PERM.send(plr, this.cmd.getPerm());
return true; return true;
} }
this.cmd.execute(plr, args); this.cmd.executeSafe(plr, args);
return true; return true;
} }
} }

View File

@ -13,9 +13,11 @@ import com.boydti.fawe.util.MathMan;
import com.boydti.fawe.util.SetQueue; import com.boydti.fawe.util.SetQueue;
import com.boydti.fawe.util.TaskManager; import com.boydti.fawe.util.TaskManager;
import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.blocks.ItemType;
import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.World;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.UUID; import java.util.UUID;
public class Rollback extends FaweCommand { public class Rollback extends FaweCommand {
@ -44,25 +46,33 @@ public class Rollback extends FaweCommand {
} }
player.deleteMeta("rollback"); player.deleteMeta("rollback");
final FaweLocation origin = player.getLocation(); final FaweLocation origin = player.getLocation();
rollback(player, Arrays.copyOfRange(args, 1, args.length), new RunnableVal<List<DiskStorageHistory>>() { rollback(player, !player.hasPermission("fawe.rollback.deep"), Arrays.copyOfRange(args, 1, args.length), new RunnableVal<List<DiskStorageHistory>>() {
@Override @Override
public void run(List<DiskStorageHistory> edits) { public void run(List<DiskStorageHistory> edits) {
long total = 0; long total = 0;
player.sendMessage("&d=== Edits ==="); player.sendMessage("&d=| Username | Bounds | Distance | Changes | Age |=");
for (DiskStorageHistory edit : edits) { for (DiskStorageHistory edit : edits) {
int[] headerAndFooter = edit.readHeaderAndFooter(new RegionWrapper(origin.x, origin.x, origin.z, origin.z)); DiskStorageHistory.DiskStorageSummary summary = edit.summarize(new RegionWrapper(origin.x, origin.x, origin.z, origin.z), !player.hasPermission("fawe.rollback.deep"));
RegionWrapper region = new RegionWrapper(headerAndFooter[0], headerAndFooter[2], headerAndFooter[1], headerAndFooter[3]); RegionWrapper region = new RegionWrapper(summary.minX, summary.maxX, summary.minZ, summary.maxZ);
int dx = region.distanceX(origin.x); int distance = region.distance(origin.x, origin.z);
int dz = region.distanceZ(origin.z);
String name = Fawe.imp().getName(edit.getUUID()); String name = Fawe.imp().getName(edit.getUUID());
long seconds = (System.currentTimeMillis() - edit.getBDFile().lastModified()) / 1000; long seconds = (System.currentTimeMillis() - edit.getBDFile().lastModified()) / 1000;
total += edit.getBDFile().length(); total += edit.getBDFile().length();
player.sendMessage(name + " : " + dx + "," + dz + " : " + MainUtil.secToTime(seconds)); int size = summary.getSize();
Map<Integer, Double> percents = summary.getPercents();
StringBuilder percentString = new StringBuilder();
String prefix = "";
for (Map.Entry<Integer, Double> entry : percents.entrySet()) {
percentString.append(prefix).append(entry.getValue()).append("% ").append(ItemType.toName(entry.getKey()));
prefix = ", ";
} }
player.sendMessage("&d============="); player.sendMessage("&c" + name + " | " + region + " | " + distance + "m | " + size + " | " + MainUtil.secToTime(seconds));
player.sendMessage("&8 - &7(" + percentString + ")");
}
player.sendMessage("&d==================================================");
player.sendMessage("&dSize: " + (((double) (total / 1024)) / 1000) + "MB"); player.sendMessage("&dSize: " + (((double) (total / 1024)) / 1000) + "MB");
player.sendMessage("&dTo rollback: /frb undo"); player.sendMessage("&dTo rollback: /frb undo");
player.sendMessage("&d============="); player.sendMessage("&d==================================================");
player.setMeta("rollback", edits); player.setMeta("rollback", edits);
} }
}); });
@ -70,6 +80,10 @@ public class Rollback extends FaweCommand {
} }
case "undo": case "undo":
case "revert": { case "revert": {
if (!player.hasPermission("fawe.rollback.perform")) {
BBC.NO_PERM.send(player, "fawe.rollback.perform");
return false;
}
final List<DiskStorageHistory> edits = (List<DiskStorageHistory>) player.getMeta("rollback"); final List<DiskStorageHistory> edits = (List<DiskStorageHistory>) player.getMeta("rollback");
player.deleteMeta("rollback"); player.deleteMeta("rollback");
if (edits == null) { if (edits == null) {
@ -97,7 +111,7 @@ public class Rollback extends FaweCommand {
return true; return true;
} }
public void rollback(final FawePlayer player, final String[] args, final RunnableVal<List<DiskStorageHistory>> result) { public void rollback(final FawePlayer player, final boolean shallow, final String[] args, final RunnableVal<List<DiskStorageHistory>> result) {
TaskManager.IMP.async(new Runnable() { TaskManager.IMP.async(new Runnable() {
@Override @Override
public void run() { public void run() {
@ -148,9 +162,13 @@ public class Rollback extends FaweCommand {
} }
} }
FaweLocation origin = player.getLocation(); FaweLocation origin = player.getLocation();
List<DiskStorageHistory> edits = MainUtil.getBDFiles(origin, user, radius, time); List<DiskStorageHistory> edits = MainUtil.getBDFiles(origin, user, radius, time, shallow);
if (edits == null) {
player.sendMessage("&cToo broad, try refining your search!");
return;
}
if (edits.size() == 0) { if (edits.size() == 0) {
player.sendMessage("No edits found!"); player.sendMessage("&cNo edits found!");
return; return;
} }
result.run(edits); result.run(edits);

View File

@ -1,5 +1,7 @@
package com.boydti.fawe.object; package com.boydti.fawe.object;
import com.boydti.fawe.config.BBC;
public abstract class FaweCommand<T> { public abstract class FaweCommand<T> {
public final String perm; public final String perm;
@ -11,5 +13,21 @@ public abstract class FaweCommand<T> {
return this.perm; return this.perm;
} }
public boolean executeSafe(final FawePlayer<T> player, final String... args) {
if (player == null) {
execute(player, args);
return true;
} else {
if (player.getMeta("fawe_action") != null) {
BBC.WORLDEDIT_COMMAND_LIMIT.send(player);
return false;
}
player.setMeta("fawe_action", true);
boolean result = execute(player, args);
player.deleteMeta("fawe_action");
return result;
}
}
public abstract boolean execute(final FawePlayer<T> player, final String... args); public abstract boolean execute(final FawePlayer<T> player, final String... args);
} }

View File

@ -34,6 +34,7 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
@ -355,19 +356,21 @@ public class DiskStorageHistory implements ChangeSet, FaweChangeSet {
return osENTT; return osENTT;
} }
int fx;
int fz;
public int[] readHeaderAndFooter(RegionWrapper requiredRegion) { private DiskStorageSummary summary;
if (fx == 0 && fz == 0 && bdFile.exists()) {
if ((ox != 0 || oz != 0) && !requiredRegion.isIn(ox, oz)) { public DiskStorageSummary summarize(RegionWrapper requiredRegion, boolean shallow) {
return new int[] {ox, oz, ox, oz}; if (summary != null) {
return summary;
} }
try { if (bdFile.exists()) {
FileInputStream fis = new FileInputStream(bdFile); if ((ox != 0 || oz != 0) && !requiredRegion.isIn(ox, oz)) {
return summary = new DiskStorageSummary(ox, oz);
}
try (FileInputStream fis = new FileInputStream(bdFile)) {
LZ4Factory factory = LZ4Factory.fastestInstance(); LZ4Factory factory = LZ4Factory.fastestInstance();
LZ4Compressor compressor = factory.fastCompressor(); LZ4Compressor compressor = factory.fastCompressor();
final InputStream gis; final LZ4InputStream gis;
if (Settings.COMPRESSION_LEVEL > 0) { if (Settings.COMPRESSION_LEVEL > 0) {
gis = new LZ4InputStream(new LZ4InputStream(fis)); gis = new LZ4InputStream(new LZ4InputStream(fis));
} else { } else {
@ -375,37 +378,31 @@ public class DiskStorageHistory implements ChangeSet, FaweChangeSet {
} }
ox = ((gis.read() << 24) + (gis.read() << 16) + (gis.read() << 8) + (gis.read() << 0)); ox = ((gis.read() << 24) + (gis.read() << 16) + (gis.read() << 8) + (gis.read() << 0));
oz = ((gis.read() << 24) + (gis.read() << 16) + (gis.read() << 8) + (gis.read() << 0)); oz = ((gis.read() << 24) + (gis.read() << 16) + (gis.read() << 8) + (gis.read() << 0));
summary = new DiskStorageSummary(ox, oz);
if (!requiredRegion.isIn(ox, oz)) { if (!requiredRegion.isIn(ox, oz)) {
fis.close(); fis.close();
gis.close(); gis.close();
return new int[] {ox, oz, ox, oz}; return summary;
} }
byte[] even = new byte[9]; byte[] buffer = new byte[9];
byte[] odd = new byte[9];
byte[] result = null;
int i = 0; int i = 0;
while (true) { while (!shallow || gis.hasBytesAvailableInDecompressedBuffer(9)) {
if ((i++ & 1) == 0) { if (gis.read(buffer) == -1) {
if (gis.read(even) == -1) {
result = odd;
break;
}
} else {
if (gis.read(odd) == -1) {
result = even;
break;
}
}
}
fx = ((byte) result[0] & 0xFF) + ((byte) result[1] << 8) + ox;
fz = ((byte) result[2] & 0xFF) + ((byte) result[3] << 8) + oz;
fis.close(); fis.close();
gis.close(); gis.close();
return summary;
}
int x = ((byte) buffer[0] & 0xFF) + ((byte) buffer[1] << 8) + ox;
int z = ((byte) buffer[2] & 0xFF) + ((byte) buffer[3] << 8) + oz;
int combined1 = buffer[7];
int combined2 = buffer[8];
summary.add(x, z, ((combined2 << 4) + (combined1 >> 4)));
}
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
} }
return new int[] {ox, oz, fx, fz}; return summary;
} }
public IntegerPair readHeader() { public IntegerPair readHeader() {
@ -555,4 +552,72 @@ public class DiskStorageHistory implements ChangeSet, FaweChangeSet {
flush(); flush();
return size.get(); return size.get();
} }
public static class DiskStorageSummary {
private final int z;
private final int x;
public int[] blocks;
public int minX;
public int minZ;
public int maxX;
public int maxZ;
public DiskStorageSummary(int x, int z) {
blocks = new int[256];
this.x = x;
this.z = z;
minX = x;
maxX = x;
minZ = z;
maxZ = z;
}
public void add(int x, int z, int id) {
blocks[id]++;
if (x < minX) {
minX = x;
} else if (x > maxX) {
maxX = x;
}
if (z < minZ) {
minZ = z;
} else if (z > maxZ) {
maxZ = z;
}
}
public HashMap<Integer, Integer> getBlocks() {
HashMap<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < blocks.length; i++) {
if (blocks[i] != 0) {
map.put(i, blocks[i]);
}
}
return map;
}
public Map<Integer, Double> getPercents() {
HashMap<Integer, Integer> map = getBlocks();
int count = getSize();
HashMap<Integer, Double> newMap = new HashMap<Integer, Double>();
for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
int id = entry.getKey();
int changes = entry.getValue();
double percent = ((changes * 1000l) / count) / 10d;
newMap.put(id, percent);
}
return newMap;
}
public int getSize() {
int count = 0;
for (int i = 0; i < blocks.length; i++) {
count += blocks[i];
}
return count;
}
}
} }

View File

@ -139,7 +139,7 @@ public class MainUtil {
return time; return time;
} }
public static List<DiskStorageHistory> getBDFiles(FaweLocation origin, UUID user, int radius, long timediff) { public static List<DiskStorageHistory> getBDFiles(FaweLocation origin, UUID user, int radius, long timediff, boolean shallow) {
File history = new File(Fawe.imp().getDirectory(), "history" + File.separator + origin.world); File history = new File(Fawe.imp().getDirectory(), "history" + File.separator + origin.world);
if (!history.exists()) { if (!history.exists()) {
return new ArrayList<>(); return new ArrayList<>();
@ -168,6 +168,9 @@ public class MainUtil {
} }
} }
} }
if (files.size() > 512) {
return null;
}
World world = origin.getWorld(); World world = origin.getWorld();
Collections.sort(files, new Comparator<File>() { Collections.sort(files, new Comparator<File>() {
@Override @Override
@ -180,11 +183,12 @@ public class MainUtil {
for (File file : files) { for (File file : files) {
UUID uuid = UUID.fromString(file.getParentFile().getName()); UUID uuid = UUID.fromString(file.getParentFile().getName());
DiskStorageHistory dsh = new DiskStorageHistory(world, uuid, Integer.parseInt(file.getName().split("\\.")[0])); DiskStorageHistory dsh = new DiskStorageHistory(world, uuid, Integer.parseInt(file.getName().split("\\.")[0]));
int[] headerAndFooter = dsh.readHeaderAndFooter(new RegionWrapper(origin.x - 512, origin.x + 512, origin.z - 512, origin.z + 512)); DiskStorageHistory.DiskStorageSummary summary = dsh.summarize(new RegionWrapper(origin.x - 512, origin.x + 512, origin.z - 512, origin.z + 512), shallow);
RegionWrapper region = new RegionWrapper(headerAndFooter[0], headerAndFooter[2], headerAndFooter[1], headerAndFooter[3]); RegionWrapper region = new RegionWrapper(summary.minX, summary.maxX, summary.minZ, summary.maxZ);
if (region.distance(origin.x, origin.z) <= radius) { if (region.distance(origin.x, origin.z) <= radius) {
result.add(dsh); result.add(dsh);
} }
} }
return result; return result;
} }

View File

@ -75,6 +75,10 @@ public class LZ4InputStream extends InputStream {
return n - numBytesRemainingToSkip; return n - numBytesRemainingToSkip;
} }
public boolean hasBytesAvailableInDecompressedBuffer(int bytes) {
return decompressedBufferPosition + bytes <= decompressedBufferLength;
}
private boolean ensureBytesAvailableInDecompressedBuffer() throws IOException { private boolean ensureBytesAvailableInDecompressedBuffer() throws IOException {
while (decompressedBufferPosition >= decompressedBufferLength) { while (decompressedBufferPosition >= decompressedBufferLength) {
if (!fillBuffer()) { if (!fillBuffer()) {

View File

@ -30,7 +30,7 @@ public class SpongeCommand implements CommandCallable {
BBC.NO_PERM.send(plr, this.cmd.getPerm()); BBC.NO_PERM.send(plr, this.cmd.getPerm());
return CommandResult.success(); return CommandResult.success();
} }
this.cmd.execute(plr, args.split(" ")); this.cmd.executeSafe(plr, args.split(" "));
return CommandResult.success(); return CommandResult.success();
} }