Clipboard compression

Reduce the size of the clipboard by encoding the BaseBlock as a short
array, with an accompanying map for nbt.
This commit is contained in:
Jesse Boyd 2016-04-03 17:13:22 +10:00
parent d11bb1ac0a
commit 0c55e3c7cb
9 changed files with 420 additions and 66 deletions

View File

@ -1,32 +1,5 @@
package com.boydti.fawe.bukkit.v1_9;
import static com.boydti.fawe.util.ReflectionUtils.getRefClass;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.World.Environment;
import org.bukkit.block.Biome;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.generator.BlockPopulator;
import org.bukkit.generator.ChunkGenerator;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.bukkit.v0.BukkitQueue_0;
@ -44,6 +17,32 @@ import com.boydti.fawe.util.ReflectionUtils.RefMethod;
import com.boydti.fawe.util.ReflectionUtils.RefMethod.RefExecutor;
import com.boydti.fawe.util.TaskManager;
import com.sk89q.worldedit.LocalSession;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.World.Environment;
import org.bukkit.block.Biome;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.generator.BlockPopulator;
import org.bukkit.generator.ChunkGenerator;
import static com.boydti.fawe.util.ReflectionUtils.getRefClass;
public class BukkitQueue_1_9 extends BukkitQueue_0 {
@ -143,7 +142,7 @@ public class BukkitQueue_1_9 extends BukkitQueue_0 {
if (i < 16) {
continue;
}
final short id = (short) (i >> 4);
final short id = (short) (i & 0xFFF);
switch (id) { // Lighting
default:
if (!fixAll) {

View File

@ -21,6 +21,7 @@ import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.command.SchematicCommands;
import com.sk89q.worldedit.command.ScriptingCommands;
import com.sk89q.worldedit.extension.platform.CommandManager;
import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard;
import com.sk89q.worldedit.function.operation.Operations;
import com.sk89q.worldedit.function.visitor.BreadthFirstSearch;
import com.sk89q.worldedit.function.visitor.DownwardVisitor;
@ -149,6 +150,9 @@ public class Fawe {
// TODO command event - queue?
TaskManager.IMP = this.IMP.getTaskManager();
if (Settings.METRICS) {
this.IMP.startMetrics();
}
SetQueue.IMP.queue = this.IMP.getQueue();
// Delete old history
@ -224,6 +228,7 @@ public class Fawe {
EntityCreate.inject();
EntityRemove.inject();
LocalSession.inject();
BlockArrayClipboard.inject();
try {
CommandManager.inject();
} catch (Throwable e) {

View File

@ -13,25 +13,8 @@ public class IntegerPair {
@Override
public int hashCode() {
if (this.hash == 0) {
long val = 0;
if (this.x >= 0) {
if (this.z >= 0) {
val = (this.x * this.x) + (3 * this.x) + (2 * this.x * this.z) + this.z + (this.z * this.z);
} else {
final int z1 = -this.z;
val = (this.x * this.x) + (3 * this.x) + (2 * this.x * z1) + z1 + (z1 * z1) + 1;
}
} else {
final int x1 = -this.x;
if (this.z >= 0) {
val = -((x1 * x1) + (3 * x1) + (2 * x1 * this.z) + this.z + (this.z * this.z));
} else {
final int z1 = -this.z;
val = -((x1 * x1) + (3 * x1) + (2 * x1 * z1) + z1 + (z1 * z1) + 1);
}
}
this.hash = (int) (val % Integer.MAX_VALUE);
if (hash == 0) {
this.hash = (x << 16) | (z & 0xFFFF);
}
return this.hash;
}

View File

@ -0,0 +1,34 @@
package com.boydti.fawe.object;
public class IntegerTrio {
private final int z;
private final int x;
private final int y;
public IntegerTrio(int x, int y, int z) {
this.x = x;
this.y = y;
this.z = z;
}
@Override
public int hashCode() {
int hash = 13;
hash = hash * 13 + x;
hash = hash * 13 + y;
hash = hash * 13 + z;
return hash;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (obj instanceof IntegerTrio) {
IntegerTrio other = (IntegerTrio) obj;
return other.x == x && other.z == z && other.y == y;
}
return false;
}
}

View File

@ -1,5 +1,6 @@
package com.boydti.fawe.object.extent;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.util.SetQueue;
import com.boydti.fawe.util.TaskManager;
import com.sk89q.worldedit.BlockVector;
@ -150,7 +151,9 @@ public class FastWorldEditExtent extends AbstractDelegateExtent {
}
}
});
return true;
}
SetQueue.IMP.setBlock(this.world, x, y, z, id, FaweCache.hasData(id) ? (byte) block.getData() : 0);
return true;
}
case 0:

View File

@ -1,5 +1,6 @@
package com.boydti.fawe.object.extent;
import com.boydti.fawe.FaweCache;
import java.util.HashSet;
import java.util.List;
@ -199,7 +200,9 @@ public class ProcessedWEExtent extends AbstractDelegateExtent {
}
}
});
return true;
}
SetQueue.IMP.setBlock(this.world, x, location.getBlockY(), z, id, FaweCache.hasData(id) ? (byte) block.getData() : 0);
return true;
}
return false;

