Anvil replace + masking extent biomes

This commit is contained in:
Jesse Boyd 2017-04-03 20:07:57 +10:00
parent b39ab79f16
commit c7d959d6dc
No known key found for this signature in database
GPG Key ID: 59F1DE6293AF6E1F
39 changed files with 1030 additions and 303 deletions

View File

@ -217,7 +217,7 @@ public class FaweBukkit implements IFawe, Listener {
Settings.IMP.QUEUE.PARALLEL_THREADS = 1; // BukkitAPI placer is too slow to parallel thread at the chunk level
Settings.IMP.HISTORY.COMBINE_STAGES = false; // Performing a chunk copy (if possible) wouldn't be faster using the BukkitAPI
if (hasNMS) {
ignore.printStackTrace();
debug("====== NO NMS BLOCK PLACER FOUND ======");
debug("FAWE couldn't find a fast block placer");
debug("Bukkit version: " + Bukkit.getVersion());
@ -227,6 +227,8 @@ public class FaweBukkit implements IFawe, Listener {
debug("Download the version of FAWE for your platform");
debug(" - http://ci.athion.net/job/FastAsyncWorldEdit/lastSuccessfulBuild/artifact/target");
debug("=======================================");
ignore.printStackTrace();
debug("=======================================");
TaskManager.IMP.laterAsync(new Runnable() {
@Override
public void run() {
@ -262,9 +264,11 @@ public class FaweBukkit implements IFawe, Listener {
} catch (Throwable ignore) {
}
}
Throwable error = null;
try {
return plugin.getQueue(world);
} catch (Throwable ignore) {
error = ignore;
}
// Disable incompatible settings
Settings.IMP.QUEUE.PARALLEL_THREADS = 1; // BukkitAPI placer is too slow to parallel thread at the chunk level
@ -273,12 +277,14 @@ public class FaweBukkit implements IFawe, Listener {
debug("====== NO NMS BLOCK PLACER FOUND ======");
debug("FAWE couldn't find a fast block placer");
debug("Bukkit version: " + Bukkit.getVersion());
debug("NMS label: " + plugin.getClass().getSimpleName().split("_")[1]);
debug("NMS label: " + plugin.getClass().getSimpleName());
debug("Fallback placer: " + BukkitQueue_All.class);
debug("=======================================");
debug("Download the version of FAWE for your platform");
debug(" - http://ci.athion.net/job/FastAsyncWorldEdit/lastSuccessfulBuild/artifact/target");
debug("=======================================");
error.printStackTrace();
debug("=======================================");
TaskManager.IMP.laterAsync(new Runnable() {
@Override
public void run() {

View File

@ -11,11 +11,13 @@ import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.RunnableVal;
import com.boydti.fawe.object.visitor.FaweChunkVisitor;
import com.boydti.fawe.util.MathMan;
import com.boydti.fawe.util.ReflectionUtils;
import com.boydti.fawe.util.TaskManager;
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
import com.sk89q.worldedit.world.biome.BaseBiome;
import java.io.File;
import java.io.RandomAccessFile;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collection;
@ -204,6 +206,11 @@ public abstract class BukkitQueue_0<CHUNK, CHUNKSECTIONS, SECTION> extends NMSMa
private static Field fieldTimingsEnabled;
private static Field fieldAsyncCatcherEnabled;
private static Method methodCheck;
private static Class<?> classRegionFile;
private static Class<?> classRegionFileCache;
private static Field fieldRegionMap;
private static Field fieldRegionFile;
private static Method methodUnloadChunk;
static {
try {
fieldAsyncCatcherEnabled = Class.forName("org.spigotmc.AsyncCatcher").getField("enabled");
@ -215,6 +222,29 @@ public abstract class BukkitQueue_0<CHUNK, CHUNKSECTIONS, SECTION> extends NMSMa
methodCheck = Class.forName("co.aikar.timings.TimingsManager").getDeclaredMethod("recheckEnabled");
methodCheck.setAccessible(true);
} catch (Throwable ignore){}
try {
classRegionFile = ReflectionUtils.getNmsClass("RegionFile");
classRegionFileCache = ReflectionUtils.getNmsClass("RegionFileCache");
for (Field field : classRegionFileCache.getDeclaredFields()) {
if (Map.class.isAssignableFrom(field.getType())) {
fieldRegionMap = field;
fieldRegionMap.setAccessible(true);
break;
}
}
for (Field field : classRegionFile.getDeclaredFields()) {
if (RandomAccessFile.class.isAssignableFrom(field.getType())) {
fieldRegionFile = field;
fieldRegionFile.setAccessible(true);
break;
}
}
Class<?> classCraftWorld = ReflectionUtils.getCbClass("CraftWorld");
methodUnloadChunk = classCraftWorld.getDeclaredMethod("unloadChunk0", int.class, int.class, boolean.class);
methodUnloadChunk.setAccessible(true);
} catch (Throwable ignore) {
ignore.printStackTrace();
}
}
@Override

View File

@ -7,6 +7,7 @@ import com.boydti.fawe.bukkit.v0.BukkitQueue_0;
import com.boydti.fawe.example.CharFaweChunk;
import com.boydti.fawe.object.FaweChunk;
import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.RegionWrapper;
import com.boydti.fawe.object.RunnableVal;
import com.boydti.fawe.object.brush.visualization.VisualChunk;
import com.boydti.fawe.object.number.LongAdder;
@ -26,9 +27,11 @@ import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
@ -57,6 +60,8 @@ import net.minecraft.server.v1_11_R1.PacketPlayOutMapChunk;
import net.minecraft.server.v1_11_R1.PacketPlayOutMultiBlockChange;
import net.minecraft.server.v1_11_R1.PlayerChunk;
import net.minecraft.server.v1_11_R1.PlayerChunkMap;
import net.minecraft.server.v1_11_R1.RegionFile;
import net.minecraft.server.v1_11_R1.RegionFileCache;
import net.minecraft.server.v1_11_R1.ServerNBTManager;
import net.minecraft.server.v1_11_R1.TileEntity;
import net.minecraft.server.v1_11_R1.WorldChunkManager;
@ -241,6 +246,72 @@ public class BukkitQueue_1_11 extends BukkitQueue_0<net.minecraft.server.v1_11_R
return super.regenerateChunk(world, x, z, biome, seed);
}
@Override
public boolean setMCA(Runnable whileLocked, final RegionWrapper allowed, boolean unload) {
try {
TaskManager.IMP.sync(new RunnableVal<Object>() {
@Override
public void run(Object value) {
try {
synchronized (RegionFileCache.class) {
ArrayDeque<net.minecraft.server.v1_11_R1.Chunk> chunks = new ArrayDeque<>();
World world = getWorld();
world.setKeepSpawnInMemory(false);
ChunkProviderServer provider = nmsWorld.getChunkProviderServer();
if (unload) { // Unload chunks
int bcx = (allowed.minX >> 9) << 5;
int bcz = (allowed.minZ >> 9) << 5;
int tcx = 31 + (allowed.maxX >> 9) << 5;
int tcz = 31 + (allowed.maxZ >> 9) << 5;
Iterator<net.minecraft.server.v1_11_R1.Chunk> iter = provider.a().iterator();
while (iter.hasNext()) {
net.minecraft.server.v1_11_R1.Chunk chunk = iter.next();
int cx = chunk.locX;
int cz = chunk.locZ;
if (cx >= bcx && cx <= tcx && cz >= bcz && cz <= tcz) {
chunks.add(chunk);
}
}
for (net.minecraft.server.v1_11_R1.Chunk chunk : chunks) {
provider.unloadChunk(chunk, true);
}
}
provider.c();
if (unload) { // Unload regions
Map<File, RegionFile> map = RegionFileCache.a;
Iterator<Map.Entry<File, RegionFile>> iter = map.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<File, RegionFile> entry = iter.next();
RegionFile regionFile = entry.getValue();
regionFile.c();
iter.remove();
}
}
whileLocked.run();
// Load the chunks again
if (unload) {
for (net.minecraft.server.v1_11_R1.Chunk chunk : chunks) {
chunk = provider.loadChunk(chunk.locX, chunk.locZ);
if (chunk != null) {
sendChunk(chunk, 0);
}
}
}
}
} catch (Throwable e) {
e.printStackTrace();
}
}
});
return true;
} catch (Throwable e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
@Override
public void setHeightMap(FaweChunk chunk, byte[] heightMap) {
CraftChunk craftChunk = (CraftChunk) chunk.getChunk();

View File

@ -63,6 +63,7 @@ import com.sk89q.worldedit.extension.platform.CommandManager;
import com.sk89q.worldedit.extension.platform.PlatformManager;
import com.sk89q.worldedit.extension.platform.PlayerProxy;
import com.sk89q.worldedit.extent.AbstractDelegateExtent;
import com.sk89q.worldedit.extent.MaskingExtent;
import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard;
import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat;
import com.sk89q.worldedit.extent.clipboard.io.SchematicReader;
@ -440,6 +441,7 @@ public class Fawe {
// Regions
CuboidRegion.inject(); // Optimizations
// Extents
MaskingExtent.inject(); // Features
BlockTransformExtent.inject(); // Fix for cache not being mutable
AbstractDelegateExtent.inject(); // Optimizations
BlockBagExtent.inject(); // Fixes + Optimizations

View File

@ -1,13 +1,16 @@
package com.boydti.fawe.command;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.config.BBC;
import com.boydti.fawe.jnbt.anvil.MCAChunk;
import com.boydti.fawe.jnbt.anvil.MCAFilter;
import com.boydti.fawe.jnbt.anvil.MCAFilterCounter;
import com.boydti.fawe.jnbt.anvil.MCAQueue;
import com.boydti.fawe.object.FaweQueue;
import com.boydti.fawe.object.RegionWrapper;
import com.boydti.fawe.object.mask.FaweBlockMatcher;
import com.boydti.fawe.object.number.LongAdder;
import com.boydti.fawe.object.number.MutableLong;
import com.boydti.fawe.util.SetQueue;
import com.boydti.fawe.util.StringMan;
import com.sk89q.minecraft.util.commands.Command;
@ -17,13 +20,19 @@ import com.sk89q.worldedit.MutableBlockVector;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.blocks.BlockType;
import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.function.pattern.Pattern;
import com.sk89q.worldedit.function.pattern.RandomPattern;
import com.sk89q.worldedit.internal.annotation.Selection;
import com.sk89q.worldedit.regions.CuboidRegion;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.command.binding.Switch;
import com.sk89q.worldedit.util.command.parametric.Optional;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
@ -45,7 +54,7 @@ public class AnvilCommands {
}
@Command(
aliases = {"/replaceall", "/rea", "/repall"},
aliases = {"replaceall", "rea", "repall"},
usage = "<folder> [from-block] <to-block>",
desc = "Replace all blocks in the selection with another",
flags = "d",
@ -66,13 +75,13 @@ public class AnvilCommands {
final FaweBlockMatcher matchTo = FaweBlockMatcher.setBlocks(worldEdit.getBlocks(player, to, true));
File root = new File(folder + File.separator + "region");
MCAQueue queue = new MCAQueue(folder, root, true);
queue.filterWorld(new MCAFilter() {
MCAFilterCounter counter = queue.filterWorld(new MCAFilterCounter() {
@Override
public void applyBlock(int x, int y, int z, BaseBlock block) {
public void applyBlock(int x, int y, int z, BaseBlock block, MutableLong ignore) {
if (matchFrom.apply(block)) matchTo.apply(block);
}
});
player.print(BBC.getPrefix() + BBC.VISITOR_BLOCK.format(-1));
player.print(BBC.getPrefix() + BBC.VISITOR_BLOCK.format(counter.getTotal()));
}
@Command(
@ -87,6 +96,7 @@ public class AnvilCommands {
public void replaceAllPattern(Player player, String folder, @Optional String from, final Pattern to, @Switch('d') boolean useData, @Switch('m') boolean useMap) throws WorldEditException {
FaweQueue defaultQueue = SetQueue.IMP.getNewQueue(folder, true, false);
MCAQueue queue = new MCAQueue(folder, defaultQueue.getSaveFolder(), defaultQueue.hasSky());
MCAFilterCounter counter;
if (useMap) {
List<String> split = StringMan.split(from, ',');
if (to instanceof RandomPattern) {
@ -116,10 +126,11 @@ public class AnvilCommands {
}
}
}
queue.filterWorld(new MCAFilter() {
counter = queue.filterWorld(new MCAFilterCounter() {
private final MutableBlockVector mutable = new MutableBlockVector(0, 0, 0);
@Override
public void applyBlock(int x, int y, int z, BaseBlock block) {
public void applyBlock(int x, int y, int z, BaseBlock block, MutableLong ignore) {
int id = block.getId();
int data = FaweCache.hasData(id) ? block.getData() : 0;
int combined = FaweCache.getCombined(id, data);
@ -137,9 +148,11 @@ public class AnvilCommands {
});
} else {
player.print(BBC.getPrefix() + "Mask:Pattern must be a 1:1 match");
return;
}
} else {
player.print(BBC.getPrefix() + "Must be a pattern list!");
return;
}
} else {
final FaweBlockMatcher matchFrom;
@ -151,9 +164,9 @@ public class AnvilCommands {
}
matchFrom = FaweBlockMatcher.fromBlocks(worldEdit.getBlocks(player, from, true), useData);
}
queue.filterWorld(new MCAFilter() {
counter = queue.filterWorld(new MCAFilterCounter() {
@Override
public void applyBlock(int x, int y, int z, BaseBlock block) {
public void applyBlock(int x, int y, int z, BaseBlock block, MutableLong ignore) {
if (matchFrom.apply(block)) {
BaseBlock newBlock = to.apply(x, y, z);
int currentId = block.getId();
@ -166,11 +179,11 @@ public class AnvilCommands {
}
});
}
player.print(BBC.getPrefix() + BBC.VISITOR_BLOCK.format(-1));
player.print(BBC.getPrefix() + BBC.VISITOR_BLOCK.format(counter.getTotal()));
}
@Command(
aliases = {"/countall"},
aliases = {"countall"},
usage = "<folder> [hasSky] <id>",
desc = "Count all blocks in a world",
flags = "d",
@ -181,7 +194,6 @@ public class AnvilCommands {
public void countAll(Player player, EditSession editSession, String folder, String arg, @Switch('d') boolean useData) throws WorldEditException {
File root = new File(folder + File.separator + "region");
MCAQueue queue = new MCAQueue(folder, root, true);
final LongAdder count = new LongAdder();
if (arg.contains(":")) {
useData = true; //override d flag, if they specified data they want it
}
@ -190,15 +202,15 @@ public class AnvilCommands {
for (BaseBlock block : searchBlocks) {
allowedId[block.getId()] = true;
}
MCAFilter filter;
MCAFilterCounter filter;
if (useData) { // Optimize for both cases
final boolean[] allowed = new boolean[Character.MAX_VALUE];
for (BaseBlock block : searchBlocks) {
allowed[FaweCache.getCombined(block)] = true;
}
filter = new MCAFilter() {
filter = new MCAFilterCounter() {
@Override
public MCAChunk applyChunk(MCAChunk chunk) {
public MCAChunk applyChunk(MCAChunk chunk, MutableLong count) {
for (int layer = 0; layer < chunk.ids.length; layer++) {
byte[] ids = chunk.ids[layer];
if (ids == null) {
@ -215,7 +227,7 @@ public class AnvilCommands {
combined += chunk.getNibble(i, datas);
}
if (allowed[combined]) {
count.add(1);
count.increment();
}
}
}
@ -223,15 +235,15 @@ public class AnvilCommands {
}
};
} else {
filter = new MCAFilter() {
filter = new MCAFilterCounter() {
@Override
public MCAChunk applyChunk(MCAChunk chunk) {
public MCAChunk applyChunk(MCAChunk chunk, MutableLong count) {
for (int layer = 0; layer < chunk.ids.length; layer++) {
byte[] ids = chunk.ids[layer];
if (ids != null) {
for (byte i : ids) {
if (allowedId[i & 0xFF]) {
count.add(1);
count.increment();
}
}
}
@ -241,7 +253,202 @@ public class AnvilCommands {
};
}
queue.filterWorld(filter);
player.print(BBC.getPrefix() + BBC.SELECTION_COUNT.format(count.longValue()));
player.print(BBC.getPrefix() + BBC.SELECTION_COUNT.format(filter.getTotal()));
}
@Command(
aliases = {"distr"},
desc = "Replace all blocks in the selection with another"
)
@CommandPermissions("worldedit.anvil.distr")
public void distr(Player player, EditSession editSession, @Selection Region selection, @Switch('d') boolean useData) throws WorldEditException {
long total = 0;
long[] count;
MCAFilter<long[]> counts;
if (useData) {
counts = runWithSelection(player, editSession, selection, new MCAFilter<long[]>() {
@Override
public void applyBlock(int x, int y, int z, BaseBlock block, long[] counts) {
counts[block.getCombined()]++;
}
@Override
public long[] init() {
return new long[Character.MAX_VALUE + 1];
}
});
count = new long[Character.MAX_VALUE + 1];
} else {
counts = runWithSelection(player, editSession, selection, new MCAFilter<long[]>() {
@Override
public void applyBlock(int x, int y, int z, BaseBlock block, long[] counts) {
counts[block.getId()]++;
}
@Override
public long[] init() {
return new long[4096];
}
});
count = new long[4096];
}
for (long[] value : counts) {
for (int i = 0; i < value.length; i++) {
count[i] += value[i];
total += value[i];
}
}
ArrayList<long[]> map = new ArrayList<>();
for (int i = 0; i < count.length; i++) {
if (count[i] != 0) map.add(new long[] { i, count[i]});
}
Collections.sort(map, new Comparator<long[]>() {
@Override
public int compare(long[] a, long[] b) {
long vA = a[1];
long vB = b[1];
return (vA < vB) ? -1 : ((vA == vB) ? 0 : 1);
}
});
if (useData) {
for (long[] c : map) {
BaseBlock block = FaweCache.CACHE_BLOCK[(int) c[0]];
String name = BlockType.fromID(block.getId()).getName();
String str = String.format("%-7s (%.3f%%) %s #%d:%d",
String.valueOf(c[1]),
((c[1] * 10000) / total) / 100d,
name == null ? "Unknown" : name,
block.getType(), block.getData());
player.print(BBC.getPrefix() + str);
}
} else {
for (long[] c : map) {
BlockType block = BlockType.fromID((int) c[0]);
String str = String.format("%-7s (%.3f%%) %s #%d",
String.valueOf(c[1]),
((c[1] * 10000) / total) / 100d,
block == null ? "Unknown" : block.getName(), c[0]);
player.print(BBC.getPrefix() + str);
}
}
}
private <G, T extends MCAFilter<G>> T runWithSelection(Player player, EditSession editSession, Region selection, T filter) {
if (!(selection instanceof CuboidRegion)) {
BBC.NO_REGION.send(player);
return null;
}
CuboidRegion cuboid = (CuboidRegion) selection;
RegionWrapper wrappedRegion = new RegionWrapper(cuboid.getMinimumPoint(), cuboid.getMaximumPoint());
String worldName = Fawe.imp().getWorldName(editSession.getWorld());
FaweQueue tmp = SetQueue.IMP.getNewQueue(worldName, true, false);
File folder = tmp.getSaveFolder();
MCAQueue queue = new MCAQueue(worldName, folder, tmp.hasSky());
player.print(BBC.getPrefix() + "Safely unloading regions...");
tmp.setMCA(new Runnable() {
@Override
public void run() {
player.print(BBC.getPrefix() + "Performing operation...");
queue.filterRegion(filter, wrappedRegion);
player.print(BBC.getPrefix() + "Safely loading regions...");
}
}, wrappedRegion, true);
return filter;
}
@Command(
aliases = {"replace"},
usage = "[from-block] <to-block>",
desc = "Replace all blocks in the selection with another"
)
@CommandPermissions("worldedit.anvil.replace")
public void replace(Player player, EditSession editSession, @Selection Region selection, @Optional String from, String to, @Switch('d') boolean useData) throws WorldEditException {
final FaweBlockMatcher matchFrom;
if (from == null) {
matchFrom = FaweBlockMatcher.NOT_AIR;
} else {
if (from.contains(":")) {
useData = true; //override d flag, if they specified data they want it
}
matchFrom = FaweBlockMatcher.fromBlocks(worldEdit.getBlocks(player, from, true), useData);
}
final FaweBlockMatcher matchTo = FaweBlockMatcher.setBlocks(worldEdit.getBlocks(player, to, true));
MCAFilterCounter filter = runWithSelection(player, editSession, selection, new MCAFilterCounter() {
@Override
public void applyBlock(int x, int y, int z, BaseBlock block, MutableLong count) {
if (matchFrom.apply(block)) {
matchTo.apply(block);
count.increment();
}
}
});
if (filter != null) {
player.print(BBC.getPrefix() + BBC.VISITOR_BLOCK.format(filter.getTotal()));
}
}
//
// @Command(
// aliases = {"copychunks"},
// desc = "Lazily copy chunks to your anvil clipboard"
// )
// @CommandPermissions("worldedit.anvil.copychunks")
// public void copy(Player player, LocalSession session, EditSession editSession, @Selection Region selection) throws WorldEditException {
// if (!(selection instanceof CuboidRegion)) {
// BBC.NO_REGION.send(player);
// return;
// }
// CuboidRegion cuboid = (CuboidRegion) selection;
// String worldName = Fawe.imp().getWorldName(editSession.getWorld());
// FaweQueue tmp = SetQueue.IMP.getNewQueue(worldName, true, false);
// File folder = tmp.getSaveFolder();
// MCAQueue queue = new MCAQueue(worldName, folder, tmp.hasSky());
// Vector origin = session.getPlacementPosition(player);
// MCAClipboard clipboard = new MCAClipboard(queue, cuboid, origin);
// FawePlayer fp = FawePlayer.wrap(player);
// fp.setMeta(FawePlayer.METADATA_KEYS.ANVIL_CLIPBOARD, clipboard);
// BBC.COMMAND_COPY.send(player, selection.getArea());
// }
//
// @Command(
// aliases = {"pastechunks"},
// desc = "Paste chunks from your anvil clipboard"
// )
// @CommandPermissions("worldedit.anvil.pastechunks")
// public void paste(Player player, LocalSession session, EditSession editSession) throws WorldEditException {
// FawePlayer fp = FawePlayer.wrap(player);
// MCAClipboard clipboard = fp.getMeta(FawePlayer.METADATA_KEYS.ANVIL_CLIPBOARD);
// if (clipboard == null) {
// fp.sendMessage(BBC.getPrefix() + "You must first copy to your clipboard");
// return;
// }
// CuboidRegion cuboid = clipboard.getRegion();
// RegionWrapper copyRegion = new RegionWrapper(cuboid.getMinimumPoint(), cuboid.getMaximumPoint());
// Vector offset = player.getPosition().subtract(clipboard.getOrigin());
// int oX = offset.getBlockX();
// int oZ = offset.getBlockZ();
// RegionWrapper pasteRegion = new RegionWrapper(copyRegion.minX + oX, copyRegion.maxX + oX, copyRegion.minZ + oZ, copyRegion.maxZ + oZ);
// String pasteWorldName = Fawe.imp().getWorldName(editSession.getWorld());
// FaweQueue tmpTo = SetQueue.IMP.getNewQueue(pasteWorldName, true, false);
// FaweQueue tmpFrom = SetQueue.IMP.getNewQueue(clipboard.getQueue().getWorldName(), true, false);
// File folder = tmpTo.getSaveFolder();
// MCAQueue copyQueue = clipboard.getQueue();
// MCAQueue pasteQueue = new MCAQueue(pasteWorldName, folder, tmpTo.hasSky());
// player.print(BBC.getPrefix() + "Safely unloading regions...");
// tmpTo.setMCA(new Runnable() {
// @Override
// public void run() {
// tmpFrom.setMCA(new Runnable() {
// @Override
// public void run() {
// try {
// player.print(BBC.getPrefix() + "Performing operation...");
// pasteQueue.pasteRegion(copyQueue, copyRegion, offset);
// player.print(BBC.getPrefix() + "Safely loading regions...");
// } catch (Throwable e) {
// e.printStackTrace();
// }
// }
// }, copyRegion, false);
// }
// }, pasteRegion, true);
// player.print("Done!");
// }
}

View File

@ -62,7 +62,7 @@ public class Rollback extends FaweCommand {
BBC.COMMAND_SYNTAX.send(player, "/frb <info|undo> u:<uuid> r:<radius> t:<time>");
return false;
}
player.deleteMeta("rollback");
player.deleteMeta(FawePlayer.METADATA_KEYS.ROLLBACK);
final FaweLocation origin = player.getLocation();
rollback(player, !player.hasPermission("fawe.rollback.deep"), Arrays.copyOfRange(args, 1, args.length), new RunnableVal<List<DiskStorageHistory>>() {
@Override
@ -91,7 +91,7 @@ public class Rollback extends FaweCommand {
player.sendMessage("&dSize: " + (((double) (total / 1024)) / 1000) + "MB");
player.sendMessage("&dTo rollback: /frb undo");
player.sendMessage("&d==================================================");
player.setMeta("rollback", edits);
player.setMeta(FawePlayer.METADATA_KEYS.ROLLBACK, edits);
}
});
break;
@ -102,8 +102,8 @@ public class Rollback extends FaweCommand {
BBC.NO_PERM.send(player, "fawe.rollback.perform");
return false;
}
final List<DiskStorageHistory> edits = (List<DiskStorageHistory>) player.getMeta("rollback");
player.deleteMeta("rollback");
final List<DiskStorageHistory> edits = (List<DiskStorageHistory>) player.getMeta(FawePlayer.METADATA_KEYS.ROLLBACK);
player.deleteMeta(FawePlayer.METADATA_KEYS.ROLLBACK);
if (edits == null) {
BBC.COMMAND_SYNTAX.send(player, "/frb info u:<uuid> r:<radius> t:<time>");
return false;

View File

@ -259,6 +259,11 @@ public abstract class MappedFaweQueue<WORLD, CHUNK, CHUNKSECTIONS, SECTION> exte
@Override
public void clear() {
lastSectionX = Integer.MIN_VALUE;
lastSectionZ = Integer.MIN_VALUE;
lastSectionY = -1;
lastChunk = null;
lastChunkSections = null;
map.clear();
runTasks();
}

View File

@ -19,6 +19,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
@ -50,8 +51,7 @@ public class MCAChunk extends FaweChunk<Void> {
private long lastUpdate;
private int[] heightMap;
public int compressedSize;
private boolean modified;
private int modified;
private boolean deleted;
public byte[] toBytes(byte[] buffer) throws IOException {
@ -113,6 +113,91 @@ public class MCAChunk extends FaweChunk<Void> {
return buffered.toByteArray();
}
public void copyFrom(MCAChunk other, int minY, int maxY) {
for (int layer = 0; layer < ids.length; layer++) {
byte[] otherIds = other.ids[layer];
byte[] currentIds = ids[layer];
int by = layer << 4;
int ty = layer >> 4;
if (by >= minY && ty <= maxY) {
if (otherIds != null) {
ids[layer] = otherIds;
data[layer] = other.data[layer];
skyLight[layer] = other.skyLight[layer];
blockLight[layer] = other.blockLight[layer];
} else {
ids[layer] = null;
}
} else {
by = Math.max(by, minY) & 15;
ty = Math.min(ty, maxY) & 15;
int indexStart = by << 8;
int indexEnd = 255 + (ty << 8);
int indexStartShift = indexStart >> 1;
int indexEndShift = indexEnd >> 1;
if (otherIds == null) {
if (currentIds != null) {
Arrays.fill(currentIds, indexStart, indexEnd, (byte) 0);
Arrays.fill(data[layer], indexStartShift, indexEndShift, (byte) 0);
Arrays.fill(skyLight[layer], indexStartShift, indexEndShift, (byte) 0);
Arrays.fill(blockLight[layer], indexStartShift, indexEndShift, (byte) 0);
}
} else {
if (currentIds == null) {
currentIds = this.ids[layer] = new byte[4096];
this.data[layer] = new byte[2048];
this.skyLight[layer] = new byte[2048];
this.blockLight[layer] = new byte[2048];
}
System.arraycopy(other.ids[layer], indexStart, ids[layer], indexStart, indexEnd - indexStart);
System.arraycopy(other.data[layer], indexStartShift, data[layer], indexStartShift, indexEndShift - indexStartShift);
System.arraycopy(other.skyLight[layer], indexStartShift, skyLight[layer], indexStartShift, indexEndShift - indexStartShift);
System.arraycopy(other.blockLight[layer], indexStartShift, blockLight[layer], indexStartShift, indexEndShift - indexStartShift);
}
}
}
// Copy nbt
if (!tiles.isEmpty()) {
Iterator<Map.Entry<Short, CompoundTag>> iter = tiles.entrySet().iterator();
while (iter.hasNext()) {
int y = MathMan.untripleBlockCoordY(iter.next().getKey());
if (y >= minY && y <= maxY) iter.remove();
}
}
if (!other.tiles.isEmpty()) {
for (Map.Entry<Short, CompoundTag> entry : other.tiles.entrySet()) {
short key = entry.getKey();
int y = MathMan.untripleBlockCoordY(key);
if (y >= minY && y <= maxY) {
tiles.put(key, entry.getValue());
}
}
}
if (!other.entities.isEmpty()) {
for (Map.Entry<UUID, CompoundTag> entry : other.entities.entrySet()) {
// TODO
}
}
}
public int getMinLayer() {
for (int layer = 0; layer < ids.length; layer++) {
if (ids[layer] != null) {
return layer;
}
}
return Integer.MAX_VALUE;
}
public int getMaxLayer() {
for (int layer = ids.length - 1; layer >= 0; layer--) {
if (ids[layer] != null) {
return layer;
}
}
return Integer.MIN_VALUE;
}
public CompoundTag toTag() {
if (deleted) {
return null;
@ -164,7 +249,7 @@ public class MCAChunk extends FaweChunk<Void> {
this.entities = new HashMap<>();
this.lastUpdate = System.currentTimeMillis();
this.heightMap = new int[256];
this.modified = true;
this.setModified();
}
public MCAChunk(MCAChunk parent, boolean shallow) {
@ -180,7 +265,6 @@ public class MCAChunk extends FaweChunk<Void> {
this.inhabitedTime = parent.inhabitedTime;
this.lastUpdate = parent.lastUpdate;
this.heightMap = parent.heightMap;
this.compressedSize = parent.compressedSize;
this.modified = parent.modified;
this.deleted = parent.deleted;
} else {
@ -194,7 +278,6 @@ public class MCAChunk extends FaweChunk<Void> {
this.inhabitedTime = parent.inhabitedTime;
this.lastUpdate = parent.lastUpdate;
this.heightMap = parent.heightMap.clone();
this.compressedSize = parent.compressedSize;
this.modified = parent.modified;
this.deleted = parent.deleted;
}
@ -206,7 +289,6 @@ public class MCAChunk extends FaweChunk<Void> {
data = new byte[16][];
skyLight = new byte[16][];
blockLight = new byte[16][];
this.compressedSize = compressedSize;
NBTStreamer streamer = new NBTStreamer(nis);
streamer.addReader(".Level.InhabitedTime", new RunnableVal2<Integer, Long>() {
@Override
@ -281,12 +363,16 @@ public class MCAChunk extends FaweChunk<Void> {
}
public boolean isModified() {
return modified != 0;
}
public int getModified() {
return modified;
}
@Deprecated
public final void setModified() {
this.modified = true;
this.modified++;
}
@Override
@ -302,7 +388,7 @@ public class MCAChunk extends FaweChunk<Void> {
@Override
public void setTile(int x, int y, int z, CompoundTag tile) {
modified = true;
setModified();
short pair = MathMan.tripleBlockCoord(x, y, z);
if (tile != null) {
tiles.put(pair, tile);
@ -313,7 +399,7 @@ public class MCAChunk extends FaweChunk<Void> {
@Override
public void setEntity(CompoundTag entityTag) {
modified = true;
setModified();
long least = entityTag.getLong("UUIDLeast");
long most = entityTag.getLong("UUIDMost");
entities.put(new UUID(most, least), entityTag);
@ -321,7 +407,7 @@ public class MCAChunk extends FaweChunk<Void> {
@Override
public void setBiome(int x, int z, byte biome) {
modified = true;
setModified();
biomes[x + (z << 4)] = biome;
}
@ -382,7 +468,7 @@ public class MCAChunk extends FaweChunk<Void> {
}
public void setSkyLight(int x, int y, int z, int value) {
modified = true;
setModified();
int layer = y >> 4;
byte[] skyLayer = skyLight[layer];
if (skyLayer == null) {
@ -393,7 +479,7 @@ public class MCAChunk extends FaweChunk<Void> {
}
public void setBlockLight(int x, int y, int z, int value) {
modified = true;
setModified();
int layer = y >> 4;
byte[] blockLayer = blockLight[layer];
if (blockLayer == null) {
@ -424,7 +510,7 @@ public class MCAChunk extends FaweChunk<Void> {
}
public void setFullbright() {
modified = true;
setModified();
for (byte[] array : skyLight) {
if (array != null) {
Arrays.fill(array, (byte) 255);
@ -478,7 +564,7 @@ public class MCAChunk extends FaweChunk<Void> {
@Override
public void setBlock(int x, int y, int z, int id, int data) {
modified = true;
setModified();
int layer = y >> 4;
byte[] idsLayer = ids[layer];
if (idsLayer == null) {
@ -500,7 +586,7 @@ public class MCAChunk extends FaweChunk<Void> {
@Override
public void removeEntity(UUID uuid) {
modified = true;
setModified();
entities.remove(uuid);
}

View File

@ -0,0 +1,28 @@
package com.boydti.fawe.jnbt.anvil;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.regions.CuboidRegion;
public class MCAClipboard {
private final MCAQueue queue;
private final CuboidRegion region;
private final Vector origin;
public MCAClipboard(MCAQueue queue, CuboidRegion region, Vector origin) {
this.queue = queue;
this.region = region;
this.origin = origin;
}
public MCAQueue getQueue() {
return queue;
}
public CuboidRegion getRegion() {
return region;
}
public Vector getOrigin() {
return origin;
}
}

View File

@ -8,13 +8,12 @@ import com.boydti.fawe.object.RunnableVal4;
import com.boydti.fawe.object.exception.FaweException;
import com.boydti.fawe.object.io.BufferedRandomAccessFile;
import com.boydti.fawe.object.io.FastByteArrayInputStream;
import com.boydti.fawe.object.io.FastByteArrayOutputStream;
import com.boydti.fawe.util.MainUtil;
import com.boydti.fawe.util.MathMan;
import com.sk89q.jnbt.NBTInputStream;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
@ -25,7 +24,6 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.TimeUnit;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.Inflater;
import java.util.zip.InflaterInputStream;
@ -37,30 +35,24 @@ public class MCAFile {
private static Field fieldBuf2;
private static Field fieldBuf3;
private static Field fieldBuf4;
private static Field fieldBuf5;
private static Field fieldBuf6;
static {
try {
fieldBuf2 = InflaterInputStream.class.getDeclaredField("buf");
fieldBuf2.setAccessible(true);
fieldBuf3 = NBTInputStream.class.getDeclaredField("buf");
fieldBuf3.setAccessible(true);
fieldBuf4 = FastByteArrayOutputStream.class.getDeclaredField("buffer");
fieldBuf4.setAccessible(true);
fieldBuf5 = DeflaterOutputStream.class.getDeclaredField("buf");
fieldBuf5.setAccessible(true);
fieldBuf6 = BufferedOutputStream.class.getDeclaredField("buf");
fieldBuf6.setAccessible(true);
} catch (Throwable e) {
e.printStackTrace();
}
}
private FaweQueue queue;
private File file;
private final FaweQueue queue;
private final File file;
private RandomAccessFile raf;
private byte[] locations;
private boolean deleted;
private final int X, Z;
private final Int2ObjectOpenHashMap<MCAChunk> chunks = new Int2ObjectOpenHashMap<>();
final ThreadLocal<byte[]> byteStore1 = new ThreadLocal<byte[]>() {
@Override
@ -81,10 +73,6 @@ public class MCAFile {
}
};
private final int X, Z;
private Int2ObjectOpenHashMap<MCAChunk> chunks = new Int2ObjectOpenHashMap<>();
public MCAFile(FaweQueue parent, File file) {
this.queue = parent;
this.file = file;
@ -96,6 +84,26 @@ public class MCAFile {
Z = Integer.parseInt(split[2]);
}
public void clear() {
if (raf != null) {
try {
raf.close();
} catch (IOException e) {
e.printStackTrace();
}
}
chunks.clear();
locations = null;
}
public void setDeleted(boolean deleted) {
this.deleted = deleted;
}
public boolean isDeleted() {
return deleted;
}
public FaweQueue getParent() {
return queue;
}
@ -105,9 +113,12 @@ public class MCAFile {
if (raf == null) {
this.locations = new byte[4096];
this.raf = new RandomAccessFile(file, "rw");
if (raf.length() < 8192) {
raf.setLength(8192);
} else {
raf.seek(0);
raf.readFully(locations);
}
}
} catch (Throwable e) {
e.printStackTrace();
@ -137,6 +148,13 @@ public class MCAFile {
}
}
public void setChunk(MCAChunk chunk) {
int cx = chunk.getX();
int cz = chunk.getZ();
int pair = MathMan.pair((short) (cx & 31), (short) (cz & 31));
chunks.put(pair, chunk);
}
public MCAChunk getChunk(int cx, int cz) throws IOException {
MCAChunk cached = getCachedChunk(cx, cz);
if (cached != null) {
@ -348,6 +366,10 @@ public class MCAFile {
private void writeHeader(RandomAccessFile raf, int cx, int cz, int offsetMedium, int sizeByte, boolean writeTime) throws IOException {
int i = ((cx & 31) << 2) + ((cz & 31) << 7);
locations[i] = (byte) (offsetMedium >> 16);
locations[i + 1] = (byte) (offsetMedium >> 8);
locations[i + 2] = (byte) (offsetMedium);
locations[i + 3] = (byte) sizeByte;
raf.seek(i);
raf.write((offsetMedium >> 16));
raf.write((offsetMedium >> 8));
@ -370,11 +392,8 @@ public class MCAFile {
} catch (IOException e) {
e.printStackTrace();
}
file = null;
raf = null;
locations = null;
queue = null;
chunks = null;
}
}
}
@ -389,6 +408,7 @@ public class MCAFile {
Int2ObjectOpenHashMap<byte[]> relocate = new Int2ObjectOpenHashMap<>();
final Int2ObjectOpenHashMap<Integer> offsetMap = new Int2ObjectOpenHashMap<>(); // Offset -> <byte cx, byte cz, short size>
final Int2ObjectOpenHashMap<byte[]> compressedMap = new Int2ObjectOpenHashMap<>();
final Int2ObjectOpenHashMap<byte[]> append = new Int2ObjectOpenHashMap<>();
boolean modified = false;
for (MCAChunk chunk : getCachedChunks()) {
if (chunk.isModified()) {
@ -400,8 +420,14 @@ public class MCAFile {
try {
byte[] compressed = toBytes(chunk);
int pair = MathMan.pair((short) (chunk.getX() & 31), (short) (chunk.getZ() & 31));
synchronized (compressedMap) {
compressedMap.put(pair, compressed);
Int2ObjectOpenHashMap map;
if (getOffset(chunk.getX(), chunk.getZ()) == 0) {
map = append;
} else {
map = compressedMap;
}
synchronized (map) {
map.put(pair, compressed);
}
} catch (Throwable e) {
e.printStackTrace();
@ -473,7 +499,8 @@ public class MCAFile {
short nextCXZ = MathMan.unpairX(nextLoc);
int nextCX = MathMan.unpairShortX(nextCXZ);
int nextCZ = MathMan.unpairShortY(nextCXZ);
if (getCachedChunk(nextCX, nextCZ) == null) {
MCAChunk cached = getCachedChunk(nextCX, nextCZ);
if (cached == null || !cached.isModified()) {
byte[] nextBytes = getChunkCompressedBytes(nextOffset2);
relocate.put(MathMan.pair((short) (nextCX & 31), (short) (nextCZ & 31)), nextBytes);
}
@ -490,6 +517,22 @@ public class MCAFile {
written = start + newBytes.length + 5;
start += newSize << 12;
}
// TODO this doesn't work
if (!append.isEmpty()) {
for (Int2ObjectMap.Entry<byte[]> entry : append.int2ObjectEntrySet()) {
int pair = entry.getIntKey();
short cx = MathMan.unpairX(pair);
short cz = MathMan.unpairY(pair);
byte[] bytes = entry.getValue();
int len = bytes.length + 5;
int newSize = (len + 4095) >> 12;
writeSafe(raf, start, bytes);
writeHeader(raf, cx, cz, start >> 12, newSize, true);
written = start + bytes.length + 5;
start += newSize << 12;
}
}
raf.setLength(4096 * ((written + 4095) / 4096));
if (raf instanceof BufferedRandomAccessFile) {
((BufferedRandomAccessFile) raf).flush();

View File

@ -1,12 +1,13 @@
package com.boydti.fawe.jnbt.anvil;
import com.boydti.fawe.object.collection.IterableThreadLocal;
import com.sk89q.worldedit.blocks.BaseBlock;
/**
* MCAQueue.filterWorld(MCAFilter)<br>
* - Read and modify the world
*/
public class MCAFilter {
public class MCAFilter<T> extends IterableThreadLocal<T> {
/**
* Check whether a .mca file should be read
@ -46,7 +47,7 @@ public class MCAFilter {
* @param chunk
* @return
*/
public MCAChunk applyChunk(MCAChunk chunk) {
public MCAChunk applyChunk(MCAChunk chunk, T cache) {
return chunk;
}
@ -59,5 +60,13 @@ public class MCAFilter {
* @param z
* @param block
*/
public void applyBlock(int x, int y, int z, BaseBlock block) {}
public void applyBlock(int x, int y, int z, BaseBlock block, T cache) {}
/**
* Do something with the MCAChunk after block filtering<br>
* @param chunk
* @param cache
* @return
*/
public void finishChunk(MCAChunk chunk, T cache) {}
}

View File

@ -0,0 +1,23 @@
package com.boydti.fawe.jnbt.anvil;
import com.boydti.fawe.object.number.MutableLong;
public class MCAFilterCounter extends MCAFilter<MutableLong> {
@Override
public void finishChunk(MCAChunk chunk, MutableLong cache) {
cache.add(chunk.getModified());
}
@Override
public MutableLong init() {
return new MutableLong();
}
public long getTotal() {
long total = 0;
for (MutableLong value : getAll()) {
total += value.get();
}
return total;
}
}

View File

@ -2,13 +2,21 @@ package com.boydti.fawe.jnbt.anvil;
import com.boydti.fawe.example.CharFaweChunk;
import com.boydti.fawe.example.NMSMappedFaweQueue;
import com.boydti.fawe.example.NullFaweChunk;
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.RunnableVal2;
import com.boydti.fawe.object.RunnableVal4;
import com.boydti.fawe.util.MainUtil;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.world.biome.BaseBiome;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
@ -22,6 +30,12 @@ public class MCAQueue extends NMSMappedFaweQueue<FaweQueue, FaweChunk, FaweChunk
private NMSMappedFaweQueue parentNMS;
private final boolean hasSky;
private final File saveFolder;
private final ThreadLocal<MutableMCABackedBaseBlock> blockStore = new ThreadLocal<MutableMCABackedBaseBlock>() {
@Override
protected MutableMCABackedBaseBlock initialValue() {
return new MutableMCABackedBaseBlock();
}
};
public MCAQueue(FaweQueue parent) {
super(parent.getWEWorld(), new MCAQueueMap());
@ -67,26 +81,180 @@ public class MCAQueue extends NMSMappedFaweQueue<FaweQueue, FaweChunk, FaweChunk
}
}
public void filterWorld(final MCAFilter filter) {
public void pasteRegion(MCAQueue from, final RegionWrapper regionFrom, Vector offset) throws IOException {
offset = new Vector((offset.getBlockX() >> 4) << 4, 0, (offset.getBlockZ() >> 4) << 4);
int oX = offset.getBlockX();
int oZ = offset.getBlockZ();
int oY = offset.getBlockY();
int oCX = oX >> 4;
int oCZ = oZ >> 4;
RegionWrapper regionTo = new RegionWrapper(regionFrom.minX + oX, regionFrom.maxX + oX, regionFrom.minZ + oZ, regionFrom.maxZ + oZ);
File folder = getSaveFolder();
final ForkJoinPool pool = new ForkJoinPool();
final ThreadLocal<MutableMCABackedBaseBlock> blockStore = new ThreadLocal<MutableMCABackedBaseBlock>() {
@Override
protected MutableMCABackedBaseBlock initialValue() {
return new MutableMCABackedBaseBlock();
int bMcaX = (regionTo.minX >> 9);
int bMcaZ = (regionTo.minZ >> 9);
int tMcaX = (regionTo.maxX >> 9);
int tMcaZ = (regionTo.maxZ >> 9);
for (int mcaZ = bMcaZ; mcaZ <= tMcaZ; mcaZ++) {
for (int mcaX = bMcaX; mcaX <= tMcaX; mcaX++) {
int bcx = Math.max(mcaX << 5, regionTo.minX >> 4);
int bcz = Math.max(mcaZ << 5, regionTo.minZ >> 4);
int tcx = Math.min((mcaX << 5) + 31, regionTo.maxX >> 4);
int tcz = Math.min((mcaZ << 5) + 31, regionTo.maxZ >> 4);
File file = new File(folder, "r." + mcaX + "." + mcaZ + ".mca");
if (!file.exists()) {
file.createNewFile();
}
};
for (final File file : folder.listFiles()) {
MCAFile mcaFile = new MCAFile(null, file);
mcaFile.init();
// If oX & 15 == 0 && oZ && 15 == 0
if ((oX & 15) == 0 && (oZ & 15) == 0) {
if (oY == 0) {
if (regionFrom.minY == 0 && regionFrom.maxY == 255) {
for (int cz = bcz; cz <= tcz; cz++) {
for (int cx = bcx; cx <= tcx; cx++) {
FaweChunk chunk = from.getFaweChunk(cx - oCX, cz - oCZ);
if (!(chunk instanceof NullFaweChunk)) {
MCAChunk mcaChunk = (MCAChunk) chunk;
mcaChunk.setLoc(null, cx, cz);
mcaChunk.setModified();
mcaFile.setChunk(mcaChunk);
}
}
}
} else {
for (int cz = bcz; cz <= tcz; cz++) {
for (int cx = bcx; cx <= tcx; cx++) {
FaweChunk chunk = from.getFaweChunk(cx - oCX, cz - oCZ);
if (!(chunk instanceof NullFaweChunk)) {
MCAChunk mcaChunk = (MCAChunk) chunk;
MCAChunk toChunk = mcaFile.getChunk(cx, cz);
if (toChunk == null || (toChunk.getMinLayer() << 4 >= regionTo.minY && (toChunk.getMaxLayer() << 4) + 15 <= regionTo.maxY)) {
mcaChunk.setLoc(null, cx, cz);
mcaChunk.setModified();
mcaFile.setChunk(mcaChunk);
} else {
}
}
}
}
}
} else if ((oY & 15) == 0) {
} else {
}
} else {
}
mcaFile.close(pool);
from.clear();
}
}
pool.awaitQuiescence(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
}
public <G, T extends MCAFilter<G>> T filterRegion(final T filter, final RegionWrapper region) {
this.filterWorld(new MCAFilter<G>() {
@Override
public boolean appliesFile(int mcaX, int mcaZ) {
return region.isInMCA(mcaX, mcaZ) && filter.appliesFile(mcaX, mcaZ);
}
@Override
public MCAFile applyFile(MCAFile file) {
return filter.applyFile(file);
}
@Override
public boolean appliesChunk(int cx, int cz) {
return region.isInChunk(cx, cz) && filter.appliesChunk(cx, cz);
}
@Override
public G get() {
return filter.get();
}
@Override
public MCAChunk applyChunk(MCAChunk chunk, G value) {
chunk = filter.applyChunk(chunk, value);
if (chunk != null) {
final MutableMCABackedBaseBlock mutableBlock = blockStore.get();
mutableBlock.setChunk(chunk);
int bx = chunk.getX() << 4;
int bz = chunk.getZ() << 4;
int tx = bx + 15;
int tz = bz + 15;
bx = Math.max(bx, region.minX);
bz = Math.max(bz, region.minZ);
tx = Math.min(tx, region.maxX);
tz = Math.min(tz, region.maxZ);
int minLayer = region.minY >> 4;
int maxLayer = region.maxY >> 4;
for (int layer = minLayer; layer <= maxLayer; layer++) {
if (chunk.doesSectionExist(layer)) {
mutableBlock.setArrays(layer);
int yStart = layer << 4;
int yEnd = yStart + 15;
yStart = Math.max(yStart, region.minY);
yEnd = Math.min(yEnd, region.maxY);
for (int y = yStart, y0 = (yStart & 15); y <= yEnd; y++, y0++) {
int yIndex = ((y0) << 8);
mutableBlock.setY(y);
for (int z = bz, z0 = bz & 15; z <= tz; z++, z0++) {
int zIndex = yIndex + ((z0) << 4);
mutableBlock.setZ(z);
for (int x = bx, x0 = bx & 15; x <= tx; x++, x0++) {
int xIndex = zIndex + x0;
mutableBlock.setX(x);
mutableBlock.setIndex(xIndex);
filter.applyBlock(x, y, z, mutableBlock, value);
}
}
}
}
}
}
return null;
}
@Override
public void finishChunk(MCAChunk chunk, G cache) {
super.finishChunk(chunk, cache);
}
});
return filter;
}
public <G, T extends MCAFilter<G>> T createRegion(final T filter, final RegionWrapper region) {
int botMcaX = region.minX >> 9;
int botMcaZ = region.minZ >> 9;
int topMcaX = region.maxX >> 9;
int topMcaZ = region.maxZ >> 9;
for (int mcaX = botMcaX >> 9; mcaX <= topMcaX; mcaX++) {
for (int mcaZ = botMcaZ >> 9; mcaZ <= topMcaZ; mcaZ++) {
}
}
return filter;
}
public <G, T extends MCAFilter<G>> T filterWorld(final T filter) {
File folder = getSaveFolder();
final ForkJoinPool pool = new ForkJoinPool();
MainUtil.traverse(folder.toPath(), new RunnableVal2<Path, BasicFileAttributes>() {
@Override
public void run(Path path, BasicFileAttributes attr) {
try {
String name = file.getName();
String name = path.getFileName().toString();
String[] split = name.split("\\.");
final int mcaX = Integer.parseInt(split[1]);
final int mcaZ = Integer.parseInt(split[2]);
if (filter.appliesFile(mcaX, mcaZ)) {
MCAFile mcaFile = new MCAFile(this, file);
File file = path.toFile();
MCAFile mcaFile = new MCAFile(MCAQueue.this, file);
final MCAFile original = mcaFile;
final MCAFile finalFile = filter.applyFile(mcaFile);
if (finalFile != null) {
if (finalFile != null && !finalFile.isDeleted()) {
finalFile.init();
// May not do anything, but seems to lead to smaller lag spikes
final int cbx = mcaX << 5;
@ -98,13 +266,14 @@ public class MCAQueue extends NMSMappedFaweQueue<FaweQueue, FaweChunk, FaweChunk
pool.submit(new Runnable() {
@Override
public void run() {
try {
int cx = cbx + rcx;
int cz = cbz + rcz;
if (filter.appliesChunk(cx, cz)) {
try {
MCAChunk chunk = finalFile.getChunk(cx, cz);
try {
chunk = filter.applyChunk(chunk);
final G value = filter.get();
chunk = filter.applyChunk(chunk, value);
if (chunk != null) {
final MutableMCABackedBaseBlock mutableBlock = blockStore.get();
mutableBlock.setChunk(chunk);
@ -122,45 +291,50 @@ public class MCAQueue extends NMSMappedFaweQueue<FaweQueue, FaweChunk, FaweChunk
for (int x = bx; x < bx + 16; x++, index++) {
mutableBlock.setX(x);
mutableBlock.setIndex(index);
filter.applyBlock(x, y, z, mutableBlock);
filter.applyBlock(x, y, z, mutableBlock, value);
}
}
}
}
}
filter.finishChunk(chunk, value);
}
} catch (Throwable e) {
e.printStackTrace();
}
}
} catch (Throwable e) {
System.out.println("Failed to load: r." + mcaX + "." + mcaZ + ".mca -> (local) " + rcx + "," + rcz);
e.printStackTrace();
}
}
}
});
}
});
pool.awaitQuiescence(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
original.close(pool);
if (original != finalFile) finalFile.close(pool);
} else {
} else if (mcaFile.isDeleted()) {
try {
original.close(pool);
file.delete();
} catch (Throwable ignore) {}
} catch (Throwable ignore) {
ignore.printStackTrace();
}
}
}
} catch (Throwable ignore) {
ignore.printStackTrace();
}
}
});
pool.shutdown();
try {
pool.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
} catch (Throwable e) {
e.printStackTrace();
}
return filter;
}
@Override

View File

@ -133,7 +133,16 @@ public class MCAQueueMap implements IFaweQueueMap {
@Override
public void clear() {
for (Map.Entry<Long, MCAFile> entry : mcaFileMap.entrySet()) {
entry.getValue().clear();
}
mcaFileMap.clear();
lastChunk = null;
lastFile = null;
lastFileX = Integer.MIN_VALUE;
lastFileZ = Integer.MIN_VALUE;
lastX = Integer.MIN_VALUE;
lastZ = Integer.MIN_VALUE;
if (isHybridQueue) {
queue.clear();
}

View File

@ -44,6 +44,11 @@ public abstract class FawePlayer<T> extends Metadatable {
public final T parent;
private LocalSession session;
public static final class METADATA_KEYS {
public static final String ANVIL_CLIPBOARD = "anvil-clipboard";
public static final String ROLLBACK = "rollback";
}
/**
* Wrap some object into a FawePlayer<br>
* - org.bukkit.entity.Player

View File

@ -220,6 +220,10 @@ public abstract class FaweQueue implements HasFaweQueue {
public abstract Collection<FaweChunk> getFaweChunks();
public boolean setMCA(Runnable whileLocked, RegionWrapper region, boolean unload) {
return false;
}
public abstract void setChunk(final FaweChunk chunk);
public abstract File getSaveFolder();

View File

@ -61,6 +61,22 @@ public class RegionWrapper {
return lr && (x >= this.minX && x <= this.maxX);
}
public boolean isInChunk(int cx, int cz) {
int bx = cx << 4;
int bz = cz << 4;
int tx = bx + 15;
int tz = bz + 15;
return ((tx >= this.minX) && (bx <= this.maxX) && (tz >= this.minZ) && (bz <= this.maxZ));
}
public boolean isInMCA(int mcaX, int mcaZ) {
int bx = mcaX << 9;
int bz = mcaZ << 9;
int tx = bx + 511;
int tz = bz + 511;
return ((tx >= this.minX) && (bx <= this.maxX) && (tz >= this.minZ) && (bz <= this.maxZ));
}
public boolean isIn(final int x, final int z) {
return ((x >= this.minX) && (x <= this.maxX) && (z >= this.minZ) && (z <= this.maxZ));
}
@ -110,7 +126,7 @@ public class RegionWrapper {
@Override
public String toString() {
return this.minX + "," + this.minZ + "->" + this.maxX + "," + this.maxZ;
return this.minX + "," + this.minY + "," + this.minZ + "->" + this.maxX + "," + this.maxY + "," + this.maxZ;
}
public Vector getBottomVector() {

View File

@ -0,0 +1,4 @@
package com.boydti.fawe.object.brush;
public class FallingSphere {
}

View File

@ -170,7 +170,6 @@ public class CPUOptimizedClipboard extends FaweClipboard {
for (int z = 0; z < length; z++) {
for (int x = 0; x < width; x++, index++) {
BaseBlock block = getBlock(index);
System.out.println(block);
if (block.getId() != 0) {
task.run(x, y, z, block);
}

View File

@ -0,0 +1,33 @@
package com.boydti.fawe.object.collection;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.concurrent.ConcurrentLinkedDeque;
public abstract class IterableThreadLocal<T> extends ThreadLocal<T> implements Iterable<T> {
private ThreadLocal<T> flag;
private ConcurrentLinkedDeque<T> allValues = new ConcurrentLinkedDeque<T>();
public IterableThreadLocal() { }
@Override
protected final T initialValue() {
T value = init();
if (value != null) {
allValues.add(value);
}
return value;
}
@Override
public final Iterator<T> iterator() {
return getAll().iterator();
}
public T init() { return null; }
public final Collection<T> getAll() {
return Collections.unmodifiableCollection(allValues);
}
}

View File

@ -1,126 +0,0 @@
package com.boydti.fawe.object.extent;
import com.boydti.fawe.jnbt.anvil.MCAFile;
import com.boydti.fawe.object.FaweQueue;
import com.boydti.fawe.object.HasFaweQueue;
import com.boydti.fawe.util.MathMan;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.Vector2D;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.entity.BaseEntity;
import com.sk89q.worldedit.entity.Entity;
import com.sk89q.worldedit.extent.AbstractDelegateExtent;
import com.sk89q.worldedit.function.operation.Operation;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.world.World;
import com.sk89q.worldedit.world.biome.BaseBiome;
import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
public class MCAExtent extends AbstractDelegateExtent implements HasFaweQueue {
private final FaweQueue queue;
private final File folder;
private Map<Long, MCAFile> regions;
public MCAExtent(World world, FaweQueue queue) {
super(world);
this.queue = queue;
this.folder = new File(queue.getSaveFolder(), "regions");
this.regions = new HashMap<>();
}
public FaweQueue getQueue() {
return queue;
}
private int lastX = Integer.MAX_VALUE;
private int lastZ = Integer.MAX_VALUE;
private MCAFile lastMCA;
private MCAFile getMCA(int x, int y, int z) {
int MCAX = x >> 9;
int MCAZ = z >> 9;
if (MCAX == lastX && MCAZ == lastZ) {
return lastMCA;
}
lastX = MCAX;
lastZ = MCAZ;
long pair = MathMan.pairInt(lastX, lastZ);
lastMCA = regions.get(pair);
if (lastMCA == null) {
// lastMCA = new MCAFile(folder, lastX, lastZ);
// TODO
regions.put(pair, lastMCA);
}
return lastMCA;
}
@Override
public BaseBlock getBlock(Vector position) {
// TODO get block from MCA
return null;
}
@Override
public BaseBlock getLazyBlock(Vector position) {
// TODO set block in MCA
return null;
}
@Override
public boolean setBlock(Vector location, BaseBlock block) throws WorldEditException {
// TODO set block in MCA
return false;
}
@Override
@Nullable
public Entity createEntity(Location location, BaseEntity entity) {
// TODO add entity to MCA
return null;
}
@Override
public List<? extends Entity> getEntities() {
// TODO get entities from MCA
return null;
}
@Override
public List<? extends Entity> getEntities(Region region) {
// TODO get entities from MCA
return null;
}
@Override
public BaseBiome getBiome(Vector2D position) {
// TODO get biome from MCA
return null;
}
@Override
public boolean setBiome(Vector2D position, BaseBiome biome) {
return false;
}
@Override
public Vector getMinimumPoint() {
return super.getMinimumPoint();
}
@Override
public Vector getMaximumPoint() {
return super.getMaximumPoint();
}
protected Operation commitBefore() {
// Save MCA file if modified
return null;
}
}

View File

@ -0,0 +1,21 @@
package com.boydti.fawe.object.number;
public final class MutableLong {
private long value;
public final void increment() {
value++;
}
public void set(long value) {
this.value = value;
}
public long get() {
return value;
}
public void add(long amount) {
this.value += amount;
}
}

View File

@ -364,6 +364,8 @@ public class CreateFromImage extends Command {
player.sendMessage("Invalid mask " + e.getMessage());
} catch (Throwable e) {
player.sendMessage("Error " + e.getMessage());
} finally {
Request.reset();
}
}
}, true, false);

View File

@ -136,6 +136,7 @@ public class PlotTrim implements Listener {
ChunkLoc loc = new ChunkLoc(mcaX, mcaZ);
if (mcas.contains(loc)) {
player.sendMessage("Delete MCA " + mca);
mca.setDeleted(true);
return null;
}
try {
@ -159,7 +160,7 @@ public class PlotTrim implements Listener {
}
@Override
public MCAChunk applyChunk(MCAChunk chunk) {
public MCAChunk applyChunk(MCAChunk chunk, Object ignore) {
long pair = MathMan.pairInt(chunk.getX(), chunk.getZ());
if (chunks.containsKey(pair)) {
chunk.setDeleted(true);

View File

@ -5,6 +5,7 @@ import com.boydti.fawe.example.Relighter;
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.RunnableVal2;
import com.boydti.fawe.object.exception.FaweException;
import com.sk89q.jnbt.CompoundTag;
@ -43,6 +44,11 @@ public class DelegateFaweQueue extends FaweQueue {
parent.sendChunk(x, z, bitMask);
}
@Override
public boolean setMCA(Runnable whileLocked, RegionWrapper region, boolean unload) {
return parent.setMCA(whileLocked, region, unload);
}
@Override
public String getWorldName() {
return parent.getWorldName();

View File

@ -1303,7 +1303,6 @@ public class LocalSession {
if (transform != null) {
editSession.addTransform(transform);
}
return editSession;
}

View File

@ -948,7 +948,7 @@ public class BrushCommands {
if (player.hasPermission("worldedit.butcher")) {
maxRadius = Math.max(config.maxBrushRadius, config.butcherMaxRadius);
}
if (radius > maxRadius) {
if (radius > maxRadius && maxRadius != -1) {
BBC.TOOL_RADIUS_ERROR.send(player, maxRadius);
return;
}

View File

@ -94,9 +94,9 @@ public class ToolUtilCommands {
}
}
if (mask == null) {
BBC.BRUSH_SOURCE_MASK_DISABLED.send(player);
BBC.BRUSH_MASK_DISABLED.send(player);
} else {
BBC.BRUSH_SOURCE_MASK.send(player);
BBC.BRUSH_MASK.send(player);
}
}

View File

@ -318,6 +318,7 @@ public class BrushTool implements DoubleActionTraceTool, ScrollTool, MovableTool
bag.flushChanges();
}
session.remember(editSession);
Request.reset();
}
return true;
}

View File

@ -212,7 +212,9 @@ public final class CommandManager {
dispatcher = graph
.registerMethods(new AnvilCommands(worldEdit))
.group("/anvil", "anvil")
.describeAs("Anvil command")
.registerMethods(new AnvilCommands(worldEdit)).parent()
.registerMethods(new BiomeCommands(worldEdit))
.registerMethods(new ChunkCommands(worldEdit))
.registerMethods(new ClipboardCommands(worldEdit))
@ -425,6 +427,7 @@ public final class CommandManager {
if (time > 250 && hasSession) {
BBC.ACTION_COMPLETE.send(finalActor, (time / 1000d));
}
Request.reset();
}
}
}

View File

@ -0,0 +1,86 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit 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.sk89q.worldedit.extent;
import com.sk89q.worldedit.MutableBlockVector;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.Vector2D;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.world.biome.BaseBiome;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Requires that all mutating methods pass a given {@link Mask}.
*/
public class MaskingExtent extends AbstractDelegateExtent {
private Mask mask;
private MutableBlockVector mutable = new MutableBlockVector();
/**
* Create a new instance.
*
* @param extent the extent
* @param mask the mask
*/
public MaskingExtent(Extent extent, Mask mask) {
super(extent);
checkNotNull(mask);
this.mask = mask;
}
/**
* Get the mask.
*
* @return the mask
*/
public Mask getMask() {
return mask;
}
/**
* Set a mask.
*
* @param mask a mask
*/
public void setMask(Mask mask) {
checkNotNull(mask);
this.mask = mask;
}
@Override
public boolean setBlock(Vector location, BaseBlock block) throws WorldEditException {
return mask.test(location) && super.setBlock(location, block);
}
@Override
public boolean setBiome(Vector2D position, BaseBiome biome) {
return mask.test(mutable.setComponents(position.getBlockX(), 0, position.getBlockZ())) && super.setBiome(position, biome);
}
public static Class<?> inject() {
return MaskingExtent.class;
}
}

View File

@ -312,14 +312,6 @@ public class CuboidRegion extends AbstractRegion implements FlatRegion {
recalculate();
}
@Override
public void shift(Vector change) throws RegionOperationException {
pos1 = pos1.add(change);
pos2 = pos2.add(change);
recalculate();
}
@Override
public Set<Vector2D> getChunks() {
Vector min = getMinimumPoint();
@ -378,6 +370,14 @@ public class CuboidRegion extends AbstractRegion implements FlatRegion {
};
}
@Override
public void shift(Vector change) throws RegionOperationException {
pos1 = pos1.add(change);
pos2 = pos2.add(change);
recalculate();
}
@Override
public Set<Vector> getChunkCubes() {
Set chunks = new LocalBlockVectorSet();

View File

@ -70,9 +70,5 @@ public class ForgeMain {
if (player.worldObj.isRemote) {
return;
}
FawePlayer fp = FawePlayer.wrap(player);
if (fp.getMeta("lastWorld") != event.getWorld()) {
fp.setMeta("lastWorld", event.getWorld());
}
}
}

View File

@ -69,9 +69,5 @@ public class ForgeMain {
if (player.world.isRemote) {
return;
}
FawePlayer fp = FawePlayer.wrap(player);
if (fp.getMeta("lastWorld") != event.getWorld()) {
fp.setMeta("lastWorld", event.getWorld());
}
}
}

View File

@ -71,9 +71,5 @@ public class ForgeMain {
if (player.worldObj.isRemote) {
return;
}
FawePlayer fp = FawePlayer.wrap(player);
if (fp.getMeta("lastWorld") != event.world) {
fp.setMeta("lastWorld", event.world);
}
}
}

View File

@ -71,9 +71,5 @@ public class ForgeMain {
if (player.worldObj.isRemote) {
return;
}
FawePlayer fp = FawePlayer.wrap(player);
if (fp.getMeta("lastWorld") != event.world) {
fp.setMeta("lastWorld", event.world);
}
}
}

View File

@ -70,9 +70,5 @@ public class ForgeMain {
if (player.worldObj.isRemote) {
return;
}
FawePlayer fp = FawePlayer.wrap(player);
if (fp.getMeta("lastWorld") != event.getWorld()) {
fp.setMeta("lastWorld", event.getWorld());
}
}
}

BIN
region/r.-1.-1.mca Normal file

Binary file not shown.