Anvil undo/region restrictions

This commit is contained in:
Jesse Boyd 2017-08-20 14:54:10 +10:00
parent d37e44e395
commit 7359100159
No known key found for this signature in database
GPG Key ID: 59F1DE6293AF6E1F
22 changed files with 437 additions and 175 deletions

View File

@ -107,9 +107,9 @@ public class BukkitQueue_All extends BukkitQueue_0<ChunkSnapshot, ChunkSnapshot,
}
@Override
public boolean setMCA(int mcaX, int mcaZ, RegionWrapper allowed, Runnable whileLocked, boolean load) {
public boolean setMCA(int mcaX, int mcaZ, RegionWrapper allowed, Runnable whileLocked, boolean saveChunks, boolean load) {
if (classRegionFileCache == null) {
return super.setMCA(mcaX, mcaZ, allowed, whileLocked, load);
return super.setMCA(mcaX, mcaZ, allowed, whileLocked, saveChunks, load);
}
TaskManager.IMP.sync(new RunnableVal<Boolean>() {
@Override
@ -134,7 +134,7 @@ public class BukkitQueue_All extends BukkitQueue_0<ChunkSnapshot, ChunkSnapshot,
int cz = chunk.getZ();
if (cx >= bcx && cx <= tcx && cz >= bcz && cz <= tcz) {
Object nmsChunk = methodGetHandleChunk.invoke(chunk);
boolean mustSave = (boolean) methodNeedsSaving.invoke(nmsChunk, false);
boolean mustSave = saveChunks && (boolean) methodNeedsSaving.invoke(nmsChunk, false);
chunk.unload(mustSave, false);
if (unloaded == null) unloaded = new ArrayDeque<Chunk>();
unloaded.add(chunk);

View File

@ -402,7 +402,7 @@ public class BukkitQueue_1_10 extends BukkitQueue_0<net.minecraft.server.v1_10_R
}
@Override
public boolean setMCA(final int mcaX, final int mcaZ, final RegionWrapper allowed, final Runnable whileLocked, final boolean load) {
public boolean setMCA(final int mcaX, final int mcaZ, final RegionWrapper allowed, final Runnable whileLocked, final boolean saveChunks, final boolean load) {
TaskManager.IMP.sync(new RunnableVal<Boolean>() {
@Override
public void run(Boolean value) {
@ -423,7 +423,7 @@ public class BukkitQueue_1_10 extends BukkitQueue_0<net.minecraft.server.v1_10_R
boolean isIn = allowed.isInChunk(chunk.locX, chunk.locZ);
if (isIn) {
if (!load) {
if (chunk.a(false)) {
if (saveChunks && chunk.a(false)) {
mustSave = true;
provider.saveChunk(chunk);
provider.saveChunkNOP(chunk);
@ -431,7 +431,7 @@ public class BukkitQueue_1_10 extends BukkitQueue_0<net.minecraft.server.v1_10_R
continue;
}
iter.remove();
boolean save = chunk.a(false);
boolean save = saveChunks && chunk.a(false);
mustSave |= save;
if (save) {
provider.unload(chunk);

View File

@ -260,7 +260,7 @@ public class BukkitQueue_1_11 extends BukkitQueue_0<net.minecraft.server.v1_11_R
}
@Override
public boolean setMCA(final int mcaX, final int mcaZ, final RegionWrapper allowed, final Runnable whileLocked, final boolean load) {
public boolean setMCA(final int mcaX, final int mcaZ, final RegionWrapper allowed, final Runnable whileLocked, final boolean saveChunks, final boolean load) {
TaskManager.IMP.sync(new RunnableVal<Boolean>() {
@Override
public void run(Boolean value) {
@ -281,7 +281,7 @@ public class BukkitQueue_1_11 extends BukkitQueue_0<net.minecraft.server.v1_11_R
boolean isIn = allowed.isInChunk(chunk.locX, chunk.locZ);
if (isIn) {
if (!load) {
if (chunk.a(false)) {
if (saveChunks && chunk.a(false)) {
mustSave = true;
provider.saveChunk(chunk);
provider.saveChunkNOP(chunk);
@ -289,7 +289,7 @@ public class BukkitQueue_1_11 extends BukkitQueue_0<net.minecraft.server.v1_11_R
continue;
}
iter.remove();
boolean save = chunk.a(false);
boolean save = saveChunks && chunk.a(false);
mustSave |= save;
provider.unloadChunk(chunk, save);
if (chunksUnloaded == null) {

View File

@ -14,6 +14,7 @@ import com.boydti.fawe.object.visitor.FaweChunkVisitor;
import com.boydti.fawe.util.MainUtil;
import com.boydti.fawe.util.MathMan;
import com.boydti.fawe.util.ReflectionUtils;
import com.boydti.fawe.util.SetQueue;
import com.boydti.fawe.util.TaskManager;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.StringTag;
@ -293,7 +294,7 @@ public class BukkitQueue_1_12 extends BukkitQueue_0<net.minecraft.server.v1_12_R
}
@Override
public boolean setMCA(final int mcaX, final int mcaZ, final RegionWrapper allowed, final Runnable whileLocked, final boolean load) {
public boolean setMCA(final int mcaX, final int mcaZ, final RegionWrapper allowed, final Runnable whileLocked, final boolean saveChunks, final boolean load) {
TaskManager.IMP.sync(new RunnableVal<Boolean>() {
@Override
public void run(Boolean value) {
@ -314,11 +315,11 @@ public class BukkitQueue_1_12 extends BukkitQueue_0<net.minecraft.server.v1_12_R
boolean isIn = allowed.isInChunk(chunk.locX, chunk.locZ);
if (isIn) {
if (!load) {
mustSave |= save(chunk, provider);
mustSave |= saveChunks && save(chunk, provider);
continue;
}
iter.remove();
boolean save = chunk.a(false);
boolean save = saveChunks && chunk.a(false);
mustSave |= save;
provider.unloadChunk(chunk, save);
if (chunksUnloaded == null) {
@ -334,7 +335,9 @@ public class BukkitQueue_1_12 extends BukkitQueue_0<net.minecraft.server.v1_12_R
}
}
}
if (mustSave) provider.c(); // TODO only the necessary chunks
if (mustSave) {
provider.c(); // TODO only the necessary chunks
}
File unloadedRegion = null;
if (load && !RegionFileCache.a.isEmpty()) {
@ -378,9 +381,9 @@ public class BukkitQueue_1_12 extends BukkitQueue_0<net.minecraft.server.v1_12_R
if (arr[z]) {
int cx = bx + x;
int cz = bz + z;
TaskManager.IMP.sync(new RunnableVal<Object>() {
SetQueue.IMP.addTask(new Runnable() {
@Override
public void run(Object value1) {
public void run() {
net.minecraft.server.v1_12_R1.Chunk chunk = provider.getChunkAt(cx, cz, null, false);
if (chunk != null) {
PlayerChunk pc = getPlayerChunk(nmsWorld, cx, cz);

View File

@ -130,7 +130,7 @@ public class BukkitQueue17 extends BukkitQueue_0<net.minecraft.server.v1_7_R4.Ch
}
@Override
public boolean setMCA(final int mcaX, final int mcaZ, final RegionWrapper allowed, final Runnable whileLocked, final boolean load) {
public boolean setMCA(final int mcaX, final int mcaZ, final RegionWrapper allowed, final Runnable whileLocked, boolean saveChunks, final boolean load) {
TaskManager.IMP.sync(new RunnableVal<Boolean>() {
@Override
public void run(Boolean value) {
@ -151,7 +151,7 @@ public class BukkitQueue17 extends BukkitQueue_0<net.minecraft.server.v1_7_R4.Ch
boolean isIn = allowed.isInChunk(chunk.locX, chunk.locZ);
if (isIn) {
if (!load) {
if (chunk.a(false)) {
if (saveChunks && chunk.a(false)) {
mustSave = true;
provider.saveChunk(chunk);
provider.saveChunkNOP(chunk);
@ -159,7 +159,7 @@ public class BukkitQueue17 extends BukkitQueue_0<net.minecraft.server.v1_7_R4.Ch
continue;
}
iter.remove();
boolean save = chunk.a(false);
boolean save = saveChunks && chunk.a(false);
mustSave |= save;
chunk.bukkitChunk.unload(save, false);
if (chunksUnloaded == null) {

View File

@ -9,6 +9,7 @@ import com.boydti.fawe.object.RunnableVal;
import com.boydti.fawe.util.MainUtil;
import com.boydti.fawe.util.MathMan;
import com.boydti.fawe.util.ReflectionUtils;
import com.boydti.fawe.util.SetQueue;
import com.boydti.fawe.util.TaskManager;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.StringTag;
@ -128,7 +129,7 @@ public class BukkitQueue18R3 extends BukkitQueue_0<net.minecraft.server.v1_8_R3.
}
@Override
public boolean setMCA(final int mcaX, final int mcaZ, final RegionWrapper allowed, final Runnable whileLocked, final boolean load) {
public boolean setMCA(final int mcaX, final int mcaZ, final RegionWrapper allowed, final Runnable whileLocked, final boolean saveChunks, final boolean load) {
TaskManager.IMP.sync(new RunnableVal<Boolean>() {
@Override
public void run(Boolean value) {
@ -149,7 +150,7 @@ public class BukkitQueue18R3 extends BukkitQueue_0<net.minecraft.server.v1_8_R3.
boolean isIn = allowed.isInChunk(chunk.locX, chunk.locZ);
if (isIn) {
if (!load) {
if (chunk.a(false)) {
if (saveChunks && chunk.a(false)) {
mustSave = true;
provider.saveChunk(chunk);
provider.saveChunkNOP(chunk);
@ -157,7 +158,7 @@ public class BukkitQueue18R3 extends BukkitQueue_0<net.minecraft.server.v1_8_R3.
continue;
}
iter.remove();
boolean save = chunk.a(false);
boolean save = saveChunks && chunk.a(false);
mustSave |= save;
chunk.bukkitChunk.unload(save, false);
if (chunksUnloaded == null) {
@ -217,9 +218,9 @@ public class BukkitQueue18R3 extends BukkitQueue_0<net.minecraft.server.v1_8_R3.
if (arr[z]) {
int cx = bx + x;
int cz = bz + z;
TaskManager.IMP.sync(new RunnableVal<Object>() {
SetQueue.IMP.addTask(new Runnable() {
@Override
public void run(Object value1) {
public void run() {
net.minecraft.server.v1_8_R3.Chunk chunk = provider.getChunkAt(cx, cz, null);
if (chunk != null) {
if (nmsWorld.getPlayerChunkMap().isChunkInUse(cx, cz)) {

View File

@ -263,7 +263,7 @@ public class BukkitQueue_1_9_R1 extends BukkitQueue_0<net.minecraft.server.v1_9_
}
@Override
public boolean setMCA(final int mcaX, final int mcaZ, final RegionWrapper allowed, final Runnable whileLocked, final boolean load) {
public boolean setMCA(final int mcaX, final int mcaZ, final RegionWrapper allowed, final Runnable whileLocked, boolean saveChunks, final boolean load) {
TaskManager.IMP.sync(new RunnableVal<Boolean>() {
@Override
public void run(Boolean value) {
@ -284,7 +284,7 @@ public class BukkitQueue_1_9_R1 extends BukkitQueue_0<net.minecraft.server.v1_9_
boolean isIn = allowed.isInChunk(chunk.locX, chunk.locZ);
if (isIn) {
if (!load) {
if (chunk.a(false)) {
if (saveChunks && chunk.a(false)) {
mustSave = true;
provider.saveChunk(chunk);
provider.saveChunkNOP(chunk);
@ -292,7 +292,7 @@ public class BukkitQueue_1_9_R1 extends BukkitQueue_0<net.minecraft.server.v1_9_
continue;
}
iter.remove();
boolean save = chunk.a(false);
boolean save = saveChunks && chunk.a(false);
mustSave |= save;
if (save) {
provider.unload(chunk);

View File

@ -23,11 +23,15 @@ import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.FaweQueue;
import com.boydti.fawe.object.RegionWrapper;
import com.boydti.fawe.object.RunnableVal4;
import com.boydti.fawe.object.changeset.AnvilHistory;
import com.boydti.fawe.object.clipboard.ClipboardRemapper;
import com.boydti.fawe.object.exception.FaweException;
import com.boydti.fawe.object.mask.FaweBlockMatcher;
import com.boydti.fawe.regions.FaweMaskManager;
import com.boydti.fawe.util.MainUtil;
import com.boydti.fawe.util.SetQueue;
import com.boydti.fawe.util.StringMan;
import com.boydti.fawe.util.WEManager;
import com.sk89q.minecraft.util.commands.Command;
import com.sk89q.minecraft.util.commands.CommandPermissions;
import com.sk89q.worldedit.EditSession;
@ -48,8 +52,10 @@ import com.sk89q.worldedit.util.command.parametric.Optional;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@ -122,7 +128,23 @@ public class AnvilCommands {
String worldName = Fawe.imp().getWorldName(editSession.getWorld());
FaweQueue tmp = SetQueue.IMP.getNewQueue(worldName, true, false);
MCAQueue queue = new MCAQueue(tmp);
queue.filterCopy(filter, wrappedRegion);
FawePlayer<Object> fp = FawePlayer.wrap(player);
LocalSession session = fp.getSession();
if (session == null || session.hasFastMode()) {
queue.filterCopy(filter, wrappedRegion);
} else {
RegionWrapper[] allowed = WEManager.IMP.getMask(fp, FaweMaskManager.MaskType.OWNER);
HashSet<RegionWrapper> allowedSet = new HashSet<>(Arrays.asList(allowed));
RegionWrapper wrappedSelection = new RegionWrapper(selection.getMinimumPoint(), selection.getMaximumPoint());
if (allowed.length == 0) {
throw new FaweException(BBC.WORLDEDIT_CANCEL_REASON_NO_REGION);
} else if (!WEManager.IMP.regionContains(wrappedSelection, allowedSet)) {
throw new FaweException(BBC.WORLDEDIT_CANCEL_REASON_MAX_FAILS);
}
AnvilHistory history = new AnvilHistory(worldName, player.getUniqueId());
queue.filterCopy(filter, wrappedRegion, history);
session.remember(player, editSession.getWorld(), history, fp.getLimit());
}
return filter;
}

View File

@ -5,6 +5,8 @@ import com.boydti.fawe.example.CharFaweChunk;
import com.boydti.fawe.example.NMSMappedFaweQueue;
import com.boydti.fawe.example.NullFaweChunk;
import com.boydti.fawe.jnbt.anvil.filters.DelegateMCAFilter;
import com.boydti.fawe.jnbt.anvil.history.IAnvilHistory;
import com.boydti.fawe.jnbt.anvil.history.NullAnvilHistory;
import com.boydti.fawe.object.FaweChunk;
import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.FaweQueue;
@ -147,9 +149,9 @@ public class MCAQueue extends NMSMappedFaweQueue<FaweQueue, FaweChunk, FaweChunk
}
@Override
public boolean setMCA(int mcaX, int mcaZ, RegionWrapper region, Runnable whileLocked, boolean unload) {
if (parent != null) return parent.setMCA(mcaX, mcaZ, region, whileLocked, unload);
return super.setMCA(mcaX, mcaZ, region, whileLocked, unload);
public boolean setMCA(int mcaX, int mcaZ, RegionWrapper region, Runnable whileLocked, boolean save, boolean unload) {
if (parent != null) return parent.setMCA(mcaX, mcaZ, region, whileLocked, save, unload);
return super.setMCA(mcaX, mcaZ, region, whileLocked, save, unload);
}
public void pasteRegion(MCAQueue from, final RegionWrapper regionFrom, Vector offset) throws IOException {
@ -279,27 +281,30 @@ public class MCAQueue extends NMSMappedFaweQueue<FaweQueue, FaweChunk, FaweChunk
from.clear();
}
private void performCopy(MCAFile original, MCAFile copy, RegionWrapper region, ForkJoinPool pool) {
private void performCopy(MCAFile original, MCAFile copy, RegionWrapper region, IAnvilHistory task, ForkJoinPool pool) {
original.clear();
File originalFile = original.getFile();
File copyFile = copy.getFile();
if (copy.isModified()) {
if (copy.isDeleted()) {
if (originalFile.delete()) return;
setMCA(original.getX(), original.getZ(), region, () -> originalFile.delete(), true);
if (task.addFileChange(originalFile)) return;
setMCA(original.getX(), original.getZ(), region, () -> task.addFileChange(originalFile), true, true);
return;
} else if (copyFile.exists()) {
try {
copy.close(pool);
Files.move(copyFile.toPath(), originalFile.toPath(), StandardCopyOption.ATOMIC_MOVE);
} catch (IOException e) {
setMCA(original.getX(), original.getZ(), region, () -> {
originalFile.delete();
if (!copyFile.renameTo(originalFile)) {
Fawe.debug("Failed to copy (2)");
}
}, true);
// If the task is the normal delete task, we can do a normal file move
copy.close(pool);
if (task.getClass() == NullAnvilHistory.class) {
try {
Files.move(copyFile.toPath(), originalFile.toPath(), StandardCopyOption.ATOMIC_MOVE);
return;
} catch (IOException ignore) {}
}
setMCA(original.getX(), original.getZ(), region, () -> {
task.addFileChange(originalFile);
if (!copyFile.renameTo(originalFile)) {
Fawe.debug("Failed to copy (2)");
}
}, true, true);
}
}
copy.clear();
@ -307,6 +312,11 @@ public class MCAQueue extends NMSMappedFaweQueue<FaweQueue, FaweChunk, FaweChunk
}
public <G, T extends MCAFilter<G>> T filterCopy(final T filter, RegionWrapper region) {
return filterCopy(filter, region, new NullAnvilHistory());
}
public <G, T extends MCAFilter<G>> T filterCopy(final T filter, RegionWrapper region, IAnvilHistory task) {
DelegateMCAFilter<G> delegate = new DelegateMCAFilter<G>(filter) {
MCAFile original;
MCAFile copy;
@ -330,11 +340,11 @@ public class MCAQueue extends NMSMappedFaweQueue<FaweQueue, FaweChunk, FaweChunk
} catch (IOException e) {
e.printStackTrace();
}
}, false);
}, true, false);
this.copy = new MCAFile(original.getParent(), copyDest);
MCAFile result = filter.applyFile(copy);
if (result == null) {
performCopy(original, copy, region, pool);
performCopy(original, copy, region, task, pool);
}
if (result == null || !copy.getFile().equals(result.getFile())) {
copy.clear();
@ -345,7 +355,7 @@ public class MCAQueue extends NMSMappedFaweQueue<FaweQueue, FaweChunk, FaweChunk
@Override
public void finishFile(MCAFile newRegion, G cache) {
performCopy(original, newRegion, region, pool);
performCopy(original, newRegion, region, task, pool);
}
};
if (region == RegionWrapper.GLOBAL()) {

View File

@ -51,7 +51,7 @@ public class MCAQueueMap implements IFaweQueueMap {
lastFile = tmp = mcaFileMap.get(pair);
if (lastFile == null) {
try {
queue.setMCA(lastFileX, lastFileZ, RegionWrapper.GLOBAL(), null, false);
queue.setMCA(lastFileX, lastFileZ, RegionWrapper.GLOBAL(), null, true, false);
lastFile = tmp = new MCAFile(queue, lastFileX, lastFileZ);
} catch (FaweException.FaweChunkLoadException ignore) {
lastFile = null;
@ -184,7 +184,7 @@ public class MCAQueueMap implements IFaweQueueMap {
public void run() {
file.close(SetQueue.IMP.getForkJoinPool());
}
}, true);
}, true, true);
} else {
break;
}

View File

@ -0,0 +1,9 @@
package com.boydti.fawe.jnbt.anvil.history;
import java.io.File;
public interface IAnvilHistory {
default boolean addFileChange(File originalMCAFile) {
return originalMCAFile.delete();
}
}

View File

@ -0,0 +1,4 @@
package com.boydti.fawe.jnbt.anvil.history;
public class NullAnvilHistory implements IAnvilHistory {
}

View File

@ -289,7 +289,7 @@ public abstract class FaweQueue implements HasFaweQueue, Extent {
public abstract Collection<FaweChunk> getFaweChunks();
public boolean setMCA(int mcaX, int mcaZ, RegionWrapper region, Runnable whileLocked, boolean load) {
public boolean setMCA(int mcaX, int mcaZ, RegionWrapper region, Runnable whileLocked, boolean save, boolean load) {
if (whileLocked != null) whileLocked.run();
return true;
}

View File

@ -0,0 +1,105 @@
package com.boydti.fawe.object.changeset;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.jnbt.anvil.history.IAnvilHistory;
import com.boydti.fawe.util.MainUtil;
import com.google.common.base.Function;
import com.google.common.collect.Iterators;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.history.change.Change;
import com.sk89q.worldedit.world.biome.BaseBiome;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.UUID;
import javax.annotation.Nullable;
public class AnvilHistory extends FaweChangeSet implements IAnvilHistory {
private final File folder;
private int size;
public AnvilHistory(String world, File folder) {
super(world);
this.folder = folder;
size = -1;
}
public AnvilHistory(String world, UUID uuid) {
super(world);
File history = MainUtil.getFile(Fawe.imp().getDirectory(), Settings.IMP.PATHS.HISTORY + File.separator + world + File.separator + uuid);
File destFolder = new File(history, Integer.toString(MainUtil.getMaxFileId(history)));
if (!destFolder.exists()) {
destFolder.mkdirs();
}
this.folder = destFolder;
this.size = 0;
}
@Override
public boolean addFileChange(File originalMCAFile) {
try {
Files.move(originalMCAFile.toPath(), Paths.get(folder.getPath(), originalMCAFile.getName()), StandardCopyOption.ATOMIC_MOVE);
if (size != -1) size++;
} catch (IOException e) {
e.printStackTrace();
originalMCAFile.delete();
}
return false;
}
@Override
public void add(int x, int y, int z, int combinedFrom, int combinedTo) {
throw new UnsupportedOperationException("Only anvil operations are supported");
}
@Override
public void addTileCreate(CompoundTag tag) {
throw new UnsupportedOperationException("Only anvil operations are supported");
}
@Override
public void addTileRemove(CompoundTag tag) {
throw new UnsupportedOperationException("Only anvil operations are supported");
}
@Override
public void addEntityRemove(CompoundTag tag) {
throw new UnsupportedOperationException("Only anvil operations are supported");
}
@Override
public void addEntityCreate(CompoundTag tag) {
throw new UnsupportedOperationException("Only anvil operations are supported");
}
@Override
public void addBiomeChange(int x, int z, BaseBiome from, BaseBiome to) {
throw new UnsupportedOperationException("Only anvil operations are supported");
}
@Override
public Iterator<Change> getIterator(boolean redo) {
if (redo) throw new UnsupportedOperationException("Only undo operations are supported");
List<File> files = Arrays.asList(folder.listFiles());
final MutableAnvilChange change = new MutableAnvilChange();
return Iterators.transform(files.iterator(), new Function<File, MutableAnvilChange>() {
@Nullable
@Override
public MutableAnvilChange apply(@Nullable File input) {
change.setSource(input.toPath());
return change;
}
});
}
@Override
public int size() {
return size == -1 ? folder.listFiles().length : size;
}
}

View File

@ -0,0 +1,71 @@
package com.boydti.fawe.object.changeset;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.object.FaweQueue;
import com.boydti.fawe.object.HasFaweQueue;
import com.boydti.fawe.object.RegionWrapper;
import com.boydti.fawe.util.ExtentTraverser;
import com.boydti.fawe.util.MainUtil;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.history.UndoContext;
import com.sk89q.worldedit.history.change.Change;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
public class MutableAnvilChange implements Change {
private Path source;
private Path destDir;
public void setSource(Path source) {
this.source = source;
}
private FaweQueue queue;
private boolean checkedQueue;
@Override
public void undo(UndoContext context) throws WorldEditException {
if (queue != null) {
perform(queue);
}
if (!checkedQueue) {
checkedQueue = true;
Extent extent = context.getExtent();
ExtentTraverser found = new ExtentTraverser(extent).find(HasFaweQueue.class);
if (found != null) {
queue = ((HasFaweQueue) found.get()).getQueue();
destDir = queue.getSaveFolder().toPath();
perform(queue);
} else {
Fawe.debug("FAWE does not support: " + extent + " for " + getClass() + " (bug Empire92)");
}
}
}
public void perform(FaweQueue queue) {
Path dest = destDir.resolve(source.getFileName());
try {
Files.move(source, dest, StandardCopyOption.ATOMIC_MOVE);
} catch (IOException ignore) {
int[] coords = MainUtil.regionNameToCoords(source.toString());
queue.setMCA(coords[0], coords[1], RegionWrapper.GLOBAL(), new Runnable() {
@Override
public void run() {
try {
Files.move(source, dest, StandardCopyOption.ATOMIC_MOVE);
} catch (IOException e1) {
e1.printStackTrace();
}
}
}, false, true);
}
}
@Override
public void redo(UndoContext context) throws WorldEditException {
throw new UnsupportedOperationException("Redo not supported");
}
}

View File

@ -47,8 +47,8 @@ public class DelegateFaweQueue extends FaweQueue {
}
@Override
public boolean setMCA(int mcaX, int mcaZ, RegionWrapper region, Runnable whileLocked, boolean load) {
return parent.setMCA(mcaX, mcaZ, region, whileLocked, load);
public boolean setMCA(int mcaX, int mcaZ, RegionWrapper region, Runnable whileLocked, boolean save, boolean load) {
return parent.setMCA(mcaX, mcaZ, region, whileLocked, save, load);
}
@Override

View File

@ -30,6 +30,7 @@ import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
@ -61,7 +62,6 @@ import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.zip.DataFormatException;
import java.util.zip.Deflater;
@ -220,27 +220,23 @@ public class MainUtil {
}
public static int getMaxFileId(File folder) {
final AtomicInteger max = new AtomicInteger();
if (folder.exists()) {
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));
if (id > max.get()) {
max.set(id);
}
} catch (NumberFormatException ignore) {
}
final int[] max = new int[1];
folder.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
String name = pathname.getName();
Integer val = null;
if (pathname.isDirectory()) {
val = StringMan.toInteger(name, 0, name.length());
} else {
int i = name.lastIndexOf('.');
if (i != -1) val = StringMan.toInteger(name, 0, i);
}
});
}
return max.get() + 1;
if (val != null && val > max[0]) max[0] = val;
return false;
}
});
return max[0] + 1;
}
public static File getFile(File base, String path) {

View File

@ -80,24 +80,50 @@ public class SetQueue {
public void run() {
try {
long now = System.currentTimeMillis();
targetTPS = 18 - Math.max(Settings.IMP.QUEUE.EXTRA_TIME_MS * 0.05, 0);
do {
Runnable task = tasks.poll();
if (task != null) {
task.run();
} else {
break;
}
} while (Fawe.get().getTimer().isAbove(targetTPS));
if (inactiveQueues.isEmpty() && activeQueues.isEmpty()) {
last = lastSuccess = now;
boolean empty = (inactiveQueues.isEmpty() && activeQueues.isEmpty());
boolean emptyTasks = tasks.isEmpty();
if (emptyTasks && empty) {
last = now;
runEmptyTasks();
return;
}
targetTPS = 18 - Math.max(Settings.IMP.QUEUE.EXTRA_TIME_MS * 0.05, 0);
long diff = (50 + SetQueue.this.last) - (SetQueue.this.last = now);
long absDiff = Math.abs(diff);
if (diff == 0) {
allocate = Math.min(50, allocate + 1);
} else if (diff < 0) {
allocate = Math.max(5, allocate + diff);
} else if (!Fawe.get().getTimer().isAbove(targetTPS)) {
allocate = Math.max(5, allocate - 1);
}
long currentAllocate = allocate - absDiff;
if (!emptyTasks) {
long taskAllocate = empty ? currentAllocate : currentAllocate >> 1;
long used = 0;
do {
Runnable task = tasks.poll();
if (task != null) {
task.run();
} else {
break;
}
} while ((used = System.currentTimeMillis() - now) < taskAllocate);
currentAllocate -= used;
}
if (empty) {
runEmptyTasks();
return;
}
if (!MemUtil.isMemoryFree()) {
final int mem = MemUtil.calculateMemory();
if (mem != Integer.MAX_VALUE) {
last = now;
allocate = Math.max(5, allocate - 1);
if ((mem <= 1) && Settings.IMP.PREVENT_CRASHES) {
for (FaweQueue queue : getAllQueues()) {
@ -113,27 +139,13 @@ public class SetQueue {
return;
}
}
FaweQueue queue = getNextQueue();
if (queue == null) {
last = now;
return;
}
if (!Fawe.get().getTimer().isAbove(targetTPS)) {
allocate = Math.max(5, allocate - 1);
last = now;
return;
}
if (Thread.currentThread() != Fawe.get().getMainThread()) {
throw new IllegalStateException("This shouldn't be possible for placement to occur off the main thread");
}
long diff = (50 + SetQueue.this.last) - (SetQueue.this.last = now);
long absDiff = Math.abs(diff);
if (diff == 0) {
allocate = Math.min(50, allocate + 1);
} else if (diff < 0) {
allocate = Math.max(5, allocate + diff);
}
long time = Settings.IMP.QUEUE.EXTRA_TIME_MS + allocate - absDiff - System.currentTimeMillis() + now;
long time = Settings.IMP.QUEUE.EXTRA_TIME_MS + currentAllocate - System.currentTimeMillis() + now;
// Disable the async catcher as it can't discern async vs parallel
boolean parallel = Settings.IMP.QUEUE.PARALLEL_THREADS > 1;
queue.startSet(parallel);

View File

@ -313,6 +313,37 @@ public class StringMan {
}
}
public static Integer toInteger(String string, int start, int end) {
int value = 0;
char char0 = string.charAt(0);
boolean negative;
if (char0 == '-') {
negative = true;
start++;
}
else negative = false;
for (int i = start; i < end; i++) {
char c = string.charAt(i);
switch (c) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
value = value * 10 + c - '0';
break;
default:
return null;
}
}
return negative ? -value : value;
}
public static String join(final int[] array, final String delimiter) {
final Integer[] wrapped = new Integer[array.length];
for (int i = 0; i < array.length; i++) {

View File

@ -208,7 +208,7 @@ public class PlayerWrapper extends AbstractPlayerActor {
edit.flushQueue();
LocalSession session = Fawe.get().getWorldEdit().getSession(this);
if (session != null) {
session.remember(edit, true, false, FawePlayer.wrap(this).getLimit().MAX_HISTORY);
session.remember(edit, true, FawePlayer.wrap(this).getLimit().MAX_HISTORY);
}
} catch (RuntimeException e) {
caught = e;

View File

@ -2934,7 +2934,7 @@ public class EditSession extends AbstractWorld implements HasFaweQueue, Lighting
int yv = (int) (y.getValue() * unit.getY() + zero2.getY());
int zv = (int) (z.getValue() * unit.getZ() + zero2.getZ());
// read block from world
BaseBlock material = getLazyBlock(xv, yv, zv);
BaseBlock material = FaweCache.CACHE_BLOCK[queue.getCombinedId4DataDebug(xv, yv, zv, 0, EditSession.this)];
// queue operation
return setBlockFast(position, material);
} catch (EvaluationException e) {

View File

@ -23,14 +23,17 @@ import com.boydti.fawe.Fawe;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.FaweInputStream;
import com.boydti.fawe.object.FaweLimit;
import com.boydti.fawe.object.FaweOutputStream;
import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.RunnableVal2;
import com.boydti.fawe.object.changeset.AnvilHistory;
import com.boydti.fawe.object.changeset.DiskStorageHistory;
import com.boydti.fawe.object.changeset.FaweChangeSet;
import com.boydti.fawe.object.collection.SparseBitSet;
import com.boydti.fawe.object.extent.ResettableExtent;
import com.boydti.fawe.util.EditSessionBuilder;
import com.boydti.fawe.util.MainUtil;
import com.boydti.fawe.util.StringMan;
import com.boydti.fawe.wrappers.WorldWrapper;
import com.sk89q.jchronic.Chronic;
import com.sk89q.jchronic.Options;
@ -47,6 +50,7 @@ import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.extent.inventory.BlockBag;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.function.mask.Masks;
import com.sk89q.worldedit.history.changeset.ChangeSet;
import com.sk89q.worldedit.internal.cui.CUIEvent;
import com.sk89q.worldedit.internal.cui.CUIRegion;
import com.sk89q.worldedit.internal.cui.SelectionShapeEvent;
@ -63,11 +67,6 @@ 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;
import java.util.HashMap;
@ -78,9 +77,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;
import static com.google.common.base.Preconditions.checkNotNull;
@ -207,84 +204,35 @@ public class LocalSession {
}
private boolean loadHistoryChangeSets(UUID uuid, World world) {
final List<Integer> editIds = new ArrayList<>();
SparseBitSet set = new SparseBitSet();
final File folder = MainUtil.getFile(Fawe.imp().getDirectory(), Settings.IMP.PATHS.HISTORY + File.separator + Fawe.imp().getWorldName(world) + File.separator + uuid);
if (folder.isDirectory()) {
final FileNameExtensionFilter filter = new FileNameExtensionFilter("BlockData files", "bd");
folder.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
String name = pathname.getName();
int i = name.lastIndexOf('.');
if ((name.length() == i + 3) && (name.charAt(i + 1) == 'b' && name.charAt(i + 2) == 'd')) {
int index = Integer.parseInt(name.substring(0, i));
editIds.add(index);
Integer val = null;
if (pathname.isDirectory()) {
val = StringMan.toInteger(name, 0, name.length());
} else {
int i = name.lastIndexOf('.');
if (i != -1) val = StringMan.toInteger(name, 0, i);
}
if (val != null) set.set(val);
return false;
}
});
}
if (editIds.size() > 0) {
if (!set.isEmpty()) {
historySize = MainUtil.getTotalSize(folder.toPath());
Collections.sort(editIds);
for (int index : editIds) {
for (int index = set.nextSetBit(0); index != -1; index = set.nextSetBit(index + 1)) {
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.IMP.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) {
}
}
});
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();
}
return !set.isEmpty();
}
private void loadHistoryNegativeIndex(UUID uuid, World world) {
@ -414,7 +362,7 @@ public class LocalSession {
public void remember(EditSession editSession) {
FawePlayer fp = editSession.getPlayer();
int limit = fp == null ? Integer.MAX_VALUE : fp.getLimit().MAX_HISTORY;
remember(editSession, true, false, limit);
remember(editSession, true, limit);
}
private FaweChangeSet getChangeSet(Object o) {
@ -424,12 +372,62 @@ public class LocalSession {
return cs;
}
if (o instanceof Integer) {
return new DiskStorageHistory(currentWorld, this.uuid, (Integer) o);
File folder = MainUtil.getFile(Fawe.imp().getDirectory(), Settings.IMP.PATHS.HISTORY + File.separator + Fawe.imp().getWorldName(currentWorld) + File.separator + uuid);
File specific = new File(folder, o.toString());
if (specific.isDirectory()) {
return new AnvilHistory(Fawe.imp().getWorldName(currentWorld), specific);
} else {
return new DiskStorageHistory(currentWorld, this.uuid, (Integer) o);
}
}
return null;
}
public synchronized void remember(final EditSession editSession, final boolean append, final boolean sendMessage, int limitMb) {
public synchronized void remember(Player player, World world, ChangeSet changeSet, FaweLimit limit) {
if (Settings.IMP.HISTORY.USE_DISK) {
LocalSession.MAX_HISTORY_SIZE = Integer.MAX_VALUE;
}
if (changeSet.size() == 0) {
return;
}
loadSessionHistoryFromDisk(player.getUniqueId(), world);
if (changeSet instanceof FaweChangeSet) {
int size = getHistoryNegativeIndex();
ListIterator<Object> iter = history.listIterator();
int i = 0;
int cutoffIndex = history.size() - getHistoryNegativeIndex();
while (iter.hasNext()) {
Object item = iter.next();
if (++i > cutoffIndex) {
FaweChangeSet oldChangeSet;
if (item instanceof FaweChangeSet) {
oldChangeSet = (FaweChangeSet) item;
} else {
oldChangeSet = getChangeSet(item);
}
historySize -= MainUtil.getSize(oldChangeSet);
iter.remove();
}
}
}
historySize += MainUtil.getSize(changeSet);
history.add(changeSet);
if (getHistoryNegativeIndex() != 0) {
setDirty();
historyNegativeIndex = 0;
}
if (limit != null) {
int limitMb = limit.MAX_HISTORY;
while (((!Settings.IMP.HISTORY.USE_DISK && history.size() > MAX_HISTORY_SIZE) || (historySize >> 20) > limitMb) && history.size() > 1) {
FaweChangeSet item = (FaweChangeSet) history.remove(0);
item.delete();
long size = MainUtil.getSize(item);
historySize -= size;
}
}
}
public synchronized void remember(final EditSession editSession, final boolean append, int limitMb) {
if (Settings.IMP.HISTORY.USE_DISK) {
LocalSession.MAX_HISTORY_SIZE = Integer.MAX_VALUE;
}