View File

@ -21,17 +21,19 @@ public class ReflectionUtils {
/**
* prefix of bukkit classes
*/
private static String preClassB = "org.bukkit.craftbukkit";
private static String preClassB;
/**
* prefix of minecraft classes
*/
private static String preClassM = "net.minecraft.server";
private static String preClassM = null;
/**
* boolean value, TRUE if server uses forge or MCPC+
*/
private static boolean forge = false;
/** check server version and class names */
public static void init() {
preClassM = "net.minecraft.server";
preClassB = "org.bukkit.craftbukkit";
if (Bukkit.getServer() != null) {
if (Bukkit.getVersion().contains("MCPC") || Bukkit.getVersion().contains("Forge")) {
forge = true;
@ -52,7 +54,9 @@ public class ReflectionUtils {
final String verM = pas[3];
preClassM += "." + verM;
}
} catch (final Exception ignored) {}
} catch (final Exception ignored) {
ignored.printStackTrace();
}
}
}

View File

@ -0,0 +1,327 @@
/*
* 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;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.object.IntegerTrio;
import com.sk89q.jnbt.CompoundTag;
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.function.operation.Operation;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.world.biome.BaseBiome;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import javax.annotation.Nullable;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Stores block data as a multi-dimensional array of {@link BaseBlock}s and
* other data as lists or maps.
*/
public class BlockArrayClipboard implements Clipboard {
private final Region region;
private final short[][][] blocks;
private final HashMap<IntegerTrio, CompoundTag> nbtMap;
private final List<ClipboardEntity> entities = new ArrayList<ClipboardEntity>();
private int mx;
private int my;
private int mz;
private Vector origin;
/**
* Create a new instance.
*
* <p>The origin will be placed at the region's lowest minimum point.</p>
*
* @param region the bounding region
*/
public BlockArrayClipboard(Region region) {
checkNotNull(region);
this.region = region.clone();
Vector dimensions = getDimensions();
blocks = new short[dimensions.getBlockX()][dimensions.getBlockY()][dimensions.getBlockZ()];
nbtMap = new HashMap<>();
this.origin = region.getMinimumPoint();
this.mx = origin.getBlockX();
this.my = origin.getBlockY();
this.mz = origin.getBlockZ();
}
@Override
public Region getRegion() {
return region.clone();
}
@Override
public Vector getOrigin() {
return origin;
}
@Override
public void setOrigin(Vector origin) {
this.origin = origin;
}
@Override
public Vector getDimensions() {
return region.getMaximumPoint().subtract(region.getMinimumPoint()).add(1, 1, 1);
}
@Override
public Vector getMinimumPoint() {
return region.getMinimumPoint();
}
@Override
public Vector getMaximumPoint() {
return region.getMaximumPoint();
}
@Override
public List<? extends Entity> getEntities(Region region) {
List<Entity> filtered = new ArrayList<Entity>();
for (Entity entity : entities) {
if (region.contains(entity.getLocation().toVector())) {
filtered.add(entity);
}
}
return Collections.unmodifiableList(filtered);
}
@Override
public List<? extends Entity> getEntities() {
return Collections.unmodifiableList(entities);
}
@Nullable
@Override
public Entity createEntity(Location location, BaseEntity entity) {
ClipboardEntity ret = new ClipboardEntity(location, entity);
entities.add(ret);
return ret;
}
@Override
public BaseBlock getBlock(Vector position) {
if (region.contains(position)) {
int x = position.getBlockX();
int y = position.getBlockY();
int z = position.getBlockZ();
short combined = blocks[x - mx][y - my][z - mz];
int id = combined >> 4;
int data = combined & 0xF;
BaseBlock block = new BaseBlock(id, data);
if (FaweCache.hasNBT(id)) {
CompoundTag nbt = nbtMap.get(new IntegerTrio(x, y, z));
if (nbt != null) {
block.setNbtData(nbt);
}
}
return block;
}
return new BaseBlock(BlockID.AIR);
}
@Override
public BaseBlock getLazyBlock(Vector position) {
return getBlock(position);
}
@Override
public boolean setBlock(Vector location, BaseBlock block) throws WorldEditException {
if (region.contains(location)) {
final int id = block.getId();
final int x = location.getBlockX();
final int y = location.getBlockY();
final int z = location.getBlockZ();
switch (id) {
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());
}
blocks[x - mx][y - my][z - mz] = (short) ((id << 4) + (block.getData()));
return true;
case 0:
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: {
blocks[x - mx][y - my][z - mz] = (short) (id << 4);
return true;
}
default: {
blocks[x - mx][y - my][z - mz] = (short) ((id << 4) + (block.getData()));
return true;
}
}
} else {
return false;
}
}
@Override
public BaseBiome getBiome(Vector2D position) {
return new BaseBiome(0);
}
@Override
public boolean setBiome(Vector2D position, BaseBiome biome) {
return false;
}
@Nullable
@Override
public Operation commit() {
return null;
}
/**
* Stores entity data.
*/
private class ClipboardEntity extends StoredEntity {
ClipboardEntity(Location location, BaseEntity entity) {
super(location, entity);
}
@Override
public boolean remove() {
return entities.remove(this);
}
@Nullable
@Override
public <T> T getFacet(Class<? extends T> cls) {
return null;
}
}
public static Class<?> inject() {
return BlockArrayClipboard.class;
}
}

