More optimizations!

Schematics now load in a fraction of a time. (took me 5.13 seconds to
load a 102,572,228 block schematic)

Also made some minor optimizations here and there.
This commit is contained in:
Jesse Boyd 2016-04-18 04:54:27 +10:00
parent d6ad027fc8
commit 9cf4f6c9f0
13 changed files with 503 additions and 183 deletions

View File

@ -10,7 +10,7 @@ buildscript {
}
group = 'com.boydti.fawe'
version = '3.3.13'
version = '3.3.14'
description = """FastAsyncWorldEdit"""
subprojects {

View File

@ -1,6 +1,6 @@
name: FastAsyncWorldEdit
main: com.boydti.fawe.bukkit.FaweBukkit
version: 3.3.13
version: 3.3.14
description: Fast Async WorldEdit plugin
authors: [Empire92]
loadbefore: [WorldEdit]

View File

@ -1,6 +1,6 @@
name: FastAsyncWorldEdit
main: com.boydti.fawe.bukkit.FaweBukkit
version: 3.3.13
version: 3.3.14
description: Fast Async WorldEdit plugin
authors: [Empire92]
loadbefore: [WorldEdit]

View File

@ -20,6 +20,10 @@ public class FaweCache {
// Faster than java random (since the game just needs to look random)
public final static PseudoRandom RANDOM = new PseudoRandom();
public static BaseBlock getBlock(int id, int data) {
return CACHE_BLOCK[(id << 4) + data];
}
static {
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {

View File

@ -42,6 +42,7 @@ public enum BBC {
WORLDEDIT_OOM("&cYour WorldEdit action was cancelled due to low memory.", "Info"),
WORLDEDIT_OOM_ADMIN("&cPossible options:\n&8 - &7//fast\n&8 - &7Do smaller edits\n&8 - &7Allocate more memory\n&8 - &7Disable this safeguard", "Info"),
NOT_PLAYER("&cYou must be a player to perform this action!", "Error"),
COMPRESSED("History compressed. Saved ~ %s0b (%s1x smaller)", "Info"),
OOM(
"&8[&cCritical&8] &cDetected low memory i.e. < 1%. FAWE will take the following actions:\n&8 - &7Terminate WE block placement\n&8 - &7Clear WE history\n&8 - &7Unload non essential chunks\n&8 - &7Kill entities\n&8 - &7Garbage collect\n&cIgnore this if trying to crash server.\n&7Note: Low memory is likely (but not necessarily) caused by WE",
"Error");

View File

@ -379,20 +379,22 @@ public class DiskStorageHistory implements ChangeSet, FaweChangeSet {
int y = gis.read() & 0xff;
int from1 = gis.read();
int from2 = gis.read();
BaseBlock from = new BaseBlock(((from2 << 4) + (from1 >> 4)), (from1 & 0xf));
BaseBlock from = FaweCache.getBlock(((from2 << 4) + (from1 >> 4)), (from1 & 0xf));
if (lastFrom != null && FaweCache.hasNBT(from.getId())) {
Map<String, Tag> t = lastFrom.getValue();
if (((IntTag) t.get("x")).getValue() == x && ((IntTag) t.get("z")).getValue() == z && ((IntTag) t.get("y")).getValue() == y) {
from = new BaseBlock(from.getId(), from.getData());
from.setNbtData(lastFrom);
lastFrom = read(nbtf);
}
}
int to1 = gis.read();
int to2 = gis.read();
BaseBlock to = new BaseBlock(((to2 << 4) + (to1 >> 4)), (to1 & 0xf));
BaseBlock to = FaweCache.getBlock(((to2 << 4) + (to1 >> 4)), (to1 & 0xf));
if (lastTo != null && FaweCache.hasNBT(to.getId())) {
Map<String, Tag> t = lastTo.getValue();
if (((IntTag) t.get("x")).getValue() == x && ((IntTag) t.get("z")).getValue() == z && ((IntTag) t.get("y")).getValue() == y) {
to = new BaseBlock(to.getId(), to.getData());
to.setNbtData(lastTo);
lastTo = read(nbtt);
}
@ -432,7 +434,7 @@ public class DiskStorageHistory implements ChangeSet, FaweChangeSet {
@Override
public void remove() {
throw new IllegalArgumentException("CANNOT REMIVE");
throw new IllegalArgumentException("CANNOT REMOVE");
}
};
}

View File

@ -13,6 +13,7 @@ import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.BlockVector;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.history.change.BlockChange;
import com.sk89q.worldedit.history.change.Change;
import com.sk89q.worldedit.history.changeset.ChangeSet;
@ -36,7 +37,8 @@ import net.jpountz.lz4.LZ4OutputStream;
* - Low memory usage
*/
public class MemoryOptimizedHistory implements ChangeSet, FaweChangeSet {
private final Actor actor;
private ArrayDeque<CompoundTag> fromTags;
private ArrayDeque<CompoundTag> toTags;
@ -54,8 +56,8 @@ public class MemoryOptimizedHistory implements ChangeSet, FaweChangeSet {
private int size;
public MemoryOptimizedHistory() {
public MemoryOptimizedHistory(Actor actor) {
this.actor = actor;
}
@ -204,20 +206,22 @@ public class MemoryOptimizedHistory implements ChangeSet, FaweChangeSet {
int y = gis.read() & 0xff;
int from1 = gis.read();
int from2 = gis.read();
BaseBlock from = new BaseBlock(((from2 << 4) + (from1 >> 4)), (from1 & 0xf));
BaseBlock from = FaweCache.getBlock(((from2 << 4) + (from1 >> 4)), (from1 & 0xf));
if (lastFrom != null && FaweCache.hasNBT(from.getId())) {
Map<String, Tag> t = lastFrom.getValue();
if (((IntTag) t.get("x")).getValue() == x && ((IntTag) t.get("z")).getValue() == z && ((IntTag) t.get("y")).getValue() == y) {
from = new BaseBlock(from.getId(), from.getData());
from.setNbtData(lastFrom);
lastFrom = read(lastFromIter);
}
}
int to1 = gis.read();
int to2 = gis.read();
BaseBlock to = new BaseBlock(((to2 << 4) + (to1 >> 4)), (to1 & 0xf));
BaseBlock to = FaweCache.getBlock(((to2 << 4) + (to1 >> 4)), (to1 & 0xf));
if (lastTo != null && FaweCache.hasNBT(to.getId())) {
Map<String, Tag> t = lastTo.getValue();
if (((IntTag) t.get("x")).getValue() == x && ((IntTag) t.get("z")).getValue() == z && ((IntTag) t.get("y")).getValue() == y) {
to = new BaseBlock(to.getId(), to.getData());
to.setNbtData(lastTo);
lastTo = read(lastToIter);
}
@ -250,7 +254,7 @@ public class MemoryOptimizedHistory implements ChangeSet, FaweChangeSet {
@Override
public void remove() {
throw new IllegalArgumentException("CANNOT REMIVE");
throw new IllegalArgumentException("CANNOT REMOVE");
}
};
}
@ -284,13 +288,29 @@ public class MemoryOptimizedHistory implements ChangeSet, FaweChangeSet {
idsStreamZip.flush();
idsStreamZip.close();
ids = idsStream.toByteArray();
// Estimate
int total = 0x18 * size;
int ratio = total / ids.length;
int saved = total - ids.length;
if (ratio > 3) {
// TODO remove this debug message
Fawe.debug(BBC.PREFIX.s() + "History compressed. Saved ~ " + saved + "b (" + ratio + "x smaller)");
/*
* BlockVector
* - reference to the object --> 8 bytes
* - object header (java internals) --> 8 bytes
* - double x, y, z --> 24 bytes
*
* BaseBlock
* - reference to the object --> 8 bytes
* - object header (java internals) --> 8 bytes
* - short id, data --> 4 bytes
* - NBTCompound (assuming null) --> 4 bytes
*
* There are usually two lists for the block changes:
* 2 * BlockVector + 2 * BaseBlock = 128b
*
* This compares FAWE's usage to standard WE.
*/
int total = 128 * size;
int current = ids.length + 16;
int ratio = total / current;
int saved = total - current;
if (ratio > 3 && Thread.currentThread() != Fawe.get().getMainThread() && actor != null && actor.isPlayer() && actor.getSessionKey().isActive() && BBC.COMPRESSED.s().length() > 0) {
actor.print(BBC.PREFIX.s() + " " + BBC.COMPRESSED.format(saved, ratio));
}
idsStream = null;
idsStreamZip = null;

View File

@ -248,7 +248,7 @@ public class EditSession implements Extent {
this.changeSet = new NullChangeSet();
return;
}
this.changeSet = Settings.STORE_HISTORY_ON_DISK ? new DiskStorageHistory(world, actor.getUniqueId()) : new MemoryOptimizedHistory();
this.changeSet = Settings.STORE_HISTORY_ON_DISK ? new DiskStorageHistory(world, actor.getUniqueId()) : new MemoryOptimizedHistory(actor);
Extent extent;
final String name = actor.getName();
final FawePlayer<Object> fp = FawePlayer.wrap(name);
@ -2089,7 +2089,7 @@ public class EditSession implements Extent {
treeGenerator.generate(EditSession.this, new Vector(vector.getX(), y + 1, vector.getZ()));
break;
} else if (t == BlockID.SNOW) {
setBlock(vector, new BaseBlock(BlockID.AIR));
setBlock(vector, nullBlock);
} else if (t != BlockID.AIR) { // Trees won't grow on this!
break;
}
@ -2192,7 +2192,7 @@ public class EditSession implements Extent {
for (int z = minZ; z <= maxZ; ++z) {
final Vector pt = new Vector(x, y, z);
final BaseBlock blk = new BaseBlock(this.getBlockType(pt), this.getBlockData(pt));
final BaseBlock blk = FaweCache.getBlock(this.getBlockType(pt), this.getBlockData(pt));
if (map.containsKey(blk)) {
map.get(blk).increment();
@ -2206,7 +2206,7 @@ public class EditSession implements Extent {
}
} else {
for (final Vector pt : region) {
final BaseBlock blk = new BaseBlock(this.getBlockType(pt), this.getBlockData(pt));
final BaseBlock blk = FaweCache.getBlock(this.getBlockType(pt), this.getBlockData(pt));
if (map.containsKey(blk)) {
map.get(blk).increment();
@ -2246,7 +2246,7 @@ public class EditSession implements Extent {
return null;
}
return new BaseBlock((int) typeVariable.getValue(), (int) dataVariable.getValue());
return FaweCache.getBlock((int) typeVariable.getValue(), (int) dataVariable.getValue());
} catch (final Exception e) {
EditSession.this.log.log(Level.WARNING, "Failed to create shape", e);
return null;

View File

@ -22,11 +22,11 @@ package com.sk89q.worldedit.extent.clipboard;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.object.IntegerTrio;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.EditSession;
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.blocks.BlockID;
import com.sk89q.worldedit.entity.BaseEntity;
import com.sk89q.worldedit.entity.Entity;
import com.sk89q.worldedit.extent.Extent;
@ -176,7 +176,7 @@ public class BlockArrayClipboard implements Clipboard {
return block;
}
return new BaseBlock(BlockID.AIR);
return EditSession.nullBlock;
}
@Override
@ -187,160 +187,70 @@ public class BlockArrayClipboard implements Clipboard {
@Override
public boolean setBlock(Vector location, BaseBlock block) throws WorldEditException {
if (region.contains(location)) {
final int id = block.getId();
final int x = location.getBlockX() - mx;
final int y = location.getBlockY() - my;
final int z = location.getBlockZ() - mz;
switch (id) {
case 0:
return true;
case 54:
case 130:
case 142:
case 27:
case 137:
case 52:
case 154:
case 84:
case 25:
case 144:
case 138:
case 176:
case 177:
case 63:
case 119:
case 68:
case 323:
case 117:
case 116:
case 28:
case 66:
case 157:
case 61:
case 62:
case 140:
case 146:
case 149:
case 150:
case 158:
case 23:
case 123:
case 124:
case 29:
case 33:
case 151:
case 178: {
if (block.hasNbtData()) {
nbtMap.put(new IntegerTrio(x, y, z), block.getNbtData());
}
int i = x + z * dx + (y >> 4) * dxz;
int y2 = y & 0xF;
byte[] idArray = ids[i];
if (idArray == null) {
idArray = new byte[16];
ids[i] = idArray;
}
idArray[y2] = (byte) id;
if (FaweCache.hasData(id)) {
int data = block.getData();
if (data == 0) {
return true;
}
if (datas == null) {
datas = new byte[dx * size.getBlockZ() * ((size.getBlockY() + 15) >> 4)][];
}
byte[] dataArray = datas[i];
if (dataArray == null) {
dataArray = datas[i] = new byte[16];
}
dataArray[y2] = (byte) data;
}
return true;
final int x = location.getBlockX();
final int y = location.getBlockY();
final int z = location.getBlockZ();
return setBlock(x, y, z, block);
}
return false;
}
public boolean setBlock(int x, int y, int z, BaseBlock block) throws WorldEditException {
x -= mx;
y -= my;
z -= mz;
final int id = block.getId();
switch (id) {
case 0:
return true;
case 54:
case 130:
case 142:
case 27:
case 137:
case 52:
case 154:
case 84:
case 25:
case 144:
case 138:
case 176:
case 177:
case 63:
case 119:
case 68:
case 323:
case 117:
case 116:
case 28:
case 66:
case 157:
case 61:
case 62:
case 140:
case 146:
case 149:
case 150:
case 158:
case 23:
case 123:
case 124:
case 29:
case 33:
case 151:
case 178: {
if (block.hasNbtData()) {
nbtMap.put(new IntegerTrio(x, y, z), block.getNbtData());
}
case 2:
case 4:
case 13:
case 14:
case 15:
case 20:
case 21:
case 22:
case 30:
case 32:
case 37:
case 39:
case 40:
case 41:
case 42:
case 45:
case 46:
case 47:
case 48:
case 49:
case 51:
case 56:
case 57:
case 58:
case 60:
case 7:
case 8:
case 9:
case 10:
case 11:
case 73:
case 74:
case 78:
case 79:
case 80:
case 81:
case 82:
case 83:
case 85:
case 87:
case 88:
case 101:
case 102:
case 103:
case 110:
case 112:
case 113:
case 121:
case 122:
case 129:
case 133:
case 165:
case 166:
case 169:
case 170:
case 172:
case 173:
case 174:
case 181:
case 182:
case 188:
case 189:
case 190:
case 191:
case 192: {
int i = x + z * dx + (y >> 4) * dxz;
int y2 = y & 0xF;
byte[] idArray = ids[i];
if (idArray == null) {
idArray = new byte[16];
ids[i] = idArray;
}
idArray[y2] = (byte) id;
return true;
int i = x + z * dx + (y >> 4) * dxz;
int y2 = y & 0xF;
byte[] idArray = ids[i];
if (idArray == null) {
idArray = new byte[16];
ids[i] = idArray;
}
default: {
int i = x + z * dx + (y >> 4) * dxz;
int y2 = y & 0xF;
byte[] idArray = ids[i];
if (idArray == null) {
idArray = new byte[16];
ids[i] = idArray;
}
idArray[y2] = (byte) id;
idArray[y2] = (byte) id;
if (FaweCache.hasData(id)) {
int data = block.getData();
if (data == 0) {
return true;
@ -353,11 +263,107 @@ public class BlockArrayClipboard implements Clipboard {
dataArray = datas[i] = new byte[16];
}
dataArray[y2] = (byte) data;
}
return true;
}
case 2:
case 4:
case 13:
case 14:
case 15:
case 20:
case 21:
case 22:
case 30:
case 32:
case 37:
case 39:
case 40:
case 41:
case 42:
case 45:
case 46:
case 47:
case 48:
case 49:
case 51:
case 56:
case 57:
case 58:
case 60:
case 7:
case 8:
case 9:
case 10:
case 11:
case 73:
case 74:
case 78:
case 79:
case 80:
case 81:
case 82:
case 83:
case 85:
case 87:
case 88:
case 101:
case 102:
case 103:
case 110:
case 112:
case 113:
case 121:
case 122:
case 129:
case 133:
case 165:
case 166:
case 169:
case 170:
case 172:
case 173:
case 174:
case 181:
case 182:
case 188:
case 189:
case 190:
case 191:
case 192: {
int i = x + z * dx + (y >> 4) * dxz;
int y2 = y & 0xF;
byte[] idArray = ids[i];
if (idArray == null) {
idArray = new byte[16];
ids[i] = idArray;
}
idArray[y2] = (byte) id;
return true;
}
default: {
int i = x + z * dx + (y >> 4) * dxz;
int y2 = y & 0xF;
byte[] idArray = ids[i];
if (idArray == null) {
idArray = new byte[16];
ids[i] = idArray;
}
idArray[y2] = (byte) id;
int data = block.getData();
if (data == 0) {
return true;
}
if (datas == null) {
datas = new byte[dx * size.getBlockZ() * ((size.getBlockY() + 15) >> 4)][];
}
byte[] dataArray = datas[i];
if (dataArray == null) {
dataArray = datas[i] = new byte[16];
}
dataArray[y2] = (byte) data;
return true;
}
} else {
return false;
}
}

View File

@ -0,0 +1,287 @@
/*
* 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.clipboard.io;
import com.boydti.fawe.FaweCache;
import com.sk89q.jnbt.ByteArrayTag;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.IntTag;
import com.sk89q.jnbt.ListTag;
import com.sk89q.jnbt.NBTInputStream;
import com.sk89q.jnbt.NamedTag;
import com.sk89q.jnbt.ShortTag;
import com.sk89q.jnbt.StringTag;
import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.BlockVector;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.entity.BaseEntity;
import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.regions.CuboidRegion;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.world.registry.WorldData;
import com.sk89q.worldedit.world.storage.NBTConversions;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Reads schematic files based that are compatible with MCEdit and other editors.
*/
public class SchematicReader implements ClipboardReader {
private static final Logger log = Logger.getLogger(SchematicReader.class.getCanonicalName());
private final NBTInputStream inputStream;
/**
* Create a new instance.
*
* @param inputStream the input stream to read from
*/
public SchematicReader(NBTInputStream inputStream) {
checkNotNull(inputStream);
this.inputStream = inputStream;
}
@Override
public Clipboard read(WorldData data) throws IOException {
// Schematic tag
NamedTag rootTag = inputStream.readNamedTag();
if (!rootTag.getName().equals("Schematic")) {
throw new IOException("Tag 'Schematic' does not exist or is not first");
}
CompoundTag schematicTag = (CompoundTag) rootTag.getTag();
// Check
Map<String, Tag> schematic = schematicTag.getValue();
if (!schematic.containsKey("Blocks")) {
throw new IOException("Schematic file is missing a 'Blocks' tag");
}
// Check type of Schematic
String materials = requireTag(schematic, "Materials", StringTag.class).getValue();
if (!materials.equals("Alpha")) {
throw new IOException("Schematic file is not an Alpha schematic");
}
// ====================================================================
// Metadata
// ====================================================================
Vector origin;
Region region;
// Get information
short width = requireTag(schematic, "Width", ShortTag.class).getValue();
short height = requireTag(schematic, "Height", ShortTag.class).getValue();
short length = requireTag(schematic, "Length", ShortTag.class).getValue();
try {
int originX = requireTag(schematic, "WEOriginX", IntTag.class).getValue();
int originY = requireTag(schematic, "WEOriginY", IntTag.class).getValue();
int originZ = requireTag(schematic, "WEOriginZ", IntTag.class).getValue();
Vector min = new Vector(originX, originY, originZ);
int offsetX = requireTag(schematic, "WEOffsetX", IntTag.class).getValue();
int offsetY = requireTag(schematic, "WEOffsetY", IntTag.class).getValue();
int offsetZ = requireTag(schematic, "WEOffsetZ", IntTag.class).getValue();
Vector offset = new Vector(offsetX, offsetY, offsetZ);
origin = min.subtract(offset);
region = new CuboidRegion(min, min.add(width, height, length).subtract(Vector.ONE));
} catch (IOException ignored) {
origin = new Vector(0, 0, 0);
region = new CuboidRegion(origin, origin.add(width, height, length).subtract(Vector.ONE));
}
// ====================================================================
// Blocks
// ====================================================================
// Get blocks
byte[] blockId = requireTag(schematic, "Blocks", ByteArrayTag.class).getValue();
byte[] blockData = requireTag(schematic, "Data", ByteArrayTag.class).getValue();
byte[] addId = null;
// We support 4096 block IDs using the same method as vanilla Minecraft, where
// the highest 4 bits are stored in a separate byte array.
if (schematic.containsKey("AddBlocks")) {
addId = requireTag(schematic, "AddBlocks", ByteArrayTag.class).getValue();
}
// Need to pull out tile entities
List<Tag> tileEntities = requireTag(schematic, "TileEntities", ListTag.class).getValue();
Map<BlockVector, Map<String, Tag>> tileEntitiesMap = new HashMap<BlockVector, Map<String, Tag>>();
for (Tag tag : tileEntities) {
if (!(tag instanceof CompoundTag)) continue;
CompoundTag t = (CompoundTag) tag;
int x = 0;
int y = 0;
int z = 0;
Map<String, Tag> values = new HashMap<String, Tag>();
for (Map.Entry<String, Tag> entry : t.getValue().entrySet()) {
if (entry.getKey().equals("x")) {
if (entry.getValue() instanceof IntTag) {
x = ((IntTag) entry.getValue()).getValue();
}
} else if (entry.getKey().equals("y")) {
if (entry.getValue() instanceof IntTag) {
y = ((IntTag) entry.getValue()).getValue();
}
} else if (entry.getKey().equals("z")) {
if (entry.getValue() instanceof IntTag) {
z = ((IntTag) entry.getValue()).getValue();
}
}
values.put(entry.getKey(), entry.getValue());
}
BlockVector vec = new BlockVector(x, y, z);
tileEntitiesMap.put(vec, values);
}
BlockArrayClipboard clipboard = new BlockArrayClipboard(region);
clipboard.setOrigin(origin);
// Don't log a torrent of errors
int failedBlockSets = 0;
Vector min = region.getMinimumPoint();
int mx = min.getBlockX();
int my = min.getBlockY();
int mz = min.getBlockZ();
BlockVector pt = new BlockVector(0, 0, 0);
int i;
for (int y = 0; y < height; y++) {
int yy = y + my;
final int i1 = y * width * length;
for (int z = 0; z < length; z++) {
int zz = z + mz;
final int i2 = (z * width) + i1;
for (int x = 0; x < width; x++) {
i = i2 + x;
int xx = x + mx;
int id = blockId[i] & 0xFF;
int db = blockData[i];
if (addId != null) {
if ((i & 1) == 0) {
id += ((addId[i >> 1] & 0x0F) << 8);
} else {
id += ((addId[i >> 1] & 0xF0) << 4);
}
}
BaseBlock block = FaweCache.getBlock(id, db);
if (FaweCache.hasNBT(id)) {
pt.x = x;
pt.y = y;
pt.z = z;
if (tileEntitiesMap.containsKey(pt)) {
block = new BaseBlock(block.getId(), block.getData());
block.setNbtData(new CompoundTag(tileEntitiesMap.get(pt)));
}
}
try {
clipboard.setBlock(xx, yy, zz, block);
} catch (Exception e) {
switch (failedBlockSets) {
case 0:
log.log(Level.WARNING, "Failed to set block on a Clipboard", e);
break;
case 1:
log.log(Level.WARNING, "Failed to set block on a Clipboard (again) -- no more messages will be logged", e);
break;
default:
}
failedBlockSets++;
}
}
}
}
// ====================================================================
// Entities
// ====================================================================
try {
List<Tag> entityTags = requireTag(schematic, "Entities", ListTag.class).getValue();
for (Tag tag : entityTags) {
if (tag instanceof CompoundTag) {
CompoundTag compound = (CompoundTag) tag;
String id = compound.getString("id");
Location location = NBTConversions.toLocation(clipboard, compound.getListTag("Pos"), compound.getListTag("Rotation"));
if (!id.isEmpty()) {
BaseEntity state = new BaseEntity(id, compound);
clipboard.createEntity(location, state);
}
}
}
} catch (IOException ignored) { // No entities? No problem
}
return clipboard;
}
private static <T extends Tag> T requireTag(Map<String, Tag> items, String key, Class<T> expected) throws IOException {
if (!items.containsKey(key)) {
throw new IOException("Schematic file is missing a \"" + key + "\" tag");
}
Tag tag = items.get(key);
if (!expected.isInstance(tag)) {
throw new IOException(key + " tag is not of tag type " + expected.getName());
}
return expected.cast(tag);
}
@Nullable
private static <T extends Tag> T getTag(CompoundTag tag, Class<T> expected, String key) {
Map<String, Tag> items = tag.getValue();
if (!items.containsKey(key)) {
return null;
}
Tag test = items.get(key);
if (!expected.isInstance(test)) {
return null;
}
return expected.cast(test);
}
}

View File

@ -91,7 +91,7 @@ public class BlockTransformExtent extends AbstractDelegateExtent {
@Override
public boolean setBlock(Vector location, BaseBlock block) throws WorldEditException {
return super.setBlock(location, transformBlock(new BaseBlock(block), true));
return super.setBlock(location, transformBlock(block, true));
}

View File

@ -13,7 +13,7 @@ import org.spongepowered.api.plugin.Plugin;
import org.spongepowered.api.plugin.PluginContainer;
import org.spongepowered.api.profile.GameProfileManager;
@Plugin(id = "com.boydti.fawe", name = "FastAsyncWorldEdit", description = "Lagless WorldEdit, Area restrictions, Memory mangement, Block logging", url = "https://github.com/boy0001/FastAsyncWorldedit", version = "3.3.13")
@Plugin(id = "com.boydti.fawe", name = "FastAsyncWorldEdit", description = "Lagless WorldEdit, Area restrictions, Memory mangement, Block logging", url = "https://github.com/boy0001/FastAsyncWorldedit", version = "3.3.14")
public class SpongeMain {
public PluginContainer plugin;

View File

@ -8,7 +8,7 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<artifactId>FastAsyncWorldEdit</artifactId>
<version>3.3.13</version>
<version>3.3.14</version>
<name>FastAsyncWorldEdit</name>
<packaging>jar</packaging>
<build>