Rename Favs jar and print error on failed load
LocalSession history on disk size limit per player (in MB)
Possible fix for ForgeEssentials incompatibility
This commit is contained in:
Jesse Boyd 2016-08-11 21:34:56 +10:00
parent 3ec42144e7
commit d0b5dab2a0
9 changed files with 168 additions and 21 deletions

View File

@ -71,8 +71,11 @@ public class Settings extends Config {
"NoteBlock, Sign, Skull, Structure"
})
public int MAX_BLOCKSTATES = 1337;
@Comment("Maximum size of the player's history in Megabytes")
public int MAX_HISTORY_MB = 200;
@Comment({
"Maximum size of the player's history in Megabytes:",
" - History on disk or memory will be deleted",
})
public int MAX_HISTORY_MB = -1;
}
public static class HISTORY {
@ -266,7 +269,7 @@ public class Settings extends Config {
limit.MAX_ENTITIES = Math.max(limit.MAX_ENTITIES, newLimit.MAX_ENTITIES != -1 ? newLimit.MAX_ENTITIES : Integer.MAX_VALUE);
limit.MAX_FAILS = Math.max(limit.MAX_FAILS, newLimit.MAX_FAILS != -1 ? newLimit.MAX_FAILS : Integer.MAX_VALUE);
limit.MAX_ITERATIONS = Math.max(limit.MAX_ITERATIONS, newLimit.MAX_ITERATIONS != -1 ? newLimit.MAX_ITERATIONS : Integer.MAX_VALUE);
limit.MAX_HISTORY = HISTORY.USE_DISK ? Integer.MAX_VALUE : Math.max(limit.MAX_HISTORY, newLimit.MAX_HISTORY_MB != -1 ? newLimit.MAX_HISTORY_MB : Integer.MAX_VALUE);
limit.MAX_HISTORY = Math.max(limit.MAX_HISTORY, newLimit.MAX_HISTORY_MB != -1 ? newLimit.MAX_HISTORY_MB : Integer.MAX_VALUE);
}
}
return limit;

View File