View File

@ -9,32 +9,27 @@
#################################################
Version string 'unspecified' does not match SemVer specification
You should try SemVer : http://semver.org/
:forge:deobfCompileDummyTask
:core:compileJava
:forge:deobfCompileDummyTask
:forge:deobfProvidedDummyTask
:forge:extractDependencyATs SKIPPED
:forge:extractMcpDatawarning: [options] bootstrap class path not set in conjunction with -source 1.7
SKIPPED
:forge:extractMcpData
:core:compileJava UP-TO-DATE
:core:processResources UP-TO-DATE
:core:classes UP-TO-DATE
:core:jar UP-TO-DATE
:forge:extractMcpData SKIPPED
:forge:extractMcpMappings SKIPPED
:forge:genSrgs SKIPPED
:forge:getVersionJson
:forge:downloadServer SKIPPED
:forge:splitServerJarNote: Some input files use or override a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
Note: Some input files use unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
1 warning
:core:processResources UP-TO-DATE
:core:classes
:core:jar
:forge:splitServerJar SKIPPED
:forge:deobfMcMCP SKIPPED
:forge:sourceApiJava UP-TO-DATE
:forge:compileApiJava UP-TO-DATE
:forge:processApiResources UP-TO-DATE
:forge:apiClasses UP-TO-DATE
:forge:sourceMainJava UP-TO-DATE
:forge:sourceMainJava
:forge:compileJavaC:\Users\Jesse\.gradle\caches\modules-2\files-2.1\com.sk89q.worldedit\worldedit-forge-mc1.8.9\6.1.1\dffd7e1882eba256eb2132fe315682c1d26522b1\worldedit-forge-mc1.8.9-6.1.1.jar(com/sk89q/worldedit/forge/ForgeWorldEdit.class): warning: Cannot find annotation method 'modid()' in type 'Mod': class file for net.minecraftforge.fml.common.Mod not found
C:\Users\Jesse\.gradle\caches\modules-2\files-2.1\com.sk89q.worldedit\worldedit-forge-mc1.8.9\6.1.1\dffd7e1882eba256eb2132fe315682c1d26522b1\worldedit-forge-mc1.8.9-6.1.1.jar(com/sk89q/worldedit/forge/ForgeWorldEdit.class): warning: Cannot find annotation method 'name()' in type 'Mod'
C:\Users\Jesse\.gradle\caches\modules-2\files-2.1\com.sk89q.worldedit\worldedit-forge-mc1.8.9\6.1.1\dffd7e1882eba256eb2132fe315682c1d26522b1\worldedit-forge-mc1.8.9-6.1.1.jar(com/sk89q/worldedit/forge/ForgeWorldEdit.class): warning: Cannot find annotation method 'version()' in type 'Mod'
@ -82,12 +77,13 @@ Note: Recompile with -Xlint:unchecked for details.
:forge:reobfShadowJar
:forge:extractRangemapReplacedMain
C:\Users\Jesse\Desktop\OTHER\GitHub\FastAsyncWorldEdit\forge\build\sources\main\java
:forge:retromapReplacedMain UP-TO-DATE
:forge:sourceJar UP-TO-DATE
:forge:retromapReplacedMain
remapping source...
:forge:sourceJar
:forge:assemble
:forge:check UP-TO-DATE
:forge:build
BUILD SUCCESSFUL
Total time: 38.081 secs
Total time: 41.27 secs