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.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 Settings.IMP.HISTORY.COMBINE_STAGES = false; // Performing a chunk copy (if possible) wouldn't be faster using the BukkitAPI
if (hasNMS) { if (hasNMS) {
ignore.printStackTrace();
debug("====== NO NMS BLOCK PLACER FOUND ======"); debug("====== NO NMS BLOCK PLACER FOUND ======");
debug("FAWE couldn't find a fast block placer"); debug("FAWE couldn't find a fast block placer");
debug("Bukkit version: " + Bukkit.getVersion()); 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("Download the version of FAWE for your platform");
debug(" - http://ci.athion.net/job/FastAsyncWorldEdit/lastSuccessfulBuild/artifact/target"); debug(" - http://ci.athion.net/job/FastAsyncWorldEdit/lastSuccessfulBuild/artifact/target");
debug("======================================="); debug("=======================================");
ignore.printStackTrace();
debug("=======================================");
TaskManager.IMP.laterAsync(new Runnable() { TaskManager.IMP.laterAsync(new Runnable() {
@Override @Override
public void run() { public void run() {
@ -262,9 +264,11 @@ public class FaweBukkit implements IFawe, Listener {
} catch (Throwable ignore) { } catch (Throwable ignore) {
} }
} }
Throwable error = null;
try { try {
return plugin.getQueue(world); return plugin.getQueue(world);
} catch (Throwable ignore) { } catch (Throwable ignore) {
error = ignore;
} }
// Disable incompatible settings // Disable incompatible settings
Settings.IMP.QUEUE.PARALLEL_THREADS = 1; // BukkitAPI placer is too slow to parallel thread at the chunk level 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("====== NO NMS BLOCK PLACER FOUND ======");
debug("FAWE couldn't find a fast block placer"); debug("FAWE couldn't find a fast block placer");
debug("Bukkit version: " + Bukkit.getVersion()); 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("Fallback placer: " + BukkitQueue_All.class);
debug("======================================="); debug("=======================================");
debug("Download the version of FAWE for your platform"); debug("Download the version of FAWE for your platform");
debug(" - http://ci.athion.net/job/FastAsyncWorldEdit/lastSuccessfulBuild/artifact/target"); debug(" - http://ci.athion.net/job/FastAsyncWorldEdit/lastSuccessfulBuild/artifact/target");
debug("======================================="); debug("=======================================");
error.printStackTrace();
debug("=======================================");
TaskManager.IMP.laterAsync(new Runnable() { TaskManager.IMP.laterAsync(new Runnable() {
@Override @Override
public void run() { 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.RunnableVal;
import com.boydti.fawe.object.visitor.FaweChunkVisitor; import com.boydti.fawe.object.visitor.FaweChunkVisitor;
import com.boydti.fawe.util.MathMan; import com.boydti.fawe.util.MathMan;
import com.boydti.fawe.util.ReflectionUtils;
import com.boydti.fawe.util.TaskManager; import com.boydti.fawe.util.TaskManager;
import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
import com.sk89q.worldedit.world.biome.BaseBiome; import com.sk89q.worldedit.world.biome.BaseBiome;
import java.io.File; import java.io.File;
import java.io.RandomAccessFile;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Collection; 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 fieldTimingsEnabled;
private static Field fieldAsyncCatcherEnabled; private static Field fieldAsyncCatcherEnabled;
private static Method methodCheck; 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 { static {
try { try {
fieldAsyncCatcherEnabled = Class.forName("org.spigotmc.AsyncCatcher").getField("enabled"); 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 = Class.forName("co.aikar.timings.TimingsManager").getDeclaredMethod("recheckEnabled");
methodCheck.setAccessible(true); methodCheck.setAccessible(true);
} catch (Throwable ignore){} } 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 @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.example.CharFaweChunk;
import com.boydti.fawe.object.FaweChunk; import com.boydti.fawe.object.FaweChunk;
import com.boydti.fawe.object.FawePlayer; import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.RegionWrapper;
import com.boydti.fawe.object.RunnableVal; import com.boydti.fawe.object.RunnableVal;
import com.boydti.fawe.object.brush.visualization.VisualChunk; import com.boydti.fawe.object.brush.visualization.VisualChunk;
import com.boydti.fawe.object.number.LongAdder; import com.boydti.fawe.object.number.LongAdder;
@ -26,9 +27,11 @@ import java.io.IOException;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.ArrayDeque;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Iterator;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.UUID; 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.PacketPlayOutMultiBlockChange;
import net.minecraft.server.v1_11_R1.PlayerChunk; import net.minecraft.server.v1_11_R1.PlayerChunk;
import net.minecraft.server.v1_11_R1.PlayerChunkMap; 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.ServerNBTManager;
import net.minecraft.server.v1_11_R1.TileEntity; import net.minecraft.server.v1_11_R1.TileEntity;
import net.minecraft.server.v1_11_R1.WorldChunkManager; 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); 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 @Override
public void setHeightMap(FaweChunk chunk, byte[] heightMap) { public void setHeightMap(FaweChunk chunk, byte[] heightMap) {
CraftChunk craftChunk = (CraftChunk) chunk.getChunk(); 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.PlatformManager;
import com.sk89q.worldedit.extension.platform.PlayerProxy; import com.sk89q.worldedit.extension.platform.PlayerProxy;
import com.sk89q.worldedit.extent.AbstractDelegateExtent; 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.BlockArrayClipboard;
import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat; import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat;
import com.sk89q.worldedit.extent.clipboard.io.SchematicReader; import com.sk89q.worldedit.extent.clipboard.io.SchematicReader;
@ -440,6 +441,7 @@ public class Fawe {
// Regions // Regions
CuboidRegion.inject(); // Optimizations CuboidRegion.inject(); // Optimizations
// Extents // Extents
MaskingExtent.inject(); // Features
BlockTransformExtent.inject(); // Fix for cache not being mutable BlockTransformExtent.inject(); // Fix for cache not being mutable
AbstractDelegateExtent.inject(); // Optimizations AbstractDelegateExtent.inject(); // Optimizations
BlockBagExtent.inject(); // Fixes + Optimizations BlockBagExtent.inject(); // Fixes + Optimizations

View File

@ -1,13 +1,16 @@
package com.boydti.fawe.command; package com.boydti.fawe.command;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.FaweCache; import com.boydti.fawe.FaweCache;
import com.boydti.fawe.config.BBC; import com.boydti.fawe.config.BBC;
import com.boydti.fawe.jnbt.anvil.MCAChunk; import com.boydti.fawe.jnbt.anvil.MCAChunk;
import com.boydti.fawe.jnbt.anvil.MCAFilter; 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.jnbt.anvil.MCAQueue;
import com.boydti.fawe.object.FaweQueue; import com.boydti.fawe.object.FaweQueue;
import com.boydti.fawe.object.RegionWrapper;
import com.boydti.fawe.object.mask.FaweBlockMatcher; 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.SetQueue;
import com.boydti.fawe.util.StringMan; import com.boydti.fawe.util.StringMan;
import com.sk89q.minecraft.util.commands.Command; 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.WorldEdit;
import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.blocks.BlockType;
import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.function.pattern.Pattern;
import com.sk89q.worldedit.function.pattern.RandomPattern; 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.binding.Switch;
import com.sk89q.worldedit.util.command.parametric.Optional; import com.sk89q.worldedit.util.command.parametric.Optional;
import java.io.File; import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
@ -45,7 +54,7 @@ public class AnvilCommands {
} }
@Command( @Command(
aliases = {"/replaceall", "/rea", "/repall"}, aliases = {"replaceall", "rea", "repall"},
usage = "<folder> [from-block] <to-block>", usage = "<folder> [from-block] <to-block>",
desc = "Replace all blocks in the selection with another", desc = "Replace all blocks in the selection with another",
flags = "d", flags = "d",
@ -66,13 +75,13 @@ public class AnvilCommands {
final FaweBlockMatcher matchTo = FaweBlockMatcher.setBlocks(worldEdit.getBlocks(player, to, true)); final FaweBlockMatcher matchTo = FaweBlockMatcher.setBlocks(worldEdit.getBlocks(player, to, true));
File root = new File(folder + File.separator + "region"); File root = new File(folder + File.separator + "region");
MCAQueue queue = new MCAQueue(folder, root, true); MCAQueue queue = new MCAQueue(folder, root, true);
queue.filterWorld(new MCAFilter() { MCAFilterCounter counter = queue.filterWorld(new MCAFilterCounter() {
@Override @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); 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( @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 { 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); FaweQueue defaultQueue = SetQueue.IMP.getNewQueue(folder, true, false);
MCAQueue queue = new MCAQueue(folder, defaultQueue.getSaveFolder(), defaultQueue.hasSky()); MCAQueue queue = new MCAQueue(folder, defaultQueue.getSaveFolder(), defaultQueue.hasSky());
MCAFilterCounter counter;
if (useMap) { if (useMap) {
List<String> split = StringMan.split(from, ','); List<String> split = StringMan.split(from, ',');
if (to instanceof RandomPattern) { 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); private final MutableBlockVector mutable = new MutableBlockVector(0, 0, 0);
@Override @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 id = block.getId();
int data = FaweCache.hasData(id) ? block.getData() : 0; int data = FaweCache.hasData(id) ? block.getData() : 0;
int combined = FaweCache.getCombined(id, data); int combined = FaweCache.getCombined(id, data);
@ -137,9 +148,11 @@ public class AnvilCommands {
}); });
} else { } else {
player.print(BBC.getPrefix() + "Mask:Pattern must be a 1:1 match"); player.print(BBC.getPrefix() + "Mask:Pattern must be a 1:1 match");
return;
} }
} else { } else {
player.print(BBC.getPrefix() + "Must be a pattern list!"); player.print(BBC.getPrefix() + "Must be a pattern list!");
return;
} }
} else { } else {
final FaweBlockMatcher matchFrom; final FaweBlockMatcher matchFrom;
@ -151,9 +164,9 @@ public class AnvilCommands {
} }
matchFrom = FaweBlockMatcher.fromBlocks(worldEdit.getBlocks(player, from, true), useData); matchFrom = FaweBlockMatcher.fromBlocks(worldEdit.getBlocks(player, from, true), useData);
} }
queue.filterWorld(new MCAFilter() { counter = queue.filterWorld(new MCAFilterCounter() {
@Override @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)) { if (matchFrom.apply(block)) {
BaseBlock newBlock = to.apply(x, y, z); BaseBlock newBlock = to.apply(x, y, z);
int currentId = block.getId(); 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( @Command(
aliases = {"/countall"}, aliases = {"countall"},
usage = "<folder> [hasSky] <id>", usage = "<folder> [hasSky] <id>",
desc = "Count all blocks in a world", desc = "Count all blocks in a world",
flags = "d", 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 { 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"); File root = new File(folder + File.separator + "region");
MCAQueue queue = new MCAQueue(folder, root, true); MCAQueue queue = new MCAQueue(folder, root, true);
final LongAdder count = new LongAdder();
if (arg.contains(":")) { if (arg.contains(":")) {
useData = true; //override d flag, if they specified data they want it useData = true; //override d flag, if they specified data they want it
} }
@ -190,15 +202,15 @@ public class AnvilCommands {
for (BaseBlock block : searchBlocks) { for (BaseBlock block : searchBlocks) {
allowedId[block.getId()] = true; allowedId[block.getId()] = true;
} }
MCAFilter filter; MCAFilterCounter filter;
if (useData) { // Optimize for both cases if (useData) { // Optimize for both cases
final boolean[] allowed = new boolean[Character.MAX_VALUE]; final boolean[] allowed = new boolean[Character.MAX_VALUE];
for (BaseBlock block : searchBlocks) { for (BaseBlock block : searchBlocks) {
allowed[FaweCache.getCombined(block)] = true; allowed[FaweCache.getCombined(block)] = true;
} }
filter = new MCAFilter() { filter = new MCAFilterCounter() {
@Override @Override
public MCAChunk applyChunk(MCAChunk chunk) { public MCAChunk applyChunk(MCAChunk chunk, MutableLong count) {
for (int layer = 0; layer < chunk.ids.length; layer++) { for (int layer = 0; layer < chunk.ids.length; layer++) {
byte[] ids = chunk.ids[layer]; byte[] ids = chunk.ids[layer];
if (ids == null) { if (ids == null) {
@ -215,7 +227,7 @@ public class AnvilCommands {
combined += chunk.getNibble(i, datas); combined += chunk.getNibble(i, datas);
} }
if (allowed[combined]) { if (allowed[combined]) {
count.add(1); count.increment();
} }
} }
} }
@ -223,15 +235,15 @@ public class AnvilCommands {
} }
}; };
} else { } else {
filter = new MCAFilter() { filter = new MCAFilterCounter() {
@Override @Override
public MCAChunk applyChunk(MCAChunk chunk) { public MCAChunk applyChunk(MCAChunk chunk, MutableLong count) {
for (int layer = 0; layer < chunk.ids.length; layer++) { for (int layer = 0; layer < chunk.ids.length; layer++) {
byte[] ids = chunk.ids[layer]; byte[] ids = chunk.ids[layer];
if (ids != null) { if (ids != null) {
for (byte i : ids) { for (byte i : ids) {
if (allowedId[i & 0xFF]) { if (allowedId[i & 0xFF]) {
count.add(1); count.increment();
} }
} }
} }
@ -241,7 +253,202 @@ public class AnvilCommands {
}; };
} }
queue.filterWorld(filter); 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>"); BBC.COMMAND_SYNTAX.send(player, "/frb <info|undo> u:<uuid> r:<radius> t:<time>");
return false; return false;
} }
player.deleteMeta("rollback"); player.deleteMeta(FawePlayer.METADATA_KEYS.ROLLBACK);
final FaweLocation origin = player.getLocation(); final FaweLocation origin = player.getLocation();
rollback(player, !player.hasPermission("fawe.rollback.deep"), Arrays.copyOfRange(args, 1, args.length), new RunnableVal<List<DiskStorageHistory>>() { rollback(player, !player.hasPermission("fawe.rollback.deep"), Arrays.copyOfRange(args, 1, args.length), new RunnableVal<List<DiskStorageHistory>>() {
@Override @Override
@ -91,7 +91,7 @@ public class Rollback extends FaweCommand {
player.sendMessage("&dSize: " + (((double) (total / 1024)) / 1000) + "MB"); player.sendMessage("&dSize: " + (((double) (total / 1024)) / 1000) + "MB");
player.sendMessage("&dTo rollback: /frb undo"); player.sendMessage("&dTo rollback: /frb undo");
player.sendMessage("&d=================================================="); player.sendMessage("&d==================================================");
player.setMeta("rollback", edits); player.setMeta(FawePlayer.METADATA_KEYS.ROLLBACK, edits);
} }
}); });
break; break;
@ -102,8 +102,8 @@ public class Rollback extends FaweCommand {
BBC.NO_PERM.send(player, "fawe.rollback.perform"); BBC.NO_PERM.send(player, "fawe.rollback.perform");
return false; return false;
} }
final List<DiskStorageHistory> edits = (List<DiskStorageHistory>) player.getMeta("rollback"); final List<DiskStorageHistory> edits = (List<DiskStorageHistory>) player.getMeta(FawePlayer.METADATA_KEYS.ROLLBACK);
player.deleteMeta("rollback"); player.deleteMeta(FawePlayer.METADATA_KEYS.ROLLBACK);
if (edits == null) { if (edits == null) {
BBC.COMMAND_SYNTAX.send(player, "/frb info u:<uuid> r:<radius> t:<time>"); BBC.COMMAND_SYNTAX.send(player, "/frb info u:<uuid> r:<radius> t:<time>");
return false; return false;

View File

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

View File

@ -19,6 +19,7 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
@ -50,8 +51,7 @@ public class MCAChunk extends FaweChunk<Void> {
private long lastUpdate; private long lastUpdate;
private int[] heightMap; private int[] heightMap;
public int compressedSize; private int modified;
private boolean modified;
private boolean deleted; private boolean deleted;
public byte[] toBytes(byte[] buffer) throws IOException { public byte[] toBytes(byte[] buffer) throws IOException {
@ -113,6 +113,91 @@ public class MCAChunk extends FaweChunk<Void> {
return buffered.toByteArray(); 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() { public CompoundTag toTag() {
if (deleted) { if (deleted) {
return null; return null;
@ -164,7 +249,7 @@ public class MCAChunk extends FaweChunk<Void> {
this.entities = new HashMap<>(); this.entities = new HashMap<>();
this.lastUpdate = System.currentTimeMillis(); this.lastUpdate = System.currentTimeMillis();
this.heightMap = new int[256]; this.heightMap = new int[256];
this.modified = true; this.setModified();
} }
public MCAChunk(MCAChunk parent, boolean shallow) { public MCAChunk(MCAChunk parent, boolean shallow) {
@ -180,7 +265,6 @@ public class MCAChunk extends FaweChunk<Void> {
this.inhabitedTime = parent.inhabitedTime; this.inhabitedTime = parent.inhabitedTime;
this.lastUpdate = parent.lastUpdate; this.lastUpdate = parent.lastUpdate;
this.heightMap = parent.heightMap; this.heightMap = parent.heightMap;
this.compressedSize = parent.compressedSize;
this.modified = parent.modified; this.modified = parent.modified;
this.deleted = parent.deleted; this.deleted = parent.deleted;
} else { } else {
@ -194,7 +278,6 @@ public class MCAChunk extends FaweChunk<Void> {
this.inhabitedTime = parent.inhabitedTime; this.inhabitedTime = parent.inhabitedTime;
this.lastUpdate = parent.lastUpdate; this.lastUpdate = parent.lastUpdate;
this.heightMap = parent.heightMap.clone(); this.heightMap = parent.heightMap.clone();
this.compressedSize = parent.compressedSize;
this.modified = parent.modified; this.modified = parent.modified;
this.deleted = parent.deleted; this.deleted = parent.deleted;
} }
@ -206,7 +289,6 @@ public class MCAChunk extends FaweChunk<Void> {
data = new byte[16][]; data = new byte[16][];
skyLight = new byte[16][]; skyLight = new byte[16][];
blockLight = new byte[16][]; blockLight = new byte[16][];
this.compressedSize = compressedSize;
NBTStreamer streamer = new NBTStreamer(nis); NBTStreamer streamer = new NBTStreamer(nis);
streamer.addReader(".Level.InhabitedTime", new RunnableVal2<Integer, Long>() { streamer.addReader(".Level.InhabitedTime", new RunnableVal2<Integer, Long>() {
@Override @Override
@ -281,12 +363,16 @@ public class MCAChunk extends FaweChunk<Void> {
} }
public boolean isModified() { public boolean isModified() {
return modified != 0;
}
public int getModified() {
return modified; return modified;
} }
@Deprecated @Deprecated
public final void setModified() { public final void setModified() {
this.modified = true; this.modified++;
} }
@Override @Override
@ -302,7 +388,7 @@ public class MCAChunk extends FaweChunk<Void> {
@Override @Override
public void setTile(int x, int y, int z, CompoundTag tile) { public void setTile(int x, int y, int z, CompoundTag tile) {
modified = true; setModified();
short pair = MathMan.tripleBlockCoord(x, y, z); short pair = MathMan.tripleBlockCoord(x, y, z);
if (tile != null) { if (tile != null) {
tiles.put(pair, tile); tiles.put(pair, tile);
@ -313,7 +399,7 @@ public class MCAChunk extends FaweChunk<Void> {
@Override @Override
public void setEntity(CompoundTag entityTag) { public void setEntity(CompoundTag entityTag) {
modified = true; setModified();
long least = entityTag.getLong("UUIDLeast"); long least = entityTag.getLong("UUIDLeast");
long most = entityTag.getLong("UUIDMost"); long most = entityTag.getLong("UUIDMost");
entities.put(new UUID(most, least), entityTag); entities.put(new UUID(most, least), entityTag);
@ -321,7 +407,7 @@ public class MCAChunk extends FaweChunk<Void> {
@Override @Override
public void setBiome(int x, int z, byte biome) { public void setBiome(int x, int z, byte biome) {
modified = true; setModified();
biomes[x + (z << 4)] = biome; 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) { public void setSkyLight(int x, int y, int z, int value) {
modified = true; setModified();
int layer = y >> 4; int layer = y >> 4;
byte[] skyLayer = skyLight[layer]; byte[] skyLayer = skyLight[layer];
if (skyLayer == null) { if (skyLayer == null) {
@ -393,7 +479,7 @@ public class MCAChunk extends FaweChunk<Void> {
} }
public void setBlockLight(int x, int y, int z, int value) { public void setBlockLight(int x, int y, int z, int value) {
modified = true; setModified();
int layer = y >> 4; int layer = y >> 4;
byte[] blockLayer = blockLight[layer]; byte[] blockLayer = blockLight[layer];
if (blockLayer == null) { if (blockLayer == null) {
@ -424,7 +510,7 @@ public class MCAChunk extends FaweChunk<Void> {
} }
public void setFullbright() { public void setFullbright() {
modified = true; setModified();
for (byte[] array : skyLight) { for (byte[] array : skyLight) {
if (array != null) { if (array != null) {
Arrays.fill(array, (byte) 255); Arrays.fill(array, (byte) 255);
@ -478,7 +564,7 @@ public class MCAChunk extends FaweChunk<Void> {
@Override @Override
public void setBlock(int x, int y, int z, int id, int data) { public void setBlock(int x, int y, int z, int id, int data) {
modified = true; setModified();
int layer = y >> 4; int layer = y >> 4;
byte[] idsLayer = ids[layer]; byte[] idsLayer = ids[layer];
if (idsLayer == null) { if (idsLayer == null) {
@ -500,7 +586,7 @@ public class MCAChunk extends FaweChunk<Void> {
@Override @Override
public void removeEntity(UUID uuid) { public void removeEntity(UUID uuid) {
modified = true; setModified();
entities.remove(uuid); 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.exception.FaweException;
import com.boydti.fawe.object.io.BufferedRandomAccessFile; import com.boydti.fawe.object.io.BufferedRandomAccessFile;
import com.boydti.fawe.object.io.FastByteArrayInputStream; 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.MainUtil;
import com.boydti.fawe.util.MathMan; import com.boydti.fawe.util.MathMan;
import com.sk89q.jnbt.NBTInputStream; import com.sk89q.jnbt.NBTInputStream;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import java.io.BufferedInputStream; import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.RandomAccessFile; import java.io.RandomAccessFile;
@ -25,7 +24,6 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ForkJoinPool; import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.Inflater; import java.util.zip.Inflater;
import java.util.zip.InflaterInputStream; import java.util.zip.InflaterInputStream;
@ -37,30 +35,24 @@ public class MCAFile {
private static Field fieldBuf2; private static Field fieldBuf2;
private static Field fieldBuf3; private static Field fieldBuf3;
private static Field fieldBuf4;
private static Field fieldBuf5;
private static Field fieldBuf6;
static { static {
try { try {
fieldBuf2 = InflaterInputStream.class.getDeclaredField("buf"); fieldBuf2 = InflaterInputStream.class.getDeclaredField("buf");
fieldBuf2.setAccessible(true); fieldBuf2.setAccessible(true);
fieldBuf3 = NBTInputStream.class.getDeclaredField("buf"); fieldBuf3 = NBTInputStream.class.getDeclaredField("buf");
fieldBuf3.setAccessible(true); 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) { } catch (Throwable e) {
e.printStackTrace(); e.printStackTrace();
} }
} }
private FaweQueue queue; private final FaweQueue queue;
private File file; private final File file;
private RandomAccessFile raf; private RandomAccessFile raf;
private byte[] locations; 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[]>() { final ThreadLocal<byte[]> byteStore1 = new ThreadLocal<byte[]>() {
@Override @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) { public MCAFile(FaweQueue parent, File file) {
this.queue = parent; this.queue = parent;
this.file = file; this.file = file;
@ -96,6 +84,26 @@ public class MCAFile {
Z = Integer.parseInt(split[2]); 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() { public FaweQueue getParent() {
return queue; return queue;
} }
@ -105,9 +113,12 @@ public class MCAFile {
if (raf == null) { if (raf == null) {
this.locations = new byte[4096]; this.locations = new byte[4096];
this.raf = new RandomAccessFile(file, "rw"); this.raf = new RandomAccessFile(file, "rw");
raf.seek(0); if (raf.length() < 8192) {
raf.readFully(locations); raf.setLength(8192);
} else {
raf.seek(0);
raf.readFully(locations);
}
} }
} catch (Throwable e) { } catch (Throwable e) {
e.printStackTrace(); 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 { public MCAChunk getChunk(int cx, int cz) throws IOException {
MCAChunk cached = getCachedChunk(cx, cz); MCAChunk cached = getCachedChunk(cx, cz);
if (cached != null) { 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 { 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); 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.seek(i);
raf.write((offsetMedium >> 16)); raf.write((offsetMedium >> 16));
raf.write((offsetMedium >> 8)); raf.write((offsetMedium >> 8));
@ -370,11 +392,8 @@ public class MCAFile {
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
file = null;
raf = null; raf = null;
locations = null; locations = null;
queue = null;
chunks = null;
} }
} }
} }
@ -389,6 +408,7 @@ public class MCAFile {
Int2ObjectOpenHashMap<byte[]> relocate = new Int2ObjectOpenHashMap<>(); Int2ObjectOpenHashMap<byte[]> relocate = new Int2ObjectOpenHashMap<>();
final Int2ObjectOpenHashMap<Integer> offsetMap = new Int2ObjectOpenHashMap<>(); // Offset -> <byte cx, byte cz, short size> final Int2ObjectOpenHashMap<Integer> offsetMap = new Int2ObjectOpenHashMap<>(); // Offset -> <byte cx, byte cz, short size>
final Int2ObjectOpenHashMap<byte[]> compressedMap = new Int2ObjectOpenHashMap<>(); final Int2ObjectOpenHashMap<byte[]> compressedMap = new Int2ObjectOpenHashMap<>();
final Int2ObjectOpenHashMap<byte[]> append = new Int2ObjectOpenHashMap<>();
boolean modified = false; boolean modified = false;
for (MCAChunk chunk : getCachedChunks()) { for (MCAChunk chunk : getCachedChunks()) {
if (chunk.isModified()) { if (chunk.isModified()) {
@ -400,8 +420,14 @@ public class MCAFile {
try { try {
byte[] compressed = toBytes(chunk); byte[] compressed = toBytes(chunk);
int pair = MathMan.pair((short) (chunk.getX() & 31), (short) (chunk.getZ() & 31)); int pair = MathMan.pair((short) (chunk.getX() & 31), (short) (chunk.getZ() & 31));
synchronized (compressedMap) { Int2ObjectOpenHashMap map;
compressedMap.put(pair, compressed); if (getOffset(chunk.getX(), chunk.getZ()) == 0) {
map = append;
} else {
map = compressedMap;
}
synchronized (map) {
map.put(pair, compressed);
} }
} catch (Throwable e) { } catch (Throwable e) {
e.printStackTrace(); e.printStackTrace();
@ -473,7 +499,8 @@ public class MCAFile {
short nextCXZ = MathMan.unpairX(nextLoc); short nextCXZ = MathMan.unpairX(nextLoc);
int nextCX = MathMan.unpairShortX(nextCXZ); int nextCX = MathMan.unpairShortX(nextCXZ);
int nextCZ = MathMan.unpairShortY(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); byte[] nextBytes = getChunkCompressedBytes(nextOffset2);
relocate.put(MathMan.pair((short) (nextCX & 31), (short) (nextCZ & 31)), nextBytes); relocate.put(MathMan.pair((short) (nextCX & 31), (short) (nextCZ & 31)), nextBytes);
} }
@ -490,6 +517,22 @@ public class MCAFile {
written = start + newBytes.length + 5; written = start + newBytes.length + 5;
start += newSize << 12; 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)); raf.setLength(4096 * ((written + 4095) / 4096));
if (raf instanceof BufferedRandomAccessFile) { if (raf instanceof BufferedRandomAccessFile) {
((BufferedRandomAccessFile) raf).flush(); ((BufferedRandomAccessFile) raf).flush();

View File

@ -1,12 +1,13 @@
package com.boydti.fawe.jnbt.anvil; package com.boydti.fawe.jnbt.anvil;
import com.boydti.fawe.object.collection.IterableThreadLocal;
import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.blocks.BaseBlock;
/** /**
* MCAQueue.filterWorld(MCAFilter)<br> * MCAQueue.filterWorld(MCAFilter)<br>
* - Read and modify the world * - Read and modify the world
*/ */
public class MCAFilter { public class MCAFilter<T> extends IterableThreadLocal<T> {
/** /**
* Check whether a .mca file should be read * Check whether a .mca file should be read
@ -46,7 +47,7 @@ public class MCAFilter {
* @param chunk * @param chunk
* @return * @return
*/ */
public MCAChunk applyChunk(MCAChunk chunk) { public MCAChunk applyChunk(MCAChunk chunk, T cache) {
return chunk; return chunk;
} }
@ -59,5 +60,13 @@ public class MCAFilter {
* @param z * @param z
* @param block * @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.CharFaweChunk;
import com.boydti.fawe.example.NMSMappedFaweQueue; import com.boydti.fawe.example.NMSMappedFaweQueue;
import com.boydti.fawe.example.NullFaweChunk;
import com.boydti.fawe.object.FaweChunk; import com.boydti.fawe.object.FaweChunk;
import com.boydti.fawe.object.FawePlayer; import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.FaweQueue; 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.object.RunnableVal4;
import com.boydti.fawe.util.MainUtil;
import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.world.biome.BaseBiome; import com.sk89q.worldedit.world.biome.BaseBiome;
import java.io.File; 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.Collection;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@ -22,6 +30,12 @@ public class MCAQueue extends NMSMappedFaweQueue<FaweQueue, FaweChunk, FaweChunk
private NMSMappedFaweQueue parentNMS; private NMSMappedFaweQueue parentNMS;
private final boolean hasSky; private final boolean hasSky;
private final File saveFolder; private final File saveFolder;
private final ThreadLocal<MutableMCABackedBaseBlock> blockStore = new ThreadLocal<MutableMCABackedBaseBlock>() {
@Override
protected MutableMCABackedBaseBlock initialValue() {
return new MutableMCABackedBaseBlock();
}
};
public MCAQueue(FaweQueue parent) { public MCAQueue(FaweQueue parent) {
super(parent.getWEWorld(), new MCAQueueMap()); super(parent.getWEWorld(), new MCAQueueMap());
@ -67,100 +81,260 @@ 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(); File folder = getSaveFolder();
final ForkJoinPool pool = new ForkJoinPool(); final ForkJoinPool pool = new ForkJoinPool();
final ThreadLocal<MutableMCABackedBaseBlock> blockStore = new ThreadLocal<MutableMCABackedBaseBlock>() { int bMcaX = (regionTo.minX >> 9);
@Override int bMcaZ = (regionTo.minZ >> 9);
protected MutableMCABackedBaseBlock initialValue() { int tMcaX = (regionTo.maxX >> 9);
return new MutableMCABackedBaseBlock(); int tMcaZ = (regionTo.maxZ >> 9);
} for (int mcaZ = bMcaZ; mcaZ <= tMcaZ; mcaZ++) {
}; for (int mcaX = bMcaX; mcaX <= tMcaX; mcaX++) {
for (final File file : folder.listFiles()) { int bcx = Math.max(mcaX << 5, regionTo.minX >> 4);
try { int bcz = Math.max(mcaZ << 5, regionTo.minZ >> 4);
String name = file.getName(); int tcx = Math.min((mcaX << 5) + 31, regionTo.maxX >> 4);
String[] split = name.split("\\."); int tcz = Math.min((mcaZ << 5) + 31, regionTo.maxZ >> 4);
final int mcaX = Integer.parseInt(split[1]); File file = new File(folder, "r." + mcaX + "." + mcaZ + ".mca");
final int mcaZ = Integer.parseInt(split[2]); if (!file.exists()) {
if (filter.appliesFile(mcaX, mcaZ)) { file.createNewFile();
MCAFile mcaFile = new MCAFile(this, file); }
final MCAFile original = mcaFile; MCAFile mcaFile = new MCAFile(null, file);
final MCAFile finalFile = filter.applyFile(mcaFile); mcaFile.init();
if (finalFile != null) { // If oX & 15 == 0 && oZ && 15 == 0
finalFile.init(); if ((oX & 15) == 0 && (oZ & 15) == 0) {
// May not do anything, but seems to lead to smaller lag spikes if (oY == 0) {
final int cbx = mcaX << 5; if (regionFrom.minY == 0 && regionFrom.maxY == 255) {
final int cbz = mcaZ << 5; 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 {
finalFile.forEachSortedChunk(new RunnableVal4<Integer, Integer, Integer, Integer>() { }
@Override }
public void run(final Integer rcx, final Integer rcz, Integer offset, Integer size) { }
pool.submit(new Runnable() { }
@Override }
public void run() { } else if ((oY & 15) == 0) {
int cx = cbx + rcx; } else {
int cz = cbz + rcz; }
if (filter.appliesChunk(cx, cz)) { } 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 = 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)) {
File file = path.toFile();
MCAFile mcaFile = new MCAFile(MCAQueue.this, file);
final MCAFile original = mcaFile;
final MCAFile finalFile = filter.applyFile(mcaFile);
if (finalFile != null && !finalFile.isDeleted()) {
finalFile.init();
// May not do anything, but seems to lead to smaller lag spikes
final int cbx = mcaX << 5;
final int cbz = mcaZ << 5;
finalFile.forEachSortedChunk(new RunnableVal4<Integer, Integer, Integer, Integer>() {
@Override
public void run(final Integer rcx, final Integer rcz, Integer offset, Integer size) {
pool.submit(new Runnable() {
@Override
public void run() {
try { try {
MCAChunk chunk = finalFile.getChunk(cx, cz); int cx = cbx + rcx;
try { int cz = cbz + rcz;
chunk = filter.applyChunk(chunk); if (filter.appliesChunk(cx, cz)) {
if (chunk != null) { MCAChunk chunk = finalFile.getChunk(cx, cz);
final MutableMCABackedBaseBlock mutableBlock = blockStore.get(); try {
mutableBlock.setChunk(chunk); final G value = filter.get();
int bx = cx << 4; chunk = filter.applyChunk(chunk, value);
int bz = cz << 4; if (chunk != null) {
for (int layer = 0; layer < chunk.ids.length; layer++) { final MutableMCABackedBaseBlock mutableBlock = blockStore.get();
if (chunk.doesSectionExist(layer)) { mutableBlock.setChunk(chunk);
mutableBlock.setArrays(layer); int bx = cx << 4;
int yStart = layer << 4; int bz = cz << 4;
int index = 0; for (int layer = 0; layer < chunk.ids.length; layer++) {
for (int y = yStart; y < yStart + 16; y++) { if (chunk.doesSectionExist(layer)) {
mutableBlock.setY(y); mutableBlock.setArrays(layer);
for (int z = bz; z < bz + 16; z++) { int yStart = layer << 4;
mutableBlock.setZ(z); int index = 0;
for (int x = bx; x < bx + 16; x++,index++) { for (int y = yStart; y < yStart + 16; y++) {
mutableBlock.setX(x); mutableBlock.setY(y);
mutableBlock.setIndex(index); for (int z = bz; z < bz + 16; z++) {
filter.applyBlock(x, y, z, mutableBlock); mutableBlock.setZ(z);
for (int x = bx; x < bx + 16; x++, index++) {
mutableBlock.setX(x);
mutableBlock.setIndex(index);
filter.applyBlock(x, y, z, mutableBlock, value);
}
} }
} }
} }
} }
filter.finishChunk(chunk, value);
} }
} catch (Throwable e) {
e.printStackTrace();
} }
} catch (Throwable e) {
e.printStackTrace();
} }
} catch (Throwable e) { } catch (Throwable e) {
System.out.println("Failed to load: r." + mcaX + "." + mcaZ + ".mca -> (local) " + rcx + "," + rcz); System.out.println("Failed to load: r." + mcaX + "." + mcaZ + ".mca -> (local) " + rcx + "," + rcz);
e.printStackTrace(); e.printStackTrace();
} }
} }
} });
}); }
} });
}); pool.awaitQuiescence(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
pool.awaitQuiescence(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
original.close(pool);
if (original != finalFile) finalFile.close(pool);
} else {
try {
original.close(pool); original.close(pool);
file.delete(); if (original != finalFile) finalFile.close(pool);
} catch (Throwable ignore) {} } else if (mcaFile.isDeleted()) {
try {
original.close(pool);
file.delete();
} catch (Throwable ignore) {
ignore.printStackTrace();
}
}
} }
} catch (Throwable ignore) {
ignore.printStackTrace();
} }
} catch (Throwable ignore) {
ignore.printStackTrace();
} }
} });
pool.shutdown(); pool.shutdown();
try { try {
pool.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS); pool.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
} catch (Throwable e) { } catch (Throwable e) {
e.printStackTrace(); e.printStackTrace();
} }
return filter;
} }
@Override @Override

View File

@ -133,7 +133,16 @@ public class MCAQueueMap implements IFaweQueueMap {
@Override @Override
public void clear() { public void clear() {
for (Map.Entry<Long, MCAFile> entry : mcaFileMap.entrySet()) {
entry.getValue().clear();
}
mcaFileMap.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) { if (isHybridQueue) {
queue.clear(); queue.clear();
} }

View File

@ -44,6 +44,11 @@ public abstract class FawePlayer<T> extends Metadatable {
public final T parent; public final T parent;
private LocalSession session; 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> * Wrap some object into a FawePlayer<br>
* - org.bukkit.entity.Player * - org.bukkit.entity.Player

View File

@ -220,6 +220,10 @@ public abstract class FaweQueue implements HasFaweQueue {
public abstract Collection<FaweChunk> getFaweChunks(); 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 void setChunk(final FaweChunk chunk);
public abstract File getSaveFolder(); public abstract File getSaveFolder();

View File

@ -61,6 +61,22 @@ public class RegionWrapper {
return lr && (x >= this.minX && x <= this.maxX); 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) { public boolean isIn(final int x, final int z) {
return ((x >= this.minX) && (x <= this.maxX) && (z >= this.minZ) && (z <= this.maxZ)); return ((x >= this.minX) && (x <= this.maxX) && (z >= this.minZ) && (z <= this.maxZ));
} }
@ -110,7 +126,7 @@ public class RegionWrapper {
@Override @Override
public String toString() { 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() { 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 z = 0; z < length; z++) {
for (int x = 0; x < width; x++, index++) { for (int x = 0; x < width; x++, index++) {
BaseBlock block = getBlock(index); BaseBlock block = getBlock(index);
System.out.println(block);
if (block.getId() != 0) { if (block.getId() != 0) {
task.run(x, y, z, block); 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

@ -26,4 +26,4 @@ public class IdDataMaskPattern extends AbstractPattern {
int newData = newBlock.getData() + oldData - (oldData & mask); int newData = newBlock.getData() + oldData - (oldData & mask);
return FaweCache.getBlock(newBlock.getId(), newData); return FaweCache.getBlock(newBlock.getId(), newData);
} }
} }

View File

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

View File

@ -136,6 +136,7 @@ public class PlotTrim implements Listener {
ChunkLoc loc = new ChunkLoc(mcaX, mcaZ); ChunkLoc loc = new ChunkLoc(mcaX, mcaZ);
if (mcas.contains(loc)) { if (mcas.contains(loc)) {
player.sendMessage("Delete MCA " + mca); player.sendMessage("Delete MCA " + mca);
mca.setDeleted(true);
return null; return null;
} }
try { try {
@ -159,7 +160,7 @@ public class PlotTrim implements Listener {
} }
@Override @Override
public MCAChunk applyChunk(MCAChunk chunk) { public MCAChunk applyChunk(MCAChunk chunk, Object ignore) {
long pair = MathMan.pairInt(chunk.getX(), chunk.getZ()); long pair = MathMan.pairInt(chunk.getX(), chunk.getZ());
if (chunks.containsKey(pair)) { if (chunks.containsKey(pair)) {
chunk.setDeleted(true); 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.FaweChunk;
import com.boydti.fawe.object.FawePlayer; import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.FaweQueue; import com.boydti.fawe.object.FaweQueue;
import com.boydti.fawe.object.RegionWrapper;
import com.boydti.fawe.object.RunnableVal2; import com.boydti.fawe.object.RunnableVal2;
import com.boydti.fawe.object.exception.FaweException; import com.boydti.fawe.object.exception.FaweException;
import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.CompoundTag;
@ -43,6 +44,11 @@ public class DelegateFaweQueue extends FaweQueue {
parent.sendChunk(x, z, bitMask); parent.sendChunk(x, z, bitMask);
} }
@Override
public boolean setMCA(Runnable whileLocked, RegionWrapper region, boolean unload) {
return parent.setMCA(whileLocked, region, unload);
}
@Override @Override
public String getWorldName() { public String getWorldName() {
return parent.getWorldName(); return parent.getWorldName();

View File

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

View File

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

View File

@ -94,9 +94,9 @@ public class ToolUtilCommands {
} }
} }
if (mask == null) { if (mask == null) {
BBC.BRUSH_SOURCE_MASK_DISABLED.send(player); BBC.BRUSH_MASK_DISABLED.send(player);
} else { } 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(); bag.flushChanges();
} }
session.remember(editSession); session.remember(editSession);
Request.reset();
} }
return true; return true;
} }

View File

@ -212,7 +212,9 @@ public final class CommandManager {
dispatcher = graph dispatcher = graph
.registerMethods(new AnvilCommands(worldEdit)) .group("/anvil", "anvil")
.describeAs("Anvil command")
.registerMethods(new AnvilCommands(worldEdit)).parent()
.registerMethods(new BiomeCommands(worldEdit)) .registerMethods(new BiomeCommands(worldEdit))
.registerMethods(new ChunkCommands(worldEdit)) .registerMethods(new ChunkCommands(worldEdit))
.registerMethods(new ClipboardCommands(worldEdit)) .registerMethods(new ClipboardCommands(worldEdit))
@ -425,6 +427,7 @@ public final class CommandManager {
if (time > 250 && hasSession) { if (time > 250 && hasSession) {
BBC.ACTION_COMPLETE.send(finalActor, (time / 1000d)); 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(); recalculate();
} }
@Override
public void shift(Vector change) throws RegionOperationException {
pos1 = pos1.add(change);
pos2 = pos2.add(change);
recalculate();
}
@Override @Override
public Set<Vector2D> getChunks() { public Set<Vector2D> getChunks() {
Vector min = getMinimumPoint(); 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 @Override
public Set<Vector> getChunkCubes() { public Set<Vector> getChunkCubes() {
Set chunks = new LocalBlockVectorSet(); Set chunks = new LocalBlockVectorSet();

View File

@ -70,9 +70,5 @@ public class ForgeMain {
if (player.worldObj.isRemote) { if (player.worldObj.isRemote) {
return; 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) { if (player.world.isRemote) {
return; 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) { if (player.worldObj.isRemote) {
return; 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) { if (player.worldObj.isRemote) {
return; 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) { if (player.worldObj.isRemote) {
return; 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.