@ -35,6 +35,7 @@ public class RollbackDatabase {
private String GET_EDITS;
private String GET_EDITS_USER;
private String DELETE_EDITS_USER;
private String DELETE_EDIT_USER;
private String PURGE;
private ConcurrentLinkedQueue<RollbackOptimizedHistory> historyChanges = new ConcurrentLinkedQueue<>();
@ -52,6 +53,7 @@ public class RollbackDatabase {
GET_EDITS = "SELECT `player`,`id` FROM `" + prefix + "edits` WHERE `x2`>=? AND `x1`<=? AND `y2`>=? AND `y1`<=? AND `z2`>=? AND `z1`<=? AND `time`>? ORDER BY `time` DESC, `id` DESC";
GET_EDITS_USER = "SELECT `player`,`id` FROM `" + prefix + "edits` WHERE `x2`>=? AND `x1`<=? AND `y2`>=? AND `y1`<=? AND `z2`>=? AND `z1`<=? AND `time`>? AND `player`=? ORDER BY `time` DESC, `id` DESC";
DELETE_EDITS_USER = "DELETE FROM `" + prefix + "edits` WHERE `x2`>=? AND `x1`<=? AND `y2`>=? AND `y1`<=? AND `z2`>=? AND `z1`<=? AND `time`>? AND `player`=?";
DELETE_EDIT_USER = "DELETE FROM `" + prefix + "edits` WHERE `player`=? AND `id`=?";
init();
purge((int) TimeUnit.DAYS.toMillis(Settings.HISTORY.DELETE_AFTER_DAYS));
TaskManager.IMP.async(new Runnable() {
@ -90,6 +92,21 @@ public class RollbackDatabase {
notify.add(run);
}
public void delete(final UUID uuid, final int id) {
addTask(new Runnable() {
@Override
public void run() {
try (PreparedStatement stmt = connection.prepareStatement(DELETE_EDIT_USER)) {
byte[] uuidBytes = ByteBuffer.allocate(16).putLong(uuid.getMostSignificantBits()).putLong(uuid.getLeastSignificantBits()).array();
stmt.setBytes(1, uuidBytes);
stmt.setInt(2, id);
} catch (SQLException e) {
e.printStackTrace();
}
}
});
}
public void purge(int diff) {
long now = System.currentTimeMillis() / 1000;
final int then = (int) (now - diff);

View File

@ -47,7 +47,7 @@ public class FaweLimit {
MAX.MAX_ITERATIONS = Integer.MAX_VALUE;
MAX.MAX_BLOCKSTATES = Integer.MAX_VALUE;
MAX.MAX_ENTITIES = Integer.MAX_VALUE;
MAX.MAX_HISTORY = 15;
MAX.MAX_HISTORY = Integer.MAX_VALUE;
}
public boolean MAX_CHANGES() {

View File

@ -2,6 +2,8 @@ package com.boydti.fawe.object.changeset;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.database.DBHandler;
import com.boydti.fawe.database.RollbackDatabase;
import com.boydti.fawe.object.FaweInputStream;
import com.boydti.fawe.object.IntegerPair;
import com.boydti.fawe.object.RegionWrapper;
@ -101,6 +103,15 @@ public class DiskStorageHistory extends FaweStreamChangeSet {
initFiles(folder);
}
public void delete() {
Fawe.debug("Deleting history: " + Fawe.imp().getWorldName(getWorld()) + "/" + uuid + "/" + index);
deleteFiles();
if (Settings.HISTORY.USE_DATABASE) {
RollbackDatabase db = DBHandler.IMP.getDatabase(Fawe.imp().getWorldName(getWorld()));
db.delete(uuid, index);
}
}
public void deleteFiles() {
bdFile.delete();
nbtfFile.delete();
@ -166,19 +177,19 @@ public class DiskStorageHistory extends FaweStreamChangeSet {
public long getSizeOnDisk() {
int total = 0;
if (bdFile.exists()) {
total += bdFile.getTotalSpace();
total += bdFile.length();
}
if (nbtfFile.exists()) {
total += entfFile.getTotalSpace();
total += entfFile.length();
}
if (nbttFile.exists()) {
total += entfFile.getTotalSpace();
total += entfFile.length();
}
if (entfFile.exists()) {
total += entfFile.getTotalSpace();
total += entfFile.length();
}
if (enttFile.exists()) {
total += entfFile.getTotalSpace();
total += entfFile.length();
}
return total;
}

View File

@ -80,6 +80,8 @@ public abstract class FaweChangeSet implements ChangeSet {
public abstract void addEntityCreate(CompoundTag tag);
public abstract Iterator<Change> getIterator(boolean redo);
public void delete() {};
public EditSession toEditSession(FawePlayer player) {
EditSession editSession = new EditSessionBuilder(world).player(player).autoQueue(false).fastmode(false).checkMemory(false).changeSet(this).allowedRegions(RegionWrapper.GLOBAL().toArray()).build();
editSession.setSize(1);

View File

@ -8,6 +8,7 @@ import com.boydti.fawe.object.FaweOutputStream;
import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.RegionWrapper;
import com.boydti.fawe.object.RunnableVal;
import com.boydti.fawe.object.RunnableVal2;
import com.boydti.fawe.object.changeset.CPUOptimizedChangeSet;
import com.boydti.fawe.object.changeset.FaweStreamChangeSet;
import com.sk89q.jnbt.CompoundTag;
@ -38,12 +39,18 @@ import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicLong;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import java.util.zip.ZipEntry;
@ -78,6 +85,42 @@ public class MainUtil {
Fawe.debug(s);
}
public static long getTotalSize(Path path) {
final AtomicLong size = new AtomicLong(0);
traverse(path, new RunnableVal2<Path, BasicFileAttributes>() {
@Override
public void run(Path path, BasicFileAttributes attrs) {
size.addAndGet (attrs.size());
}
});
return size.get();
}
public static long traverse(Path path, final RunnableVal2<Path, BasicFileAttributes> onEach) {
final AtomicLong size = new AtomicLong(0);
try {
Files.walkFileTree(path, new SimpleFileVisitor<Path>() {
@Override public FileVisitResult
visitFile(Path file, BasicFileAttributes attrs) {
onEach.run(file, attrs);
return FileVisitResult.CONTINUE;
}
@Override public FileVisitResult
visitFileFailed(Path file, IOException exc) {
return FileVisitResult.CONTINUE;
}
@Override public FileVisitResult
postVisitDirectory (Path dir, IOException exc) {
return FileVisitResult.CONTINUE;
}
});
}
catch (IOException e) {
throw new AssertionError ("walkFileTree will not throw IOException if the FileVisitor does not");
}
return size.get();
}
public static File getFile(File base, String path) {
if (Paths.get(path).isAbsolute()) {
return new File(path);
@ -93,9 +136,10 @@ public class MainUtil {
return getCompressedOS(os, Settings.HISTORY.COMPRESSION_LEVEL);
}
public static long getSizeInMemory(ChangeSet changeSet) {
public static long getSize(ChangeSet changeSet) {
if (changeSet instanceof FaweStreamChangeSet){
return 92 + ((FaweStreamChangeSet) changeSet).getSizeInMemory();
FaweStreamChangeSet fscs = (FaweStreamChangeSet) changeSet;
return fscs.getSizeOnDisk() + fscs.getSizeInMemory();
} else if (changeSet instanceof CPUOptimizedChangeSet) {
return changeSet.size() + 32;
} else if (changeSet != null) {

View File

@ -24,11 +24,13 @@ import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.FaweInputStream;
import com.boydti.fawe.object.FaweOutputStream;
import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.RunnableVal2;
import com.boydti.fawe.object.changeset.DiskStorageHistory;
import com.boydti.fawe.object.changeset.FaweChangeSet;
import com.boydti.fawe.object.clipboard.DiskOptimizedClipboard;
import com.boydti.fawe.util.EditSessionBuilder;
import com.boydti.fawe.util.MainUtil;
import com.boydti.fawe.wrappers.WorldWrapper;
import com.sk89q.jchronic.Chronic;
import com.sk89q.jchronic.Options;
import com.sk89q.jchronic.utils.Span;
@ -61,6 +63,10 @@ import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
@ -72,6 +78,7 @@ import java.util.Map;
import java.util.TimeZone;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import javax.annotation.Nullable;
import javax.swing.filechooser.FileNameExtensionFilter;
@ -118,7 +125,6 @@ public class LocalSession {
}
});
private transient volatile Integer historyNegativeIndex;
private transient volatile long historySize = 0;
private transient ClipboardHolder clipboard;
private transient boolean toolControl = true;
private transient boolean superPickaxe = false;
@ -133,9 +139,9 @@ public class LocalSession {
private transient Mask mask;
private transient TimeZone timezone = TimeZone.getDefault();
// May be null
private transient World currentWorld;
private transient UUID uuid;
private transient volatile long historySize = 0;
// Saved properties
private String lastScript;
@ -222,16 +228,70 @@ public class LocalSession {
}
});
}
historySize = 0;
if (editIds.size() > 0) {
historySize = MainUtil.getTotalSize(folder.toPath());
Collections.sort(editIds);
for (int index : editIds) {
history.add(index);
}
} else {
historySize = 0;
}
return editIds.size() > 0;
}
@Deprecated
private void deleteOldFiles(UUID uuid, World world, long maxBytes) throws IOException {
final File folder = MainUtil.getFile(Fawe.imp().getDirectory(), Settings.PATHS.HISTORY + File.separator + Fawe.imp().getWorldName(world) + File.separator + uuid);
final ArrayList<Integer> ids = new ArrayList<Integer>();
final HashMap<Integer, ArrayDeque<Path>> paths = new HashMap<>();
final HashMap<Integer, AtomicLong> sizes = new HashMap<>();
final AtomicLong totalSize = new AtomicLong();
MainUtil.traverse(folder.toPath(), new RunnableVal2<Path, BasicFileAttributes>() {
@Override
public void run(Path path, BasicFileAttributes attr) {
try {
String file = path.getFileName().toString();
int index = file.indexOf('.');
if (index == -1) {
return;
}
int id = Integer.parseInt(file.substring(0, index));
long size = attr.size();
totalSize.addAndGet(size);
ArrayDeque<Path> existingPaths = paths.get(id);
if (existingPaths == null) {
existingPaths = new ArrayDeque<Path>();
paths.put(id, existingPaths);
}
existingPaths.add(path);
AtomicLong existingSize = sizes.get(id);
if (existingSize == null) {
existingSize = new AtomicLong();
sizes.put(id, existingSize);
}
existingSize.addAndGet(size);
} catch (NumberFormatException ignore){
ignore.printStackTrace();
}
}
});
if (totalSize.get() < maxBytes) {
return;
}
Collections.sort(ids);
long total = totalSize.get();
for (int i = 0; i < ids.size() && total > maxBytes; i++) {
int id = ids.get(i);
for (Path path : paths.get(id)) {
Files.delete(path);
}
total -= sizes.get(id).get();
}
}
private void loadHistoryNegativeIndex(UUID uuid, World world) {
File file = MainUtil.getFile(Fawe.imp().getDirectory(), Settings.PATHS.HISTORY + File.separator + Fawe.imp().getWorldName(world) + File.separator + uuid + File.separator + "index");
if (file.exists()) {
@ -392,16 +452,19 @@ public class LocalSession {
while (iter.hasNext()) {
Object item = iter.next();
if (++i > cutoffIndex) {
FaweChangeSet changeSet;
if (item instanceof FaweChangeSet) {
FaweChangeSet changeSet = (FaweChangeSet) item;
historySize -= MainUtil.getSizeInMemory(changeSet);
changeSet = (FaweChangeSet) item;
} else {
changeSet = getChangeSet(item);
}
historySize -= MainUtil.getSize(changeSet);
iter.remove();
}
}
}
FaweChangeSet changeSet = (FaweChangeSet) editSession.getChangeSet();
historySize += MainUtil.getSizeInMemory(changeSet);
historySize += MainUtil.getSize(changeSet);
if (append) {
history.add(changeSet);
if (getHistoryNegativeIndex() != 0) {
@ -413,7 +476,8 @@ public class LocalSession {
}
while ((history.size() > MAX_HISTORY_SIZE || (historySize >> 20) > limitMb) && history.size() > 1) {
FaweChangeSet item = (FaweChangeSet) history.remove(0);
historySize -= MainUtil.getSizeInMemory(item);
item.delete();
historySize -= MainUtil.getSize(item);
}
}
@ -648,7 +712,11 @@ public class LocalSession {
* @return the the world of the selection
*/
public World getSelectionWorld() {
return selector.getIncompleteRegion().getWorld();
World world = selector.getIncompleteRegion().getWorld();
if (world instanceof WorldWrapper) {
return ((WorldWrapper) world).getParent();
}
return world;
}
/**

View File

@ -14,4 +14,4 @@ processResources {
}
jar.destinationDir = file '../target'
jar.archiveName = "${parent.name}-${project.name}-${parent.version}.jar"
jar.archiveName = "FastAsyncVoxelSniper-${project.name}-${parent.version}.jar"

View File

@ -45,6 +45,8 @@ public class Favs extends JavaPlugin {
}
});
Fawe.debug("Injected VoxelSniper classes");
} catch (Throwable ignore) {}
} catch (Throwable ignore) {
ignore.printStackTrace();
}
}
}