Various minor

Fix nukkit compile
Fix setBlocks with BlockPattern
MCAQueue filterCopy API (performs operations on a copy of the world)
Add //anvil trimallplots
This commit is contained in:
Jesse Boyd 2017-07-05 18:53:05 +10:00
parent 3c44e75139
commit 7f01ac7790
No known key found for this signature in database
GPG Key ID: 59F1DE6293AF6E1F
16 changed files with 707 additions and 122 deletions

View File

@ -69,6 +69,7 @@ if ( project.hasProperty("lzNoVersion") ) { // gradle build -PlzNoVersion
description = """FastAsyncWorldEdit"""
subprojects {
apply plugin: 'java'
apply plugin: 'maven'
apply plugin: 'eclipse'
@ -79,10 +80,11 @@ subprojects {
repositories {
mavenCentral()
maven {url "http://repo.dmulloy2.net/content/groups/public/"}
maven {url "https://repo.destroystokyo.com/repository/maven-public//"}
maven { url = "https://mvnrepository.com/artifact/"}
maven {url "http://ci.emc.gs/nexus/content/groups/aikar/" }
maven {url "http://ci.mengcraft.com:8080/plugin/repository/everything/"}
maven {url "http://ci.mengcraft.com:8080/plugin/repository/everything"}
maven {url "http://ci.athion.net/job/PlotSquared/ws/mvn/"}
maven {url "http://empcraft.com/maven2"}
maven {url "https://hub.spigotmc.org/nexus/content/groups/public/"}
@ -90,7 +92,7 @@ subprojects {
maven {url "http://nexus.hc.to/content/repositories/pub_releases"}
maven {url "http://repo.maven.apache.org/maven2"}
maven {url "http://ci.frostcast.net/plugin/repository/everything"}
maven {url "http://maven.sk89q.com/artifactory/repo/"}
maven {url "http://maven.sk89q.com/artifactory/repo"}
maven {url "http://repo.spongepowered.org/maven"}
}
}

View File

@ -25,6 +25,7 @@ dependencies {
compile 'org.bukkit.craftbukkit.v1_9R2:craftbukkitv1_9R2:1.9.4'
compile 'org.bukkit.craftbukkit:Craftbukkit:1.7.10'
compile 'org.bukkit.craftbukkit:CraftBukkit:1.8.8'
compile 'com.comphenix.protocol:ProtocolLib-API:4.3.1-SNAPSHOT'
}
processResources {

View File

@ -0,0 +1,85 @@
package com.boydti.fawe.bukkit.v1_12;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.jnbt.anvil.MCAChunk;
import com.boydti.fawe.object.FaweOutputStream;
import com.boydti.fawe.object.io.FastByteArrayOutputStream;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.wrappers.nbt.NbtBase;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import net.minecraft.server.v1_12_R1.Block;
import net.minecraft.server.v1_12_R1.DataBits;
import net.minecraft.server.v1_12_R1.MathHelper;
public class FaweChunkPacket {
private final MCAChunk chunk;
private final boolean full;
private final boolean biomes;
private final boolean sky;
public FaweChunkPacket(MCAChunk fc, boolean replaceAllSections, boolean sendBiomeData, boolean hasSky) {
this.chunk = fc;
this.full = replaceAllSections;
this.biomes = sendBiomeData;
this.sky = hasSky;
}
public void write(PacketContainer packet) throws IOException {
try {
StructureModifier<Integer> ints = packet.getIntegers();
StructureModifier<byte[]> byteArray = packet.getByteArrays();
StructureModifier<Boolean> bools = packet.getBooleans();
ints.write(0, this.chunk.getX());
ints.write(1, this.chunk.getZ());
bools.write(0, this.full);
ints.write(2, this.chunk.getBitMask()); // writeVarInt
FastByteArrayOutputStream fbaos = new FastByteArrayOutputStream();
FaweOutputStream buffer = new FaweOutputStream(fbaos);
byte[][] ids = chunk.ids;
for (int layer = 0; layer < ids.length; layer++) {
byte[] layerIds = ids[layer];
if (layerIds == null) {
continue;
}
byte[] layerData = chunk.data[layer];
int num = MathHelper.d(Block.REGISTRY_ID.a());
buffer.write(num); // num blocks, anything > 8 - doesn't need to be accurate
buffer.writeVarInt(0); // varint 0 - data palette global
DataBits bits = new DataBits(num, 4096);
for (int i = 0; i < 4096; i++) {
int id = layerIds[i];
if (id != 0) {
int data = FaweCache.hasData(id) ? chunk.getNibble(i, layerData) : 0;
int combined = FaweCache.getCombined(id, data);
bits.a(i, combined);
}
}
buffer.write(bits.a());
buffer.write(chunk.blockLight[layer]);
if (sky) {
buffer.write(chunk.skyLight[layer]);
}
}
if (this.biomes && chunk.biomes != null) {
buffer.write(chunk.biomes);
}
byteArray.write(0, fbaos.toByteArray());
// TODO - empty
StructureModifier<List<NbtBase<?>>> list = packet.getListNbtModifier();
list.write(0, new ArrayList<>());
} catch (Throwable e) {
e.printStackTrace();
}
}
}

View File

@ -11,8 +11,10 @@ import com.boydti.fawe.jnbt.anvil.MCAFilterCounter;
import com.boydti.fawe.jnbt.anvil.MCAQueue;
import com.boydti.fawe.jnbt.anvil.filters.CountFilter;
import com.boydti.fawe.jnbt.anvil.filters.CountIdFilter;
import com.boydti.fawe.jnbt.anvil.filters.DeleteOldFilter;
import com.boydti.fawe.jnbt.anvil.filters.DeleteUninhabitedFilter;
import com.boydti.fawe.jnbt.anvil.filters.MappedReplacePatternFilter;
import com.boydti.fawe.jnbt.anvil.filters.PlotTrimFilter;
import com.boydti.fawe.jnbt.anvil.filters.RemoveLayerFilter;
import com.boydti.fawe.jnbt.anvil.filters.ReplacePatternFilter;
import com.boydti.fawe.jnbt.anvil.filters.ReplaceSimpleFilter;
@ -21,6 +23,7 @@ import com.boydti.fawe.object.FaweQueue;
import com.boydti.fawe.object.RegionWrapper;
import com.boydti.fawe.object.RunnableVal4;
import com.boydti.fawe.object.mask.FaweBlockMatcher;
import com.boydti.fawe.util.MainUtil;
import com.boydti.fawe.util.SetQueue;
import com.boydti.fawe.util.StringMan;
import com.sk89q.minecraft.util.commands.Command;
@ -77,15 +80,23 @@ public class AnvilCommands {
* @param <T>
* @return
*/
public static <G, T extends MCAFilter<G>> T runWithWorld(Player player, String folder, T filter) {
public static <G, T extends MCAFilter<G>> T runWithWorld(Player player, String folder, T filter, boolean force) {
boolean copy = false;
if (FaweAPI.getWorld(folder) != null) {
if (!force) {
BBC.WORLD_IS_LOADED.send(player);
return null;
}
copy = true;
}
FaweQueue defaultQueue = SetQueue.IMP.getNewQueue(folder, true, false);
MCAQueue queue = new MCAQueue(folder, defaultQueue.getSaveFolder(), defaultQueue.hasSky());
if (copy) {
return queue.filterCopy(filter, true);
} else {
return queue.filterWorld(filter);
}
}
/**
* Run safely on an existing world within a selection
@ -132,7 +143,7 @@ public class AnvilCommands {
max = 4
)
@CommandPermissions("worldedit.anvil.replaceall")
public void replaceAll(Player player, String folder, @Optional String from, String to, @Switch('d') boolean useData) throws WorldEditException {
public void replaceAll(Player player, String folder, @Optional String from, String to, @Switch('d') boolean useData, @Switch('f') boolean force) throws WorldEditException {
final FaweBlockMatcher matchFrom;
if (from == null) {
matchFrom = FaweBlockMatcher.NOT_AIR;
@ -144,21 +155,60 @@ public class AnvilCommands {
}
final FaweBlockMatcher matchTo = FaweBlockMatcher.setBlocks(worldEdit.getBlocks(player, to, true));
ReplaceSimpleFilter filter = new ReplaceSimpleFilter(matchFrom, matchTo);
ReplaceSimpleFilter result = runWithWorld(player, folder, filter);
ReplaceSimpleFilter result = runWithWorld(player, folder, filter, force);
if (result != null) player.print(BBC.getPrefix() + BBC.VISITOR_BLOCK.format(result.getTotal()));
}
@Command(
aliases = {"deleteallold"},
aliases = {"deleteallunvisited", "delunvisited" },
usage = "<folder> <age-ticks> [file-age=60000]",
desc = "Delete all chunks which haven't been occupied for `age-ticks` and have been accessed since `file-age` (ms) after creation",
desc = "Delete all chunks which haven't been occupied for `age-ticks` (20t = 1s) and \n" +
"Have not been accessed since `file-duration` (ms) after creation and\n" +
"Have not been used in the past `chunk-inactivity` (ms)" +
"The auto-save interval is the recommended value for `file-duration` and `chunk-inactivity`",
min = 2,
max = 3
)
@CommandPermissions("worldedit.anvil.deleteallold")
public void deleteAllOld(Player player, String folder, int inhabitedTicks, @Optional("60000") int fileAgeMillis, @Switch('f') boolean force) throws WorldEditException {
DeleteUninhabitedFilter filter = new DeleteUninhabitedFilter(fileAgeMillis, inhabitedTicks);
DeleteUninhabitedFilter result = runWithWorld(player, folder, filter);
@CommandPermissions("worldedit.anvil.deleteallunvisited")
public void deleteAllUnvisited(Player player, String folder, int inhabitedTicks, @Optional("60000") int fileDurationMillis, @Switch('f') boolean force) throws WorldEditException {
long chunkInactivityMillis = fileDurationMillis; // Use same value for now
DeleteUninhabitedFilter filter = new DeleteUninhabitedFilter(fileDurationMillis, inhabitedTicks, chunkInactivityMillis);
DeleteUninhabitedFilter result = runWithWorld(player, folder, filter, force);
if (result != null) player.print(BBC.getPrefix() + BBC.VISITOR_BLOCK.format(result.getTotal()));
}
@Command(
aliases = {"deletealloldregions", "deloldreg" },
usage = "<folder> <time>",
desc = "Delete regions which haven't been accessed in a certain amount of time\n" +
"You can use seconds (s), minutes (m), hours (h), days (d), weeks (w), years (y)\n" +
"(months are not a unit of time)\n" +
"E.g. 8h5m12s\n",
min = 2,
max = 3
)
@CommandPermissions("worldedit.anvil.deletealloldregions")
public void deleteAllOldRegions(Player player, String folder, String time, @Switch('f') boolean force) throws WorldEditException {
long duration = MainUtil.timeToSec(time) * 1000l;
DeleteOldFilter filter = new DeleteOldFilter(duration);
DeleteOldFilter result = runWithWorld(player, folder, filter, force);
if (result != null) player.print(BBC.getPrefix() + BBC.VISITOR_BLOCK.format(result.getTotal()));
}
@Command(
aliases = {"trimallplots", },
desc = "Trim chunks in a Plot World",
help = "Trim chunks in a Plot World\n" +
"Unclaimed chunks will be deleted\n" +
"Unmodified chunks will be deleted\n" +
"Use -v to also delete unvisited chunks\n"
)
@CommandPermissions("worldedit.anvil.trimallplots")
public void trimAllPlots(Player player, @Switch('v') boolean deleteUnvisited) throws WorldEditException {
String folder = Fawe.imp().getWorldName(player.getWorld());
int visitTime = deleteUnvisited ? 1 : -1;
PlotTrimFilter filter = new PlotTrimFilter(player.getWorld(), 0, visitTime, 600000);
PlotTrimFilter result = runWithWorld(player, folder, filter, true);
if (result != null) player.print(BBC.getPrefix() + BBC.VISITOR_BLOCK.format(result.getTotal()));
}
@ -171,7 +221,7 @@ public class AnvilCommands {
max = 4
)
@CommandPermissions("worldedit.anvil.replaceall")
public void replaceAllPattern(Player player, String folder, @Optional String from, final Pattern to, @Switch('d') boolean useData, @Switch('m') boolean useMap) throws WorldEditException {
public void replaceAllPattern(Player player, String folder, @Optional String from, final Pattern to, @Switch('d') boolean useData, @Switch('m') boolean useMap, @Switch('f') boolean force) throws WorldEditException {
MCAFilterCounter filter;
if (useMap) {
if (to instanceof RandomPattern) {
@ -190,7 +240,7 @@ public class AnvilCommands {
}
filter = new ReplacePatternFilter(matchFrom, to);
}
MCAFilterCounter result = runWithWorld(player, folder, filter);
MCAFilterCounter result = runWithWorld(player, folder, filter, force);
if (result != null) player.print(BBC.getPrefix() + BBC.VISITOR_BLOCK.format(result.getTotal()));
}
@ -203,7 +253,7 @@ public class AnvilCommands {
max = 3
)
@CommandPermissions("worldedit.anvil.countall")
public void countAll(Player player, EditSession editSession, String folder, String arg, @Switch('d') boolean useData) throws WorldEditException {
public void countAll(Player player, EditSession editSession, String folder, String arg, @Switch('d') boolean useData, @Switch('f') boolean force) throws WorldEditException {
Set<BaseBlock> searchBlocks = worldEdit.getBlocks(player, arg, true);
MCAFilterCounter filter;
if (useData || arg.contains(":")) { // Optimize for both cases
@ -215,7 +265,7 @@ public class AnvilCommands {
searchBlocks.forEach(counter::addBlock);
filter = counter;
}
MCAFilterCounter result = runWithWorld(player, folder, filter);
MCAFilterCounter result = runWithWorld(player, folder, filter, force);
if (result != null) player.print(BBC.getPrefix() + BBC.SELECTION_COUNT.format(result.getTotal()));
}

View File

@ -30,7 +30,7 @@ public class Settings extends Config {
public String LANGUAGE = "";
@Comment("Allow the plugin to update")
public boolean UPDATE = true;
@Comment("Send anonymous usage statistics to MCStats.org")
@Comment("Send anonymous usage statistics to mcstats.org")
public boolean METRICS = true;
@Comment("FAWE will skip chunks when there's not enough memory available")
public boolean PREVENT_CRASHES = false;

View File

@ -94,7 +94,9 @@ public class MCAFile {
e.printStackTrace();
}
}
synchronized (chunks) {
chunks.clear();
}
locations = null;
}
@ -166,8 +168,10 @@ public class MCAFile {
int cx = chunk.getX();
int cz = chunk.getZ();
int pair = MathMan.pair((short) (cx & 31), (short) (cz & 31));
synchronized (chunks) {
chunks.put(pair, chunk);
}
}
public MCAChunk getChunk(int cx, int cz) throws IOException {
MCAChunk cached = getCachedChunk(cx, cz);
@ -275,7 +279,10 @@ public class MCAFile {
}
public List<Integer> getChunks() {
final List<Integer> values = new ArrayList<>(chunks.size());
final List<Integer> values;
synchronized (chunks) {
values = new ArrayList<>(chunks.size());
}
for (int i = 0; i < locations.length; i += 4) {
int offset = (((locations[i] & 0xFF) << 16) + ((locations[i + 1] & 0xFF) << 8) + ((locations[i + 2] & 0xFF)));
values.add(offset);
@ -433,6 +440,21 @@ public class MCAFile {
}
}
public boolean isModified() {
if (isDeleted()) {
return true;
}
synchronized (chunks) {
for (Int2ObjectMap.Entry<MCAChunk> entry : chunks.int2ObjectEntrySet()) {
MCAChunk chunk = entry.getValue();
if (chunk.isModified() || chunk.isDeleted()) {
return true;
}
}
}
return false;
}
public void flush(ForkJoinPool pool) {
synchronized (raf) {
boolean wait;
@ -445,9 +467,11 @@ public class MCAFile {
final Int2ObjectOpenHashMap<byte[]> compressedMap = new Int2ObjectOpenHashMap<>();
final Int2ObjectOpenHashMap<byte[]> append = new Int2ObjectOpenHashMap<>();
boolean modified = false;
long now = System.currentTimeMillis();
for (MCAChunk chunk : getCachedChunks()) {
if (chunk.isModified() || chunk.isDeleted()) {
modified = true;
chunk.setLastUpdate(now);
if (!chunk.isDeleted()) {
pool.submit(new Runnable() {
@Override

View File

@ -2,6 +2,8 @@ package com.boydti.fawe.jnbt.anvil;
import com.boydti.fawe.object.collection.IterableThreadLocal;
import com.sk89q.worldedit.blocks.BaseBlock;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributes;
/**
* MCAQueue.filterWorld(MCAFilter)<br>
@ -9,6 +11,10 @@ import com.sk89q.worldedit.blocks.BaseBlock;
*/
public class MCAFilter<T> extends IterableThreadLocal<T> {
public boolean appliesFile(Path path, BasicFileAttributes attr) {
return true;
}
/**
* Check whether a .mca file should be read
*

View File

@ -8,16 +8,20 @@ import com.boydti.fawe.object.FaweChunk;
import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.FaweQueue;
import com.boydti.fawe.object.RegionWrapper;
import com.boydti.fawe.object.RunnableVal;
import com.boydti.fawe.object.RunnableVal2;
import com.boydti.fawe.object.RunnableVal4;
import com.boydti.fawe.object.collection.IterableThreadLocal;
import com.boydti.fawe.util.MainUtil;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.world.biome.BaseBiome;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Collection;
import java.util.Map;
@ -269,8 +273,104 @@ public class MCAQueue extends NMSMappedFaweQueue<FaweQueue, FaweChunk, FaweChunk
pool.shutdown();
}
public <G, T extends MCAFilter<G>> T filterCopy(final T filter, boolean deleteOnCopyFail) {
this.filterWorld(new MCAFilter<G>() {
@Override
public boolean appliesFile(int mcaX, int mcaZ) {
return filter.appliesFile(mcaX, mcaZ);
}
@Override
public boolean appliesFile(Path path, BasicFileAttributes attr) {
return filter.appliesFile(path, attr);
}
@Override
public MCAFile applyFile(MCAFile mca) {
File file = mca.getFile();
File copyDest = new File(file.getParentFile(), file.getName() + "-copy");
try {
Files.copy(file.toPath(), copyDest.toPath(), StandardCopyOption.REPLACE_EXISTING);
MCAFile copy = new MCAFile(mca.getParent(), copyDest);
MCAFile result = filter.applyFile(copy);
if (result == null) {
if (copy.isDeleted()) {
copy.clear();
result.clear();
if (file.exists()) {
file.delete();
}
if (copyDest.exists()) {
if (!copyDest.delete()) {
copyDest.deleteOnExit();
}
}
} else if (copy.isModified()) {
if (copyDest.exists()) {
copy.clear();
file.delete();
if (!copyDest.renameTo(file) && deleteOnCopyFail) {
if (!copyDest.delete()) {
copyDest.deleteOnExit();
}
}
}
} else {
copy.clear();
if (!copyDest.delete()) {
copyDest.deleteOnExit();
}
}
}
return result;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
@Override
public boolean appliesChunk(int cx, int cz) {
return filter.appliesChunk(cx, cz);
}
@Override
public MCAChunk applyChunk(MCAChunk chunk, G cache) {
return filter.applyChunk(chunk, cache);
}
@Override
public void applyBlock(int x, int y, int z, BaseBlock block, G cache) {
filter.applyBlock(x, y, z, block, cache);
}
}, true, new RunnableVal<MCAFile>() {
@Override
public void run(MCAFile value) {
if (deleteOnCopyFail) {
File file = value.getFile();
boolean result = file.delete();
if (!result) {
file.deleteOnExit();
}
Fawe.debug("Deleted " + file + " = " + result);
}
}
});
return filter;
}
public <G, T extends MCAFilter<G>> T filterRegion(final T filter, final RegionWrapper region) {
this.filterWorld(new MCAFilter<G>() {
@Override
public boolean appliesFile(Path path, BasicFileAttributes attr) {
String name = path.getFileName().toString();
String[] split = name.split("\\.");
final int mcaX = Integer.parseInt(split[1]);
final int mcaZ = Integer.parseInt(split[2]);
return region.isInMCA(mcaX, mcaZ) && filter.appliesFile(path, attr);
}
@Override
public boolean appliesFile(int mcaX, int mcaZ) {
return region.isInMCA(mcaX, mcaZ) && filter.appliesFile(mcaX, mcaZ);
@ -356,6 +456,10 @@ public class MCAQueue extends NMSMappedFaweQueue<FaweQueue, FaweChunk, FaweChunk
}
public <G, T extends MCAFilter<G>> T filterWorld(final T filter) {
return filterWorld(filter, false, null);
}
private <G, T extends MCAFilter<G>> T filterWorld(final T filter, boolean replaceOriginalOnCopy, RunnableVal<MCAFile> onReplaceFail) {
File folder = getSaveFolder();
final ForkJoinPool pool = new ForkJoinPool();
MainUtil.traverse(folder.toPath(), new RunnableVal2<Path, BasicFileAttributes>() {
@ -363,15 +467,20 @@ public class MCAQueue extends NMSMappedFaweQueue<FaweQueue, FaweChunk, FaweChunk
public void run(Path path, BasicFileAttributes attr) {
try {
String name = path.getFileName().toString();
if (!name.endsWith(".mca")) {
return;
}
if (!filter.appliesFile(path, attr)) {
return;
}
String[] split = name.split("\\.");
final int mcaX = Integer.parseInt(split[1]);
final int mcaZ = Integer.parseInt(split[2]);
if (filter.appliesFile(mcaX, mcaZ)) {
File file = path.toFile();
Fawe.debug("Apply file " + file);
MCAFile mcaFile = new MCAFile(MCAQueue.this, file);
final MCAFile original = mcaFile;
final MCAFile finalFile = filter.applyFile(mcaFile);
final MCAFile original = new MCAFile(MCAQueue.this, file);
final MCAFile finalFile = filter.applyFile(original);
if (finalFile != null && !finalFile.isDeleted()) {
finalFile.init();
// May not do anything, but seems to lead to smaller lag spikes
@ -428,10 +537,7 @@ public class MCAQueue extends NMSMappedFaweQueue<FaweQueue, FaweChunk, FaweChunk
});
}
});
pool.awaitQuiescence(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
original.close(pool);
if (original != finalFile) finalFile.close(pool);
} else if (mcaFile.isDeleted()) {
} else if (original.isDeleted()) {
try {
original.close(pool);
file.delete();
@ -439,6 +545,36 @@ public class MCAQueue extends NMSMappedFaweQueue<FaweQueue, FaweChunk, FaweChunk
ignore.printStackTrace();
}
}
pool.awaitQuiescence(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
original.close(pool);
if (original.isDeleted()) {
file.delete();
}
if (finalFile != null) {
if (original != finalFile) {
if (finalFile.isModified()) {
finalFile.close(pool);
if (finalFile.isDeleted()) {
finalFile.getFile().delete();
if (replaceOriginalOnCopy && file.exists()) {
file.delete();
}
} else if (replaceOriginalOnCopy) {
File from = finalFile.getFile();
file.delete();
if (!from.renameTo(file)) {
Fawe.debug("Could not rename " + from + "to " + file + ".");
if (onReplaceFail != null) {
onReplaceFail.run(finalFile);
}
}
}
} else if (replaceOriginalOnCopy) {
finalFile.clear();
finalFile.getFile().delete();
}
}
}
}
} catch (Throwable ignore) {
ignore.printStackTrace();

View File

@ -0,0 +1,28 @@
package com.boydti.fawe.jnbt.anvil.filters;
import com.boydti.fawe.jnbt.anvil.MCAFilterCounter;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributes;
public class DeleteOldFilter extends MCAFilterCounter {
private final long time;
public DeleteOldFilter(long time) {
this.time = time;
if (time < 1) {
throw new IllegalArgumentException("Time must be positive");
}
}
@Override
public boolean appliesFile(Path path, BasicFileAttributes attr) {
long modified = attr.lastModifiedTime().toMillis();
long access = attr.lastAccessTime().toMillis();
long last = Math.max(modified, access);
if (last != 0 && System.currentTimeMillis() - last > this.time) {
path.toFile().delete();
get().add(512 * 512 * 256);
}
return false;
}
}

View File

@ -10,7 +10,7 @@ import com.boydti.fawe.object.RunnableVal4;
import com.boydti.fawe.object.exception.FaweException;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.TimeUnit;
@ -21,31 +21,53 @@ import java.util.concurrent.TimeUnit;
*/
public class DeleteUninhabitedFilter extends MCAFilterCounter {
private final long inhabitedTicks;
private final long fileAgeMillis;
private final long fileDurationMillis;
private final long cutoffChunkAgeEpoch;
public DeleteUninhabitedFilter(long fileAgeMillis, long inhabitedTicks) {
this.fileAgeMillis = fileAgeMillis;
public DeleteUninhabitedFilter(long fileDurationMillis, long inhabitedTicks, long chunkInactivityMillis) {
this.fileDurationMillis = fileDurationMillis;
this.inhabitedTicks = inhabitedTicks;
this.cutoffChunkAgeEpoch = System.currentTimeMillis() - chunkInactivityMillis;
}
public long getInhabitedTicks() {
return inhabitedTicks;
}
public long getFileAgeMillis() {
return fileAgeMillis;
public long getFileDurationMillis() {
return fileDurationMillis;
}
public long getCutoffChunkAgeEpoch() {
return cutoffChunkAgeEpoch;
}
@Override
public boolean appliesFile(Path path, BasicFileAttributes attr) {
String name = path.getFileName().toString();
String[] split = name.split("\\.");
final int mcaX = Integer.parseInt(split[1]);
final int mcaZ = Integer.parseInt(split[2]);
File file = path.toFile();
long lastModified = attr.lastModifiedTime().toMillis();
if (lastModified > cutoffChunkAgeEpoch) {
return false;
}
try {
if (shouldDelete(file, attr, mcaX, mcaZ)) {
file.delete();
get().add(512 * 512 * 256);
return false;
}
} catch (IOException e) {
e.printStackTrace();
return false;
}
return true;
}
@Override
public MCAFile applyFile(MCAFile mca) {
try {
if (shouldDelete(mca)) {
mca.setDeleted(true);
get().add(512 * 512 * 256);
return null;
}
} catch (IOException | UnsupportedOperationException ignore) {
}
try {
ForkJoinPool pool = new ForkJoinPool();
mca.init();
@ -53,18 +75,19 @@ public class DeleteUninhabitedFilter extends MCAFilterCounter {
pool.awaitQuiescence(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
mca.close(pool);
pool.shutdown();
pool.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
public boolean shouldDelete(MCAFile mca) throws IOException {
File file = mca.getFile();
BasicFileAttributes attr = Files.readAttributes(file.toPath(), BasicFileAttributes.class);
public boolean shouldDelete(File file, BasicFileAttributes attr, int mcaX, int mcaZ) throws IOException {
long creation = attr.creationTime().toMillis();
long modified = attr.lastModifiedTime().toMillis();
if ((modified - creation < fileAgeMillis && modified > creation) || file.length() < 12288) {
if ((modified - creation < fileDurationMillis && modified > creation) || file.length() < 12288) {
return true;
}
return false;
@ -78,7 +101,6 @@ public class DeleteUninhabitedFilter extends MCAFilterCounter {
mca.forEachSortedChunk(new RunnableVal4<Integer, Integer, Integer, Integer>() {
@Override
public void run(Integer x, Integer z, Integer offset, Integer size) {
try {
int bx = mca.getX() << 5;
int bz = mca.getZ() << 5;
if (shouldDeleteChunk(mca, bx, bz)) {
@ -90,8 +112,6 @@ public class DeleteUninhabitedFilter extends MCAFilterCounter {
get().add(16 * 16 * 256);
return;
}
byte[] bytes = mca.getChunkCompressedBytes(offset);
if (bytes == null) return;
Runnable task = new Runnable() {
@Override
public void run() {
@ -109,9 +129,6 @@ public class DeleteUninhabitedFilter extends MCAFilterCounter {
}
};
pool.submit(task);
} catch (IOException e) {
e.printStackTrace();
}
}
});
}

View File

@ -1,8 +1,10 @@
package com.boydti.fawe.jnbt.anvil.filters;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.jnbt.anvil.MCAChunk;
import com.boydti.fawe.jnbt.anvil.MCAFile;
import com.boydti.fawe.object.RunnableVal;
import com.boydti.fawe.object.RunnableVal4;
import com.boydti.fawe.object.collection.LongHashSet;
import com.intellectualcrafters.plot.PS;
import com.intellectualcrafters.plot.generator.HybridGen;
import com.intellectualcrafters.plot.generator.HybridPlotWorld;
@ -11,8 +13,12 @@ import com.intellectualcrafters.plot.object.Location;
import com.intellectualcrafters.plot.object.Plot;
import com.intellectualcrafters.plot.object.PlotArea;
import com.intellectualcrafters.plot.util.expiry.ExpireManager;
import com.sk89q.worldguard.util.collect.LongHashSet;
import com.sk89q.worldedit.world.World;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.concurrent.ForkJoinPool;
@ -32,27 +38,37 @@ public class PlotTrimFilter extends DeleteUninhabitedFilter {
return false;
}
public PlotTrimFilter(PlotArea area, long fileAgeMillis, long inhabitedTicks) {
super(fileAgeMillis, inhabitedTicks);
public PlotTrimFilter(World world, long fileDuration, long inhabitedTicks, long chunkInactivity) {
super(fileDuration, inhabitedTicks, chunkInactivity);
String worldName = Fawe.imp().getWorldName(world);
PlotArea area = PS.get().getPlotAreaByString(worldName);
IndependentPlotGenerator gen = area.getGenerator();
if (!(area instanceof HybridPlotWorld) || !(gen instanceof HybridGen)) {
throw new UnsupportedOperationException("Trim does not support non hybrid plot worlds");
}
this.hg = (HybridGen) gen;
this.hpw = (HybridPlotWorld) area;
if (hpw.PLOT_BEDROCK && !hpw.PLOT_SCHEMATIC && hpw.MAIN_BLOCK.length == 1 && hpw.TOP_BLOCK.length == 1) {
this.reference = new MCAChunk(null, 0, 0);
this.reference.fillCuboid(0, 15, 0, 0, 0, 15, 7, (byte) 0);
this.reference.fillCuboid(0, 15, 1, hpw.PLOT_HEIGHT - 1, 0, 15, hpw.MAIN_BLOCK[0].id, (byte) 0);
this.reference.fillCuboid(0, 15, hpw.PLOT_HEIGHT, hpw.PLOT_HEIGHT, 0, 15, hpw.TOP_BLOCK[0].id, (byte) 0);
} else {
this.reference = null;
if (!hpw.PLOT_BEDROCK || hpw.PLOT_SCHEMATIC || hpw.MAIN_BLOCK.length != 1 || hpw.TOP_BLOCK.length != 1) {
throw new UnsupportedOperationException("WIP - will implement later");
}
this.occupiedRegions = new LongHashSet();
this.unoccupiedChunks = new LongHashSet();
ArrayList<Plot> plots = new ArrayList<>();
plots.addAll(PS.get().getPlots(area));
this.reference = calculateReference();
this.calculateClaimedArea();
}
private MCAChunk calculateReference() {
MCAChunk reference = new MCAChunk(null, 0, 0);
reference.fillCuboid(0, 15, 0, 0, 0, 15, 7, (byte) 0);
reference.fillCuboid(0, 15, 1, hpw.PLOT_HEIGHT - 1, 0, 15, hpw.MAIN_BLOCK[0].id, (byte) 0);
reference.fillCuboid(0, 15, hpw.PLOT_HEIGHT, hpw.PLOT_HEIGHT, 0, 15, hpw.TOP_BLOCK[0].id, (byte) 0);
return reference;
}
private void calculateClaimedArea() {
ArrayList<Plot> plots = new ArrayList<>(hpw.getPlots());
if (ExpireManager.IMP != null) {
plots.removeAll(ExpireManager.IMP.getPendingExpired());
}
@ -65,13 +81,12 @@ public class PlotTrimFilter extends DeleteUninhabitedFilter {
int ccz2 = pos2.getZ() >> 9;
for (int x = ccx1; x <= ccx2; x++) {
for (int z = ccz1; z <= ccz2; z++) {
if (!occupiedRegions.containsKey(x, z)) {
occupiedRegions.add(x, z);
int bcx = x << 5;
int bcz = z << 5;
int tcx = bcx + 32;
int tcz = bcz + 32;
if (!occupiedRegions.containsKey(x, z)) {
occupiedRegions.add(x, z);
} else {
for (int cz = bcz; cz < tcz; cz++) {
for (int cx = bcx; cx < tcx; cx++) {
unoccupiedChunks.add(cx, cz);
@ -84,8 +99,8 @@ public class PlotTrimFilter extends DeleteUninhabitedFilter {
int cz1 = pos1.getZ() >> 4;
int cx2 = pos2.getX() >> 4;
int cz2 = pos2.getZ() >> 4;
for (int cz = cx1; cz < cx2; cz++) {
for (int cx = cz1; cx < cz2; cx++) {
for (int cz = cz1; cz <= cz2; cz++) {
for (int cx = cx1; cx <= cx2; cx++) {
unoccupiedChunks.remove(cx, cz);
}
}
@ -93,15 +108,13 @@ public class PlotTrimFilter extends DeleteUninhabitedFilter {
}
@Override
public boolean shouldDelete(MCAFile mca) throws IOException {
int x = mca.getX();
int z = mca.getZ();
return !occupiedRegions.containsKey(x, z) || super.shouldDelete(mca);
public boolean shouldDelete(File file, BasicFileAttributes attr, int mcaX, int mcaZ) throws IOException {
return !occupiedRegions.containsKey(mcaX, mcaZ) || super.shouldDelete(file, attr, mcaX, mcaZ);
}
@Override
public boolean shouldDeleteChunk(MCAFile mca, int cx, int cz) {
return !unoccupiedChunks.containsKey(cx, cz);
return unoccupiedChunks.containsKey(cx, cz);
}
@Override
@ -110,17 +123,47 @@ public class PlotTrimFilter extends DeleteUninhabitedFilter {
super.filter(mca, pool);
return;
}
mca.forEachChunk(new RunnableVal<MCAChunk>() {
Path file = mca.getFile().toPath();
BasicFileAttributes attr = Files.readAttributes(file, BasicFileAttributes.class);
long creationDate = attr.creationTime().toMillis();
mca.forEachSortedChunk(new RunnableVal4<Integer, Integer, Integer, Integer>() {
@Override
public void run(MCAChunk value) {
if (value.getInhabitedTime() < getInhabitedTicks()) {
value.setDeleted(true);
public void run(Integer x, Integer z, Integer offset, Integer size) {
int bx = mca.getX() << 5;
int bz = mca.getZ() << 5;
int cx = bx + x;
int cz = bz + z;
if (shouldDeleteChunk(mca, cx, cz)) {
MCAChunk chunk = new MCAChunk(null, x, z);
chunk.setDeleted(true);
synchronized (mca) {
mca.setChunk(chunk);
}
get().add(16 * 16 * 256);
return;
}
if (reference.idsEqual(value, false)) {
value.setDeleted(true);
Runnable task = new Runnable() {
@Override
public void run() {
try {
MCAChunk chunk = mca.getChunk(x, z);
if (chunk.getInhabitedTime() <= getInhabitedTicks()) {
chunk.setDeleted(true);
get().add(16 * 16 * 256);
return;
}
if (reference.idsEqual(chunk, false)) {
chunk.setDeleted(true);
get().add(16 * 16 * 256);
return;
}
} catch (IOException e) {
e.printStackTrace();
}
}
};
pool.submit(task);
}
});
}

View File

@ -36,6 +36,23 @@ public class FaweOutputStream extends DataOutputStream {
write((byte) (m));
}
public void writeVarInt(int i) throws IOException {
while((i & -128) != 0) {
this.writeByte(i & 127 | 128);
i >>>= 7;
}
this.writeByte(i);
}
public void write(long[] data) throws IOException {
this.writeVarInt(data.length);
for(int j = 0; j < data.length; ++j) {
this.writeLong(data[j]);
}
}
private NBTOutputStream nbtOut;
public void writeNBT(String name, Tag tag) throws IOException {

View File

@ -0,0 +1,178 @@
/*
* WorldGuard, a suite of tools for Minecraft
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldGuard team and contributors
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.boydti.fawe.object.collection;
import java.util.Arrays;
public class LongHashSet {
protected long[][][] values = new long[256][][];
protected int count = 0;
public static long toLong(int msw, int lsw) {
return ((long) msw << 32) + lsw - Integer.MIN_VALUE;
}
public static int msw(long l) {
return (int) (l >> 32);
}
public static int lsw(long l) {
return (int) (l & 0xFFFFFFFF) + Integer.MIN_VALUE;
}
public boolean containsKey(int msw, int lsw) {
return containsKey(toLong(msw, lsw));
}
public void remove(int msw, int lsw) {
remove(toLong(msw, lsw));
}
public boolean isEmpty() {
return this.count == 0;
}
public int size() {
return count;
}
public void add(int msw, int lsw) {
add(toLong(msw, lsw));
}
public void add(long key) {
int mainIdx = (int) (key & 255);
long outer[][] = this.values[mainIdx];
if (outer == null) this.values[mainIdx] = outer = new long[256][];
int outerIdx = (int) ((key >> 32) & 255);
long inner[] = outer[outerIdx];
if (inner == null) {
synchronized (this) {
outer[outerIdx] = inner = new long[1];
inner[0] = key;
this.count++;
}
} else {
int i;
for (i = 0; i < inner.length; i++) {
if (inner[i] == key) {
return;
}
}
inner = Arrays.copyOf(inner, i + 1);
outer[outerIdx] = inner;
inner[i] = key;
this.count++;
}
}
public boolean containsKey(long key) {
long[][] outer = this.values[(int) (key & 255)];
if (outer == null) return false;
long[] inner = outer[(int) ((key >> 32) & 255)];
if (inner == null) return false;
for (long entry : inner) {
if (entry == key) return true;
}
return false;
}
public void remove(long key) {
long[][] outer = this.values[(int) (key & 255)];
if (outer == null) return;
long[] inner = outer[(int) ((key >> 32) & 255)];
if (inner == null) return;
int max = inner.length - 1;
for (int i = 0; i <= max; i++) {
if (inner[i] == key) {
this.count--;
if (i != max) {
inner[i] = inner[max];
}
outer[(int) ((key >> 32) & 255)] = (max == 0 ? null : Arrays.copyOf(inner, max));
return;
}
}
}
public long popFirst() {
for (long[][] outer: this.values) {
if (outer == null) continue;
for (int i = 0; i < outer.length; i++) {
long[] inner = outer[i];
if (inner == null || inner.length == 0) continue;
this.count--;
long ret = inner[inner.length - 1];
outer[i] = Arrays.copyOf(inner, inner.length - 1);
return ret;
}
}
return 0;
}
public long[] popAll() {
int index = 0;
long[] ret = new long[this.count];
for (long[][] outer : this.values) {
if (outer == null) continue;
for (int oIdx = outer.length - 1; oIdx >= 0; oIdx--) {
long[] inner = outer[oIdx];
if (inner == null) continue;
for (long entry: inner) {
ret[index++] = entry;
}
outer[oIdx] = null;
}
}
count = 0;
return ret;
}
public long[] keys() {
int index = 0;
long[] ret = new long[this.count];
for (long[][] outer : this.values) {
if (outer == null) continue;
for (long[] inner : outer) {
if (inner == null) continue;
for (long entry : inner) {
ret[index++] = entry;
}
}
}
return ret;
}
}

View File

@ -1696,7 +1696,7 @@ public class EditSession extends AbstractWorld implements HasFaweQueue, Lighting
checkNotNull(region);
checkNotNull(pattern);
if (pattern instanceof BlockPattern) {
return setBlocks(region, ((BaseBlock) pattern));
return setBlocks(region, ((BlockPattern) pattern).getBlock());
}
if (pattern instanceof BaseBlock) {
return setBlocks(region, (BaseBlock) pattern);

View File

@ -3,7 +3,7 @@ repositories {
}
dependencies {
compile project(':core')
compile 'cn.nukkit:nukkit:1.0-SNAPSHOT'
compile group: "cn.nukkit", name: "nukkit", version: "1.0-20170704.231613-609", changing: true
compile name: 'worldedit-core-6.1.4-SNAPSHOT-dist', changing: true
}

View File

@ -46,14 +46,12 @@ public class FaweNukkit implements IFawe, Listener {
@EventHandler
public void onPlayerQuit(PlayerQuitEvent event) {
Player player = event.getPlayer();
if (!event.getReason().equals("disconnectionScreen.serverFull")) {
FawePlayer fp = FawePlayer.wrap(player);
if (fp != null) {
fp.unregister();
}
Fawe.get().unregister(event.getPlayer().getName());
}
}
@Override
public void debug(String s) {