mirror of
https://github.com/boy0001/FastAsyncWorldedit.git
synced 2025-02-17 21:11:26 +01:00
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:
parent
d6ad027fc8
commit
9cf4f6c9f0
@ -10,7 +10,7 @@ buildscript {
|
||||
}
|
||||
|
||||
group = 'com.boydti.fawe'
|
||||
version = '3.3.13'
|
||||
version = '3.3.14'
|
||||
description = """FastAsyncWorldEdit"""
|
||||
|
||||
subprojects {
|
||||
|
@ -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]
|
||||
|
@ -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]
|
||||
|
@ -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++) {
|
||||
|
@ -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");
|
||||
|
@ -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");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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));
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user