mirror of
https://github.com/boy0001/FastAsyncWorldedit.git
synced 2024-11-24 19:46:34 +01:00
Anvil replace + masking extent biomes
This commit is contained in:
parent
b39ab79f16
commit
c7d959d6dc
@ -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() {
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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!");
|
||||
// }
|
||||
}
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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();
|
||||
|
@ -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) {}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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() {
|
||||
|
@ -0,0 +1,4 @@
|
||||
package com.boydti.fawe.object.brush;
|
||||
|
||||
public class FallingSphere {
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -1303,7 +1303,6 @@ public class LocalSession {
|
||||
if (transform != null) {
|
||||
editSession.addTransform(transform);
|
||||
}
|
||||
|
||||
return editSession;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -318,6 +318,7 @@ public class BrushTool implements DoubleActionTraceTool, ScrollTool, MovableTool
|
||||
bag.flushChanges();
|
||||
}
|
||||
session.remember(editSession);
|
||||
Request.reset();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
BIN
region/r.-1.-1.mca
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user