Merge branch 'commanding-pipeline' of https://github.com/IntellectualSites/FastAsyncWorldEdit-1.13 into commanding-pipeline

 Conflicts:
	worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitPlayer.java
	worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitWorld.java
	worldedit-core/src/main/java/com/boydti/fawe/beta/filters/DistrFilter.java
	worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/ChunkCache.java
	worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/ParallelQueueExtent.java
	worldedit-core/src/main/java/com/boydti/fawe/command/CFICommands.java
	worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/cfi/HeightMapMCAGenerator.java
	worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/cfi/MCAWriter.java
	worldedit-core/src/main/java/com/boydti/fawe/wrappers/WorldWrapper.java
	worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java
	worldedit-core/src/main/java/com/sk89q/worldedit/command/ToolUtilCommands.java
	worldedit-core/src/main/java/com/sk89q/worldedit/world/NullWorld.java
	worldedit-core/src/main/java/com/sk89q/worldedit/world/World.java
	worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockTypes.java
This commit is contained in:
MattBDev 2019-11-02 15:58:46 -04:00
commit d8d7c8c53f
220 changed files with 8294 additions and 5283 deletions

View File

@ -20,6 +20,7 @@ repositories {
maven { url = uri("https://repo.destroystokyo.com/repository/maven-public//") }
maven { url = uri("http://repo.dmulloy2.net/content/groups/public/") }
maven { url = uri("http://ci.ender.zone/plugin/repository/everything/") }
maven { url = uri("https://repo.inventivetalent.org/content/groups/public/")}
}
configurations.all {
@ -49,6 +50,9 @@ dependencies {
exclude("com.sk89q.worldedit.worldedit-libs", "bukkit")
exclude("com.sk89q.worldedit.worldedit-libs", "core")
}
"implementation"("org.inventivetalent:mapmanager:1.7.3-SNAPSHOT") { isTransitive = false }
"implementation"("org.inventivetalent:mapmanager:1.7.3-SNAPSHOT") { isTransitive = false }
"implementation"("com.massivecraft:factions:2.8.0") { isTransitive = false }
"implementation"("com.drtshock:factions:1.6.9.5") { isTransitive = false }
"implementation"("com.github.TechFortress:GriefPrevention:16.12.0") { isTransitive = false }

View File

@ -2,7 +2,9 @@ package com.boydti.fawe.bukkit;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.IFawe;
import com.boydti.fawe.beta.implementation.QueueHandler;
import com.boydti.fawe.beta.implementation.queue.QueueHandler;
import com.boydti.fawe.beta.implementation.cache.preloader.AsyncPreloader;
import com.boydti.fawe.beta.implementation.cache.preloader.Preloader;
import com.boydti.fawe.bukkit.adapter.BukkitQueueHandler;
import com.boydti.fawe.bukkit.listener.BrushListener;
import com.boydti.fawe.bukkit.listener.BukkitImageListener;
@ -54,14 +56,14 @@ import org.bukkit.plugin.java.JavaPlugin;
public class FaweBukkit implements IFawe, Listener {
// private final WorldEditPlugin plugin;
private final Plugin plugin;
private VaultUtil vault;
private ItemUtil itemUtil;
private boolean listeningImages;
private BukkitImageListener imageListener;
//private CFIPacketListener packetListener;
public static boolean PAPER;
public VaultUtil getVault() {
return this.vault;
@ -69,6 +71,13 @@ public class FaweBukkit implements IFawe, Listener {
public FaweBukkit(Plugin plugin) {
this.plugin = plugin;
try {
Class.forName("com.destroystokyo.paper.Namespaced");
PAPER = true;
} catch (Throwable e) {
e.printStackTrace();
// TODO no paper
}
try {
Settings.IMP.TICK_LIMITER.ENABLED = !Bukkit.hasWhitelist();
Fawe.set(this);
@ -103,7 +112,6 @@ public class FaweBukkit implements IFawe, Listener {
} catch (ClassNotFoundException e) {
new ChunkListener_9();
}
});
}
@ -129,15 +137,15 @@ public class FaweBukkit implements IFawe, Listener {
PluginManager manager = Bukkit.getPluginManager();
if (manager.getPlugin("PacketListenerApi") == null) {
File output = new File(plugin.getDataFolder().getParentFile(), "PacketListenerAPI_v3.6.0-SNAPSHOT.jar");
byte[] jarData = Jars.PL_v3_6_0.download();
File output = new File(plugin.getDataFolder().getParentFile(), "PacketListenerAPI_v3.7.3-SNAPSHOT.jar");
byte[] jarData = Jars.PL_v3_7_3.download();
try (FileOutputStream fos = new FileOutputStream(output)) {
fos.write(jarData);
}
}
if (manager.getPlugin("MapManager") == null) {
File output = new File(plugin.getDataFolder().getParentFile(), "MapManager_v1.4.0-SNAPSHOT.jar");
byte[] jarData = Jars.MM_v1_4_0.download();
File output = new File(plugin.getDataFolder().getParentFile(), "MapManager_v1.7.3-SNAPSHOT.jar");
byte[] jarData = Jars.MM_v1_7_3.download();
try (FileOutputStream fos = new FileOutputStream(output)) {
fos.write(jarData);
}
@ -397,4 +405,12 @@ public class FaweBukkit implements IFawe, Listener {
return null;
// return ((BlocksHubBukkit) blocksHubPlugin).getApi();
}
@Override
public Preloader getPreloader() {
if (PAPER) {
return new AsyncPreloader();
}
return null;
}
}

View File

@ -1,7 +1,7 @@
package com.boydti.fawe.bukkit.adapter;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.beta.implementation.QueueHandler;
import com.boydti.fawe.beta.implementation.queue.QueueHandler;
import com.boydti.fawe.bukkit.listener.ChunkListener;
import java.lang.reflect.Field;

View File

@ -2,6 +2,7 @@ package com.boydti.fawe.bukkit.adapter.mc1_14;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.bukkit.FaweBukkit;
import com.boydti.fawe.bukkit.adapter.DelegateLock;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.collection.BitArray4096;
@ -10,6 +11,7 @@ import com.boydti.fawe.util.TaskManager;
import com.sk89q.worldedit.world.block.BlockID;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockTypes;
import com.sk89q.worldedit.world.block.BlockTypesCache;
import net.jpountz.util.UnsafeUtils;
import net.minecraft.server.v1_14_R1.Block;
import net.minecraft.server.v1_14_R1.Chunk;
@ -21,6 +23,7 @@ import net.minecraft.server.v1_14_R1.DataPaletteBlock;
import net.minecraft.server.v1_14_R1.DataPaletteLinear;
import net.minecraft.server.v1_14_R1.GameProfileSerializer;
import net.minecraft.server.v1_14_R1.IBlockData;
import net.minecraft.server.v1_14_R1.PacketPlayOutMapChunk;
import net.minecraft.server.v1_14_R1.PlayerChunk;
import net.minecraft.server.v1_14_R1.PlayerChunkMap;
import org.bukkit.craftbukkit.v1_14_R1.CraftChunk;
@ -78,7 +81,7 @@ public class BukkitAdapter_1_14 {
fieldDirtyBits.setAccessible(true);
{
Field tmp = null;
Field tmp;
try {
tmp = DataPaletteBlock.class.getDeclaredField("writeLock");
} catch (NoSuchFieldException paper) {
@ -133,8 +136,6 @@ public class BukkitAdapter_1_14 {
}
}
private static boolean PAPER = true;
public static Chunk ensureLoaded(net.minecraft.server.v1_14_R1.World nmsWorld, int X, int Z) {
Chunk nmsChunk = nmsWorld.getChunkIfLoaded(X, Z);
if (nmsChunk != null) {
@ -143,7 +144,7 @@ public class BukkitAdapter_1_14 {
if (Fawe.isMainThread()) {
return nmsWorld.getChunkAt(X, Z);
}
if (PAPER) {
if (FaweBukkit.PAPER) {
CraftWorld craftWorld = nmsWorld.getWorld();
CompletableFuture<org.bukkit.Chunk> future = craftWorld.getChunkAtAsync(X, Z, true);
try {
@ -154,15 +155,14 @@ public class BukkitAdapter_1_14 {
} catch (ExecutionException e) {
e.printStackTrace();
} catch (Throwable e) {
System.out.println("Error, cannot load chunk async (paper not installed?)");
PAPER = false;
e.printStackTrace();
}
}
// TODO optimize
return TaskManager.IMP.sync(() -> nmsWorld.getChunkAt(X, Z));
}
private static PlayerChunk getPlayerChunk(net.minecraft.server.v1_14_R1.WorldServer nmsWorld, final int cx, final int cz) {
public static PlayerChunk getPlayerChunk(net.minecraft.server.v1_14_R1.WorldServer nmsWorld, final int cx, final int cz) {
PlayerChunkMap chunkMap = nmsWorld.getChunkProvider().playerChunkMap;
PlayerChunk playerChunk = chunkMap.visibleChunks.get(ChunkCoordIntPair.pair(cx, cz));
if (playerChunk == null) {
@ -274,7 +274,7 @@ public class BukkitAdapter_1_14 {
for (int i = 0; i < num_palette; i++) {
final int ordinal = paletteToBlock[i];
blockToPalette[ordinal] = Integer.MAX_VALUE;
final BlockState state = BlockTypes.states[ordinal];
final BlockState state = BlockTypesCache.states[ordinal];
final IBlockData ibd = ((BlockMaterial_1_14) state.getMaterial()).getState();
palette.a(ibd);
}

View File

@ -3,11 +3,10 @@ package com.boydti.fawe.bukkit.adapter.mc1_14;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.beta.IChunkSet;
import com.boydti.fawe.beta.implementation.QueueHandler;
import com.boydti.fawe.beta.implementation.queue.QueueHandler;
import com.boydti.fawe.beta.implementation.blocks.CharGetBlocks;
import com.boydti.fawe.bukkit.adapter.DelegateLock;
import com.boydti.fawe.object.collection.AdaptedMap;
import com.boydti.fawe.object.collection.AdaptedSetCollection;
import com.boydti.fawe.object.collection.BitArray4096;
import com.boydti.fawe.util.ReflectionUtils;
import com.google.common.collect.Iterables;
@ -58,7 +57,7 @@ public class BukkitGetBlocks_1_14 extends CharGetBlocks {
public Chunk nmsChunk;
public CraftWorld world;
public int X, Z;
private boolean forceLoad;
// private boolean forceLoad;
public BukkitGetBlocks_1_14(World world, int X, int Z, boolean forceLoad) {
this.world = (CraftWorld) world;
@ -76,6 +75,15 @@ public class BukkitGetBlocks_1_14 extends CharGetBlocks {
// }
// }
public int getX() {
return X;
}
public int getZ() {
return Z;
}
@Override
public BiomeType getBiomeType(int x, int z) {
BiomeBase base = getChunk().getBiomeIndex()[(z << 4) + x];
@ -304,7 +312,7 @@ public class BukkitGetBlocks_1_14 extends CharGetBlocks {
System.out.println("Failed to set chunk section:" + X + "," + Z + " layer: " + layer);
continue;
} else {
updateGet(this, nmsChunk, sections, newSection, setArr, layer);
updateGet(this, nmsChunk, sections, newSection, getArr, layer);
}
}
}

View File

@ -0,0 +1,79 @@
package com.boydti.fawe.bukkit.adapter.mc1_14;
import com.boydti.fawe.beta.implementation.packet.ChunkPacket;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
import com.sk89q.worldedit.math.BlockVector3;
import net.minecraft.server.v1_14_R1.NBTBase;
import net.minecraft.server.v1_14_R1.NBTTagCompound;
import net.minecraft.server.v1_14_R1.PacketPlayOutMapChunk;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Map;
public class MapChunkUtil_1_14 {
private static final Field fieldX;
private static final Field fieldZ;
private static final Field fieldHeightMap;
private static final Field fieldBitMask;
private static final Field fieldChunkData;
private static final Field fieldBlockEntities;
private static final Field fieldFull;
static {
try {
fieldX = PacketPlayOutMapChunk.class.getDeclaredField("a");
fieldZ = PacketPlayOutMapChunk.class.getDeclaredField("b");
fieldBitMask = PacketPlayOutMapChunk.class.getDeclaredField("c");
fieldHeightMap = PacketPlayOutMapChunk.class.getDeclaredField("d");
fieldChunkData = PacketPlayOutMapChunk.class.getDeclaredField("e");
fieldBlockEntities = PacketPlayOutMapChunk.class.getDeclaredField("f");
fieldFull = PacketPlayOutMapChunk.class.getDeclaredField("g");
fieldX.setAccessible(true);
fieldZ.setAccessible(true);
fieldBitMask.setAccessible(true);
fieldHeightMap.setAccessible(true);
fieldBlockEntities.setAccessible(true);
fieldFull.setAccessible(true);
} catch (Throwable e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
public static PacketPlayOutMapChunk create(BukkitImplAdapter<NBTBase> adapter, ChunkPacket packet) {
PacketPlayOutMapChunk nmsPacket = new PacketPlayOutMapChunk();
try {
fieldX.setInt(nmsPacket, packet.getChunkX());
fieldZ.setInt(nmsPacket, packet.getChunkZ());
fieldBitMask.set(nmsPacket, packet.getChunk().getBitMask());
NBTBase heightMap = adapter.fromNative(packet.getHeightMap());
fieldHeightMap.set(nmsPacket, heightMap);
fieldChunkData.set(nmsPacket, packet.get());
Map<BlockVector3, CompoundTag> tiles = packet.getChunk().getTiles();
ArrayList<NBTTagCompound> nmsTiles = new ArrayList<>(tiles.size());
for (Map.Entry<BlockVector3, CompoundTag> entry : tiles.entrySet()) {
NBTBase nmsTag = adapter.fromNative(entry.getValue());
nmsTiles.add((NBTTagCompound) nmsTag);
}
fieldBlockEntities.set(nmsPacket, nmsTiles);
fieldFull.set(nmsPacket, packet.isFull());
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return nmsPacket;
}
}

View File

@ -20,6 +20,8 @@
package com.boydti.fawe.bukkit.adapter.mc1_14;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.beta.implementation.packet.ChunkPacket;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
@ -62,6 +64,7 @@ import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import com.sk89q.worldedit.world.block.BlockType;
import com.sk89q.worldedit.world.block.BlockTypes;
import com.sk89q.worldedit.world.block.BlockTypesCache;
import com.sk89q.worldedit.world.entity.EntityType;
import com.sk89q.worldedit.world.registry.BlockMaterial;
import net.minecraft.server.v1_14_R1.Block;
@ -75,6 +78,7 @@ import net.minecraft.server.v1_14_R1.Chunk;
import net.minecraft.server.v1_14_R1.ChunkCoordIntPair;
import net.minecraft.server.v1_14_R1.ChunkSection;
import net.minecraft.server.v1_14_R1.Entity;
import net.minecraft.server.v1_14_R1.EntityPlayer;
import net.minecraft.server.v1_14_R1.EntityTypes;
import net.minecraft.server.v1_14_R1.EnumDirection;
import net.minecraft.server.v1_14_R1.EnumHand;
@ -102,7 +106,9 @@ import net.minecraft.server.v1_14_R1.NBTTagLongArray;
import net.minecraft.server.v1_14_R1.NBTTagShort;
import net.minecraft.server.v1_14_R1.NBTTagString;
import net.minecraft.server.v1_14_R1.PacketPlayOutEntityStatus;
import net.minecraft.server.v1_14_R1.PacketPlayOutMapChunk;
import net.minecraft.server.v1_14_R1.PacketPlayOutTileEntityData;
import net.minecraft.server.v1_14_R1.PlayerChunk;
import net.minecraft.server.v1_14_R1.PlayerChunkMap;
import net.minecraft.server.v1_14_R1.TileEntity;
import net.minecraft.server.v1_14_R1.Vec3D;
@ -132,10 +138,12 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.OptionalInt;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static com.google.common.base.Preconditions.checkNotNull;
@ -171,7 +179,7 @@ public final class Spigot_v1_14_R4 extends CachedBukkitAdapter implements Bukkit
if (idbToStateOrdinal != null) return false;
idbToStateOrdinal = new char[Block.REGISTRY_ID.a()]; // size
for (int i = 0; i < idbToStateOrdinal.length; i++) {
BlockState state = BlockTypes.states[i];
BlockState state = BlockTypesCache.states[i];
BlockMaterial_1_14 material = (BlockMaterial_1_14) state.getMaterial();
int id = Block.REGISTRY_ID.getId(material.getState());
idbToStateOrdinal[id] = state.getOrdinalChar();
@ -590,6 +598,13 @@ public final class Spigot_v1_14_R4 extends CachedBukkitAdapter implements Bukkit
}
}
@Override
public OptionalInt getInternalBlockStateId(BlockState state) {
BlockMaterial_1_14 material = (BlockMaterial_1_14) state.getMaterial();
IBlockData mcState = material.getCraftBlockData().getState();
return OptionalInt.of(Block.REGISTRY_ID.getId(mcState));
}
@Override
public BlockState adapt(BlockData blockData) {
CraftBlockData cbd = ((CraftBlockData) blockData);
@ -598,7 +613,7 @@ public final class Spigot_v1_14_R4 extends CachedBukkitAdapter implements Bukkit
}
public BlockState adapt(IBlockData ibd) {
return BlockTypes.states[adaptToInt(ibd)];
return BlockTypesCache.states[adaptToInt(ibd)];
}
public int adaptToInt(IBlockData ibd) {
@ -648,6 +663,35 @@ public final class Spigot_v1_14_R4 extends CachedBukkitAdapter implements Bukkit
));
}
@Override
public void sendFakeChunk(org.bukkit.World world, Player player, ChunkPacket packet) {
WorldServer nmsWorld = ((CraftWorld) world).getHandle();
PlayerChunk map = BukkitAdapter_1_14.getPlayerChunk(nmsWorld, packet.getChunkX(), packet.getChunkZ());
if (map != null && map.hasBeenLoaded()) {
boolean flag = false;
PlayerChunk.d players = map.players;
Stream<EntityPlayer> stream = players.a(new ChunkCoordIntPair(packet.getChunkX(), packet.getChunkZ()), flag);
EntityPlayer checkPlayer = player == null ? null : ((CraftPlayer) player).getHandle();
stream.filter(entityPlayer -> checkPlayer == null || entityPlayer == checkPlayer)
.forEach(entityPlayer -> {
synchronized (packet) {
PacketPlayOutMapChunk nmsPacket = (PacketPlayOutMapChunk) packet.getNativePacket();
if (nmsPacket == null) {
nmsPacket = MapChunkUtil_1_14.create(this, packet);
packet.setNativePacket(nmsPacket);
}
try {
FaweCache.IMP.CHUNK_FLAG.get().set(true);
entityPlayer.playerConnection.sendPacket(nmsPacket);
} finally {
FaweCache.IMP.CHUNK_FLAG.get().set(false);
}
}
});
}
}
private static EnumDirection adapt(Direction face) {
switch (face) {
case NORTH: return EnumDirection.NORTH;

View File

@ -0,0 +1,34 @@
package com.boydti.fawe.bukkit.adapter.mc1_14.test;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.ProtocolManager;
import com.comphenix.protocol.events.ListenerPriority;
import com.comphenix.protocol.events.PacketAdapter;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.wrappers.WrappedBlockData;
import org.bukkit.plugin.Plugin;
import java.util.List;
public class TestChunkPacketSend {
public TestChunkPacketSend(Plugin plugin) {
// Disable all sound effects
ProtocolManager protocolManager = ProtocolLibrary.getProtocolManager();
protocolManager.addPacketListener(
new PacketAdapter(plugin, ListenerPriority.NORMAL, PacketType.Play.Server.MAP_CHUNK) {
@Override
public void onPacketSending(PacketEvent event) {
if (event.getPacketType() != PacketType.Play.Server.MAP_CHUNK) {
return;
}
PacketContainer packet = event.getPacket();
StructureModifier<Byte> bytes = packet.getBytes();
StructureModifier<WrappedBlockData> blockData = packet.getBlockData();
List<WrappedBlockData> values = blockData.getValues();
}
});
}
}

View File

@ -1,111 +0,0 @@
package com.boydti.fawe.bukkit.chat;
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Collection;
import org.apache.commons.lang.Validate;
/**
* Represents a wrapper around an array class of an arbitrary reference type,
* which properly implements "value" hash code and equality functions.
* <p>
* This class is intended for use as a key to a map.
* </p>
*
* @param <E> The type of elements in the array.
* @author Glen Husman
* @see Arrays
*/
public final class ArrayWrapper<E> {
/**
* Creates an array wrapper with some elements.
*
* @param elements The elements of the array.
*/
@SafeVarargs
public ArrayWrapper(E... elements) {
setArray(elements);
}
private E[] _array;
/**
* Retrieves a reference to the wrapped array instance.
*
* @return The array wrapped by this instance.
*/
public E[] getArray() {
return _array;
}
/**
* Set this wrapper to wrap a new array instance.
*
* @param array The new wrapped array.
*/
public void setArray(E[] array) {
Validate.notNull(array, "The array must not be null.");
_array = array;
}
/**
* Determines if this object has a value equivalent to another object.
*
* @see Arrays#equals(Object[], Object[])
*/
@SuppressWarnings("rawtypes")
@Override
public boolean equals(Object other) {
if (!(other instanceof ArrayWrapper)) {
return false;
}
return Arrays.equals(_array, ((ArrayWrapper) other)._array);
}
/**
* Gets the hash code represented by this objects value.
*
* @return This object's hash code.
* @see Arrays#hashCode(Object[])
*/
@Override
public int hashCode() {
return Arrays.hashCode(_array);
}
/**
* Converts an iterable element collection to an array of elements.
* The iteration order of the specified object will be used as the array element order.
*
* @param list The iterable of objects which will be converted to an array.
* @param c The type of the elements of the array.
* @return An array of elements in the specified iterable.
*/
@SuppressWarnings("unchecked")
public static <T> T[] toArray(Iterable<? extends T> list, Class<T> c) {
int size = -1;
if (list instanceof Collection<?>) {
@SuppressWarnings("rawtypes")
Collection coll = (Collection) list;
size = coll.size();
}
if (size < 0) {
size = 0;
// Ugly hack: Count it ourselves
for (@SuppressWarnings("unused") T element : list) {
size++;
}
}
T[] result = (T[]) Array.newInstance(c, size);
int i = 0;
for (T element : list) { // Assumes iteration order is consistent
result[i++] = element; // Assign array element at index THEN increment counter
}
return result;
}
}

View File

@ -1,18 +0,0 @@
package com.boydti.fawe.bukkit.chat;
import com.google.gson.stream.JsonWriter;
import java.io.IOException;
/**
* Represents an object that can be serialized to a JSON writer instance.
*/
interface JsonRepresentedObject {
/**
* Writes the JSON representation of this object to the specified writer.
* @param writer The JSON writer which will receive the object.
* @throws IOException If an error occurs writing to the stream.
*/
public void writeJson(JsonWriter writer) throws IOException;
}

View File

@ -1,46 +0,0 @@
package com.boydti.fawe.bukkit.chat;
import com.google.gson.stream.JsonWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import org.bukkit.configuration.serialization.ConfigurationSerializable;
/**
* Represents a JSON string value.
* Writes by this object will not write name values nor begin/end objects in the JSON stream.
* All writes merely write the represented string value.
*/
final class JsonString implements JsonRepresentedObject, ConfigurationSerializable {
private String _value;
public JsonString(CharSequence value) {
_value = value == null ? null : value.toString();
}
@Override
public void writeJson(JsonWriter writer) throws IOException {
writer.value(getValue());
}
public String getValue() {
return _value;
}
public Map<String, Object> serialize() {
HashMap<String, Object> theSingleValue = new HashMap<>();
theSingleValue.put("stringValue", _value);
return theSingleValue;
}
public static JsonString deserialize(Map<String, Object> map) {
return new JsonString(map.get("stringValue").toString());
}
@Override
public String toString() {
return _value;
}
}

View File

@ -1,201 +0,0 @@
package com.boydti.fawe.bukkit.chat;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import org.bukkit.Bukkit;
/**
* A class containing static utility methods and caches which are intended as reflective conveniences.
* Unless otherwise noted, upon failure methods will return {@code null}.
*/
public final class Reflection {
/**
* Stores loaded classes from the {@code net.minecraft.server} package.
*/
private static final Map<String, Class<?>> _loadedNMSClasses = new HashMap<>();
/**
* Stores loaded classes from the {@code org.bukkit.craftbukkit} package (and subpackages).
*/
private static final Map<String, Class<?>> _loadedOBCClasses = new HashMap<>();
private static final Map<Class<?>, Map<String, Field>> _loadedFields = new HashMap<>();
/**
* Contains loaded methods in a cache.
* The map maps [types to maps of [method names to maps of [parameter types to method instances]]].
*/
private static final Map<Class<?>, Map<String, Map<ArrayWrapper<Class<?>>, Method>>> _loadedMethods = new HashMap<>();
private static String _versionString;
private Reflection() { }
/**
* Gets the version string from the package name of the CraftBukkit server implementation.
* This is needed to bypass the JAR package name changing on each update.
*
* @return The version string of the OBC and NMS packages, <em>including the trailing dot</em>.
*/
public synchronized static String getVersion() {
if (_versionString == null) {
String name = Bukkit.getServer().getClass().getPackage().getName();
_versionString = name.substring(name.lastIndexOf('.') + 1) + ".";
}
return _versionString;
}
/**
* Gets a {@link Class} object representing a type contained within the {@code net.minecraft.server} versioned package.
* The class instances returned by this method are cached, such that no lookup will be done twice (unless multiple threads are accessing this method simultaneously).
*
* @param className The name of the class, excluding the package, within NMS.
* @return The class instance representing the specified NMS class, or {@code null} if it could not be loaded.
*/
public synchronized static Class<?> getNMSClass(String className) {
if (_loadedNMSClasses.containsKey(className)) {
return _loadedNMSClasses.get(className);
}
String fullName = "net.minecraft.server." + getVersion() + className;
Class<?> clazz;
try {
clazz = Class.forName(fullName);
} catch (ClassNotFoundException e) {
_loadedNMSClasses.put(className, null);
throw new RuntimeException(e);
}
_loadedNMSClasses.put(className, clazz);
return clazz;
}
/**
* Gets a {@link Class} object representing a type contained within the {@code org.bukkit.craftbukkit} versioned package.
* The class instances returned by this method are cached, such that no lookup will be done twice (unless multiple threads are accessing this method simultaneously).
*
* @param className The name of the class, excluding the package, within OBC. This name may contain a subpackage name, such as {@code inventory.CraftItemStack}.
* @return The class instance representing the specified OBC class, or {@code null} if it could not be loaded.
*/
public synchronized static Class<?> getOBCClass(String className) {
if (_loadedOBCClasses.containsKey(className)) {
return _loadedOBCClasses.get(className);
}
String fullName = "org.bukkit.craftbukkit." + getVersion() + className;
Class<?> clazz;
try {
clazz = Class.forName(fullName);
} catch (ClassNotFoundException e) {
_loadedOBCClasses.put(className, null);
throw new RuntimeException(e);
}
_loadedOBCClasses.put(className, clazz);
return clazz;
}
/**
* Attempts to get the NMS handle of a CraftBukkit object.
* <p>
* The only match currently attempted by this method is a retrieval by using a parameterless {@code getHandle()} method implemented by the runtime type of the specified object.
* </p>
*
* @param obj The object for which to retrieve an NMS handle.
* @return The NMS handle of the specified object, or {@code null} if it could not be retrieved using {@code getHandle()}.
*/
public synchronized static Object getHandle(Object obj) throws InvocationTargetException, IllegalAccessException, IllegalArgumentException {
return getMethod(obj.getClass(), "getHandle").invoke(obj);
}
/**
* Retrieves a {@link Field} instance declared by the specified class with the specified name.
* Java access modifiers are ignored during this retrieval. No guarantee is made as to whether the field
* returned will be an instance or static field.
* <p>
* A global caching mechanism within this class is used to store fields. Combined with synchronization, this guarantees that
* no field will be reflectively looked up twice.
* </p>
* <p>
* If a field is deemed suitable for return, {@link Field#setAccessible(boolean) setAccessible} will be invoked with an argument of {@code true} before it is returned.
* This ensures that callers do not have to check or worry about Java access modifiers when dealing with the returned instance.
* </p>
*
* @param clazz The class which contains the field to retrieve.
* @param name The declared name of the field in the class.
* @return A field object with the specified name declared by the specified class.
* @see Class#getDeclaredField(String)
*/
public synchronized static Field getField(Class<?> clazz, String name) {
Map<String, Field> loaded;
if (!_loadedFields.containsKey(clazz)) {
loaded = new HashMap<>();
_loadedFields.put(clazz, loaded);
} else {
loaded = _loadedFields.get(clazz);
}
if (loaded.containsKey(name)) {
// If the field is loaded (or cached as not existing), return the relevant value, which might be null
return loaded.get(name);
}
try {
Field field = clazz.getDeclaredField(name);
field.setAccessible(true);
loaded.put(name, field);
return field;
} catch (NoSuchFieldException | SecurityException e) {
// Error loading
e.printStackTrace();
// Cache field as not existing
loaded.put(name, null);
return null;
}
}
/**
* Retrieves a {@link Method} instance declared by the specified class with the specified name and argument types.
* Java access modifiers are ignored during this retrieval. No guarantee is made as to whether the field
* returned will be an instance or static field.
* <p>
* A global caching mechanism within this class is used to store method. Combined with synchronization, this guarantees that
* no method will be reflectively looked up twice.
* <p>
* If a method is deemed suitable for return, {@link Method#setAccessible(boolean) setAccessible} will be invoked with an argument of {@code true} before it is returned.
* This ensures that callers do not have to check or worry about Java access modifiers when dealing with the returned instance.
* <p>
* This method does <em>not</em> search superclasses of the specified type for methods with the specified signature.
* Callers wishing this behavior should use {@link Class#getDeclaredMethod(String, Class...)}.
*
* @param clazz The class which contains the method to retrieve.
* @param name The declared name of the method in the class.
* @param args The formal argument types of the method.
* @return A method object with the specified name declared by the specified class.
*/
public synchronized static Method getMethod(Class<?> clazz, String name, Class<?>... args) {
if (!_loadedMethods.containsKey(clazz)) {
_loadedMethods.put(clazz, new HashMap<>());
}
Map<String, Map<ArrayWrapper<Class<?>>, Method>> loadedMethodNames = _loadedMethods.get(clazz);
if (!loadedMethodNames.containsKey(name)) {
loadedMethodNames.put(name, new HashMap<>());
}
Map<ArrayWrapper<Class<?>>, Method> loadedSignatures = loadedMethodNames.get(name);
ArrayWrapper<Class<?>> wrappedArg = new ArrayWrapper<>(args);
if (loadedSignatures.containsKey(wrappedArg)) {
return loadedSignatures.get(wrappedArg);
}
for (Method m : clazz.getMethods()) {
if (m.getName().equals(name) && Arrays.equals(args, m.getParameterTypes())) {
m.setAccessible(true);
loadedSignatures.put(wrappedArg, m);
return m;
}
}
loadedSignatures.put(wrappedArg, null);
return null;
}
}

View File

@ -1,5 +1,6 @@
package com.boydti.fawe.bukkit.listener;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.command.CFICommands;
import com.boydti.fawe.object.RunnableVal3;
import com.boydti.fawe.object.brush.visualization.VirtualWorld;
@ -15,20 +16,32 @@ import com.comphenix.protocol.wrappers.BlockPosition;
import com.comphenix.protocol.wrappers.ChunkCoordIntPair;
import com.comphenix.protocol.wrappers.EnumWrappers;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.bukkit.BukkitPlayer;
import com.sk89q.worldedit.event.platform.BlockInteractEvent;
import com.sk89q.worldedit.event.platform.Interaction;
import com.sk89q.worldedit.extension.platform.PlatformManager;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.math.Vector3;
import com.sk89q.worldedit.world.block.BlockState;
import java.lang.reflect.InvocationTargetException;
import java.net.URI;
import java.util.List;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import com.sk89q.worldedit.world.block.BlockTypes;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
/**
* The CFIPacketListener handles packets for editing the VirtualWorld
@ -45,99 +58,90 @@ public class CFIPacketListener implements Listener {
this.plugin = plugin;
this.protocolmanager = ProtocolLibrary.getProtocolManager();
// TODO NOT IMPLEMENTED
// // Direct digging to the virtual world
// registerBlockEvent(PacketType.Play.Client.BLOCK_DIG, false, new RunnableVal3<PacketEvent, VirtualWorld, BlockVector3>() {
// @Override
// public void run(Builder event, URI gen, String pt) {
// try {
// Player plr = event.getPlayer();
// BlockVector3 realPos = pt.add(gen.getOrigin().toBlockPoint());
// if (!sendBlockChange(plr, gen, pt, Interaction.HIT)) {
// gen.setBlock(pt, BlockTypes.AIR.getDefaultState());
// }
// } catch (WorldEditException e) {
// e.printStackTrace();
// }
// }
// });
//
// // Direct placing to the virtual world
// RunnableVal3<PacketEvent, VirtualWorld, BlockVector3> placeTask = new RunnableVal3<PacketEvent, VirtualWorld, BlockVector3>() {
// @Override
// public void run(Builder event, URI gen, String pt) {
// try {
// Player plr = event.getPlayer();
// List<EnumWrappers.Hand> hands = event.getPacket().getHands().getValues();
//
// EnumWrappers.Hand enumHand = hands.isEmpty() ? EnumWrappers.Hand.MAIN_HAND : hands.get(0);
// PlayerInventory inv = plr.getInventory();
// ItemStack hand = enumHand == EnumWrappers.Hand.MAIN_HAND ? inv.getItemInMainHand() : inv.getItemInOffHand();
// if (hand.getType().isBlock()) {
// Material type = hand.getType();
// switch (type) {
// case AIR:
// case CAVE_AIR:
// case VOID_AIR:
// break;
// default: {
// BlockStateHolder block = BukkitAdapter.asBlockState(hand);
// if (block != null) {
// gen.setBlock(pt, block);
// return;
// }
// }
// }
// }
// pt = getRelPos(event, gen);
// sendBlockChange(plr, gen, pt, Interaction.OPEN);
// } catch (WorldEditException e) {
// e.printStackTrace();
// }
// }
// };
// registerBlockEvent(PacketType.Play.Client.BLOCK_PLACE, true, placeTask);
// registerBlockEvent(PacketType.Play.Client.USE_ITEM, true, placeTask);
//
// // Cancel block change packets where the real world overlaps with the virtual one
// registerBlockEvent(PacketType.Play.Server.BLOCK_CHANGE, false, new RunnableVal3<PacketEvent, VirtualWorld, BlockVector3>() {
// @Override
// public void run(Builder event, URI gen, String pt) {
// // Do nothing
// }
// });
//
// // Modify chunk packets where the real world overlaps with the virtual one
// protocolmanager.addPacketListener(new PacketAdapter(plugin, ListenerPriority.NORMAL, PacketType.Play.Server.MAP_CHUNK) {
// @Override
// public void onPacketSending(PacketEvent event) {
// if (!event.isServerPacket()) return;
//
// VirtualWorld gen = getGenerator(event);
// if (gen != null) {
// BlockVector3 origin = gen.getOrigin().toBlockPoint();
// PacketContainer packet = event.getPacket();
// StructureModifier<Integer> ints = packet.getIntegers();
// int cx = ints.read(0);
// int cz = ints.read(1);
//
// int ocx = origin.getBlockX() >> 4;
// int ocz = origin.getBlockZ() >> 4;
//
// if (gen.contains(BlockVector3.at((cx - ocx) << 4, 0, (cz - ocz) << 4))) {
// event.setCancelled(true);
//
// Player plr = event.getPlayer();
//
// FaweQueue queue = SetQueue.IMP.getNewQueue(plr.getWorld().getName(), true, false);
//
// FaweChunk toSend = gen.getSnapshot(cx - ocx, cz - ocz);
// toSend.setLoc(gen, cx, cz);
// queue.sendChunkUpdate(toSend, FawePlayer.wrap(plr));
// }
// }
// }
// });
// Direct digging to the virtual world
registerBlockEvent(PacketType.Play.Client.BLOCK_DIG, false, new RunnableVal3<PacketEvent, VirtualWorld, BlockVector3>() {
@Override
public void run(PacketEvent event, VirtualWorld gen, BlockVector3 pt) {
try {
Player plr = event.getPlayer();
BlockVector3 realPos = pt.add(gen.getOrigin().toBlockPoint());
if (!sendBlockChange(plr, gen, pt, Interaction.HIT)) {
gen.setBlock(pt, BlockTypes.AIR.getDefaultState());
}
} catch (WorldEditException e) {
e.printStackTrace();
}
}
});
// Direct placing to the virtual world
RunnableVal3<PacketEvent, VirtualWorld, BlockVector3> placeTask = new RunnableVal3<PacketEvent, VirtualWorld, BlockVector3>() {
@Override
public void run(PacketEvent event, VirtualWorld gen, BlockVector3 pt) {
try {
Player plr = event.getPlayer();
List<EnumWrappers.Hand> hands = event.getPacket().getHands().getValues();
EnumWrappers.Hand enumHand = hands.isEmpty() ? EnumWrappers.Hand.MAIN_HAND : hands.get(0);
PlayerInventory inv = plr.getInventory();
ItemStack hand = enumHand == EnumWrappers.Hand.MAIN_HAND ? inv.getItemInMainHand() : inv.getItemInOffHand();
if (hand.getType().isBlock()) {
Material type = hand.getType();
switch (type) {
case AIR:
case CAVE_AIR:
case VOID_AIR:
break;
default: {
BlockStateHolder block = BukkitAdapter.asBlockState(hand);
if (block != null) {
gen.setBlock(pt, block);
return;
}
}
}
}
pt = getRelPos(event, gen);
sendBlockChange(plr, gen, pt, Interaction.OPEN);
} catch (WorldEditException e) {
e.printStackTrace();
}
}
};
registerBlockEvent(PacketType.Play.Client.BLOCK_PLACE, true, placeTask);
registerBlockEvent(PacketType.Play.Client.USE_ITEM, true, placeTask);
// Cancel block change packets where the real world overlaps with the virtual one
registerBlockEvent(PacketType.Play.Server.BLOCK_CHANGE, false, new RunnableVal3<PacketEvent, VirtualWorld, BlockVector3>() {
@Override
public void run(PacketEvent event, VirtualWorld gen, BlockVector3 pt) {
// Do nothing
}
});
// Modify chunk packets where the real world overlaps with the virtual one
protocolmanager.addPacketListener(new PacketAdapter(plugin, ListenerPriority.NORMAL, PacketType.Play.Server.MAP_CHUNK) {
@Override
public void onPacketSending(PacketEvent event) {
if (!event.isServerPacket() || FaweCache.IMP.CHUNK_FLAG.get().get()) return;
VirtualWorld gen = getGenerator(event);
if (gen != null) {
BlockVector3 origin = gen.getOrigin().toBlockPoint();
PacketContainer packet = event.getPacket();
StructureModifier<Integer> ints = packet.getIntegers();
int cx = ints.read(0);
int cz = ints.read(1);
int ocx = origin.getBlockX() >> 4;
int ocz = origin.getBlockZ() >> 4;
if (gen.contains(BlockVector3.at((cx - ocx) << 4, 0, (cz - ocz) << 4))) {
event.setCancelled(true);
gen.refreshChunk(cx - ocx, cz - ocz);
}
}
}
});
// The following few listeners are to ignore block collisions where the virtual and real world overlap

View File

@ -1,4 +0,0 @@
package com.boydti.fawe.bukkit.listener;
public class ChunkCache {
}

View File

@ -0,0 +1,196 @@
package com.boydti.fawe.bukkit.preloader;
import com.boydti.fawe.Fawe;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.math.BlockVector2;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.regions.Region;
import org.bukkit.Server;
import org.bukkit.World;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.generator.ChunkGenerator;
import org.bukkit.plugin.PluginBase;
import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.PluginLoader;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.InputStream;
import java.lang.ref.PhantomReference;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger;
public class PluginPreloader extends PluginBase {
private World world;
private Set<BlockVector2> loaded;
private int index;
private AtomicBoolean invalidator;
private final Object invalidatorLock;
public PluginPreloader() {
invalidator = new AtomicBoolean();
invalidatorLock = new Object();
}
public AtomicBoolean invalidate() {
synchronized (invalidatorLock) {
invalidator.set(false);
return invalidator = new AtomicBoolean(true);
}
}
private synchronized void unload() {
World oldWorld = world;
if (oldWorld != null) {
Set<BlockVector2> toUnload = loaded;
if (loaded != null && index > 0) {
Iterator<BlockVector2> iter = toUnload.iterator();
Fawe.get().getQueueHandler().sync(() -> {
for (int i = 0; i < index && iter.hasNext(); i++) {
BlockVector2 chunk = iter.next();
world.removePluginChunkTicket(chunk.getX(), chunk.getZ(), this);
}
});
}
}
this.world = null;
this.loaded = null;
this.index = 0;
}
public void update(Region region) {
AtomicBoolean invalidator = invalidate();
synchronized (this) {
com.sk89q.worldedit.world.World weWorld = region.getWorld();
if (weWorld == null) {
return;
}
unload();
index = 0;
world = BukkitAdapter.adapt(weWorld);
loaded = region.getChunks();
Iterator<BlockVector2> iter = loaded.iterator();
if (!invalidator.get()) return;
Fawe.get().getQueueHandler().syncWhenFree(() -> {
for (; iter.hasNext() && invalidator.get();index++) {
BlockVector2 chunk = iter.next();
if (!world.isChunkLoaded(chunk.getX(), chunk.getZ())) {
world.addPluginChunkTicket(chunk.getX(), chunk.getZ(), this);
}
}
});
}
}
public void clear() {
invalidate();
unload();
}
@Override
public @NotNull File getDataFolder() {
return null;
}
@Override
public @NotNull PluginDescriptionFile getDescription() {
return null;
}
@Override
public @NotNull FileConfiguration getConfig() {
return null;
}
@Override
public @Nullable InputStream getResource(@NotNull String filename) {
return null;
}
@Override
public void saveConfig() {
}
@Override
public void saveDefaultConfig() {
}
@Override
public void saveResource(@NotNull String resourcePath, boolean replace) {
}
@Override
public void reloadConfig() {
}
@Override
public @NotNull PluginLoader getPluginLoader() {
return null;
}
@Override
public @NotNull Server getServer() {
return null;
}
@Override
public boolean isEnabled() {
return true;
}
@Override
public void onDisable() {
}
@Override
public void onLoad() {
}
@Override
public void onEnable() {
}
@Override
public boolean isNaggable() {
return false;
}
@Override
public void setNaggable(boolean canNag) {
}
@Override
public @Nullable ChunkGenerator getDefaultWorldGenerator(@NotNull String worldName, @Nullable String id) {
return null;
}
@Override
public @NotNull Logger getLogger() {
return null;
}
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
return false;
}
@Override
public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, String[] args) {
return null;
}
}

View File

@ -19,173 +19,173 @@ import org.bukkit.entity.ItemFrame;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
//import org.inventivetalent.mapmanager.MapManagerPlugin;
//import org.inventivetalent.mapmanager.controller.MapController;
//import org.inventivetalent.mapmanager.controller.MultiMapController;
//import org.inventivetalent.mapmanager.manager.MapManager;
//import org.inventivetalent.mapmanager.wrapper.MapWrapper;
import org.inventivetalent.mapmanager.MapManagerPlugin;
import org.inventivetalent.mapmanager.controller.MapController;
import org.inventivetalent.mapmanager.controller.MultiMapController;
import org.inventivetalent.mapmanager.manager.MapManager;
import org.inventivetalent.mapmanager.wrapper.MapWrapper;
public class BukkitImageViewer implements ImageViewer {
// private final MapManager mapManager;
// private final Player player;
// private BufferedImage last;
private final MapManager mapManager;
private final Player player;
private BufferedImage last;
private ItemFrame[][] frames;
// private boolean reverse;
private boolean reverse;
public BukkitImageViewer(Player player) {
// mapManager = ((MapManagerPlugin) Bukkit.getPluginManager().getPlugin("MapManager")).getMapManager();
// this.player = player;
mapManager = ((MapManagerPlugin) Bukkit.getPluginManager().getPlugin("MapManager")).getMapManager();
this.player = player;
}
//
public void selectFrame(ItemFrame start) {
// Location pos1 = start.getLocation().clone();
// Location pos2 = start.getLocation().clone();
//
// BlockFace facing = start.getFacing();
// int planeX = facing.getModX() == 0 ? 1 : 0;
// int planeY = facing.getModY() == 0 ? 1 : 0;
// int planeZ = facing.getModZ() == 0 ? 1 : 0;
//
// ItemFrame[][] res = find(pos1, pos2, facing);
// Location tmp;
// while (true) {
// if (res != null) {
// frames = res;
// }
// tmp = pos1.clone().subtract(planeX, planeY, planeZ);
// if ((res = find(tmp, pos2, facing)) != null) {
// pos1 = tmp;
// continue;
// }
// tmp = pos2.clone().add(planeX, planeY, planeZ);
// if ((res = find(pos1, tmp, facing)) != null) {
// pos2 = tmp;
// continue;
// }
// tmp = pos1.clone().subtract(planeX, 0, planeZ);
// if ((res = find(tmp, pos2, facing)) != null) {
// pos1 = tmp;
// continue;
// }
// tmp = pos2.clone().add(planeX, 0, planeZ);
// if ((res = find(pos1, tmp, facing)) != null) {
// pos2 = tmp;
// continue;
// }
// tmp = pos1.clone().subtract(0, 1, 0);
// if ((res = find(tmp, pos2, facing)) != null) {
// pos1 = tmp;
// continue;
// }
// tmp = pos2.clone().add(0, 1, 0);
// if ((res = find(pos1, tmp, facing)) != null) {
// pos2 = tmp;
// continue;
// }
// break;
// }
Location pos1 = start.getLocation().clone();
Location pos2 = start.getLocation().clone();
BlockFace facing = start.getFacing();
int planeX = facing.getModX() == 0 ? 1 : 0;
int planeY = facing.getModY() == 0 ? 1 : 0;
int planeZ = facing.getModZ() == 0 ? 1 : 0;
ItemFrame[][] res = find(pos1, pos2, facing);
Location tmp;
while (true) {
if (res != null) {
frames = res;
}
tmp = pos1.clone().subtract(planeX, planeY, planeZ);
if ((res = find(tmp, pos2, facing)) != null) {
pos1 = tmp;
continue;
}
tmp = pos2.clone().add(planeX, planeY, planeZ);
if ((res = find(pos1, tmp, facing)) != null) {
pos2 = tmp;
continue;
}
tmp = pos1.clone().subtract(planeX, 0, planeZ);
if ((res = find(tmp, pos2, facing)) != null) {
pos1 = tmp;
continue;
}
tmp = pos2.clone().add(planeX, 0, planeZ);
if ((res = find(pos1, tmp, facing)) != null) {
pos2 = tmp;
continue;
}
tmp = pos1.clone().subtract(0, 1, 0);
if ((res = find(tmp, pos2, facing)) != null) {
pos1 = tmp;
continue;
}
tmp = pos2.clone().add(0, 1, 0);
if ((res = find(pos1, tmp, facing)) != null) {
pos2 = tmp;
continue;
}
break;
}
}
//
public ItemFrame[][] getItemFrames() {
return frames;
}
//
// private ItemFrame[][] find(Location pos1, Location pos2, BlockFace facing) {
// try {
// Location distance = pos2.clone().subtract(pos1).add(1, 1, 1);
// int width = Math.max(distance.getBlockX(), distance.getBlockZ());
// ItemFrame[][] frames = new ItemFrame[width][distance.getBlockY()];
//
// World world = pos1.getWorld();
//
// this.reverse = (facing == BlockFace.NORTH || facing == BlockFace.EAST);
// int v = 0;
// for (double y = pos1.getY(); y <= pos2.getY(); y++, v++) {
// int h = 0;
// for (double z = pos1.getZ(); z <= pos2.getZ(); z++) {
// for (double x = pos1.getX(); x <= pos2.getX(); x++, h++) {
// Location pos = new Location(world, x, y, z);
// Collection<Entity> entities = world.getNearbyEntities(pos, 0.1, 0.1, 0.1);
// boolean contains = false;
// for (Entity ent : entities) {
// if (ent instanceof ItemFrame && ((ItemFrame) ent).getFacing() == facing) {
// ItemFrame itemFrame = (ItemFrame) ent;
// itemFrame.setRotation(Rotation.NONE);
// contains = true;
// frames[reverse ? width - 1 - h : h][v] = (ItemFrame) ent;
// break;
// }
// }
// if (!contains) return null;
// }
// }
// }
// return frames;
// } catch (Throwable e) {
// e.printStackTrace();
// }
// return null;
// }
private ItemFrame[][] find(Location pos1, Location pos2, BlockFace facing) {
try {
Location distance = pos2.clone().subtract(pos1).add(1, 1, 1);
int width = Math.max(distance.getBlockX(), distance.getBlockZ());
ItemFrame[][] frames = new ItemFrame[width][distance.getBlockY()];
World world = pos1.getWorld();
this.reverse = (facing == BlockFace.NORTH || facing == BlockFace.EAST);
int v = 0;
for (double y = pos1.getY(); y <= pos2.getY(); y++, v++) {
int h = 0;
for (double z = pos1.getZ(); z <= pos2.getZ(); z++) {
for (double x = pos1.getX(); x <= pos2.getX(); x++, h++) {
Location pos = new Location(world, x, y, z);
Collection<Entity> entities = world.getNearbyEntities(pos, 0.1, 0.1, 0.1);
boolean contains = false;
for (Entity ent : entities) {
if (ent instanceof ItemFrame && ((ItemFrame) ent).getFacing() == facing) {
ItemFrame itemFrame = (ItemFrame) ent;
itemFrame.setRotation(Rotation.NONE);
contains = true;
frames[reverse ? width - 1 - h : h][v] = (ItemFrame) ent;
break;
}
}
if (!contains) return null;
}
}
}
return frames;
} catch (Throwable e) {
e.printStackTrace();
}
return null;
}
@Override
public void view(Drawable drawable) {
// view(null, drawable);
view(null, drawable);
}
//
// private void view(@Nullable BufferedImage image, @Nullable Drawable drawable) {
// if (image == null && drawable == null) throw new IllegalArgumentException("An image or drawable must be provided. Both cannot be null");
// boolean initializing = last == null;
//
// if (this.frames != null) {
// if (image == null && drawable != null) image = drawable.draw();
// last = image;
// int width = frames.length;
// int height = frames[0].length;
// BufferedImage scaled = ImageUtil.getScaledInstance(image, 128 * width, 128 * height, RenderingHints.VALUE_INTERPOLATION_BILINEAR, false);
// MapWrapper mapWrapper = mapManager.wrapMultiImage(scaled, width, height);
// MultiMapController controller = (MultiMapController) mapWrapper.getController();
// controller.addViewer(player);
// controller.sendContent(player);
// controller.showInFrames(player, frames, true);
// } else {
// int slot = getMapSlot(player);
// if (slot == -1) {
// if (initializing) {
// player.getInventory().setItemInMainHand(new ItemStack(Material.MAP));
// } else {
// return;
// }
// } else if (player.getInventory().getHeldItemSlot() != slot) {
// player.getInventory().setHeldItemSlot(slot);
// }
// if (image == null && drawable != null) image = drawable.draw();
// last = image;
// BufferedImage scaled = ImageUtil.getScaledInstance(image, 128, 128, RenderingHints.VALUE_INTERPOLATION_BILINEAR, false);
// MapWrapper mapWrapper = mapManager.wrapImage(scaled);
// MapController controller = mapWrapper.getController();
// controller.addViewer(player);
// controller.sendContent(player);
// controller.showInHand(player, true);
// }
// }
//
// private int getMapSlot(Player player) {
// PlayerInventory inventory = player.getInventory();
// for (int i = 0; i < 9; i++) {
// ItemStack item = inventory.getItem(i);
// if (item != null && item.getType() == Material.MAP) {
// return i;
// }
// }
// return -1;
// }
//
private void view(@Nullable BufferedImage image, @Nullable Drawable drawable) {
if (image == null && drawable == null) throw new IllegalArgumentException("An image or drawable must be provided. Both cannot be null");
boolean initializing = last == null;
if (this.frames != null) {
if (image == null && drawable != null) image = drawable.draw();
last = image;
int width = frames.length;
int height = frames[0].length;
BufferedImage scaled = ImageUtil.getScaledInstance(image, 128 * width, 128 * height, RenderingHints.VALUE_INTERPOLATION_BILINEAR, false);
MapWrapper mapWrapper = mapManager.wrapMultiImage(scaled, width, height);
MultiMapController controller = (MultiMapController) mapWrapper.getController();
controller.addViewer(player);
controller.sendContent(player);
controller.showInFrames(player, frames, true);
} else {
int slot = getMapSlot(player);
if (slot == -1) {
if (initializing) {
player.getInventory().setItemInMainHand(new ItemStack(Material.MAP));
} else {
return;
}
} else if (player.getInventory().getHeldItemSlot() != slot) {
player.getInventory().setHeldItemSlot(slot);
}
if (image == null && drawable != null) image = drawable.draw();
last = image;
BufferedImage scaled = ImageUtil.getScaledInstance(image, 128, 128, RenderingHints.VALUE_INTERPOLATION_BILINEAR, false);
MapWrapper mapWrapper = mapManager.wrapImage(scaled);
MapController controller = mapWrapper.getController();
controller.addViewer(player);
controller.sendContent(player);
controller.showInHand(player, true);
}
}
private int getMapSlot(Player player) {
PlayerInventory inventory = player.getInventory();
for (int i = 0; i < 9; i++) {
ItemStack item = inventory.getItem(i);
if (item != null && item.getType() == Material.MAP) {
return i;
}
}
return -1;
}
public void refresh() {
// if (last != null) view(last, null);
if (last != null) view(last, null);
}
@Override
public void close() throws IOException {
// last = null;
last = null;
}
}

View File

@ -11,6 +11,7 @@ import com.sk89q.worldedit.world.block.BlockID;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockType;
import com.sk89q.worldedit.world.block.BlockTypes;
import com.sk89q.worldedit.world.block.BlockTypesCache;
import org.bukkit.FluidCollisionMode;
import org.bukkit.Location;
import org.bukkit.Material;
@ -57,7 +58,7 @@ public class AsyncBlock implements Block {
}
public int getPropertyId() {
return world.getBlock(x, y, z).getInternalId() >> BlockTypes.BIT_OFFSET;
return world.getBlock(x, y, z).getInternalId() >> BlockTypesCache.BIT_OFFSET;
}
public int getCombinedId() {
@ -105,7 +106,7 @@ public class AsyncBlock implements Block {
@Deprecated
public boolean setTypeIdAndPropertyId(int id, int propertyId) {
return setCombinedId(id + (propertyId << BlockTypes.BIT_OFFSET));
return setCombinedId(id + (propertyId << BlockTypesCache.BIT_OFFSET));
}
@Deprecated

View File

@ -9,6 +9,7 @@ import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockType;
import com.sk89q.worldedit.world.block.BlockTypes;
import com.sk89q.worldedit.world.block.BlockTypesCache;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.Material;
@ -42,7 +43,7 @@ public class AsyncBlockState implements BlockState {
}
public int getPropertyId() {
return state.getInternalId() >> BlockTypes.BIT_OFFSET;
return state.getInternalId() >> BlockTypesCache.BIT_OFFSET;
}
@Override
@ -158,12 +159,12 @@ public class AsyncBlockState implements BlockState {
@Override
public byte getRawData() {
return (byte) (state.getInternalId() >> BlockTypes.BIT_OFFSET);
return (byte) (state.getInternalId() >> BlockTypesCache.BIT_OFFSET);
}
@Override
public void setRawData(byte data) {
int combinedId = getTypeId() + (data << BlockTypes.BIT_OFFSET);
int combinedId = getTypeId() + (data << BlockTypesCache.BIT_OFFSET);
state = com.sk89q.worldedit.world.block.BlockState.getFromInternalId(combinedId).toBaseBlock(state.getNbtData());
this.blockData = BukkitAdapter.adapt(state);
}

View File

@ -139,7 +139,7 @@ public enum BukkitAdapter {
*/
public static Player adapt(com.sk89q.worldedit.entity.Player player) {
player = PlayerProxy.unwrap(player);
return ((BukkitPlayer) player).getPlayer();
return player == null ? null : ((BukkitPlayer) player).getPlayer();
}
/**

View File

@ -34,6 +34,7 @@ import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
import java.util.OptionalInt;
public class BukkitBlockRegistry extends BundledBlockRegistry {
@ -93,6 +94,10 @@ public class BukkitBlockRegistry extends BundledBlockRegistry {
this.material = bukkitMaterial;
}
public int getId() {
return material.getId();
}
@Override
public boolean isAir() {
switch (material) {
@ -132,4 +137,9 @@ public class BukkitBlockRegistry extends BundledBlockRegistry {
}
return blocks;
}
@Override
public OptionalInt getInternalBlockStateId(BlockState state) {
return WorldEditPlugin.getInstance().getBukkitImplAdapter().getInternalBlockStateId(state);
}
}

View File

@ -47,11 +47,6 @@ import com.sk89q.worldedit.world.block.BlockStateHolder;
import com.sk89q.worldedit.world.block.BlockTypes;
import com.sk89q.worldedit.world.gamemode.GameMode;
import com.sk89q.worldedit.world.gamemode.GameModes;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.UUID;
import javax.annotation.Nullable;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Location;
@ -62,23 +57,44 @@ import org.bukkit.event.player.PlayerDropItemEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
import javax.annotation.Nullable;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
public class BukkitPlayer extends AbstractPlayerActor {
private Player player;
private WorldEditPlugin plugin;
public BukkitPlayer(Player player) {
this(WorldEditPlugin.getInstance(), player);
super(getExistingMap(WorldEditPlugin.getInstance(), player));
this.plugin = WorldEditPlugin.getInstance();
this.player = player;
}
public BukkitPlayer(WorldEditPlugin plugin, Player player) {
this.plugin = plugin;
this.player = player;
init();
}
private void init() {
if (Settings.IMP.CLIPBOARD.USE_DISK) {
loadClipboardFromDisk();
}
}
private static Map<String, Object> getExistingMap(WorldEditPlugin plugin, Player player) {
BukkitPlayer cached = plugin.getCachedPlayer(player);
if (cached != null) {
return cached.getRawMeta();
}
return new ConcurrentHashMap<>();
}
@Override
public UUID getUniqueId() {
return player.getUniqueId();
@ -87,16 +103,16 @@ public class BukkitPlayer extends AbstractPlayerActor {
@Override
public BaseItemStack getItemInHand(HandSide handSide) {
ItemStack itemStack = handSide == HandSide.MAIN_HAND
? player.getInventory().getItemInMainHand()
: player.getInventory().getItemInOffHand();
? getPlayer().getInventory().getItemInMainHand()
: getPlayer().getInventory().getItemInOffHand();
return BukkitAdapter.adapt(itemStack);
}
@Override
public BaseBlock getBlockInHand(HandSide handSide) throws WorldEditException {
ItemStack itemStack = handSide == HandSide.MAIN_HAND
? player.getInventory().getItemInMainHand()
: player.getInventory().getItemInOffHand();
? getPlayer().getInventory().getItemInMainHand()
: getPlayer().getInventory().getItemInOffHand();
return BukkitAdapter.asBlockState(itemStack).toBaseBlock();
}
@ -107,18 +123,18 @@ public class BukkitPlayer extends AbstractPlayerActor {
@Override
public String getDisplayName() {
return player.getDisplayName();
return getPlayer().getDisplayName();
}
@Override
public void giveItem(BaseItemStack itemStack) {
final PlayerInventory inv = player.getInventory();
final PlayerInventory inv = getPlayer().getInventory();
ItemStack newItem = BukkitAdapter.adapt(itemStack);
if (itemStack.getType().getId().equalsIgnoreCase(WorldEdit.getInstance().getConfiguration().wandItem)) {
inv.remove(newItem);
}
final ItemStack item = player.getInventory().getItemInMainHand();
player.getInventory().setItemInMainHand(newItem);
final ItemStack item = getPlayer().getInventory().getItemInMainHand();
getPlayer().getInventory().setItemInMainHand(newItem);
HashMap<Integer, ItemStack> overflow = inv.addItem(item);
if (!overflow.isEmpty()) {
TaskManager.IMP.sync(new RunnableVal<Object>() {
@ -128,7 +144,7 @@ public class BukkitPlayer extends AbstractPlayerActor {
ItemStack stack = entry.getValue();
if (stack.getType() != Material.AIR && stack.getAmount() > 0) {
Item
dropped = player.getWorld().dropItem(player.getLocation(), stack);
dropped = getPlayer().getWorld().dropItem(getPlayer().getLocation(), stack);
PlayerDropItemEvent event = new PlayerDropItemEvent(player, dropped);
if (event.isCancelled()) {
dropped.remove();
@ -138,45 +154,45 @@ public class BukkitPlayer extends AbstractPlayerActor {
}
});
}
player.updateInventory();
getPlayer().updateInventory();
}
@Override
public void printRaw(String msg) {
for (String part : msg.split("\n")) {
player.sendMessage(part);
getPlayer().sendMessage(part);
}
}
@Override
public void print(String msg) {
for (String part : msg.split("\n")) {
player.sendMessage("\u00A7d" + part);
getPlayer().sendMessage("\u00A7d" + part);
}
}
@Override
public void printDebug(String msg) {
for (String part : msg.split("\n")) {
player.sendMessage("\u00A77" + part);
getPlayer().sendMessage("\u00A77" + part);
}
}
@Override
public void printError(String msg) {
for (String part : msg.split("\n")) {
player.sendMessage("§c" + part);
getPlayer().sendMessage("§c" + part);
}
}
@Override
public void print(Component component) {
TextAdapter.sendComponent(player, WorldEditText.format(component));
TextAdapter.sendComponent(getPlayer(), WorldEditText.format(component));
}
@Override
public void setPosition(Vector3 pos, float pitch, float yaw) {
org.bukkit.World world = player.getWorld();
org.bukkit.World world = getPlayer().getWorld();
if (pos instanceof com.sk89q.worldedit.util.Location) {
com.sk89q.worldedit.util.Location loc = (com.sk89q.worldedit.util.Location) pos;
Extent extent = loc.getExtent();
@ -184,42 +200,41 @@ public class BukkitPlayer extends AbstractPlayerActor {
world = Bukkit.getWorld(((World) extent).getName());
}
}
player.teleport(new Location(world, pos.getX(), pos.getY(), pos.getZ(), yaw, pitch));
getPlayer().teleport(new Location(world, pos.getX(), pos.getY(), pos.getZ(), yaw, pitch));
}
@Override
public String[] getGroups() {
return plugin.getPermissionsResolver().getGroups(player);
return plugin.getPermissionsResolver().getGroups(getPlayer());
}
@Override
public BlockBag getInventoryBlockBag() {
return new BukkitPlayerBlockBag(player);
return new BukkitPlayerBlockBag(getPlayer());
}
@Override
public GameMode getGameMode() {
return GameModes.get(player.getGameMode().name().toLowerCase(Locale.ROOT));
return GameModes.get(getPlayer().getGameMode().name().toLowerCase(Locale.ROOT));
}
@Override
public void setGameMode(GameMode gameMode) {
player.setGameMode(org.bukkit.GameMode.valueOf(gameMode.getId().toUpperCase(Locale.ROOT)));
getPlayer().setGameMode(org.bukkit.GameMode.valueOf(gameMode.getId().toUpperCase(Locale.ROOT)));
}
@Override
public boolean hasPermission(String perm) {
return (!plugin.getLocalConfiguration().noOpPermissions && player.isOp())
|| plugin.getPermissionsResolver().hasPermission(
player.getWorld().getName(), player, perm);
|| plugin.getPermissionsResolver().hasPermission(player.getWorld().getName(), player, perm);
}
@Override public boolean togglePermission(String permission) {
if (this.hasPermission(permission)) {
player.addAttachment(plugin).setPermission(permission, false);
getPlayer().addAttachment(plugin).setPermission(permission, false);
return false;
} else {
player.addAttachment(plugin).setPermission(permission, true);
getPlayer().addAttachment(plugin).setPermission(permission, true);
return true;
}
}
@ -231,19 +246,19 @@ public class BukkitPlayer extends AbstractPlayerActor {
* - The `/wea` command will give/remove the required bypass permission
*/
if (Fawe.<FaweBukkit>imp().getVault() == null || Fawe.<FaweBukkit> imp().getVault().permission == null) {
player.addAttachment(Fawe.<FaweBukkit> imp().getPlugin()).setPermission(permission, value);
getPlayer().addAttachment(Fawe.<FaweBukkit> imp().getPlugin()).setPermission(permission, value);
} else if (value) {
if (!Fawe.<FaweBukkit> imp().getVault().permission.playerAdd(player, permission)) {
player.addAttachment(Fawe.<FaweBukkit> imp().getPlugin()).setPermission(permission, value);
getPlayer().addAttachment(Fawe.<FaweBukkit> imp().getPlugin()).setPermission(permission, value);
}
} else if (!Fawe.<FaweBukkit>imp().getVault().permission.playerRemove(player, permission)) {
player.addAttachment(Fawe.<FaweBukkit>imp().getPlugin()).setPermission(permission, value);
getPlayer().addAttachment(Fawe.<FaweBukkit>imp().getPlugin()).setPermission(permission, value);
}
}
@Override
public World getWorld() {
return BukkitAdapter.adapt(player.getWorld());
return BukkitAdapter.adapt(getPlayer().getWorld());
}
@Override
@ -253,21 +268,30 @@ public class BukkitPlayer extends AbstractPlayerActor {
if (params.length > 0) {
send = send + "|" + StringUtil.joinString(params, "|");
}
player.sendPluginMessage(plugin, WorldEditPlugin.CUI_PLUGIN_CHANNEL, send.getBytes(CUIChannelListener.UTF_8_CHARSET));
getPlayer().sendPluginMessage(plugin, WorldEditPlugin.CUI_PLUGIN_CHANNEL, send.getBytes(CUIChannelListener.UTF_8_CHARSET));
}
public Player getPlayer() {
if (!player.isValid()) {
Player tmp = Bukkit.getPlayer(getUniqueId());
if (tmp != null) {
player = tmp;
} else {
System.out.println("Invalid player " + player.getName());
new Exception().printStackTrace();
}
}
return player;
}
@Override
public boolean isAllowedToFly() {
return player.getAllowFlight();
return getPlayer().getAllowFlight();
}
@Override
public void setFlying(boolean flying) {
player.setFlying(flying);
getPlayer().setFlying(flying);
}
@Override
@ -277,7 +301,7 @@ public class BukkitPlayer extends AbstractPlayerActor {
@Override
public com.sk89q.worldedit.util.Location getLocation() {
Location nativeLocation = player.getLocation();
Location nativeLocation = getPlayer().getLocation();
Vector3 position = BukkitAdapter.asVector(nativeLocation);
return new com.sk89q.worldedit.util.Location(
getWorld(),
@ -288,7 +312,7 @@ public class BukkitPlayer extends AbstractPlayerActor {
@Override
public boolean setLocation(com.sk89q.worldedit.util.Location location) {
return player.teleport(BukkitAdapter.adapt(location));
return getPlayer().teleport(BukkitAdapter.adapt(location));
}
@Nullable
@ -299,7 +323,7 @@ public class BukkitPlayer extends AbstractPlayerActor {
@Override
public SessionKey getSessionKey() {
return new SessionKeyImpl(this.player.getUniqueId(), player.getName());
return new SessionKeyImpl(getUniqueId(), getName());
}
private static class SessionKeyImpl implements SessionKey {
@ -342,11 +366,11 @@ public class BukkitPlayer extends AbstractPlayerActor {
@Override
public <B extends BlockStateHolder<B>> void sendFakeBlock(BlockVector3 pos, B block) {
Location loc = new Location(player.getWorld(), pos.getX(), pos.getY(), pos.getZ());
Location loc = new Location(getPlayer().getWorld(), pos.getX(), pos.getY(), pos.getZ());
if (block == null) {
player.sendBlockChange(loc, player.getWorld().getBlockAt(loc).getBlockData());
getPlayer().sendBlockChange(loc, getPlayer().getWorld().getBlockAt(loc).getBlockData());
} else {
player.sendBlockChange(loc, BukkitAdapter.adapt(block));
getPlayer().sendBlockChange(loc, BukkitAdapter.adapt(block));
if (block instanceof BaseBlock && ((BaseBlock) block).hasNbtData()) {
BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter();
if (adapter != null) {
@ -361,8 +385,13 @@ public class BukkitPlayer extends AbstractPlayerActor {
@Override
public void sendTitle(String title, String sub) {
player.sendTitle(ChatColor.GOLD + title, ChatColor.GOLD + sub, 0, 70, 20);
getPlayer().sendTitle(ChatColor.GOLD + title, ChatColor.GOLD + sub, 0, 70, 20);
Bukkit.getServer().dispatchCommand(player, "title " + getName() + " subtitle [{\"text\":\"" + sub + "\",\"color\":\"gold\"}]");
Bukkit.getServer().dispatchCommand(player, "title " + getName() + " title [{\"text\":\"" + title + "\",\"color\":\"gold\"}]");
}
@Override
public void unregister() {
getPlayer().removeMetadata("WE", WorldEditPlugin.getInstance());
}
}

View File

@ -21,7 +21,10 @@ package com.sk89q.worldedit.bukkit;
import static com.google.common.base.Preconditions.checkNotNull;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.beta.IChunkGet;
import com.boydti.fawe.beta.implementation.packet.ChunkPacket;
import com.boydti.fawe.bukkit.FaweBukkit;
import com.boydti.fawe.bukkit.adapter.mc1_14.BukkitGetBlocks_1_14;
import com.boydti.fawe.config.Settings;
import com.sk89q.jnbt.CompoundTag;
@ -31,6 +34,7 @@ import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.blocks.BaseItemStack;
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
import com.sk89q.worldedit.entity.BaseEntity;
import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.history.change.BlockChange;
import com.sk89q.worldedit.math.BlockVector2;
import com.sk89q.worldedit.math.BlockVector3;
@ -52,6 +56,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import javax.annotation.Nullable;
import org.bukkit.Effect;
import org.bukkit.TreeType;
import org.bukkit.World;
@ -317,8 +322,19 @@ public class BukkitWorld extends AbstractWorld {
@Override
public void checkLoadedChunk(BlockVector3 pt) {
World world = getWorld();
world.getChunkAt(pt.getBlockX() >> 4, pt.getBlockZ() >> 4);
int X = pt.getBlockX() >> 4;
int Z = pt.getBlockZ() >> 4;
if (Fawe.isMainThread()) {
world.getChunkAt(X, Z);
} else if (!world.isChunkLoaded(X, Z)) {
if (FaweBukkit.PAPER) {
world.getChunkAtAsync(X, Z, true);
} else {
Fawe.get().getQueueHandler().sync(() -> {
world.getChunkAt(X, Z);
});
}
}
}
@Override
@ -516,11 +532,18 @@ public class BukkitWorld extends AbstractWorld {
}
@Override
public void sendChunk(int chunkX, int chunkZ, int bitMask) {
public void refreshChunk(int X, int Z) {
getWorld().refreshChunk(X, Z);
}
@Override
public IChunkGet get(int chunkX, int chunkZ) {
return new BukkitGetBlocks_1_14(getWorldChecked(), chunkX, chunkZ, Settings.IMP.QUEUE.POOL);
}
@Override
public void sendFakeChunk(Player player, ChunkPacket packet) {
org.bukkit.entity.Player bukkitPlayer = BukkitAdapter.adapt(player);
WorldEditPlugin.getInstance().getBukkitImplAdapter().sendFakeChunk(getWorld(), bukkitPlayer, packet);
}
}

View File

@ -84,6 +84,7 @@ import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.world.ChunkUnloadEvent;
import org.bukkit.event.world.WorldInitEvent;
import org.bukkit.metadata.FixedMetadataValue;
import org.bukkit.metadata.MetadataValue;
@ -559,15 +560,26 @@ public class WorldEditPlugin extends JavaPlugin { //implements TabCompleter
* @return a wrapped player
*/
public BukkitPlayer wrapPlayer(Player player) {
synchronized (player) {
@NotNull List<MetadataValue> meta = player.getMetadata("WE");
if (meta == null || meta.isEmpty()) {
BukkitPlayer wePlayer = new BukkitPlayer(this, player);
player.setMetadata("WE", new FixedMetadataValue(this, wePlayer));
return wePlayer;
BukkitPlayer wePlayer = getCachedPlayer(player);
if (wePlayer == null) {
synchronized (player) {
wePlayer = getCachedPlayer(player);
if (wePlayer == null) {
wePlayer = new BukkitPlayer(this, player);
player.setMetadata("WE", new FixedMetadataValue(this, wePlayer));
return wePlayer;
}
}
return (BukkitPlayer) meta.get(0).value();
}
return wePlayer;
}
public BukkitPlayer getCachedPlayer(Player player) {
List<MetadataValue> meta = player.getMetadata("WE");
if (meta == null || meta.isEmpty()) {
return null;
}
return (BukkitPlayer) meta.get(0).value();
}
public Actor wrapCommandSender(CommandSender sender) {

View File

@ -20,6 +20,7 @@
package com.sk89q.worldedit.bukkit.adapter;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.beta.implementation.packet.ChunkPacket;
import com.boydti.fawe.bukkit.FaweBukkit;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.Tag;
@ -141,6 +142,13 @@ public interface BukkitImplAdapter<T> extends IBukkitAdapter {
*/
void sendFakeOP(Player player);
/**
* Send a fake chunk packet to a player
* @param player
* @param packet
*/
void sendFakeChunk(org.bukkit.World world, Player player, ChunkPacket packet);
/**
* Simulates a player using an item.
*

View File

@ -6,6 +6,7 @@ import com.sk89q.worldedit.registry.state.Property;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockType;
import com.sk89q.worldedit.world.block.BlockTypes;
import com.sk89q.worldedit.world.block.BlockTypesCache;
import com.sk89q.worldedit.world.item.ItemType;
import com.sk89q.worldedit.world.item.ItemTypes;
import java.util.List;
@ -58,7 +59,7 @@ public abstract class CachedBukkitAdapter implements IBukkitAdapter {
@Override
public BlockType adapt(Material material) {
try {
return BlockTypes.values[blockTypes[material.ordinal()]];
return BlockTypesCache.values[blockTypes[material.ordinal()]];
} catch (NullPointerException e) {
if (init()) return adapt(material);
throw e;

View File

@ -154,3 +154,76 @@ permissions:
worldedit.extinguish: true
worldedit.calc: true
worldedit.fill: true
fawe.voxelbrush:
default: op
children:
voxelsniper.brush.ball: true
voxelsniper.brush.biome: true
voxelsniper.brush.blendball: true
voxelsniper.brush.blenddisc: true
voxelsniper.brush.blendvoxel: true
voxelsniper.brush.blendvoxeldisc: true
voxelsniper.brush.blob: true
voxelsniper.brush.blockreset: true
voxelsniper.brush.blockresetsurface: true
voxelsniper.brush.canyon: true
voxelsniper.brush.canyonselection: true
voxelsniper.brush.checkervoxeldisc: true
voxelsniper.brush.cleansnow: true
voxelsniper.brush.clonestamp: true
voxelsniper.brush.copypasta: true
voxelsniper.brush.cylinder: true
voxelsniper.brush.disc: true
voxelsniper.brush.discface: true
voxelsniper.brush.dome: true
voxelsniper.brush.drain: true
voxelsniper.brush.ellipse: true
voxelsniper.brush.ellipsoid: true
voxelsniper.brush.eraser: true
voxelsniper.brush.erode: true
voxelsniper.brush.extrude: true
voxelsniper.brush.filldown: true
voxelsniper.brush.flatocean: true
voxelsniper.brush.heatray: true
voxelsniper.brush.jaggedline: true
voxelsniper.brush.line: true
voxelsniper.brush.move: true
voxelsniper.brush.ocean: true
voxelsniper.brush.overlay: true
voxelsniper.brush.pull: true
voxelsniper.brush.randomerode: true
voxelsniper.brush.ring: true
voxelsniper.brush.rot2d: true
voxelsniper.brush.rot2dvert: true
voxelsniper.brush.rot3d: true
voxelsniper.brush.ruler: true
voxelsniper.brush.scanner: true
voxelsniper.brush.set: true
voxelsniper.brush.setredstoneflip: true
voxelsniper.brush.setredstonerotate: true
voxelsniper.brush.shellball: true
voxelsniper.brush.shellset: true
voxelsniper.brush.shellvoxel: true
voxelsniper.brush.signoverwrite: true
voxelsniper.brush.snipe: true
voxelsniper.brush.snowcone: true
voxelsniper.brush.spiralstaircase: true
voxelsniper.brush.splatterball: true
voxelsniper.brush.splatterdisc: true
voxelsniper.brush.splatteroverlay: true
voxelsniper.brush.splattervoxel: true
voxelsniper.brush.splattervoxeldisc: true
voxelsniper.brush.spline: true
voxelsniper.brush.stamp: true
voxelsniper.brush.stencil: true
voxelsniper.brush.stencillist: true
voxelsniper.brush.threepointcircle: true
voxelsniper.brush.triangle: true
voxelsniper.brush.underlay: true
voxelsniper.brush.voltmeter: true
voxelsniper.brush.voxel: true
voxelsniper.brush.voxeldisc: true
voxelsniper.brush.voxeldiscface: true
voxelsniper.brush.warp: true
voxelsniper.goto: true
voxelsniper.sniper: true

View File

@ -1,6 +1,6 @@
package com.boydti.fawe;
import com.boydti.fawe.beta.implementation.QueueHandler;
import com.boydti.fawe.beta.implementation.queue.QueueHandler;
import com.boydti.fawe.config.BBC;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.brush.visualization.VisualQueue;
@ -16,7 +16,6 @@ import com.boydti.fawe.util.TextureUtil;
import com.boydti.fawe.util.WEManager;
import com.github.luben.zstd.util.Native;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.extension.factory.DefaultTransformParser;
import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.session.request.Request;
@ -30,12 +29,9 @@ import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryPoolMXBean;
import java.lang.management.MemoryUsage;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nullable;
import javax.management.InstanceAlreadyExistsException;

View File

@ -7,7 +7,6 @@ import com.boydti.fawe.object.RegionWrapper;
import com.boydti.fawe.object.changeset.DiskStorageHistory;
import com.boydti.fawe.object.exception.FaweException;
import com.boydti.fawe.object.extent.LightingExtent;
import com.boydti.fawe.object.schematic.Schematic;
import com.boydti.fawe.regions.FaweMaskManager;
import com.boydti.fawe.util.EditSessionBuilder;
import com.boydti.fawe.util.MainUtil;
@ -192,7 +191,7 @@ public class FaweAPI {
* @see ClipboardFormat
* @see Schematic
*/
public static Schematic load(File file) throws IOException {
public static Clipboard load(File file) throws IOException {
return ClipboardFormats.findByFile(file).load(file);
}

View File

@ -28,6 +28,7 @@ import com.sk89q.worldedit.math.MutableBlockVector3;
import com.sk89q.worldedit.math.MutableVector3;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockTypes;
import com.sk89q.worldedit.world.block.BlockTypesCache;
import java.lang.reflect.Field;
import java.util.ArrayList;
@ -38,10 +39,14 @@ import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Supplier;
import static com.google.common.base.Preconditions.checkNotNull;
@ -101,19 +106,25 @@ public enum FaweCache implements Trimable {
@Override
public synchronized boolean trim(boolean aggressive) {
BLOCK_TO_PALETTE.clean();
PALETTE_TO_BLOCK.clean();
BLOCK_STATES.clean();
SECTION_BLOCKS.clean();
PALETTE_CACHE.clean();
PALETTE_TO_BLOCK_CHAR.clean();
INDEX_STORE.clean();
if (aggressive) {
CleanableThreadLocal.cleanAll();
} else {
CHUNK_FLAG.clean();
BYTE_BUFFER_8192.clean();
BLOCK_TO_PALETTE.clean();
PALETTE_TO_BLOCK.clean();
BLOCK_STATES.clean();
SECTION_BLOCKS.clean();
PALETTE_CACHE.clean();
PALETTE_TO_BLOCK_CHAR.clean();
INDEX_STORE.clean();
MUTABLE_VECTOR3.clean();
MUTABLE_BLOCKVECTOR3.clean();
SECTION_BITS_TO_CHAR.clean();
for (Map.Entry<Class, CleanableThreadLocal> entry : REGISTERED_SINGLETONS.entrySet()) {
entry.getValue().clean();
MUTABLE_VECTOR3.clean();
MUTABLE_BLOCKVECTOR3.clean();
SECTION_BITS_TO_CHAR.clean();
for (Map.Entry<Class, CleanableThreadLocal> entry : REGISTERED_SINGLETONS.entrySet()) {
entry.getValue().clean();
}
}
for (Map.Entry<Class, Pool> entry : REGISTERED_POOLS.entrySet()) {
Pool pool = entry.getValue();
@ -197,13 +208,19 @@ public enum FaweCache implements Trimable {
public static final FaweException LOW_MEMORY = new FaweException(BBC.WORLDEDIT_CANCEL_REASON_LOW_MEMORY);
public static final FaweException MAX_ENTITIES = new FaweException(BBC.WORLDEDIT_CANCEL_REASON_MAX_ENTITIES);
public static final FaweException MAX_TILES = new FaweException(BBC.WORLDEDIT_CANCEL_REASON_MAX_TILES);
public static final FaweException MAX_ITERATIONS = new FaweException(BBC.WORLDEDIT_CANCEL_REASON_MAX_ITERATIONS);
/*
thread cache
*/
public final CleanableThreadLocal<AtomicBoolean> CHUNK_FLAG = new CleanableThreadLocal<>(AtomicBoolean::new); // resets to false
public final CleanableThreadLocal<long[]> LONG_BUFFER_1024 = new CleanableThreadLocal<>(() -> new long[1024]);
public final CleanableThreadLocal<byte[]> BYTE_BUFFER_8192 = new CleanableThreadLocal<>(() -> new byte[8192]);
public final CleanableThreadLocal<int[]> BLOCK_TO_PALETTE = new CleanableThreadLocal<>(() -> {
int[] result = new int[BlockTypes.states.length];
int[] result = new int[BlockTypesCache.states.length];
Arrays.fill(result, Integer.MAX_VALUE);
return result;
});
@ -228,6 +245,8 @@ public enum FaweCache implements Trimable {
* Holds data for a palette used in a chunk section
*/
public final class Palette {
public int bitsPerEntry;
public int paletteToBlockLength;
/**
* Reusable buffer array, MUST check paletteToBlockLength for actual length
@ -278,7 +297,7 @@ public enum FaweCache implements Trimable {
int ordinal = blocksChars[i];
int palette = blockToPalette[ordinal];
if (palette == Integer.MAX_VALUE) {
// BlockState state = BlockTypes.states[ordinal];
// BlockState state = BlockTypesCache.states[ordinal];
blockToPalette[ordinal] = palette = num_palette;
paletteToBlock[num_palette] = ordinal;
num_palette++;
@ -290,7 +309,7 @@ public enum FaweCache implements Trimable {
int ordinal = blocksInts[i];
int palette = blockToPalette[ordinal];
if (palette == Integer.MAX_VALUE) {
BlockState state = BlockTypes.states[ordinal];
BlockState state = BlockTypesCache.states[ordinal];
blockToPalette[ordinal] = palette = num_palette;
paletteToBlock[num_palette] = ordinal;
num_palette++;
@ -319,6 +338,7 @@ public enum FaweCache implements Trimable {
// Construct palette
Palette palette = PALETTE_CACHE.get();
palette.bitsPerEntry = bitsPerEntry;
palette.paletteToBlockLength = num_palette;
palette.paletteToBlock = paletteToBlock;
@ -499,6 +519,31 @@ public enum FaweCache implements Trimable {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS, queue
, Executors.defaultThreadFactory(),
new ThreadPoolExecutor.CallerRunsPolicy());
new ThreadPoolExecutor.CallerRunsPolicy()) {
protected void afterExecute(Runnable r, Throwable t) {
try {
super.afterExecute(r, t);
if (t == null && r instanceof Future<?>) {
try {
Future<?> future = (Future<?>) r;
if (future.isDone()) {
future.get();
}
} catch (CancellationException ce) {
t = ce;
} catch (ExecutionException ee) {
t = ee.getCause();
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
}
}
if (t != null) {
t.printStackTrace();
}
} catch (Throwable e) {
e.printStackTrace();
}
}
};
}
}

View File

@ -1,11 +1,11 @@
package com.boydti.fawe;
import com.boydti.fawe.beta.implementation.QueueHandler;
import com.boydti.fawe.beta.implementation.queue.QueueHandler;
import com.boydti.fawe.beta.implementation.cache.preloader.Preloader;
import com.boydti.fawe.object.FaweCommand;
import com.boydti.fawe.regions.FaweMaskManager;
import com.boydti.fawe.util.TaskManager;
import com.boydti.fawe.util.image.ImageViewer;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.world.World;
import java.io.File;
@ -54,4 +54,6 @@ public interface IFawe {
QueueHandler getQueueHandler();
Preloader getPreloader();
}

View File

@ -1,61 +0,0 @@
package com.boydti.fawe.beta;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
public class ChunkFuture implements Future<Void> {
private final IChunk chunk;
private volatile boolean cancelled;
private volatile boolean done;
public ChunkFuture(IChunk chunk) {
this.chunk = chunk;
}
public IChunk getChunk() {
return chunk;
}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
cancelled = true;
return !done;
}
@Override
public boolean isCancelled() {
return cancelled;
}
@Override
public boolean isDone() {
return done;
}
@Override
public Void get() throws InterruptedException, ExecutionException {
synchronized (chunk) {
if (!done) {
this.wait();
}
}
return null;
}
@Override
public Void get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
synchronized (chunk) {
if (!done) {
this.wait(unit.toMillis(timeout));
if (!done) {
throw new TimeoutException();
}
}
}
return null;
}
}

View File

@ -1,5 +1,6 @@
package com.boydti.fawe.beta;
import com.boydti.fawe.beta.implementation.filter.block.FilterBlock;
import com.sk89q.worldedit.regions.Region;
import javax.annotation.Nullable;
import org.jetbrains.annotations.Range;

View File

@ -1,5 +1,7 @@
package com.boydti.fawe.beta;
import com.boydti.fawe.beta.implementation.filter.block.FilterBlock;
public interface FilterBlockMask {
boolean applyBlock(FilterBlock block);

View File

@ -1,7 +1,7 @@
package com.boydti.fawe.beta;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.beta.implementation.QueueHandler;
import com.boydti.fawe.beta.implementation.queue.QueueHandler;
import com.boydti.fawe.util.MathMan;
import com.sk89q.worldedit.util.Direction;
import com.sk89q.worldedit.world.World;

View File

@ -1,8 +1,8 @@
package com.boydti.fawe.beta;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.beta.implementation.EmptyBatchProcessor;
import com.boydti.fawe.beta.implementation.MultiBatchProcessor;
import com.boydti.fawe.beta.implementation.processors.EmptyBatchProcessor;
import com.boydti.fawe.beta.implementation.processors.MultiBatchProcessor;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.math.BlockVector3;
@ -46,7 +46,7 @@ public interface IBatchProcessor {
if (set.hasSection(layer)) {
if (layer == minLayer) {
char[] arr = set.getArray(layer);
int index = (minY & 15) << 12;
int index = (minY & 15) << 8;
for (int i = 0; i < index; i++) arr[i] = 0;
set.setBlocks(layer, arr);
} else {
@ -59,7 +59,7 @@ public interface IBatchProcessor {
if (set.hasSection(layer)) {
if (layer == minLayer) {
char[] arr = set.getArray(layer);
int index = ((maxY + 1) & 15) << 12;
int index = ((maxY + 1) & 15) << 8;
for (int i = index; i < arr.length; i++) arr[i] = 0;
set.setBlocks(layer, arr);
} else {

View File

@ -1,10 +1,20 @@
package com.boydti.fawe.beta;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.object.FaweOutputStream;
import com.boydti.fawe.object.collection.BitArray4096;
import com.boydti.fawe.object.io.FastByteArrayOutputStream;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.extension.platform.Capability;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.biome.BiomeTypes;
import com.sk89q.worldedit.world.block.BlockID;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.registry.BlockRegistry;
import java.io.IOException;
import java.util.Map;
import java.util.Set;
@ -19,5 +29,115 @@ public interface IBlocks extends Trimable {
BlockState getBlock(int x, int y, int z);
Map<BlockVector3, CompoundTag> getTiles();
Set<CompoundTag> getEntities();
BiomeType getBiomeType(int x, int z);
default int getBitMask() {
int mask = 0;
for (int layer = 0; layer < FaweCache.IMP.CHUNK_LAYERS; layer++) {
if (hasSection(layer)) {
mask += (1 << layer);
}
}
return mask;
}
IBlocks reset();
default byte[] toByteArray(boolean writeBiomes) {
return toByteArray(null, writeBiomes);
}
default byte[] toByteArray(byte[] buffer, boolean writeBiomes) {
if (buffer == null) {
buffer = new byte[1024];
}
BlockRegistry registry = WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.GAME_HOOKS).getRegistries().getBlockRegistry();
FastByteArrayOutputStream sectionByteArray = new FastByteArrayOutputStream(buffer);
FaweOutputStream sectionWriter = new FaweOutputStream(sectionByteArray);
try {
for (int layer = 0; layer < FaweCache.IMP.CHUNK_LAYERS; layer++) {
if (!this.hasSection(layer)) continue;
char[] ids = this.getArray(layer);
int nonEmpty = 0; // TODO optimize into same loop as toPalette
for (char id : ids) {
if (id != 0) nonEmpty++;
}
sectionWriter.writeShort(nonEmpty); // non empty
sectionWriter.writeByte(14); // globalPaletteBitsPerBlock
if (true) {
BitArray4096 bits = new BitArray4096(14); // globalPaletteBitsPerBlock
bits.setAt(0, 0);
for (int i = 0; i < 4096; i++) {
int ordinal = ids[i];
BlockState state = BlockState.getFromOrdinal(ordinal);
if (!state.getMaterial().isAir()) {
int mcId = registry.getInternalBlockStateId(state).getAsInt();
bits.setAt(i, mcId);
}
}
sectionWriter.write(bits.getData());
} else {
FaweCache.Palette palette = FaweCache.IMP.toPalette(0, ids);
sectionWriter.writeByte(palette.bitsPerEntry); // bits per block
sectionWriter.writeVarInt(palette.paletteToBlockLength);
for (int i = 0; i < palette.paletteToBlockLength; i++) {
int ordinal = palette.paletteToBlock[i];
switch (ordinal) {
case BlockID.CAVE_AIR:
case BlockID.VOID_AIR:
case BlockID.AIR:
case BlockID.__RESERVED__:
sectionWriter.writeVarInt(0);
break;
default:
BlockState state = BlockState.getFromOrdinal(ordinal);
int mcId = registry.getInternalBlockStateId(state).getAsInt();
sectionWriter.writeVarInt(mcId);
break;
}
}
sectionWriter.writeVarInt(palette.blockStatesLength);
for (int i = 0; i < palette.blockStatesLength; i++) {
sectionWriter.writeLong(palette.blockStates[i]);
}
}
}
// if (writeBiomes) {
// for (int x = 0; x < 16; x++) {
// for (int z = 0; z < 16; z++) {
// BiomeType biome = this.getBiomeType(x, z);
// if (biome == null) {
// if (writeBiomes) {
// break;
// } else {
// biome = BiomeTypes.FOREST;
// }
// }
// }
// }
// }
if (writeBiomes) {
for (int i = 0; i < 256; i++) {
// TODO biomes
sectionWriter.writeInt(0);
}
}
} catch (IOException e) {
e.printStackTrace();
}
return sectionByteArray.toByteArray();
}
}

View File

@ -1,5 +1,6 @@
package com.boydti.fawe.beta;
import com.boydti.fawe.beta.implementation.filter.block.ChunkFilterBlock;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.world.biome.BiomeType;
@ -27,12 +28,6 @@ public interface IChunk<T extends Future<T>> extends Trimable, Callable<T>, IChu
*/
void init(IQueueExtent extent, int x, int z);
/**
* Get the queue
* @return
*/
IQueueExtent getQueue();
/**
* Get chunkX
* @return

View File

@ -1,4 +1,4 @@
package com.boydti.fawe.beta.implementation;
package com.boydti.fawe.beta;
import com.boydti.fawe.beta.Trimable;

View File

@ -28,8 +28,10 @@ public interface IChunkGet extends IBlocks, Trimable, InputExtent {
CompoundTag getTag(int x, int y, int z);
@Override
Map<BlockVector3, CompoundTag> getTiles();
@Override
Set<CompoundTag> getEntities();
@Override

View File

@ -38,8 +38,13 @@ public interface IChunkSet extends IBlocks, OutputExtent {
BiomeType[] getBiomes();
@Override
BiomeType getBiomeType(int x, int z);
@Override
Map<BlockVector3, CompoundTag> getTiles();
@Override
Set<CompoundTag> getEntities();
@Override

View File

@ -1,5 +1,6 @@
package com.boydti.fawe.beta;
import com.boydti.fawe.beta.implementation.filter.block.ChunkFilterBlock;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.regions.Region;
@ -38,11 +39,6 @@ public interface IDelegateChunk<U extends IChunk> extends IChunk {
return getParent().call(set, finalize);
}
@Override
default IQueueExtent getQueue() {
return getParent().getQueue();
}
@Override
default CompoundTag getTag(int x, int y, int z) {
return getParent().getTag(x, y, z);

View File

@ -1,5 +1,6 @@
package com.boydti.fawe.beta;
import com.boydti.fawe.beta.implementation.filter.block.FilterBlock;
import com.sk89q.worldedit.regions.Region;
import javax.annotation.Nullable;

View File

@ -1,13 +1,11 @@
package com.boydti.fawe.beta;
import com.boydti.fawe.beta.implementation.IChunkCache;
import com.boydti.fawe.beta.implementation.MultiBatchProcessor;
import com.boydti.fawe.beta.implementation.filter.block.ChunkFilterBlock;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.MaxChangedBlocksException;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.entity.BaseEntity;
import com.sk89q.worldedit.entity.Entity;
import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard;
import com.sk89q.worldedit.function.generator.GenBase;
@ -45,16 +43,6 @@ public interface IDelegateQueueExtent extends IQueueExtent {
return getParent().isQueueEnabled();
}
@Override
default void clearBlockUpdates(Player... players) {
getParent().clearBlockUpdates(players);
}
@Override
default void sendBlockUpdates(Player... players) {
getParent().sendBlockUpdates(players);
}
@Override
default void enableQueue() {
getParent().enableQueue();

View File

@ -1,15 +1,15 @@
package com.boydti.fawe.beta;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.beta.implementation.IBatchProcessorHolder;
import com.boydti.fawe.beta.implementation.IChunkCache;
import com.boydti.fawe.beta.implementation.MultiBatchProcessor;
import com.boydti.fawe.beta.implementation.filter.block.ChunkFilterBlock;
import com.boydti.fawe.beta.implementation.processors.IBatchProcessorHolder;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.function.operation.Operation;
import com.sk89q.worldedit.math.BlockVector2;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
@ -17,7 +17,7 @@ import com.sk89q.worldedit.world.block.BlockStateHolder;
import javax.annotation.Nullable;
import java.io.Flushable;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Future;
/**
@ -31,22 +31,6 @@ public interface IQueueExtent extends Flushable, Trimable, Extent, IBatchProcess
return true;
}
/**
* Clear any block updates
* @param players
*/
default void clearBlockUpdates(Player... players) {
throw new UnsupportedOperationException("TODO NOT IMPLEMENTED");
}
/**
* Send all the chunks as block updates
* @param players
*/
default void sendBlockUpdates(Player... players) {
throw new UnsupportedOperationException("TODO NOT IMPLEMENTED");
}
/**
* Must ensure that it is enqueued with QueueHandler
*/
@ -204,4 +188,35 @@ public interface IQueueExtent extends Flushable, Trimable, Extent, IBatchProcess
* @return <tt>true</tt> if this queue contains no elements
*/
boolean isEmpty();
default ChunkFilterBlock apply(ChunkFilterBlock block, Filter filter, Region region, int chunkX, int chunkZ) {
if (!filter.appliesChunk(chunkX, chunkZ)) {
return block;
}
IChunk chunk = this.getOrCreateChunk(chunkX, chunkZ);
// Initialize
chunk.init(this, chunkX, chunkZ);
IChunk newChunk = filter.applyChunk(chunk, region);
if (newChunk != null) {
chunk = newChunk;
if (block == null) {
block = this.initFilterBlock();
}
chunk.filterBlocks(filter, block, region);
}
this.submit(chunk);
return block;
}
@Override
default <T extends Filter> T apply(Region region, T filter) {
final Set<BlockVector2> chunks = region.getChunks();
ChunkFilterBlock block = null;
for (BlockVector2 chunk : chunks) {
block = apply(block, filter, region, chunk.getX(), chunk.getZ());
}
flush();
return filter;
}
}

View File

@ -1,4 +1,4 @@
package com.boydti.fawe.beta.implementation;
package com.boydti.fawe.beta;
import com.boydti.fawe.beta.IQueueExtent;

View File

@ -1,105 +0,0 @@
package com.boydti.fawe.beta;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
public class NorthVector extends BlockVector3 {
private final BlockVector3 parent;
public NorthVector(BlockVector3 parent) {
this.parent = parent;
}
// @Override
// public BlockVector3 south(BlockVector3 orDefault) {
// return parent;
// }
@Override
public int getX() {
return parent.getX();
}
@Override
public int getY() {
return parent.getY();
}
@Override
public int getZ() {
return parent.getZ();
}
@Override
public boolean setOrdinal(Extent orDefault, int ordinal) {
return orDefault.setBlock(this, BlockState.getFromOrdinal(ordinal));
}
@Override
public boolean setBlock(Extent orDefault, BlockState state) {
return orDefault.setBlock(this, state);
}
@Override
public boolean setFullBlock(Extent orDefault, BaseBlock block) {
return orDefault.setBlock(this, block);
}
@Override
public boolean setBiome(Extent orDefault, BiomeType biome) {
return orDefault.setBiome(getX(), getY(), getZ(), biome);
}
@Override
public int getOrdinal(Extent orDefault) {
return getBlock(orDefault).getOrdinal();
}
@Override
public char getOrdinalChar(Extent orDefault) {
return (char) getOrdinal(orDefault);
}
@Override
public BlockState getBlock(Extent orDefault) {
return orDefault.getBlock(this);
}
@Override
public BaseBlock getFullBlock(Extent orDefault) {
return orDefault.getFullBlock(this);
}
@Override
public CompoundTag getNbtData(Extent orDefault) {
return orDefault.getFullBlock(getX(), getY(), getZ()).getNbtData();
}
@Override
public BlockState getOrdinalBelow(Extent orDefault) {
return getStateRelative(orDefault, 0, -1, 0);
}
@Override
public BlockState getStateAbove(Extent orDefault) {
return getStateRelative(orDefault, 0, 1, 0);
}
@Override
public BlockState getStateRelativeY(Extent orDefault, int y) {
return getStateRelative(orDefault, 0, y, 0);
}
public BlockState getStateRelative(Extent orDefault, int x, int y, int z) {
return getFullBlockRelative(orDefault, x, y, z).toBlockState();
}
public BaseBlock getFullBlockRelative(Extent orDefault, int x, int y, int z) {
return orDefault.getFullBlock(x + getX(), y + getY(), z + getZ());
}
}

View File

@ -1,22 +0,0 @@
package com.boydti.fawe.beta.implementation;
import com.boydti.fawe.beta.IBatchProcessor;
public class BatchProcessorHolder implements IBatchProcessorHolder {
private IBatchProcessor processor = EmptyBatchProcessor.INSTANCE;
@Override
public IBatchProcessor getProcessor() {
return processor;
}
@Override
public void setProcessor(IBatchProcessor set) {
this.processor = set;
}
@Override
public String toString() {
return super.toString() + "{" + getProcessor() + "}";
}
}

View File

@ -115,6 +115,11 @@ public class BitSetBlocks implements IChunkSet {
return null;
}
@Override
public BiomeType getBiomeType(int x, int z) {
return null;
}
@Override
public Map<BlockVector3, CompoundTag> getTiles() {
return null;

View File

@ -7,11 +7,12 @@ import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockTypes;
import com.sk89q.worldedit.world.block.BlockTypesCache;
import java.util.Map;
import java.util.Set;
public class CharBlocks implements IBlocks {
public abstract class CharBlocks implements IBlocks {
public static final Section FULL = new Section() {
@Override
@ -96,7 +97,7 @@ public class CharBlocks implements IBlocks {
@Override
public BlockState getBlock(int x, int y, int z) {
return BlockTypes.states[get(x, y, z)];
return BlockTypesCache.states[get(x, y, z)];
}
public char get(int x, int y, int z) {

View File

@ -5,17 +5,18 @@ import com.boydti.fawe.beta.IChunkSet;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockTypes;
import com.sk89q.worldedit.world.block.BlockTypesCache;
public abstract class CharGetBlocks extends CharBlocks implements IChunkGet {
@Override
public BaseBlock getFullBlock(int x, int y, int z) {
return BlockTypes.states[get(x, y, z)].toBaseBlock();
return BlockTypesCache.states[get(x, y, z)].toBaseBlock();
}
@Override
public BlockState getBlock(int x, int y, int z) {
return BlockTypes.states[get(x, y, z)];
return BlockTypesCache.states[get(x, y, z)];
}
@Override

View File

@ -13,6 +13,7 @@ import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import com.sk89q.worldedit.world.block.BlockTypes;
import com.sk89q.worldedit.world.block.BlockTypesCache;
import java.util.Collections;
import java.util.HashSet;
@ -43,6 +44,11 @@ public class CharSetBlocks extends CharBlocks implements IChunkSet {
return biomes;
}
@Override
public BiomeType getBiomeType(int x, int z) {
return biomes[(z << 4) | x];
}
@Override
public Map<BlockVector3, CompoundTag> getTiles() {
return tiles == null ? Collections.emptyMap() : tiles;
@ -69,7 +75,7 @@ public class CharSetBlocks extends CharBlocks implements IChunkSet {
@Override
public BlockState getBlock(int x, int y, int z) {
return BlockTypes.states[get(x, y, z)];
return BlockTypesCache.states[get(x, y, z)];
}
@Override

View File

@ -1,4 +1,4 @@
package com.boydti.fawe.beta.implementation;
package com.boydti.fawe.beta.implementation.blocks;
import com.boydti.fawe.beta.IChunkSet;
import com.sk89q.jnbt.CompoundTag;

View File

@ -1,4 +1,4 @@
package com.boydti.fawe.beta.implementation;
package com.boydti.fawe.beta.implementation.blocks;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.beta.IBlocks;

View File

@ -1,4 +1,4 @@
package com.boydti.fawe.beta.implementation;
package com.boydti.fawe.beta.implementation.blocks;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.beta.IBlocks;

View File

@ -1,10 +1,12 @@
package com.boydti.fawe.beta.implementation;
package com.boydti.fawe.beta.implementation.cache;
import com.boydti.fawe.beta.IChunkCache;
import com.boydti.fawe.beta.Trimable;
import com.boydti.fawe.util.MathMan;
import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.objects.ObjectIterator;
import java.lang.ref.WeakReference;
public class ChunkCache<T extends Trimable> implements IChunkCache<T> {
@ -12,11 +14,18 @@ public class ChunkCache<T extends Trimable> implements IChunkCache<T> {
protected final Long2ObjectLinkedOpenHashMap<WeakReference<T>> getCache;
private final IChunkCache<T> delegate;
protected ChunkCache(IChunkCache<T> delegate) {
public ChunkCache(IChunkCache<T> delegate) {
this.getCache = new Long2ObjectLinkedOpenHashMap<>();
this.delegate = delegate;
}
/**
* Get or create the IGetBlocks
*
* @param index chunk index {@link com.boydti.fawe.util.MathMan#pairInt(int, int)}
* @param provider used to create if it isn't already cached
* @return cached IGetBlocks
*/
@Override
public synchronized T get(int x, int z) {
long pair = MathMan.pairInt(x, z);

View File

@ -0,0 +1,110 @@
package com.boydti.fawe.beta.implementation.cache.preloader;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.object.collection.MutablePair;
import com.boydti.fawe.util.FaweTimer;
import com.sk89q.worldedit.IncompleteRegionException;
import com.sk89q.worldedit.LocalSession;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.math.BlockVector2;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.regions.CuboidRegion;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.world.World;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
public class AsyncPreloader implements Preloader, Runnable {
private final ConcurrentHashMap<UUID, MutablePair<World, Set<BlockVector2>>> update;
public AsyncPreloader() {
this.update = new ConcurrentHashMap<>();
Fawe.get().getQueueHandler().async(this);
}
@Override
public void cancel(Player player) {
cancelAndGet(player);
}
private MutablePair<World, Set<BlockVector2>> cancelAndGet(Player player) {
MutablePair<World, Set<BlockVector2>> existing = update.get(player.getUniqueId());
if (existing != null) {
existing.setValue(null);
}
return existing;
}
@Override
public void update(Player player) {
LocalSession session = WorldEdit.getInstance().getSessionManager().getIfPresent(player);
if (session == null) return;
World world = player.getWorld();
MutablePair<World, Set<BlockVector2>> existing = cancelAndGet(player);
try {
Region region = session.getSelection(world);
if (!(region instanceof CuboidRegion) || region.getArea() > 50466816) {
// TOO LARGE or NOT CUBOID
return;
}
if (existing == null) {
MutablePair<World, Set<BlockVector2>> previous = update.putIfAbsent(player.getUniqueId(), existing = new MutablePair<>());
if (previous != null) {
existing = previous;
}
synchronized (existing) { // Ensure key & value are mutated together
existing.setKey(world);
existing.setValue(region.getChunks());
}
synchronized (update) {
update.notify();
}
}
} catch (IncompleteRegionException ignore){}
}
@Override
public void run() {
FaweTimer timer = Fawe.get().getTimer();
try {
while (true) {
if (!update.isEmpty()) {
if (timer.getTPS() > 19) {
Iterator<Map.Entry<UUID, MutablePair<World, Set<BlockVector2>>>> plrIter = update.entrySet().iterator();
Map.Entry<UUID, MutablePair<World, Set<BlockVector2>>> entry = plrIter.next();
MutablePair<World, Set<BlockVector2>> pair = entry.getValue();
World world = pair.getKey();
Set<BlockVector2> chunks = pair.getValue();
if (chunks != null) {
Iterator<BlockVector2> chunksIter = chunks.iterator();
while (chunksIter.hasNext() && pair.getValue() == chunks) { // Ensure the queued load is still valid
BlockVector2 chunk = chunksIter.next();
queueLoad(world, chunk);
}
}
plrIter.remove();
} else {
Thread.sleep(1000);
}
} else {
synchronized (update) {
update.wait();
}
}
}
} catch (InterruptedException e) {
e.printStackTrace();
return;
}
}
public void queueLoad(World world, BlockVector2 chunk) {
world.checkLoadedChunk(BlockVector3.at(chunk.getX() << 4, 0, chunk.getZ() << 4));
}
}

View File

@ -0,0 +1,9 @@
package com.boydti.fawe.beta.implementation.cache.preloader;
import com.sk89q.worldedit.entity.Player;
public interface Preloader {
void cancel(Player player);
void update(Player player);
}

View File

@ -1,7 +1,7 @@
package com.boydti.fawe.beta.implementation.holder;
package com.boydti.fawe.beta.implementation.chunk;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.beta.ChunkFilterBlock;
import com.boydti.fawe.beta.implementation.filter.block.ChunkFilterBlock;
import com.boydti.fawe.beta.Filter;
import com.boydti.fawe.beta.FilterBlockMask;
import com.boydti.fawe.beta.Flood;
@ -59,11 +59,6 @@ public class ChunkHolder<T extends Future<T>> implements IChunk {
return delegate;
}
@Override
public IQueueExtent getQueue() {
return extent;
}
@Override
public boolean setTile(int x, int y, int z, CompoundTag tag) {
return false;

View File

@ -1,4 +1,4 @@
package com.boydti.fawe.beta.implementation.holder;
package com.boydti.fawe.beta.implementation.chunk;
import com.boydti.fawe.beta.IChunk;
import com.boydti.fawe.beta.IDelegateChunk;

View File

@ -1,4 +1,4 @@
package com.boydti.fawe.beta.implementation.holder;
package com.boydti.fawe.beta.implementation.chunk;
import com.boydti.fawe.beta.IChunk;
import com.boydti.fawe.beta.IQueueExtent;

View File

@ -0,0 +1,137 @@
package com.boydti.fawe.beta.implementation.chunk;
import com.boydti.fawe.beta.implementation.filter.block.ChunkFilterBlock;
import com.boydti.fawe.beta.Filter;
import com.boydti.fawe.beta.FilterBlockMask;
import com.boydti.fawe.beta.Flood;
import com.boydti.fawe.beta.IChunk;
import com.boydti.fawe.beta.IChunkSet;
import com.boydti.fawe.beta.IQueueExtent;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import com.sk89q.worldedit.world.block.BlockTypes;
import javax.annotation.Nullable;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.Future;
public enum NullChunk implements IChunk {
INSTANCE;
@Override
public void init(IQueueExtent extent, int x, int z) {}
@Override
public int getX() {
return 0;
}
@Override
public int getZ() {
return 0;
}
@Override
public boolean isEmpty() {
return true;
}
@Override
public Future call() {
return null;
}
@Override
public void filterBlocks(Filter filter, ChunkFilterBlock block, @Nullable Region region) {
}
@Override
public void flood(Flood flood, FilterBlockMask mask, ChunkFilterBlock block) {
}
@Override
public boolean setBiome(int x, int y, int z, BiomeType biome) {
return false;
}
@Override
public boolean setTile(int x, int y, int z, CompoundTag tag) {
return false;
}
@Override
public boolean setBlock(int x, int y, int z, BlockStateHolder block) {
return false;
}
@Override
public BiomeType getBiomeType(int x, int z) {
return null;
}
@Override
public boolean hasSection(int layer) {
return false;
}
@Override
public char[] getArray(int layer) {
return null;
}
@Override
public BlockState getBlock(int x, int y, int z) {
return BlockTypes.__RESERVED__.getDefaultState();
}
@Override
public BaseBlock getFullBlock(int x, int y, int z) {
return BlockTypes.__RESERVED__.getDefaultState().toBaseBlock();
}
@Override
public CompoundTag getTag(int x, int y, int z) {
return null;
}
@Override
public Map<BlockVector3, CompoundTag> getTiles() {
return Collections.emptyMap();
}
@Override
public Set<CompoundTag> getEntities() {
return Collections.emptySet();
}
@Override
public char[] load(int layer) {
return null;
}
@Override
public CompoundTag getEntity(UUID uuid) {
return null;
}
@Override
public Future call(IChunkSet set, Runnable finalize) {
return null;
}
@Override
public boolean trim(boolean aggressive) {
return true;
}
}

View File

@ -1,4 +1,4 @@
package com.boydti.fawe.beta.implementation.holder;
package com.boydti.fawe.beta.implementation.chunk;
import com.boydti.fawe.beta.IChunk;
import com.boydti.fawe.beta.IDelegateChunk;

View File

@ -1,4 +1,4 @@
package com.boydti.fawe.beta.implementation.holder;
package com.boydti.fawe.beta.implementation.chunk;
import com.boydti.fawe.beta.IChunk;
import com.boydti.fawe.beta.IQueueExtent;

View File

@ -1,16 +1,10 @@
package com.boydti.fawe.beta.implementation.holder;
package com.boydti.fawe.beta.implementation.chunk;
import com.boydti.fawe.beta.IChunk;
import com.boydti.fawe.beta.IQueueExtent;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.world.biome.BiomeType;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
/**
* A {@link ReferenceChunk} using {@link WeakReference} to hold the chunk.

View File

@ -1,6 +1,6 @@
package com.boydti.fawe.beta.filters;
package com.boydti.fawe.beta.implementation.filter;
import com.boydti.fawe.beta.FilterBlock;
import com.boydti.fawe.beta.implementation.filter.block.FilterBlock;
import com.boydti.fawe.beta.FilterBlockMask;
import java.awt.image.BufferedImage;
import java.util.concurrent.ThreadLocalRandom;

View File

@ -1,6 +1,6 @@
package com.boydti.fawe.beta.filters;
package com.boydti.fawe.beta.implementation.filter;
import com.boydti.fawe.beta.FilterBlock;
import com.boydti.fawe.beta.implementation.filter.block.FilterBlock;
public class CountFilter extends ForkedFilter<CountFilter> {

View File

@ -1,12 +1,14 @@
package com.boydti.fawe.beta.filters;
package com.boydti.fawe.beta.implementation.filter;
import com.boydti.fawe.beta.FilterBlock;
import com.boydti.fawe.beta.implementation.filter.block.FilterBlock;
import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.function.mask.ABlockMask;
import com.sk89q.worldedit.util.Countable;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockType;
import com.sk89q.worldedit.world.block.BlockTypes;
import com.sk89q.worldedit.world.block.BlockTypesCache;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@ -14,7 +16,7 @@ import java.util.List;
public class DistrFilter extends ForkedFilter<DistrFilter> {
private final int[] counter = new int[BlockTypes.states.length];
private final int[] counter = new int[BlockTypesCache.states.length];
public DistrFilter() {
super(null);
@ -45,7 +47,7 @@ public class DistrFilter extends ForkedFilter<DistrFilter> {
int total = 0;
for (int i = 0; i < counter.length; i++) {
int value = counter[i];
if (value != 0 && mask.test(BlockTypes.states[i])) {
if (value != 0 && mask.test(BlockTypesCache.states[i])) {
total += value;
}
}
@ -61,7 +63,7 @@ public class DistrFilter extends ForkedFilter<DistrFilter> {
for (int i = 0; i < counter.length; i++) {
final int count = counter[i];
if (count != 0) {
distribution.add(new Countable<>(BlockTypes.states[i], count));
distribution.add(new Countable<>(BlockTypesCache.states[i], count));
}
}
Collections.sort(distribution);
@ -70,18 +72,18 @@ public class DistrFilter extends ForkedFilter<DistrFilter> {
public List<Countable<BlockType>> getTypeDistribution() {
final List<Countable<BlockType>> distribution = new ArrayList<>();
int[] typeCounter = new int[BlockTypes.values.length];
int[] typeCounter = new int[BlockTypesCache.values.length];
for (int i = 0; i < counter.length; i++) {
final int count = counter[i];
if (count != 0) {
BlockState state = BlockTypes.states[i];
BlockState state = BlockTypesCache.states[i];
typeCounter[state.getBlockType().getInternalId()] += count;
}
}
for (int i = 0; i < typeCounter.length; i++) {
final int count = typeCounter[i];
if (count != 0) {
distribution.add(new Countable<>(BlockTypes.values[i], count));
distribution.add(new Countable<>(BlockTypesCache.values[i], count));
}
}
Collections.sort(distribution);
@ -89,10 +91,10 @@ public class DistrFilter extends ForkedFilter<DistrFilter> {
}
public void print(Actor actor, long size) {
for (Countable<BlockState> c : getDistribution()) {
for (Countable c : getDistribution()) {
final String name = c.getID().toString();
final String str = String.format("%-7s (%.3f%%) %s",
c.getAmount(),
String.valueOf(c.getAmount()),
c.getAmount() / (double) size * 100,
name);
actor.print(str);

View File

@ -1,4 +1,4 @@
package com.boydti.fawe.beta.filters;
package com.boydti.fawe.beta.implementation.filter;
import com.boydti.fawe.beta.Filter;
import java.util.Map;

View File

@ -1,7 +1,7 @@
package com.boydti.fawe.beta.filters;
package com.boydti.fawe.beta.implementation.filter;
import com.boydti.fawe.beta.Filter;
import com.boydti.fawe.beta.FilterBlock;
import com.boydti.fawe.beta.implementation.filter.block.FilterBlock;
import com.sk89q.worldedit.world.block.BlockState;
public class SetFilter implements Filter {

View File

@ -0,0 +1,94 @@
package com.boydti.fawe.beta.implementation.filter.block;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import javax.annotation.Nullable;
public abstract class AbstractFilterBlock extends FilterBlock {
public abstract BaseBlock getFullBlock();
public abstract void setFullBlock(BaseBlock block);
public abstract BlockVector3 getPosition();
@Override
public Extent getExtent() {
return this;
}
@Override
public int getX() {
return getPosition().getX();
}
@Override
public int getY() {
return getPosition().getY();
}
@Override
public int getZ() {
return getPosition().getZ();
}
@Override
public int getOrdinal() {
return getBlock().getOrdinal();
}
@Override
public void setOrdinal(int ordinal) {
setBlock(BlockState.getFromOrdinal(ordinal));
}
@Override
public BlockState getBlock() {
return getFullBlock().toBlockState();
}
@Override
public void setBlock(BlockState state) {
setFullBlock(state.toBaseBlock(getBlock().getNbtData()));
}
@Override
public CompoundTag getNbtData() {
return getFullBlock().getNbtData();
}
@Override
public void setNbtData(@Nullable CompoundTag nbtData) {
setFullBlock(getFullBlock().toBaseBlock(nbtData));
}
@Override
public BlockVector3 getMinimumPoint() {
return BlockVector3.at(getX(), getY(), getZ());
}
@Override
public BlockVector3 getMaximumPoint() {
return BlockVector3.at(getX(), getY(), getZ());
}
@Override
public <T extends BlockStateHolder<T>> boolean setBlock(int x, int y, int z, T block)
throws WorldEditException {
if (x == this.getX() && y == this.getY() && z == this.getZ()) {
setFullBlock(block.toBaseBlock());
return true;
}
return getExtent().setBlock(x,y, z, block);
}
@Override
public boolean setBiome(int x, int y, int z, BiomeType biome) {
return getExtent().setBiome(x, y, z,biome);
}
}

View File

@ -1,25 +1,21 @@
package com.boydti.fawe.beta;
package com.boydti.fawe.beta.implementation.filter.block;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.math.BlockVector2;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import javax.annotation.Nullable;
public class SingleFilterBlock extends FilterBlock {
public abstract class AbstractSingleFilterBlock extends FilterBlock {
private BaseBlock block;
private int x, y, z;
public SingleFilterBlock init(int x, int y, int z, BaseBlock block) {
this.x = x;
this.y = y;
this.z = z;
public AbstractSingleFilterBlock init(BaseBlock block) {
this.block = block;
return this;
}
@ -69,35 +65,20 @@ public class SingleFilterBlock extends FilterBlock {
block = block.toBaseBlock(nbtData);
}
@Override
public int getX() {
return x;
}
@Override
public int getY() {
return y;
}
@Override
public int getZ() {
return z;
}
@Override
public BlockVector3 getMinimumPoint() {
return BlockVector3.at(x, y, z);
return BlockVector3.at(getX(), getY(), getZ());
}
@Override
public BlockVector3 getMaximumPoint() {
return BlockVector3.at(x, y, z);
return BlockVector3.at(getX(), getY(), getZ());
}
@Override
public <T extends BlockStateHolder<T>> boolean setBlock(int x, int y, int z, T block)
throws WorldEditException {
if (x == this.x && y == this.y && z == this.z) {
if (x == this.getX() && y == this.getY() && z == this.getZ()) {
setFullBlock(block.toBaseBlock());
return true;
}
@ -106,6 +87,6 @@ public class SingleFilterBlock extends FilterBlock {
@Override
public boolean setBiome(int x, int y, int z, BiomeType biome) {
return getExtent().setBiome(x,y, z,biome);
return getExtent().setBiome(x, y, z,biome);
}
}

View File

@ -1,15 +1,16 @@
package com.boydti.fawe.beta;
package com.boydti.fawe.beta.implementation.filter.block;
import com.boydti.fawe.beta.Filter;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.math.BlockVector2;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import com.sk89q.worldedit.world.block.BlockTypes;
import com.sk89q.worldedit.world.block.BlockTypesCache;
import javax.annotation.Nullable;
public class ArrayFilterBlock extends SimpleFilterBlock {
@ -52,7 +53,7 @@ public class ArrayFilterBlock extends SimpleFilterBlock {
@Override
public BlockState getBlock() {
return BlockTypes.states[ordinal];
return BlockTypesCache.states[ordinal];
}
@Override

View File

@ -1,7 +1,13 @@
package com.boydti.fawe.beta;
package com.boydti.fawe.beta.implementation.filter.block;
import static com.sk89q.worldedit.world.block.BlockTypes.states;
import static com.sk89q.worldedit.world.block.BlockTypesCache.states;
import com.boydti.fawe.beta.Filter;
import com.boydti.fawe.beta.FilterBlockMask;
import com.boydti.fawe.beta.Flood;
import com.boydti.fawe.beta.IChunkGet;
import com.boydti.fawe.beta.IChunkSet;
import com.boydti.fawe.beta.IQueueExtent;
import com.boydti.fawe.beta.implementation.blocks.CharGetBlocks;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.WorldEditException;
@ -13,6 +19,7 @@ import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import com.sk89q.worldedit.world.block.BlockTypes;
import com.sk89q.worldedit.world.block.BlockTypesCache;
import com.sk89q.worldedit.world.registry.BlockMaterial;
import javax.annotation.Nullable;
@ -225,7 +232,7 @@ public class CharFilterBlock extends ChunkFilterBlock {
@Override
public final BlockState getBlock() {
final int ordinal = getArr[index];
return BlockTypes.states[ordinal];
return BlockTypesCache.states[ordinal];
}
@Override

View File

@ -1,5 +1,10 @@
package com.boydti.fawe.beta;
package com.boydti.fawe.beta.implementation.filter.block;
import com.boydti.fawe.beta.Filter;
import com.boydti.fawe.beta.FilterBlockMask;
import com.boydti.fawe.beta.Flood;
import com.boydti.fawe.beta.IChunkGet;
import com.boydti.fawe.beta.IChunkSet;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.regions.Region;
@ -15,7 +20,7 @@ public abstract class ChunkFilterBlock extends SimpleFilterBlock {
int layer);
public abstract void flood(IChunkGet iget, IChunkSet iset, int layer,
Flood flood, FilterBlockMask mask);
Flood flood, FilterBlockMask mask);
public abstract void filter(Filter filter, int x, int y, int z);

View File

@ -1,4 +1,7 @@
package com.boydti.fawe.beta;
package com.boydti.fawe.beta.implementation.filter.block;
import com.boydti.fawe.beta.Filter;
import com.boydti.fawe.beta.IDelegateFilter;
public abstract class DelegateFilter<T extends Filter> implements IDelegateFilter {

View File

@ -1,4 +1,4 @@
package com.boydti.fawe.beta;
package com.boydti.fawe.beta.implementation.filter.block;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.MaxChangedBlocksException;

View File

@ -0,0 +1,35 @@
package com.boydti.fawe.beta.implementation.filter.block;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.world.block.BaseBlock;
public class ExtentFilterBlock extends AbstractFilterBlock {
private final Extent extent;
private BlockVector3 pos;
public ExtentFilterBlock(Extent extent) {
this.extent = extent;
this.pos = BlockVector3.ZERO;
}
public ExtentFilterBlock init(BlockVector3 pos) {
this.pos = pos;
return this;
}
@Override
public BaseBlock getFullBlock() {
return pos.getFullBlock(extent);
}
@Override
public void setFullBlock(BaseBlock block) {
pos.setFullBlock(extent, block);
}
@Override
public BlockVector3 getPosition() {
return pos;
}
}

View File

@ -1,4 +1,4 @@
package com.boydti.fawe.beta;
package com.boydti.fawe.beta.implementation.filter.block;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.StringTag;

View File

@ -1,4 +1,4 @@
package com.boydti.fawe.beta;
package com.boydti.fawe.beta.implementation.filter.block;
import com.sk89q.worldedit.extent.Extent;

View File

@ -0,0 +1,31 @@
package com.boydti.fawe.beta.implementation.filter.block;
import com.sk89q.worldedit.world.block.BaseBlock;
public class SingleFilterBlock extends AbstractSingleFilterBlock {
private int x, y, z;
public SingleFilterBlock init(int x, int y, int z, BaseBlock block) {
this.x = x;
this.y = y;
this.z = z;
super.init(block);
return this;
}
@Override
public int getX() {
return x;
}
@Override
public int getY() {
return y;
}
@Override
public int getZ() {
return z;
}
}

View File

@ -0,0 +1,32 @@
package com.boydti.fawe.beta.implementation.filter.block;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.world.block.BaseBlock;
public class VectorSingleFilterBlock extends AbstractSingleFilterBlock {
private final BlockVector3 mutable;
public VectorSingleFilterBlock(BlockVector3 mutable) {
this.mutable = mutable;
}
public VectorSingleFilterBlock init(BaseBlock block) {
super.init(block);
return this;
}
@Override
public int getX() {
return mutable.getX();
}
@Override
public int getY() {
return mutable.getY();
}
@Override
public int getZ() {
return mutable.getZ();
}
}

View File

@ -0,0 +1,125 @@
package com.boydti.fawe.beta.implementation.packet;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.beta.IBlocks;
import com.boydti.fawe.object.FaweOutputStream;
import com.boydti.fawe.object.io.FastByteArrayOutputStream;
import com.sk89q.jnbt.CompoundTag;
import java.util.HashMap;
import java.util.function.Function;
import java.util.function.Supplier;
public class ChunkPacket implements Function<byte[], byte[]>, Supplier<byte[]> {
private final boolean full;
private final Supplier<IBlocks> chunkSupplier;
private IBlocks chunk;
private int chunkX;
private int chunkZ;
private byte[] sectionBytes;
private Object nativePacket;
public ChunkPacket(int chunkX, int chunkZ, Supplier<IBlocks> chunkSupplier, boolean replaceAllSections) {
this.chunkX = chunkX;
this.chunkZ = chunkZ;
this.chunkSupplier = chunkSupplier;
this.full = replaceAllSections;
}
public int getChunkX() {
return chunkX;
}
public int getChunkZ() {
return chunkZ;
}
public synchronized void setPosition(int chunkX, int chunkZ) {
this.chunkX = chunkX;
this.chunkZ = chunkZ;
nativePacket = null;
}
public boolean isFull() {
return full;
}
public IBlocks getChunk() {
if (this.chunk == null) {
synchronized (this) {
if (this.chunk == null) {
this.chunk = chunkSupplier.get();
}
}
}
return chunk;
}
private byte[] getSectionBytes() {
byte[] tmp = this.sectionBytes;
if (tmp == null) {
synchronized (this) {
if (sectionBytes == null) {
sectionBytes = getChunk().toByteArray(FaweCache.IMP.BYTE_BUFFER_8192.get(), this.full);
}
tmp = sectionBytes;
}
}
return tmp;
}
public Object getNativePacket() {
return nativePacket;
}
public void setNativePacket(Object nativePacket) {
this.nativePacket = nativePacket;
}
@Override
@Deprecated
public byte[] get() {
return apply(FaweCache.IMP.BYTE_BUFFER_8192.get());
}
public CompoundTag getHeightMap() {
HashMap<String, Object> map = new HashMap<>();
map.put("MOTION_BLOCKING", new long[36]);
CompoundTag tag = FaweCache.IMP.asTag(map);
// TODO
return tag;
}
@Override
public byte[] apply(byte[] buffer) {
try {
byte[] sectionBytes = getSectionBytes();
FastByteArrayOutputStream baos = new FastByteArrayOutputStream(buffer);
FaweOutputStream fos = new FaweOutputStream(baos);
fos.writeInt(this.chunkX);
fos.writeInt(this.chunkZ);
fos.writeBoolean(this.full);
fos.writeVarInt(getChunk().getBitMask());
fos.writeNBT("", getHeightMap());
fos.writeVarInt(sectionBytes.length);
fos.write(sectionBytes);
// TODO entities / NBT
// Set<CompoundTag> entities = chunk.getEntities();
// Map<BlockVector3, CompoundTag> tiles = chunk.getTiles();
fos.writeVarInt(0); // (Entities / NBT)
return baos.toByteArray();
} catch (Throwable e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
}

View File

@ -0,0 +1,31 @@
package com.boydti.fawe.beta.implementation.processors;
import com.boydti.fawe.beta.IBatchProcessor;
import com.boydti.fawe.beta.IChunk;
import com.boydti.fawe.beta.IChunkGet;
import com.boydti.fawe.beta.IChunkSet;
public class BatchProcessorHolder implements IBatchProcessorHolder {
private IBatchProcessor processor = EmptyBatchProcessor.INSTANCE;
@Override
public IBatchProcessor getProcessor() {
return processor;
}
@Override
public IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) {
return getProcessor().processSet(chunk, get, set);
}
@Override
public void setProcessor(IBatchProcessor set) {
this.processor = set;
}
@Override
public String toString() {
IBatchProcessor tmp = getProcessor();
return super.toString() + "{" + (tmp == this ? "" : getProcessor()) + "}";
}
}

View File

@ -0,0 +1,44 @@
package com.boydti.fawe.beta.implementation.processors;
import com.boydti.fawe.beta.IBatchProcessor;
import com.boydti.fawe.beta.IChunk;
import com.boydti.fawe.beta.IChunkGet;
import com.boydti.fawe.beta.IChunkSet;
import com.boydti.fawe.beta.implementation.packet.ChunkPacket;
import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.world.World;
import java.util.function.Supplier;
import java.util.stream.Stream;
public class ChunkSendProcessor implements IBatchProcessor {
private final Supplier<Stream<Player>> players;
private final World world;
public ChunkSendProcessor(World world, Supplier<Stream<Player>> players) {
this.players = players;
this.world = world;
}
@Override
public IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) {
int chunkX = chunk.getX();
int chunkZ = chunk.getZ();
boolean replaceAll = true;
ChunkPacket packet = new ChunkPacket(chunkX, chunkZ, () -> set, replaceAll);
Stream<Player> stream = this.players.get();
if (stream == null) {
world.sendFakeChunk(null, packet);
} else {
stream.filter(player -> player.getWorld().equals(world))
.forEach(player -> world.sendFakeChunk(player, packet));
}
return set;
}
@Override
public Extent construct(Extent child) {
return null;
}
}

View File

@ -1,4 +1,4 @@
package com.boydti.fawe.beta.implementation;
package com.boydti.fawe.beta.implementation.processors;
import com.boydti.fawe.beta.IBatchProcessor;
import com.boydti.fawe.beta.IChunk;

View File

@ -1,4 +1,4 @@
package com.boydti.fawe.beta.implementation;
package com.boydti.fawe.beta.implementation.processors;
import com.boydti.fawe.beta.IBatchProcessor;
import com.boydti.fawe.beta.IChunk;

View File

@ -0,0 +1,634 @@
package com.boydti.fawe.beta.implementation.processors;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.beta.Filter;
import com.boydti.fawe.beta.IBatchProcessor;
import com.boydti.fawe.beta.implementation.filter.block.ExtentFilterBlock;
import com.boydti.fawe.object.FaweLimit;
import com.boydti.fawe.object.changeset.FaweChangeSet;
import com.boydti.fawe.object.exception.FaweException;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.MaxChangedBlocksException;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.entity.BaseEntity;
import com.sk89q.worldedit.entity.Entity;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.extent.PassthroughExtent;
import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard;
import com.sk89q.worldedit.function.generator.GenBase;
import com.sk89q.worldedit.function.generator.Resource;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.function.operation.Operation;
import com.sk89q.worldedit.function.pattern.Pattern;
import com.sk89q.worldedit.math.BlockVector2;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.session.ClipboardHolder;
import com.sk89q.worldedit.util.Countable;
import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.biome.BiomeTypes;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import com.sk89q.worldedit.world.block.BlockType;
import com.sk89q.worldedit.world.block.BlockTypes;
import javax.annotation.Nullable;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.UUID;
public class LimitExtent extends PassthroughExtent {
private final FaweLimit limit;
/**
* Create a new instance.
*
* @param extent the extent
*/
public LimitExtent(Extent extent, FaweLimit limit) {
super(extent);
this.limit = limit;
}
public List<? extends Entity> getEntities(Region region) {
limit.THROW_MAX_CHECKS(region.getArea());
try {
return getExtent().getEntities(region);
} catch (FaweException e) {
if (!limit.MAX_FAILS()) {
throw e;
}
return Collections.emptyList();
}
}
public List<? extends Entity> getEntities() {
limit.THROW_MAX_CHECKS();
try {
return getExtent().getEntities();
} catch (FaweException e) {
if (!limit.MAX_FAILS()) {
throw e;
}
return Collections.emptyList();
}
}
@Nullable
public Entity createEntity(Location location, BaseEntity entity) {
limit.THROW_MAX_CHANGES();
limit.THROW_MAX_ENTITIES();
try {
return getExtent().createEntity(location, entity);
} catch (FaweException e) {
if (!limit.MAX_FAILS()) {
throw e;
}
return null;
}
}
@Override
@Nullable
public void removeEntity(int x, int y, int z, UUID uuid) {
limit.THROW_MAX_CHANGES();
limit.THROW_MAX_ENTITIES();
try {
getExtent().removeEntity(x, y, z, uuid);
} catch (FaweException e) {
if (!limit.MAX_FAILS()) {
throw e;
}
}
}
@Override
public boolean regenerateChunk(int x, int z, @Nullable BiomeType type, @Nullable Long seed) {
limit.THROW_MAX_CHANGES(Character.MAX_VALUE);
try {
return getExtent().regenerateChunk(x, z, type, seed);
} catch (FaweException e) {
if (!limit.MAX_FAILS()) {
throw e;
}
return false;
}
}
@Override
public int getHighestTerrainBlock(int x, int z, int minY, int maxY) {
limit.THROW_MAX_CHECKS(FaweCache.IMP.WORLD_HEIGHT);
try {
return getExtent().getHighestTerrainBlock(x, z, minY, maxY);
} catch (FaweException e) {
if (!limit.MAX_FAILS()) {
throw e;
}
return 0;
}
}
@Override
public int getHighestTerrainBlock(int x, int z, int minY, int maxY, Mask filter) {
limit.THROW_MAX_CHECKS(FaweCache.IMP.WORLD_HEIGHT);
try {
return getExtent().getHighestTerrainBlock(x, z, minY, maxY, filter);
} catch (FaweException e) {
if (!limit.MAX_FAILS()) {
throw e;
}
return 0;
}
}
@Override
public int getNearestSurfaceLayer(int x, int z, int y, int minY, int maxY) {
limit.THROW_MAX_CHECKS(FaweCache.IMP.WORLD_HEIGHT);
try {
return getExtent().getNearestSurfaceLayer(x, z, y, minY, maxY);
} catch (FaweException e) {
if (!limit.MAX_FAILS()) {
throw e;
}
return 0;
}
}
@Override
public int getNearestSurfaceTerrainBlock(int x, int z, int y, int minY, int maxY, boolean ignoreAir) {
limit.THROW_MAX_CHECKS(FaweCache.IMP.WORLD_HEIGHT);
try {
return getExtent().getNearestSurfaceTerrainBlock(x, z, y, minY, maxY, ignoreAir);
} catch (FaweException e) {
if (!limit.MAX_FAILS()) {
throw e;
}
return 0;
}
}
@Override
public int getNearestSurfaceTerrainBlock(int x, int z, int y, int minY, int maxY) {
limit.THROW_MAX_CHECKS(FaweCache.IMP.WORLD_HEIGHT);
try {
return getExtent().getNearestSurfaceTerrainBlock(x, z, y, minY, maxY);
} catch (FaweException e) {
if (!limit.MAX_FAILS()) {
throw e;
}
return 0;
}
}
@Override
public int getNearestSurfaceTerrainBlock(int x, int z, int y, int minY, int maxY, int failedMin, int failedMax) {
limit.THROW_MAX_CHECKS(FaweCache.IMP.WORLD_HEIGHT);
try {
return getExtent().getNearestSurfaceTerrainBlock(x, z, y, minY, maxY, failedMin, failedMax);
} catch (FaweException e) {
if (!limit.MAX_FAILS()) {
throw e;
}
return 0;
}
}
@Override
public int getNearestSurfaceTerrainBlock(int x, int z, int y, int minY, int maxY, int failedMin, int failedMax, Mask mask) {
limit.THROW_MAX_CHECKS(FaweCache.IMP.WORLD_HEIGHT);
try {
return getExtent().getNearestSurfaceTerrainBlock(x, z, y, minY, maxY, failedMin, failedMax, mask);
} catch (FaweException e) {
if (!limit.MAX_FAILS()) {
throw e;
}
return 0;
}
}
@Override
public int getNearestSurfaceTerrainBlock(int x, int z, int y, int minY, int maxY, int failedMin, int failedMax, boolean ignoreAir) {
limit.THROW_MAX_CHECKS(FaweCache.IMP.WORLD_HEIGHT);
try {
return getExtent().getNearestSurfaceTerrainBlock(x, z, y, minY, maxY, failedMin, failedMax, ignoreAir);
} catch (FaweException e) {
if (!limit.MAX_FAILS()) {
throw e;
}
return 0;
}
}
@Override
public void addCaves(Region region) throws WorldEditException {
limit.THROW_MAX_CHECKS(region.getArea());
limit.THROW_MAX_CHANGES(region.getArea());
try {
getExtent().addCaves(region);
} catch (FaweException e) {
if (!limit.MAX_FAILS()) {
throw e;
}
}
}
@Override
public void generate(Region region, GenBase gen) throws WorldEditException {
limit.THROW_MAX_CHECKS(region.getArea());
limit.THROW_MAX_CHANGES(region.getArea());
try {
getExtent().generate(region, gen);
} catch (FaweException e) {
if (!limit.MAX_FAILS()) {
throw e;
}
}
}
@Override
public void addSchems(Region region, Mask mask, List<ClipboardHolder> clipboards, int rarity, boolean rotate) throws WorldEditException {
limit.THROW_MAX_CHECKS(region.getArea());
limit.THROW_MAX_CHANGES(region.getArea());
try {
getExtent().addSchems(region, mask, clipboards, rarity, rotate);
} catch (FaweException e) {
if (!limit.MAX_FAILS()) {
throw e;
}
}
}
@Override
public void spawnResource(Region region, Resource gen, int rarity, int frequency) throws WorldEditException {
limit.THROW_MAX_CHECKS(region.getArea());
limit.THROW_MAX_CHANGES(region.getArea());
try {
getExtent().spawnResource(region, gen, rarity, frequency);
} catch (FaweException e) {
if (!limit.MAX_FAILS()) {
throw e;
}
}
}
@Override
public void addOre(Region region, Mask mask, Pattern material, int size, int frequency, int rarity, int minY, int maxY) throws WorldEditException {
limit.THROW_MAX_CHECKS(region.getArea());
limit.THROW_MAX_CHANGES(region.getArea());
try {
getExtent().addOre(region, mask, material, size, frequency, rarity, minY, maxY);
} catch (FaweException e) {
if (!limit.MAX_FAILS()) {
throw e;
}
}
}
@Override
public void addOres(Region region, Mask mask) throws WorldEditException {
limit.THROW_MAX_CHECKS(region.getArea());
limit.THROW_MAX_CHANGES(region.getArea());
try {
getExtent().addOres(region, mask);
} catch (FaweException e) {
if (!limit.MAX_FAILS()) {
throw e;
}
}
}
@Override
public List<Countable<BlockType>> getBlockDistribution(Region region) {
limit.THROW_MAX_CHECKS(region.getArea());
try {
return getExtent().getBlockDistribution(region);
} catch (FaweException e) {
if (!limit.MAX_FAILS()) {
throw e;
}
return Collections.emptyList();
}
}
@Override
public List<Countable<BlockState>> getBlockDistributionWithData(Region region) {
limit.THROW_MAX_CHECKS(region.getArea());
try {
return getExtent().getBlockDistributionWithData(region);
} catch (FaweException e) {
if (!limit.MAX_FAILS()) {
throw e;
}
return Collections.emptyList();
}
}
@Override
public int countBlocks(Region region, Set<BaseBlock> searchBlocks) {
limit.THROW_MAX_CHECKS(region.getArea());
try {
return getExtent().countBlocks(region, searchBlocks);
} catch (FaweException e) {
if (!limit.MAX_FAILS()) {
throw e;
}
return 0;
}
}
@Override
public int countBlocks(Region region, Mask searchMask) {
limit.THROW_MAX_CHECKS(region.getArea());
try {
return getExtent().countBlocks(region, searchMask);
} catch (FaweException e) {
if (!limit.MAX_FAILS()) {
throw e;
}
return 0;
}
}
@Override
public <B extends BlockStateHolder<B>> int setBlocks(Region region, B block) throws MaxChangedBlocksException {
limit.THROW_MAX_CHANGES(region.getArea());
try {
return getExtent().setBlocks(region, block);
} catch (FaweException e) {
if (!limit.MAX_FAILS()) {
throw e;
}
return 0;
}
}
@Override
public int setBlocks(Region region, Pattern pattern) throws MaxChangedBlocksException {
limit.THROW_MAX_CHANGES(region.getArea());
try {
return getExtent().setBlocks(region, pattern);
} catch (FaweException e) {
if (!limit.MAX_FAILS()) {
throw e;
}
return 0;
}
}
@Override
public <B extends BlockStateHolder<B>> int replaceBlocks(Region region, Set<BaseBlock> filter, B replacement) throws MaxChangedBlocksException {
limit.THROW_MAX_CHECKS(region.getArea());
limit.THROW_MAX_CHANGES(region.getArea());
try {
return getExtent().replaceBlocks(region, filter, replacement);
} catch (FaweException e) {
if (!limit.MAX_FAILS()) {
throw e;
}
return 0;
}
}
@Override
public int replaceBlocks(Region region, Set<BaseBlock> filter, Pattern pattern) throws MaxChangedBlocksException {
limit.THROW_MAX_CHECKS(region.getArea());
limit.THROW_MAX_CHANGES(region.getArea());
try {
return getExtent().replaceBlocks(region, filter, pattern);
} catch (FaweException e) {
if (!limit.MAX_FAILS()) {
throw e;
}
return 0;
}
}
@Override
public int replaceBlocks(Region region, Mask mask, Pattern pattern) throws MaxChangedBlocksException {
limit.THROW_MAX_CHECKS(region.getArea());
limit.THROW_MAX_CHANGES(region.getArea());
try {
return getExtent().replaceBlocks(region, mask, pattern);
} catch (FaweException e) {
if (!limit.MAX_FAILS()) {
throw e;
}
return 0;
}
}
@Override
public int center(Region region, Pattern pattern) throws MaxChangedBlocksException {
limit.THROW_MAX_CHECKS(region.getArea());
limit.THROW_MAX_CHANGES(region.getArea());
try {
return getExtent().center(region, pattern);
} catch (FaweException e) {
if (!limit.MAX_FAILS()) {
throw e;
}
return 0;
}
}
@Override
public int setBlocks(Set<BlockVector3> vset, Pattern pattern) {
limit.THROW_MAX_CHANGES(vset.size());
try {
return getExtent().setBlocks(vset, pattern);
} catch (FaweException e) {
if (!limit.MAX_FAILS()) {
throw e;
}
return 0;
}
}
@Override
public <T extends Filter> T apply(Region region, T filter) {
limit.THROW_MAX_CHECKS(region.getArea());
limit.THROW_MAX_CHANGES(region.getArea());
try {
return getExtent().apply(region, filter);
} catch (FaweException e) {
if (!limit.MAX_FAILS()) {
throw e;
}
return filter;
}
}
@Override
public <T extends Filter> T apply(Iterable<BlockVector3> positions, T filter) {
int size;
if (positions instanceof Collection) {
size = ((Collection<BlockVector3>) positions).size();
} else if (positions instanceof Region) {
BlockVector3 dim = ((Region) positions).getDimensions();
size = dim.getX() * dim.getY() * dim.getZ();
} else if (positions instanceof Extent) {
BlockVector3 min = ((Extent) positions).getMinimumPoint();
BlockVector3 max = ((Extent) positions).getMinimumPoint();
BlockVector3 dim = max.subtract(min).add(BlockVector3.ONE);
size = dim.getX() * dim.getY() * dim.getZ();
} else {
ExtentFilterBlock block = new ExtentFilterBlock(this);
for (BlockVector3 pos : positions) {
limit.THROW_MAX_CHECKS();
filter.applyBlock(block.init(pos));
}
return filter;
}
limit.THROW_MAX_CHECKS(size);
limit.THROW_MAX_CHANGES(size);
try {
return getExtent().apply(positions, filter);
} catch (FaweException e) {
if (!limit.MAX_FAILS()) {
throw e;
}
return filter;
}
}
public BlockState getBlock(BlockVector3 position) {
limit.THROW_MAX_CHECKS();
try {
return getExtent().getBlock(position);
} catch (FaweException e) {
if (!limit.MAX_FAILS()) {
throw e;
}
return BlockTypes.AIR.getDefaultState();
}
}
public BlockState getBlock(int x, int y, int z) {
limit.THROW_MAX_CHECKS();
try {
return getExtent().getBlock(x, y, z);
} catch (FaweException e) {
if (!limit.MAX_FAILS()) {
throw e;
}
return BlockTypes.AIR.getDefaultState();
}
}
@Override
public BaseBlock getFullBlock(BlockVector3 position) {
limit.THROW_MAX_CHECKS();
try {
return getExtent().getFullBlock(position);
} catch (FaweException e) {
if (!limit.MAX_FAILS()) {
throw e;
}
return BlockTypes.AIR.getDefaultState().toBaseBlock();
}
}
public BaseBlock getFullBlock(int x, int y, int z) {
limit.THROW_MAX_CHECKS();
try {
return getExtent().getFullBlock(x, y, z);
} catch (FaweException e) {
if (!limit.MAX_FAILS()) {
throw e;
}
return BlockTypes.AIR.getDefaultState().toBaseBlock();
}
}
public BiomeType getBiome(BlockVector2 position) {
limit.THROW_MAX_CHECKS();
try {
return getExtent().getBiome(position);
} catch (FaweException e) {
if (!limit.MAX_FAILS()) {
throw e;
}
return BiomeTypes.FOREST;
}
}
public BiomeType getBiomeType(int x, int z) {
limit.THROW_MAX_CHECKS();
try {
return getExtent().getBiomeType(x, z);
} catch (FaweException e) {
if (!limit.MAX_FAILS()) {
throw e;
}
return BiomeTypes.FOREST;
}
}
@Deprecated
public <T extends BlockStateHolder<T>> boolean setBlock(BlockVector3 position, T block) throws WorldEditException {
limit.THROW_MAX_CHANGES();
if (block.hasNbtData()) limit.MAX_BLOCKSTATES();
try {
return getExtent().setBlock(position, block);
} catch (FaweException e) {
if (!limit.MAX_FAILS()) {
throw e;
}
return false;
}
}
public <T extends BlockStateHolder<T>> boolean setBlock(int x, int y, int z, T block) throws WorldEditException {
limit.THROW_MAX_CHANGES();
if (block.hasNbtData()) limit.MAX_BLOCKSTATES();
try {
return getExtent().setBlock(x, y, z, block);
} catch (FaweException e) {
if (!limit.MAX_FAILS()) {
throw e;
}
return false;
}
}
public boolean setTile(int x, int y, int z, CompoundTag tile) throws WorldEditException {
limit.THROW_MAX_CHANGES();
limit.MAX_BLOCKSTATES();
try {
return getExtent().setTile(x, y, z, tile);
} catch (FaweException e) {
if (!limit.MAX_FAILS()) {
throw e;
}
return false;
}
}
public boolean setBiome(BlockVector2 position, BiomeType biome) {
limit.THROW_MAX_CHANGES();
try {
return getExtent().setBiome(position, biome);
} catch (FaweException e) {
if (!limit.MAX_FAILS()) {
throw e;
}
return false;
}
}
public boolean setBiome(int x, int y, int z, BiomeType biome) {
limit.THROW_MAX_CHANGES();
try {
return getExtent().setBiome(x, y, z, biome);
} catch (FaweException e) {
if (!limit.MAX_FAILS()) {
throw e;
}
return false;
}
}
}

View File

@ -0,0 +1,47 @@
package com.boydti.fawe.beta.implementation.processors;
import com.boydti.fawe.beta.IBatchProcessor;
import com.boydti.fawe.beta.IChunk;
import com.boydti.fawe.beta.IChunkGet;
import com.boydti.fawe.beta.IChunkSet;
import com.boydti.fawe.object.FaweLimit;
import com.boydti.fawe.object.exception.FaweException;
import com.sk89q.worldedit.extent.Extent;
public class LimitProcessor implements IBatchProcessor {
private final FaweLimit limit;
private final IBatchProcessor parent;
public LimitProcessor(FaweLimit limit, IBatchProcessor parent) {
this.limit = limit;
this.parent = parent;
}
@Override
public IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) {
try {
return parent.processSet(chunk, get, set);
} catch (FaweException e) {
if (!limit.MAX_CHANGES()) {
throw e;
}
return null;
}
}
@Override
public boolean processGet(int chunkX, int chunkZ) {
try {
return parent.processGet(chunkX, chunkZ);
} catch (FaweException e) {
if (!limit.MAX_CHECKS()) {
throw e;
}
return false;
}
}
@Override
public Extent construct(Extent child) {
return new LimitExtent(parent.construct(child), limit);
}
}

View File

@ -1,4 +1,4 @@
package com.boydti.fawe.beta.implementation;
package com.boydti.fawe.beta.implementation.processors;
import com.boydti.fawe.beta.IBatchProcessor;
import com.boydti.fawe.beta.IChunk;
@ -55,13 +55,18 @@ public class MultiBatchProcessor implements IBatchProcessor {
@Override
public IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) {
for (IBatchProcessor processor : this.processors) {
set = processor.processSet(chunk, get, set);
if (set == null) {
return null;
try {
for (IBatchProcessor processor : this.processors) {
set = processor.processSet(chunk, get, set);
if (set == null) {
return null;
}
}
return set;
} catch (Throwable e) {
e.printStackTrace();
throw e;
}
return set;
}
@Override

View File

@ -0,0 +1,21 @@
package com.boydti.fawe.beta.implementation.processors;
import com.boydti.fawe.beta.IBatchProcessor;
import com.boydti.fawe.beta.IChunk;
import com.boydti.fawe.beta.IChunkGet;
import com.boydti.fawe.beta.IChunkSet;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.extent.NullExtent;
public enum NullProcessor implements IBatchProcessor {
INSTANCE;
@Override
public IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) {
return null;
}
@Override
public Extent construct(Extent child) {
return new NullExtent();
}
}

View File

@ -1,11 +1,12 @@
package com.boydti.fawe.beta.implementation;
package com.boydti.fawe.beta.implementation.queue;
import com.boydti.fawe.beta.ChunkFilterBlock;
import com.boydti.fawe.beta.IQueueWrapper;
import com.boydti.fawe.beta.implementation.filter.block.ChunkFilterBlock;
import com.boydti.fawe.beta.Filter;
import com.boydti.fawe.beta.IChunk;
import com.boydti.fawe.beta.IQueueExtent;
import com.boydti.fawe.beta.filters.CountFilter;
import com.boydti.fawe.beta.filters.DistrFilter;
import com.boydti.fawe.beta.implementation.filter.CountFilter;
import com.boydti.fawe.beta.implementation.filter.DistrFilter;
import com.boydti.fawe.beta.implementation.processors.BatchProcessorHolder;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.changeset.FaweChangeSet;
import com.boydti.fawe.object.clipboard.WorldCopyClipboard;
@ -27,12 +28,15 @@ import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import com.sk89q.worldedit.world.block.BlockType;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ForkJoinTask;
import java.util.stream.IntStream;
import static com.google.common.base.Preconditions.checkNotNull;
public class ParallelQueueExtent extends PassthroughExtent implements IQueueWrapper {
private final World world;
@ -67,26 +71,7 @@ public class ParallelQueueExtent extends PassthroughExtent implements IQueueWrap
return super.enableHistory(changeSet);
}
private ChunkFilterBlock apply(ChunkFilterBlock block, Filter filter, IQueueExtent queue, Region region, int X, int Z) {
if (!filter.appliesChunk(X, Z)) {
return block;
}
IChunk chunk = queue.getOrCreateChunk(X, Z);
// Initialize
chunk.init(queue, X, Z);
IChunk newChunk = filter.applyChunk(chunk, region);
if (newChunk != null) {
chunk = newChunk;
if (block == null) {
block = queue.initFilterBlock();
}
chunk.filterBlocks(filter, block, region);
}
queue.submit(chunk);
return block;
}
@Override
public <T extends Filter> T apply(Region region, T filter) {
// The chunks positions to iterate over
final Set<BlockVector2> chunks = region.getChunks();
@ -96,7 +81,7 @@ public class ParallelQueueExtent extends PassthroughExtent implements IQueueWrap
final int size = Math.min(chunks.size(), Settings.IMP.QUEUE.PARALLEL_THREADS);
if (size <= 1) {
BlockVector2 pos = chunksIter.next();
apply(null, filter, getExtent(), region, pos.getX(), pos.getZ());
getExtent().apply(null, filter, region, pos.getX(), pos.getZ());
} else {
final ForkJoinTask[] tasks = IntStream.range(0, size).mapToObj(i -> handler.submit(() -> {
try {
@ -117,7 +102,7 @@ public class ParallelQueueExtent extends PassthroughExtent implements IQueueWrap
X = pos.getX();
Z = pos.getZ();
}
block = apply(block, newFilter, queue, region, X, Z);
block = queue.apply(block, newFilter, region, X, Z);
}
queue.flush();
}
@ -167,6 +152,7 @@ public class ParallelQueueExtent extends PassthroughExtent implements IQueueWrap
if (vset instanceof Region) {
setBlocks((Region) vset, pattern);
}
// TODO optimize parallel
for (BlockVector3 blockVector3 : vset) {
pattern.apply(this, blockVector3, blockVector3);
}
@ -226,7 +212,7 @@ public class ParallelQueueExtent extends PassthroughExtent implements IQueueWrap
* returned by a given pattern.
*
* @param region the region to replace the blocks within
* @param filter a list of block types to match, or null to use {@link ExistingBlockMask}
* @param filter a list of block types to match, or null to use {@link com.sk89q.worldedit.function.mask.ExistingBlockMask}
* @param replacement the replacement block
* @return number of blocks affected
* @throws MaxChangedBlocksException thrown if too many blocks are changed
@ -241,7 +227,7 @@ public class ParallelQueueExtent extends PassthroughExtent implements IQueueWrap
* returned by a given pattern.
*
* @param region the region to replace the blocks within
* @param filter a list of block types to match, or null to use {@link ExistingBlockMask}
* @param filter a list of block types to match, or null to use {@link com.sk89q.worldedit.function.mask.ExistingBlockMask}
* @param pattern the pattern that provides the new blocks
* @return number of blocks affected
* @throws MaxChangedBlocksException thrown if too many blocks are changed

View File

@ -1,4 +1,4 @@
package com.boydti.fawe.beta.implementation;
package com.boydti.fawe.beta.implementation.queue;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.FaweCache;
@ -8,6 +8,8 @@ import com.boydti.fawe.beta.IChunkGet;
import com.boydti.fawe.beta.IChunkSet;
import com.boydti.fawe.beta.IQueueExtent;
import com.boydti.fawe.beta.Trimable;
import com.boydti.fawe.beta.implementation.cache.ChunkCache;
import com.boydti.fawe.beta.IChunkCache;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.collection.CleanableThreadLocal;
import com.boydti.fawe.util.MemUtil;
@ -19,6 +21,7 @@ import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutionException;
@ -39,6 +42,7 @@ public abstract class QueueHandler implements Trimable, Runnable {
private ForkJoinPool forkJoinPoolSecondary = new ForkJoinPool();
private ThreadPoolExecutor blockingExecutor = FaweCache.IMP.newBlockingExecutor();
private ConcurrentLinkedQueue<FutureTask> syncTasks = new ConcurrentLinkedQueue<>();
private ConcurrentLinkedQueue<FutureTask> syncWhenFree = new ConcurrentLinkedQueue<>();
private Map<World, WeakReference<IChunkCache<IChunkGet>>> chunkGetCache = new HashMap<>();
private CleanableThreadLocal<IQueueExtent> queuePool = new CleanableThreadLocal<>(QueueHandler.this::create);
@ -60,53 +64,61 @@ public abstract class QueueHandler implements Trimable, Runnable {
throw new IllegalStateException("Not main thread");
}
if (!syncTasks.isEmpty()) {
long now = System.currentTimeMillis();
targetTPS = 18 - Math.max(Settings.IMP.QUEUE.EXTRA_TIME_MS * 0.05, 0);
long diff = 50 + this.last - (this.last = now);
long absDiff = Math.abs(diff);
if (diff == 0) {
allocate = Math.min(50, allocate + 1);
} else if (diff < 0) {
allocate = Math.max(5, allocate + diff);
} else if (!Fawe.get().getTimer().isAbove(targetTPS)) {
allocate = Math.max(5, allocate - 1);
}
long currentAllocate = allocate - absDiff;
long currentAllocate = getAllocate();
if (!MemUtil.isMemoryFree()) {
// TODO reduce mem usage
// FaweCache trim
// Preloader trim
}
boolean wait = false;
do {
Runnable task = syncTasks.poll();
if (task == null) {
if (wait) {
synchronized (syncTasks) {
try {
syncTasks.wait(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
task = syncTasks.poll();
wait = false;
} else {
break;
}
}
if (task != null) {
task.run();
wait = true;
}
} while (System.currentTimeMillis() - now < currentAllocate);
operate(syncTasks, last, currentAllocate);
} else if (!syncWhenFree.isEmpty()) {
operate(syncWhenFree, last, getAllocate());
} else {
// trim??
}
while (!syncTasks.isEmpty()) {
final FutureTask task = syncTasks.poll();
}
private long getAllocate() {
long now = System.currentTimeMillis();
targetTPS = 18 - Math.max(Settings.IMP.QUEUE.EXTRA_TIME_MS * 0.05, 0);
long diff = 50 + this.last - (this.last = now);
long absDiff = Math.abs(diff);
if (diff == 0) {
allocate = Math.min(50, allocate + 1);
} else if (diff < 0) {
allocate = Math.max(5, allocate + diff);
} else if (!Fawe.get().getTimer().isAbove(targetTPS)) {
allocate = Math.max(5, allocate - 1);
}
return allocate - absDiff;
}
private void operate(Queue<FutureTask> queue, long start, long currentAllocate) {
boolean wait = false;
do {
Runnable task = queue.poll();
if (task == null) {
if (wait) {
synchronized (syncTasks) {
try {
queue.wait(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
task = queue.poll();
wait = false;
} else {
break;
}
}
if (task != null) {
task.run();
wait = true;
}
}
} while (System.currentTimeMillis() - start < currentAllocate);
}
public <T extends Future<T>> void complete(Future<T> task) {
@ -136,50 +148,83 @@ public abstract class QueueHandler implements Trimable, Runnable {
}
public <T> Future<T> sync(Runnable run, T value) {
return sync(run, value, syncTasks);
}
public <T> Future<T> sync(Runnable run) {
return sync(run, syncTasks);
}
public <T> Future<T> sync(Callable<T> call) throws Exception {
return sync(call, syncTasks);
}
public <T> Future<T> sync(Supplier<T> call) {
return sync(call, syncTasks);
}
// Lower priorty sync task (runs only when there are no other tasks)
public <T> Future<T> syncWhenFree(Runnable run, T value) {
return sync(run, value, syncWhenFree);
}
public <T> Future<T> syncWhenFree(Runnable run) {
return sync(run, syncWhenFree);
}
public <T> Future<T> syncWhenFree(Callable<T> call) throws Exception {
return sync(call, syncWhenFree);
}
public <T> Future<T> syncWhenFree(Supplier<T> call) {
return sync(call, syncWhenFree);
}
private <T> Future<T> sync(Runnable run, T value, Queue<FutureTask> queue) {
if (Fawe.isMainThread()) {
run.run();
return Futures.immediateFuture(value);
}
final FutureTask<T> result = new FutureTask<>(run, value);
syncTasks.add(result);
notifySync();
queue.add(result);
notifySync(queue);
return result;
}
public <T> Future<T> sync(Runnable run) {
private <T> Future<T> sync(Runnable run, Queue<FutureTask> queue) {
if (Fawe.isMainThread()) {
run.run();
return Futures.immediateCancelledFuture();
}
final FutureTask<T> result = new FutureTask<>(run, null);
syncTasks.add(result);
notifySync();
queue.add(result);
notifySync(queue);
return result;
}
public <T> Future<T> sync(Callable<T> call) throws Exception {
private <T> Future<T> sync(Callable<T> call, Queue<FutureTask> queue) throws Exception {
if (Fawe.isMainThread()) {
return Futures.immediateFuture(call.call());
}
final FutureTask<T> result = new FutureTask<>(call);
syncTasks.add(result);
notifySync();
queue.add(result);
notifySync(queue);
return result;
}
public <T> Future<T> sync(Supplier<T> call) {
private <T> Future<T> sync(Supplier<T> call, Queue<FutureTask> queue) {
if (Fawe.isMainThread()) {
return Futures.immediateFuture(call.get());
}
final FutureTask<T> result = new FutureTask<>(call::get);
syncTasks.add(result);
notifySync();
queue.add(result);
notifySync(queue);
return result;
}
private void notifySync() {
synchronized (syncTasks) {
syncTasks.notifyAll();
private void notifySync(Object object) {
synchronized (object) {
object.notifyAll();
}
}

View File

@ -1,29 +1,28 @@
package com.boydti.fawe.beta.implementation;
package com.boydti.fawe.beta.implementation.queue;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.beta.CharFilterBlock;
import com.boydti.fawe.beta.ChunkFilterBlock;
import com.boydti.fawe.beta.implementation.filter.block.CharFilterBlock;
import com.boydti.fawe.beta.implementation.filter.block.ChunkFilterBlock;
import com.boydti.fawe.beta.IBatchProcessor;
import com.boydti.fawe.beta.IChunk;
import com.boydti.fawe.beta.IChunkGet;
import com.boydti.fawe.beta.IChunkSet;
import com.boydti.fawe.beta.IQueueExtent;
import com.boydti.fawe.beta.IChunkCache;
import com.boydti.fawe.beta.implementation.chunk.NullChunk;
import com.boydti.fawe.beta.implementation.blocks.CharSetBlocks;
import com.boydti.fawe.beta.implementation.holder.ChunkHolder;
import com.boydti.fawe.beta.implementation.holder.ReferenceChunk;
import com.boydti.fawe.beta.implementation.chunk.ChunkHolder;
import com.boydti.fawe.beta.implementation.chunk.ReferenceChunk;
import com.boydti.fawe.beta.implementation.processors.BatchProcessorHolder;
import com.boydti.fawe.beta.implementation.processors.EmptyBatchProcessor;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.changeset.FaweChangeSet;
import com.boydti.fawe.object.exception.FaweException;
import com.boydti.fawe.util.MathMan;
import com.boydti.fawe.util.MemUtil;
import com.google.common.util.concurrent.Futures;
import com.sk89q.worldedit.extent.Extent;
import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
@ -221,13 +220,15 @@ public class SingleThreadQueueExtent extends BatchProcessorHolder implements IQu
@Override
public final IChunk getOrCreateChunk(int x, int z) {
if (!processGet(x, z)) {
throw FaweCache.CHUNK;
}
final long pair = (long) x << 32 | z & 0xffffffffL;
if (pair == lastPair) {
return lastChunk;
}
if (!processGet(x, z)) {
lastPair = pair;
lastChunk = NullChunk.INSTANCE;
return NullChunk.INSTANCE;
}
IChunk chunk = chunks.get(pair);
if (chunk instanceof ReferenceChunk) {
@ -276,11 +277,20 @@ public class SingleThreadQueueExtent extends BatchProcessorHolder implements IQu
private void pollSubmissions(int targetSize, boolean aggressive) {
final int overflow = submissions.size() - targetSize;
if (aggressive) {
if (targetSize == 0) {
while (!submissions.isEmpty()) {
Future future = submissions.poll();
try {
while (future != null) future = (Future) future.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
for (int i = 0; i < overflow; i++) {
Future first = submissions.poll();
try {
while ((first = (Future) first.get()) != null) {
}
while (first != null) first = (Future) first.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
@ -327,7 +337,6 @@ public class SingleThreadQueueExtent extends BatchProcessorHolder implements IQu
chunks.clear();
}
pollSubmissions(0, true);
reset();
}
@Override

View File

@ -6,7 +6,7 @@ import static com.sk89q.worldedit.util.formatting.text.TextComponent.newline;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.FaweAPI;
import com.boydti.fawe.beta.SingleFilterBlock;
import com.boydti.fawe.beta.implementation.filter.block.SingleFilterBlock;
import com.boydti.fawe.config.BBC;
import com.boydti.fawe.object.RunnableVal;
import com.boydti.fawe.object.brush.visualization.cfi.HeightMapMCAGenerator;
@ -75,6 +75,7 @@ import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.IntStream;
import javax.imageio.ImageIO;
import org.enginehub.piston.annotation.Command;
import org.enginehub.piston.annotation.CommandContainer;
import org.enginehub.piston.annotation.param.Arg;
@ -431,7 +432,7 @@ public class CFICommands {
Clipboard clipboard = holder.getClipboard();
boolean[] ids = new boolean[BlockTypes.size()];
for (BlockVector3 pt : clipboard.getRegion()) {
ids[clipboard.getBlock(pt).getInternalBlockTypeId()] = true;
ids[clipboard.getBlock(pt).getBlockType().getInternalId()] = true;
}
blocks = new HashSet<>();
for (int combined = 0; combined < ids.length; combined++) {
@ -605,7 +606,7 @@ public class CFICommands {
@CommandPermissions("worldedit.anvil.cfi")
public void waterId(Player player, BlockStateHolder block) throws WorldEditException {
CFISettings settings = assertSettings(player);
settings.getGenerator().setWaterId(block.getBlockType().getInternalId());
settings.getGenerator().setWater(block.toImmutableState());
player.print("Set water id!");
settings.resetComponent();
@ -620,7 +621,7 @@ public class CFICommands {
@CommandPermissions("worldedit.anvil.cfi")
public void baseId(Player player, BlockStateHolder block) throws WorldEditException {
CFISettings settings = assertSettings(player);
settings.getGenerator().setBedrockId(block.getBlockType().getInternalId());
settings.getGenerator().setBedrock(block.toImmutableState());
player.print(TextComponent.of("Set base id!"));
settings.resetComponent();
component(player);

View File

@ -14,6 +14,8 @@ public class Settings extends Config {
@Ignore
public boolean PROTOCOL_SUPPORT_FIX = false;
@Ignore
public boolean PLOTSQUARED_HOOK = false;
@Comment("These first 6 aren't configurable") // This is a comment
@Final // Indicates that this value isn't configurable

View File

@ -4,17 +4,11 @@ import com.boydti.fawe.Fawe;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.clipboard.CPUOptimizedClipboard;
import com.boydti.fawe.object.clipboard.DiskOptimizedClipboard;
import com.boydti.fawe.object.clipboard.FaweClipboard;
import com.boydti.fawe.object.clipboard.LinearClipboard;
import com.boydti.fawe.object.clipboard.MemoryOptimizedClipboard;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.ListTag;
import com.sk89q.jnbt.NBTInputStream;
import com.sk89q.worldedit.entity.BaseEntity;
import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.math.Vector3;
import com.sk89q.worldedit.regions.CuboidRegion;
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.IOException;
@ -29,7 +23,7 @@ public class CorruptSchematicStreamer {
private final InputStream stream;
private final UUID uuid;
private FaweClipboard fc;
private LinearClipboard fc;
final AtomicInteger volume = new AtomicInteger();
final AtomicInteger width = new AtomicInteger();
final AtomicInteger height = new AtomicInteger();
@ -78,7 +72,7 @@ public class CorruptSchematicStreamer {
}
}
public FaweClipboard setupClipboard() {
public LinearClipboard setupClipboard() {
if (fc != null) {
return fc;
}
@ -87,11 +81,11 @@ public class CorruptSchematicStreamer {
Fawe.debug("No dimensions found! Estimating based on factors:" + dimensions);
}
if (Settings.IMP.CLIPBOARD.USE_DISK) {
fc = new DiskOptimizedClipboard(dimensions.getBlockX(), dimensions.getBlockY(), dimensions.getBlockZ(), uuid);
fc = new DiskOptimizedClipboard(dimensions, uuid);
} else if (Settings.IMP.CLIPBOARD.COMPRESSION_LEVEL == 0) {
fc = new CPUOptimizedClipboard(dimensions.getBlockX(), dimensions.getBlockY(), dimensions.getBlockZ());
fc = new CPUOptimizedClipboard(dimensions);
} else {
fc = new MemoryOptimizedClipboard(dimensions.getBlockX(), dimensions.getBlockY(), dimensions.getBlockZ());
fc = new MemoryOptimizedClipboard(dimensions);
}
return fc;
}

View File

@ -1,90 +0,0 @@
package com.boydti.fawe.jnbt;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.config.BBC;
import com.boydti.fawe.object.exception.FaweException;
import com.sk89q.jnbt.NBTInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.function.BiConsumer;
public class NBTStreamer {
private final NBTInputStream is;
private final HashMap<String, BiConsumer> readers;
public NBTStreamer(NBTInputStream stream) {
this.is = stream;
readers = new HashMap<>();
}
/**
* Reads the entire stream and runs the applicable readers
*
* @throws IOException
*/
public void readFully() throws IOException {
is.readNamedTagLazy(readers::get);
is.close();
}
/**
* Reads the stream until all readers have been used<br>
* - Use readFully if you expect a reader to appear more than once
* - Can exit early without having reading the entire file
*
* @throws IOException
*/
public void readQuick() throws IOException {
try {
is.readNamedTagLazy(node -> {
if (readers.isEmpty()) {
throw FaweCache.MANUAL;
}
return readers.remove(node);
});
} catch (FaweException ignore) {}
is.close();
}
public <T, V> void addReader(String node, BiConsumer<T, V> run) {
if (run instanceof NBTStreamReader) {
((NBTStreamReader) run).init(node);
}
readers.put(node, run);
}
public static abstract class NBTStreamReader<T, V> implements BiConsumer<T, V> {
private String node;
public void init(String node) {
this.node = node;
}
public String getNode() {
return node;
}
}
public static abstract class ByteReader implements BiConsumer<Integer, Integer> {
@Override
public void accept(Integer index, Integer value) {
run(index, value);
}
public abstract void run(int index, int byteValue);
}
public interface LazyReader extends BiConsumer<Integer, DataInputStream> {}
public static abstract class LongReader implements BiConsumer<Integer, Long> {
@Override
public void accept(Integer index, Long value) {
run(index, value);
}
public abstract void run(int index, long byteValue);
}
}

View File

@ -1,420 +0,0 @@
package com.boydti.fawe.jnbt;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.FaweInputStream;
import com.boydti.fawe.object.FaweOutputStream;
import com.boydti.fawe.object.clipboard.CPUOptimizedClipboard;
import com.boydti.fawe.object.clipboard.DiskOptimizedClipboard;
import com.boydti.fawe.object.clipboard.FaweClipboard;
import com.boydti.fawe.object.clipboard.MemoryOptimizedClipboard;
import com.boydti.fawe.object.io.FastByteArrayOutputStream;
import com.boydti.fawe.object.io.FastByteArraysInputStream;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.ListTag;
import com.sk89q.jnbt.NBTInputStream;
import com.sk89q.jnbt.StringTag;
import com.sk89q.worldedit.entity.BaseEntity;
import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.regions.CuboidRegion;
import com.sk89q.worldedit.registry.state.PropertyKey;
import com.sk89q.worldedit.util.Direction;
import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.biome.BiomeTypes;
import com.sk89q.worldedit.world.block.BlockCategories;
import com.sk89q.worldedit.world.block.BlockID;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import com.sk89q.worldedit.world.block.BlockType;
import com.sk89q.worldedit.world.block.BlockTypeSwitch;
import com.sk89q.worldedit.world.block.BlockTypeSwitchBuilder;
import com.sk89q.worldedit.world.entity.EntityType;
import com.sk89q.worldedit.world.entity.EntityTypes;
import com.sk89q.worldedit.world.registry.BlockMaterial;
import com.sk89q.worldedit.world.registry.LegacyMapper;
import net.jpountz.lz4.LZ4BlockInputStream;
import net.jpountz.lz4.LZ4BlockOutputStream;
import java.io.IOException;
import java.util.UUID;
import java.util.function.BiConsumer;
// TODO FIXME
public class SchematicStreamer extends NBTStreamer {
private final UUID uuid;
private FastByteArrayOutputStream idOut = new FastByteArrayOutputStream();
private FastByteArrayOutputStream dataOut = new FastByteArrayOutputStream();
private FastByteArrayOutputStream addOut;
private FaweOutputStream ids;
private FaweOutputStream datas;
private FaweOutputStream adds;
public SchematicStreamer(NBTInputStream stream, UUID uuid) {
super(stream);
this.uuid = uuid;
clipboard = new BlockArrayClipboard(new CuboidRegion(BlockVector3.at(0, 0, 0), BlockVector3.at(0, 0, 0)), fc);
}
public void addBlockReaders() throws IOException {
NBTStreamReader<? extends Integer, ? extends Integer> idInit = new NBTStreamReader<Integer, Integer>() {
@Override
public void accept(Integer length, Integer type) {
setupClipboard(length);
ids = new FaweOutputStream(new LZ4BlockOutputStream(idOut));
}
};
NBTStreamReader<? extends Integer, ? extends Integer> dataInit = new NBTStreamReader<Integer, Integer>() {
@Override
public void accept(Integer length, Integer type) {
setupClipboard(length);
datas = new FaweOutputStream(new LZ4BlockOutputStream(dataOut));
}
};
NBTStreamReader<? extends Integer, ? extends Integer> addInit = new NBTStreamReader<Integer, Integer>() {
@Override
public void accept(Integer length, Integer type) {
setupClipboard(length*2);
addOut = new FastByteArrayOutputStream();
adds = new FaweOutputStream(new LZ4BlockOutputStream(addOut));
}
};
addReader("Schematic.Blocks.?", idInit);
addReader("Schematic.Data.?", dataInit);
addReader("Schematic.AddBlocks.?", addInit);
addReader("Schematic.Blocks.#", new ByteReader() {
@Override
public void run(int index, int value) {
try {
ids.write(value);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
});
addReader("Schematic.Data.#", new ByteReader() {
@Override
public void run(int index, int value) {
try {
datas.write(value);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
});
addReader("Schematic.AddBlocks.#", new ByteReader() {
@Override
public void run(int index, int value) {
if (value != 0) {
int first = value & 0x0F;
int second = (value & 0xF0) >> 4;
try {
if (first != 0) adds.write(first);
if (second != 0) adds.write(second);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
});
ByteReader biomeReader = new ByteReader() {
@Override
public void run(int index, int value) {
BiomeType biome = BiomeTypes.getLegacy(value);
if (biome != null) {
fc.setBiome(index, biome);
}
}
};
NBTStreamReader<Integer, Integer> initializer23 = new NBTStreamReader<Integer, Integer>() {
@Override
public void accept(Integer value1, Integer value2) {
if (fc == null) setupClipboard(length * width * height);
}
};
addReader("Schematic.AWEBiomes.?", initializer23);
addReader("Schematic.Biomes.?", initializer23);
addReader("Schematic.AWEBiomes.#", biomeReader); // AWE stores as an int[]
addReader("Schematic.Biomes.#", biomeReader); // FAWE stores as a byte[] (4x smaller)
// Tiles
addReader("Schematic.TileEntities.#", (BiConsumer<Integer, CompoundTag>) (index, value) -> {
if (fc == null) {
setupClipboard(0);
}
int x = value.getInt("x");
int y = value.getInt("y");
int z = value.getInt("z");
fc.setTile(x, y, z, value);
});
// Entities
addReader("Schematic.Entities.#", (BiConsumer<Integer, CompoundTag>) (index, compound) -> {
if (fc == null) {
setupClipboard(0);
}
String id = compound.getString("id");
if (id.isEmpty()) {
return;
}
ListTag positionTag = compound.getListTag("Pos");
ListTag directionTag = compound.getListTag("Rotation");
EntityType type = EntityTypes.parse(id);
if (type != null) {
compound.getValue().put("Id", new StringTag(type.getId()));
BaseEntity state = new BaseEntity(type, compound);
fc.createEntity(clipboard, positionTag.asDouble(0), positionTag.asDouble(1), positionTag.asDouble(2), (float) directionTag.asDouble(0), (float) directionTag.asDouble(1), state);
} else {
Fawe.debug("Invalid entity: " + id);
}
});
}
@Override
public void readFully() throws IOException {
super.readFully();
if (ids != null) ids.close();
if (datas != null) datas.close();
if (adds != null) adds.close();
FaweInputStream idIn = new FaweInputStream(new LZ4BlockInputStream(new FastByteArraysInputStream(idOut.toByteArrays())));
FaweInputStream dataIn = new FaweInputStream(new LZ4BlockInputStream(new FastByteArraysInputStream(dataOut.toByteArrays())));
LegacyMapper remap = LegacyMapper.getInstance();
BlockVector3 dimensions = fc.getDimensions();
int length = dimensions.getBlockX() * dimensions.getBlockY() * dimensions.getBlockZ();
if (adds == null) {
for (int i = 0; i < length; i++) {
fc.setBlock(i, remap.getBlockFromLegacyCombinedId(((idIn.read() & 0xFF) << 4) + (dataIn.read() & 0xF)));
}
} else {
FaweInputStream addIn = new FaweInputStream(new LZ4BlockInputStream(new FastByteArraysInputStream(dataOut.toByteArrays())));
for (int i = 0; i < length; i++) {
fc.setBlock(i, remap.getBlockFromLegacyCombinedId(((addIn.read() & 0xFF) << 8) + ((idIn.read() & 0xFF) << 4) + (dataIn.read() & 0xF)));
}
addIn.close();
}
idIn.close();
dataIn.close();
}
private void fixStates() {
fc.forEach(new FaweClipboard.BlockReader() {
@Override
public <B extends BlockStateHolder<B>> void run(int x, int y, int z, B block) {
BlockType type = block.getBlockType();
if (BlockCategories.STAIRS.contains(type)) {
Object half = block.getState(PropertyKey.HALF);
Direction facing = block.getState(PropertyKey.FACING);
BlockVector3 forward = facing.toBlockVector();
Direction left = facing.getLeft();
Direction right = facing.getRight();
BlockStateHolder<com.sk89q.worldedit.world.block.BaseBlock> forwardBlock = fc.getBlock(x + forward.getBlockX(), y + forward.getBlockY(), z + forward.getBlockZ());
BlockType forwardType = forwardBlock.getBlockType();
if (forwardType.hasProperty(PropertyKey.SHAPE) && forwardType.hasProperty(PropertyKey.FACING)) {
Direction forwardFacing = (Direction) forwardBlock.getState(PropertyKey.FACING);
if (forwardFacing == left) {
BlockStateHolder<com.sk89q.worldedit.world.block.BaseBlock> rightBlock = fc.getBlock(x + right.toBlockVector().getBlockX(), y + right.toBlockVector().getBlockY(), z + right.toBlockVector().getBlockZ());
BlockType rightType = rightBlock.getBlockType();
if (!rightType.hasProperty(PropertyKey.SHAPE) || rightBlock.getState(PropertyKey.FACING) != facing) {
fc.setBlock(x, y, z, block.with(PropertyKey.SHAPE, "inner_left"));
}
return;
} else if (forwardFacing == right) {
BlockStateHolder<com.sk89q.worldedit.world.block.BaseBlock> leftBlock = fc.getBlock(x + left.toBlockVector().getBlockX(), y + left.toBlockVector().getBlockY(), z + left.toBlockVector().getBlockZ());
BlockType leftType = leftBlock.getBlockType();
if (!leftType.hasProperty(PropertyKey.SHAPE) || leftBlock.getState(PropertyKey.FACING) != facing) {
fc.setBlock(x, y, z, block.with(PropertyKey.SHAPE, "inner_right"));
}
return;
}
}
BlockStateHolder<com.sk89q.worldedit.world.block.BaseBlock> backwardsBlock = fc.getBlock(x - forward.getBlockX(), y - forward.getBlockY(), z - forward.getBlockZ());
BlockType backwardsType = backwardsBlock.getBlockType();
if (backwardsType.hasProperty(PropertyKey.SHAPE) && backwardsType.hasProperty(PropertyKey.FACING)) {
Direction backwardsFacing = (Direction) backwardsBlock.getState(PropertyKey.FACING);
if (backwardsFacing == left) {
BlockStateHolder<com.sk89q.worldedit.world.block.BaseBlock> rightBlock = fc.getBlock(x + right.toBlockVector().getBlockX(), y + right.toBlockVector().getBlockY(), z + right.toBlockVector().getBlockZ());
BlockType rightType = rightBlock.getBlockType();
if (!rightType.hasProperty(PropertyKey.SHAPE) || rightBlock.getState(PropertyKey.FACING) != facing) {
fc.setBlock(x, y, z, block.with(PropertyKey.SHAPE, "outer_left"));
}
return;
} else if (backwardsFacing == right) {
BlockStateHolder<com.sk89q.worldedit.world.block.BaseBlock> leftBlock = fc.getBlock(x + left.toBlockVector().getBlockX(), y + left.toBlockVector().getBlockY(), z + left.toBlockVector().getBlockZ());
BlockType leftType = leftBlock.getBlockType();
if (!leftType.hasProperty(PropertyKey.SHAPE) || leftBlock.getState(PropertyKey.FACING) != facing) {
fc.setBlock(x, y, z, block.with(PropertyKey.SHAPE, "outer_right"));
}
return;
}
}
} else {
int group = group(type);
if (group == -1) return;
BlockStateHolder set = block;
if (set.getState(PropertyKey.NORTH) == Boolean.FALSE && merge(group, x, y, z - 1)) set = set.with(PropertyKey.NORTH, true);
if (set.getState(PropertyKey.EAST) == Boolean.FALSE && merge(group, x + 1, y, z)) set = set.with(PropertyKey.EAST, true);
if (set.getState(PropertyKey.SOUTH) == Boolean.FALSE && merge(group, x, y, z + 1)) set = set.with(PropertyKey.SOUTH, true);
if (set.getState(PropertyKey.WEST) == Boolean.FALSE && merge(group, x - 1, y, z)) set = set.with(PropertyKey.WEST, true);
if (group == 2) {
int ns = ((Boolean) set.getState(PropertyKey.NORTH) ? 1 : 0) + ((Boolean) set.getState(PropertyKey.SOUTH) ? 1 : 0);
int ew = ((Boolean) set.getState(PropertyKey.EAST) ? 1 : 0) + ((Boolean) set.getState(PropertyKey.WEST) ? 1 : 0);
if (Math.abs(ns - ew) != 2 || fc.getBlock(x, y + 1, z).getBlockType().getMaterial().isSolid()) {
set = set.with(PropertyKey.UP, true);
}
}
if (set != block) fc.setBlock(x, y, z, set);
}
}
}, false);
}
private BlockTypeSwitch<Boolean> fullCube = new BlockTypeSwitchBuilder<>(false).add(type -> {
BlockMaterial mat = type.getMaterial();
return (mat.isFullCube() && !mat.isFragileWhenPushed() && mat.getLightValue() == 0 && mat.isOpaque() && mat.isSolid() && !mat.isTranslucent());
}, true).build();
private boolean merge(int group, int x, int y, int z) {
BlockStateHolder<com.sk89q.worldedit.world.block.BaseBlock> block = fc.getBlock(x, y, z);
BlockType type = block.getBlockType();
return group(type) == group || fullCube.apply(type);
}
private int group(BlockType type) {
switch (type.getInternalId()) {
case BlockID.ACACIA_FENCE:
case BlockID.BIRCH_FENCE:
case BlockID.DARK_OAK_FENCE:
case BlockID.JUNGLE_FENCE:
case BlockID.OAK_FENCE:
case BlockID.SPRUCE_FENCE:
return 0;
case BlockID.NETHER_BRICK_FENCE:
return 1;
case BlockID.COBBLESTONE_WALL:
case BlockID.MOSSY_COBBLESTONE_WALL:
return 2;
case BlockID.IRON_BARS:
case BlockID.BLACK_STAINED_GLASS_PANE:
case BlockID.BLUE_STAINED_GLASS_PANE:
case BlockID.BROWN_MUSHROOM_BLOCK:
case BlockID.BROWN_STAINED_GLASS_PANE:
case BlockID.CYAN_STAINED_GLASS_PANE:
case BlockID.GLASS_PANE:
case BlockID.GRAY_STAINED_GLASS_PANE:
case BlockID.GREEN_STAINED_GLASS_PANE:
case BlockID.LIGHT_BLUE_STAINED_GLASS_PANE:
case BlockID.LIGHT_GRAY_STAINED_GLASS_PANE:
case BlockID.LIME_STAINED_GLASS_PANE:
case BlockID.MAGENTA_STAINED_GLASS_PANE:
case BlockID.ORANGE_STAINED_GLASS_PANE:
case BlockID.PINK_STAINED_GLASS_PANE:
case BlockID.PURPLE_STAINED_GLASS_PANE:
case BlockID.RED_STAINED_GLASS_PANE:
case BlockID.WHITE_STAINED_GLASS_PANE:
case BlockID.YELLOW_STAINED_GLASS_PANE:
return 3;
default:
return -1;
}
}
public void addDimensionReaders() {
addReader("Schematic.Height",
(BiConsumer<Integer, Short>) (index, value) -> height = (value));
addReader("Schematic.Width", (BiConsumer<Integer, Short>) (index, value) -> width = (value));
addReader("Schematic.Length",
(BiConsumer<Integer, Short>) (index, value) -> length = (value));
addReader("Schematic.WEOriginX",
(BiConsumer<Integer, Integer>) (index, value) -> originX = (value));
addReader("Schematic.WEOriginY",
(BiConsumer<Integer, Integer>) (index, value) -> originY = (value));
addReader("Schematic.WEOriginZ",
(BiConsumer<Integer, Integer>) (index, value) -> originZ = (value));
addReader("Schematic.WEOffsetX",
(BiConsumer<Integer, Integer>) (index, value) -> offsetX = (value));
addReader("Schematic.WEOffsetY",
(BiConsumer<Integer, Integer>) (index, value) -> offsetY = (value));
addReader("Schematic.WEOffsetZ",
(BiConsumer<Integer, Integer>) (index, value) -> offsetZ = (value));
}
private int height;
private int width;
private int length;
private int originX;
private int originY;
private int originZ;
private int offsetX;
private int offsetY;
private int offsetZ;
private BlockArrayClipboard clipboard;
private FaweClipboard fc;
private FaweClipboard setupClipboard(int size) {
if (fc != null) {
if (fc.getDimensions().getX() == 0) {
fc.setDimensions(BlockVector3.at(size, 1, 1));
}
return fc;
}
if (Settings.IMP.CLIPBOARD.USE_DISK) {
return fc = new DiskOptimizedClipboard(size, 1, 1, uuid);
} else if (Settings.IMP.CLIPBOARD.COMPRESSION_LEVEL == 0) {
return fc = new CPUOptimizedClipboard(size, 1, 1);
} else {
return fc = new MemoryOptimizedClipboard(size, 1, 1);
}
}
public BlockVector3 getOrigin() {
return BlockVector3.at(originX, originY, originZ);
}
public BlockVector3 getOffset() {
return BlockVector3.at(offsetX, offsetY, offsetZ);
}
public BlockVector3 getDimensions() {
return BlockVector3.at(width, height, length);
}
public void setClipboard(FaweClipboard clipboard) {
this.fc = clipboard;
}
public Clipboard getClipboard() throws IOException {
try {
setupClipboard(0);
addDimensionReaders();
addBlockReaders();
readFully();
BlockVector3 min = BlockVector3.at(originX, originY, originZ);
BlockVector3 offset = BlockVector3.at(offsetX, offsetY, offsetZ);
BlockVector3 origin = min.subtract(offset);
BlockVector3 dimensions = BlockVector3.at(width, height, length);
fc.setDimensions(dimensions);
fixStates();
CuboidRegion region = new CuboidRegion(min, min.add(width, height, length).subtract(BlockVector3.ONE));
clipboard.init(region, fc);
clipboard.setOrigin(origin);
return clipboard;
} catch (Throwable e) {
if (fc != null) {
fc.close();
}
throw e;
}
}
}

View File

@ -1,49 +1,161 @@
package com.boydti.fawe.object.brush.visualization.cfi;
package com.boydti.fawe.jnbt.anvil;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.beta.IChunkSet;
import com.boydti.fawe.jnbt.streamer.StreamDelegate;
import com.boydti.fawe.jnbt.streamer.ValueReader;
import com.boydti.fawe.object.collection.BitArray4096;
import com.boydti.fawe.object.collection.BlockVector3ChunkMap;
import com.boydti.fawe.object.io.FastByteArrayOutputStream;
import com.boydti.fawe.util.MathMan;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.ListTag;
import com.sk89q.jnbt.NBTConstants;
import com.sk89q.jnbt.NBTInputStream;
import com.sk89q.jnbt.NBTOutputStream;
import com.sk89q.worldedit.math.BlockVector2;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.registry.state.Property;
import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.biome.BiomeTypes;
import com.sk89q.worldedit.world.block.BlockID;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import com.sk89q.worldedit.world.block.BlockType;
import com.sk89q.worldedit.world.block.BlockTypes;
import com.sk89q.worldedit.world.block.BlockTypesCache;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
public class WritableMCAChunk {
public class MCAChunk implements IChunkSet {
public final boolean[] hasSections = new boolean[16];
public final byte[] skyLight = new byte[65536];
public final byte[] blockLight = new byte[65536];
public boolean hasBiomes = false;
public final int[] biomes = new int[256];
public final byte[] biomes = new byte[256];
public final int[] blocks = new int[65536];
public final char[] blocks = new char[65536];
public Map<Short, CompoundTag> tiles = new HashMap<>();
public Map<UUID, CompoundTag> entities = new HashMap<>();
public final BlockVector3ChunkMap<CompoundTag> tiles = new BlockVector3ChunkMap<CompoundTag>();
public final Map<UUID, CompoundTag> entities = new HashMap<>();
public long inhabitedTime = System.currentTimeMillis();
public long lastUpdate;
public int modified;
public boolean deleted;
public int chunkX, chunkZ;
public int chunkX;
public int chunkZ;
protected WritableMCAChunk() {
public MCAChunk() {}
private boolean readLayer(Section section) {
if (section.palette == null || section.layer == -1 || section.blocksLength == -1 || section.palette[section.palette.length - 1] == null || section.blocks == null) {
// not initialized
return false;
}
int bitsPerEntry = MathMan.log2nlz(section.palette.length - 1);
BitArray4096 bitArray = new BitArray4096(section.blocks, bitsPerEntry);
char[] buffer = FaweCache.IMP.SECTION_BITS_TO_CHAR.get();
bitArray.toRaw(buffer);
int offset = section.layer << 12;
for (int i = 0; i < buffer.length; i++) {
BlockState block = section.palette[buffer[i]];
blocks[offset + i] = block.getOrdinalChar();
}
section.layer = -1;
section.blocksLength = -1;
section.blocks = null;
section.palette = null;
return true;
}
private static class Section {
public int layer = -1;
public long[] blocks;
public int blocksLength = -1;
public BlockState[] palette;
}
public MCAChunk(NBTInputStream nis, int chunkX, int chunkZ, boolean readPos) throws IOException {
this.chunkX = chunkX;
this.chunkZ = chunkZ;
read(nis, readPos);
}
public void read(NBTInputStream nis, boolean readPos) throws IOException {
StreamDelegate root = createDelegate(nis, readPos);
nis.readNamedTagLazy(root);
}
public StreamDelegate createDelegate(NBTInputStream nis, boolean readPos) {
StreamDelegate root = new StreamDelegate();
StreamDelegate level = root.add("").add("Level");
level.add("InhabitedTime").withLong((i, v) -> inhabitedTime = v);
level.add("LastUpdate").withLong((i, v) -> lastUpdate = v);
if (readPos) {
level.add("xPos").withInt((i, v) -> MCAChunk.this.chunkX = v);
level.add("zPos").withInt((i, v) -> MCAChunk.this.chunkZ = v);
}
Section section = new Section();
StreamDelegate layers = level.add("Sections");
StreamDelegate layer = layers.add();
layer.withInfo((length, type) -> {
section.layer = -1;
section.blocksLength = -1;
});
layer.add("Y").withInt((i, y) -> section.layer = y);
layer.add("Palette").withElem((ValueReader<Map<String, Object>>) (index, map) -> {
String name = (String) map.get("Name");
BlockType type = BlockTypes.get(name);
BlockState state = type.getDefaultState();
Map<String, String> properties = (Map<String, String>) map.get("Properties");
if (properties != null) {
for (Map.Entry<String, String> entry : properties.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
Property property = type.getProperty(key);
state = state.with(property, property.getValueFor(value));
}
}
section.palette[index] = state;
readLayer(section);
});
StreamDelegate blockStates = layer.add("BlockStates");
blockStates.withInfo((length, type) -> {
if (section.blocks == null) {
section.blocks = FaweCache.IMP.LONG_BUFFER_1024.get();
}
section.blocksLength = length;
});
blockStates.withLong((index, value) -> section.blocks[index] = value);
level.add("TileEntities").withElem((ValueReader<Map<String, Object>>) (index, value) -> {
CompoundTag tile = FaweCache.IMP.asTag(value);
int x = tile.getInt("x") & 15;
int y = tile.getInt("y");
int z = tile.getInt("z") & 15;
tiles.put(x, y, z, tile);
});
level.add("Entities").withElem((ValueReader<Map<String, Object>>) (index, value) -> {
CompoundTag entity = FaweCache.IMP.asTag(value);
entities.put(entity.getUUID(), entity);
});
level.add("Biomes").withInt((index, value) -> biomes[index] = (byte) value);
return root;
}
public int getX() {
@ -54,14 +166,22 @@ public class WritableMCAChunk {
return chunkZ;
}
public void setLoc(int X, int Z) {
@Override
public boolean hasSection(int layer) {
return hasSections[layer];
}
public void setPosition(int X, int Z) {
this.chunkX = X;
this.chunkZ = Z;
}
public void clear(int X, int Z) {
this.chunkX = X;
this.chunkZ = Z;
@Override
public IChunkSet reset() {
return this.reset(true);
}
public IChunkSet reset(boolean full) {
if (!tiles.isEmpty()) {
tiles.clear();
}
@ -71,11 +191,13 @@ public class WritableMCAChunk {
modified = 0;
deleted = false;
hasBiomes = false;
// TODO optimize
for (int i = 0; i < 65536; i++) {
blocks[i] = BlockID.AIR;
if (full) {
for (int i = 0; i < 65536; i++) {
blocks[i] = BlockID.AIR;
}
}
Arrays.fill(hasSections, false);
return this;
}
public void write(NBTOutputStream nbtOut) throws IOException {
@ -127,11 +249,10 @@ public class WritableMCAChunk {
int num_palette = 0;
try {
for (int i = blockIndexStart, j = 0; i < blockIndexEnd; i++, j++) {
int stateId = blocks[i];
int ordinal = BlockState.getFromInternalId(stateId).getOrdinal(); // TODO fixme Remove all use of BlockTypes.BIT_OFFSET so that this conversion isn't necessary
int ordinal = blocks[i];
int palette = blockToPalette[ordinal];
if (palette == Integer.MAX_VALUE) {
BlockState state = BlockTypes.states[ordinal];
// BlockState state = BlockTypesCache.states[ordinal];
blockToPalette[ordinal] = palette = num_palette;
paletteToBlock[num_palette] = ordinal;
num_palette++;
@ -149,7 +270,7 @@ public class WritableMCAChunk {
for (int i = 0; i < num_palette; i++) {
int ordinal = paletteToBlock[i];
BlockState state = BlockTypes.states[ordinal];
BlockState state = BlockTypesCache.states[ordinal];
BlockType type = state.getBlockType();
out.writeNamedTag("Name", type.getId());
@ -192,13 +313,13 @@ public class WritableMCAChunk {
}
out.writeNamedTagName("BlockLight", NBTConstants.TYPE_BYTE_ARRAY);
out.writeInt(2048);
out.write(blockLight, layer << 11, 1 << 11);
out.writeNamedTagName("SkyLight", NBTConstants.TYPE_BYTE_ARRAY);
out.writeInt(2048);
out.write(skyLight, layer << 11, 1 << 11);
// out.writeNamedTagName("BlockLight", NBTConstants.TYPE_BYTE_ARRAY);
// out.writeInt(2048);
// out.write(blockLight, layer << 11, 1 << 11);
//
// out.writeNamedTagName("SkyLight", NBTConstants.TYPE_BYTE_ARRAY);
// out.writeInt(2048);
// out.write(skyLight, layer << 11, 1 << 11);
out.writeEndTag();
@ -250,6 +371,15 @@ public class WritableMCAChunk {
return deleted;
}
@Override
public boolean isEmpty() {
if (deleted) return true;
for (boolean hasSection : hasSections) {
if (hasSection) return false;
}
return true;
}
public boolean isModified() {
return modified != 0;
}
@ -272,14 +402,17 @@ public class WritableMCAChunk {
return bitMask;
}
public void setTile(int x, int y, int z, CompoundTag tile) {
@Override
public boolean setTile(int x, int y, int z, CompoundTag tile) {
setModified();
short pair = MathMan.tripleBlockCoord(x, y, z);
if (tile != null) {
tiles.put(pair, tile);
tiles.put(x, y, z, tile);
} else {
tiles.remove(pair);
if (tiles.remove(x, y, z) == null) {
return false;
}
}
return true;
}
public void setEntity(CompoundTag entityTag) {
@ -289,17 +422,39 @@ public class WritableMCAChunk {
entities.put(new UUID(most, least), entityTag);
}
public void setBiome(int x, int z, BiomeType biome) {
@Override
public BiomeType getBiomeType(int x, int z) {
return BiomeTypes.get(this.biomes[(z << 4) | x] & 0xFF);
}
@Override
public BiomeType[] getBiomes() {
BiomeType[] tmp = new BiomeType[256];
for (int i = 0; i < 256; i++) {
tmp[i] = BiomeTypes.get(this.biomes[i] & 0xFF);
}
return tmp;
}
@Override
public boolean setBiome(BlockVector2 pos, BiomeType biome) {
return this.setBiome(pos.getX(), 0, pos.getZ(), biome);
}
@Override
public boolean setBiome(int x, int y, int z, BiomeType biome) {
setModified();
biomes[x + (z << 4)] = biome.getInternalId();
biomes[x + (z << 4)] = (byte) biome.getInternalId();
return true;
}
public Set<CompoundTag> getEntities() {
return new HashSet<>(entities.values());
}
public Map<Short, CompoundTag> getTiles() {
return tiles == null ? new HashMap<>() : tiles;
@Override
public Map<BlockVector3, CompoundTag> getTiles() {
return tiles == null ? Collections.emptyMap() : tiles;
}
public CompoundTag getTile(int x, int y, int z) {
@ -310,94 +465,51 @@ public class WritableMCAChunk {
return tiles.get(pair);
}
public boolean doesSectionExist(int cy) {
return hasSections[cy];
}
private final int getIndex(int x, int y, int z) {
return x | (z << 4) | (y << 8);
}
public int getBlockCombinedId(int x, int y, int z) {
public int getBlockOrdinal(int x, int y, int z) {
return blocks[x | (z << 4) | (y << 8)];
}
public BiomeType[] getBiomeArray() {
return null;
@Override
public BlockState getBlock(int x, int y, int z) {
int ordinal = getBlockOrdinal(x, y, z);
return BlockState.getFromOrdinal(ordinal);
}
public Set<UUID> getEntityRemoves() {
return new HashSet<>();
}
public void setSkyLight(int x, int y, int z, int value) {
setNibble(getIndex(x, y, z), skyLight, value);
@Override
public boolean setBlock(int x, int y, int z, BlockStateHolder holder) {
setBlock(x, y, z, holder.getOrdinalChar());
holder.applyTileEntity(this, x, y, z);
return true;
}
public void setBlockLight(int x, int y, int z, int value) {
setNibble(getIndex(x, y, z), blockLight, value);
}
public int getSkyLight(int x, int y, int z) {
if (!hasSections[y >> 4]) {
return 0;
}
return getNibble(getIndex(x, y, z), skyLight);
}
public int getBlockLight(int x, int y, int z) {
if (!hasSections[y >> 4]) {
return 0;
}
return getNibble(getIndex(x, y, z), blockLight);
}
public void setFullbright() {
for (int layer = 0; layer < 16; layer++) {
if (hasSections[layer]) {
Arrays.fill(skyLight, layer << 7, ((layer + 1) << 7), (byte) 255);
}
@Override
public void setBlocks(int layer, char[] data) {
int offset = layer << 12;
for (int i = 0; i < 4096; i++) {
blocks[offset + i] = data[i];
}
}
public void removeLight() {
for (int i = 0; i < 16; i++) {
removeLight(i);
@Override
public char[] getArray(int layer) {
char[] tmp = FaweCache.IMP.SECTION_BITS_TO_CHAR.get();
int offset = layer << 12;
for (int i = 0; i < 4096; i++) {
tmp[i] = blocks[offset + i];
}
return tmp;
}
public void removeLight(int i) {
if (hasSections[i]) {
Arrays.fill(skyLight, i << 7, ((i + 1) << 7), (byte) 0);
Arrays.fill(blockLight, i << 7, ((i + 1) << 7), (byte) 0);
}
}
public int getNibble(int index, byte[] array) {
int indexShift = index >> 1;
if ((index & 1) == 0) {
return array[indexShift] & 15;
} else {
return array[indexShift] >> 4 & 15;
}
}
public void setNibble(int index, byte[] array, int value) {
int indexShift = index >> 1;
byte existing = array[indexShift];
int valueShift = value << 4;
if (existing == value + valueShift) {
return;
}
if ((index & 1) == 0) {
array[indexShift] = (byte) (existing & 240 | value);
} else {
array[indexShift] = (byte) (existing & 15 | valueShift);
}
}
public void setBlock(int x, int y, int z, int combinedId) {
blocks[getIndex(x, y, z)] = combinedId;
public void setBlock(int x, int y, int z, char ordinal) {
blocks[getIndex(x, y, z)] = ordinal;
}
public void setBiome(BiomeType biome) {
@ -407,4 +519,9 @@ public class WritableMCAChunk {
public void removeEntity(UUID uuid) {
entities.remove(uuid);
}
@Override
public boolean trim(boolean aggressive) {
return isEmpty();
}
}

View File

@ -0,0 +1,708 @@
package com.boydti.fawe.jnbt.anvil;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.beta.Trimable;
import com.boydti.fawe.jnbt.streamer.StreamDelegate;
import com.boydti.fawe.object.RunnableVal4;
import com.boydti.fawe.object.collection.CleanableThreadLocal;
import com.boydti.fawe.object.io.BufferedRandomAccessFile;
import com.boydti.fawe.object.io.FastByteArrayInputStream;
import com.boydti.fawe.util.MainUtil;
import com.boydti.fawe.util.MathMan;
import com.sk89q.jnbt.NBTInputStream;
import com.sk89q.worldedit.world.World;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.zip.Inflater;
import java.util.zip.InflaterInputStream;
/**
* Chunk format: http://minecraft.gamepedia.com/Chunk_format#Entity_format
* e.g.: `.Level.Entities.#` (Starts with a . as the root tag is unnamed)
* Note: This class isn't thread safe. You can use it in an async thread, but not multiple at the same time
*/
public class MCAFile implements Trimable {
private static Field fieldBuf2;
private static Field fieldBuf3;
static {
try {
fieldBuf2 = InflaterInputStream.class.getDeclaredField("buf");
fieldBuf2.setAccessible(true);
fieldBuf3 = NBTInputStream.class.getDeclaredField("buf");
fieldBuf3.setAccessible(true);
} catch (Throwable e) {
e.printStackTrace();
}
}
private final ForkJoinPool pool;
private final byte[] locations;
private boolean readLocations;
private File file;
private RandomAccessFile raf;
private boolean deleted;
private int X, Z;
private MCAChunk[] chunks;
private boolean[] chunkInitialized;
private Object[] locks;
final ThreadLocal<byte[]> byteStore1 = new ThreadLocal<byte[]>() {
@Override
protected byte[] initialValue() {
return new byte[4096];
}
};
final ThreadLocal<byte[]> byteStore2 = new ThreadLocal<byte[]>() {
@Override
protected byte[] initialValue() {
return new byte[4096];
}
};
final ThreadLocal<byte[]> byteStore3 = new ThreadLocal<byte[]>() {
@Override
protected byte[] initialValue() {
return new byte[1024];
}
};
public MCAFile(ForkJoinPool pool) {
this.pool = pool;
this.locations = new byte[4096];
this.chunks = new MCAChunk[32 * 32];
this.chunkInitialized = new boolean[this.chunks.length];
this.locks = new Object[this.chunks.length];
for (int i = 0; i < locks.length; i++) {
locks[i] = new Object();
}
}
@Override
public boolean trim(boolean aggressive) {
boolean hasChunk = false;
for (int i = 0; i < chunkInitialized.length; i++) {
if (!chunkInitialized[i]) {
chunks[i] = null;
} else {
hasChunk = true;
}
}
CleanableThreadLocal.clean(byteStore1);
CleanableThreadLocal.clean(byteStore2);
CleanableThreadLocal.clean(byteStore3);
return !hasChunk;
}
public MCAFile init(File file) throws FileNotFoundException {
String[] split = file.getName().split("\\.");
int X = Integer.parseInt(split[1]);
int Z = Integer.parseInt(split[2]);
return init(file, X, Z);
}
public MCAFile init(File file, int mcrX, int mcrZ) throws FileNotFoundException {
if (raf != null) {
flush(true);
for (int i = 0; i < 4096; i++) {
locations[i] = 0;
}
try {
raf.close();
} catch (IOException e) {
e.printStackTrace();
}
raf = null;
}
deleted = false;
Arrays.fill(chunkInitialized, false);
readLocations = false;
this.X = mcrX;
this.Z = mcrZ;
this.file = file;
if (!file.exists()) {
throw new FileNotFoundException(file.getName());
}
return this;
}
public MCAFile init(World world, int mcrX, int mcrZ) throws FileNotFoundException {
return init(new File(world.getStoragePath().toFile(), File.separator + "regions" + File.separator + "r." + mcrX + "." + mcrZ + ".mca"));
}
public int getIndex(int chunkX, int chunkZ) {
return ((chunkX & 31) << 2) + ((chunkZ & 31) << 7);
}
private RandomAccessFile getRaf() throws FileNotFoundException {
if (this.raf == null) {
this.raf = new RandomAccessFile(file, "rw");
}
return this.raf;
}
private void readHeader() throws IOException {
if (!readLocations) {
readLocations = true;
getRaf();
if (raf.length() < 8192) {
raf.setLength(8192);
} else {
raf.seek(0);
raf.readFully(locations);
}
}
}
public void clear() {
if (raf != null) {
try {
raf.close();
} catch (IOException e) {
e.printStackTrace();
}
}
deleted = false;
readLocations = false;
Arrays.fill(chunkInitialized, false);
}
@Override
protected void finalize() throws Throwable {
CleanableThreadLocal.clean(byteStore1);
CleanableThreadLocal.clean(byteStore2);
CleanableThreadLocal.clean(byteStore3);
super.finalize();
}
public void setDeleted(boolean deleted) {
this.deleted = deleted;
}
public boolean isDeleted() {
return deleted;
}
public int getX() {
return X;
}
public int getZ() {
return Z;
}
public RandomAccessFile getRandomAccessFile() {
return raf;
}
public File getFile() {
return file;
}
public MCAChunk getCachedChunk(int cx, int cz) {
int pair = getIndex(cx, cz);
MCAChunk chunk = chunks[pair];
if (chunk != null && chunkInitialized[pair]) {
return chunk;
}
return null;
}
public void setChunk(MCAChunk chunk) {
int cx = chunk.getX();
int cz = chunk.getZ();
int pair = getIndex(cx, cz);
chunks[pair] = chunk;
}
public MCAChunk getChunk(int cx, int cz) throws IOException {
int pair = getIndex(cx, cz);
MCAChunk chunk = chunks[pair];
if (chunk == null) {
Object lock = locks[pair];
synchronized (lock) {
chunk = chunks[pair];
if (chunk == null) {
chunk = new MCAChunk();
chunk.setPosition(cx, cz);
chunks[pair] = chunk;
}
}
} else if (chunkInitialized[pair]) {
return chunk;
}
synchronized (chunk) {
if (!chunkInitialized[pair]) {
readChunk(chunk, pair);
chunkInitialized[pair] = true;
}
}
return chunk;
}
private MCAChunk readChunk(MCAChunk chunk, int i) throws IOException {
int offset = (((locations[i] & 0xFF) << 16) + ((locations[i + 1] & 0xFF) << 8) + ((locations[i + 2] & 0xFF))) << 12;
if (offset == 0) {
return null;
}
int size = (locations[i + 3] & 0xFF) << 12;
try (NBTInputStream nis = getChunkIS(offset)) {
chunk.read(nis, false);
}
System.out.println("TODO multithreaded"); // TODO
return chunk;
}
/**
* CX, CZ, OFFSET, SIZE
*
* @param onEach
* @throws IOException
*/
public void forEachSortedChunk(RunnableVal4<Integer, Integer, Integer, Integer> onEach) throws IOException {
char[] offsets = new char[(int) (raf.length() / 4096) - 2];
Arrays.fill(offsets, Character.MAX_VALUE);
char i = 0;
for (int z = 0; z < 32; z++) {
for (int x = 0; x < 32; x++, i += 4) {
int offset = (((locations[i] & 0xFF) << 16) + ((locations[i + 1] & 0xFF) << 8) + ((locations[i + 2] & 0xFF))) - 2;
int size = locations[i + 3] & 0xFF;
if (size != 0) {
if (offset < offsets.length) {
offsets[offset] = i;
} else {
Fawe.debug("Ignoring invalid offset " + offset);
}
}
}
}
for (i = 0; i < offsets.length; i++) {
int index = offsets[i];
if (index != Character.MAX_VALUE) {
int offset = i + 2;
int size = locations[index + 3] & 0xFF;
int index2 = index >> 2;
int x = (index2) & 31;
int z = (index2) >> 5;
onEach.run(x, z, offset << 12, size << 12);
}
}
}
/**
* @param onEach cx, cz, offset, size
*/
public void forEachChunk(RunnableVal4<Integer, Integer, Integer, Integer> onEach) {
int i = 0;
for (int z = 0; z < 32; z++) {
for (int x = 0; x < 32; x++, i += 4) {
int offset = (((locations[i] & 0xFF) << 16) + ((locations[i + 1] & 0xFF) << 8) + ((locations[i + 2] & 0xFF)));
int size = locations[i + 3] & 0xFF;
if (size != 0) {
onEach.run(x, z, offset << 12, size << 12);
}
}
}
}
public void forEachChunk(Consumer<MCAChunk> onEach) {
int i = 0;
for (int z = 0; z < 32; z++) {
for (int x = 0; x < 32; x++, i += 4) {
int offset = (((locations[i] & 0xFF) << 16) + ((locations[i + 1] & 0xFF) << 8) + ((locations[i + 2] & 0xFF)));
int size = locations[i + 3] & 0xFF;
if (size != 0) {
try {
onEach.accept(getChunk(x, z));
} catch (Throwable ignore) {
}
}
}
}
}
public int getOffset(int cx, int cz) {
int i = getIndex(cx, cz);
int offset = (((locations[i] & 0xFF) << 16) + ((locations[i + 1] & 0xFF) << 8) + ((locations[i + 2] & 0xFF)));
return offset << 12;
}
public int getSize(int cx, int cz) {
int i = getIndex(cx, cz);
return (locations[i + 3] & 0xFF) << 12;
}
public byte[] getChunkCompressedBytes(int offset) throws IOException {
if (offset == 0) {
return null;
}
synchronized (raf) {
raf.seek(offset);
int size = raf.readInt();
int compression = raf.read();
byte[] data = new byte[size];
raf.readFully(data);
return data;
}
}
private NBTInputStream getChunkIS(int offset) throws IOException {
try {
byte[] data = getChunkCompressedBytes(offset);
FastByteArrayInputStream bais = new FastByteArrayInputStream(data);
InflaterInputStream iis = new InflaterInputStream(bais, new Inflater(), 1);
fieldBuf2.set(iis, byteStore2.get());
BufferedInputStream bis = new BufferedInputStream(iis);
NBTInputStream nis = new NBTInputStream(bis);
fieldBuf3.set(nis, byteStore3.get());
return nis;
} catch (IllegalAccessException unlikely) {
unlikely.printStackTrace();
return null;
}
}
public void streamChunk(int cx, int cz, StreamDelegate delegate) throws IOException {
streamChunk(getOffset(cx, cz), delegate);
}
public void streamChunk(int offset, StreamDelegate delegate) throws IOException {
byte[] data = getChunkCompressedBytes(offset);
streamChunk(data, delegate);
}
public void streamChunk(byte[] data, StreamDelegate delegate) throws IOException {
if (data != null) {
try {
FastByteArrayInputStream bais = new FastByteArrayInputStream(data);
InflaterInputStream iis = new InflaterInputStream(bais, new Inflater(), 1);
fieldBuf2.set(iis, byteStore2.get());
BufferedInputStream bis = new BufferedInputStream(iis);
NBTInputStream nis = new NBTInputStream(bis);
fieldBuf3.set(nis, byteStore3.get());
nis.readNamedTagLazy(delegate);
} catch (IllegalAccessException unlikely) {
unlikely.printStackTrace();
}
}
}
/**
* @param onEach chunk
*/
public void forEachCachedChunk(Consumer<MCAChunk> onEach) {
for (int i = 0; i < chunks.length; i++) {
MCAChunk chunk = chunks[i];
if (chunk != null && this.chunkInitialized[i]) {
onEach.accept(chunk);
}
}
}
public List<MCAChunk> getCachedChunks() {
int size = 0;
for (int i = 0; i < chunks.length; i++) {
if (chunks[i] != null && this.chunkInitialized[i]) size++;
}
ArrayList<MCAChunk> list = new ArrayList<>(size);
for (int i = 0; i < chunks.length; i++) {
MCAChunk chunk = chunks[i];
if (chunk != null && this.chunkInitialized[i]) {
list.add(chunk);
}
}
return list;
}
private byte[] toBytes(MCAChunk chunk) throws Exception {
if (chunk.isDeleted()) {
return null;
}
byte[] uncompressed = chunk.toBytes(byteStore3.get());
byte[] compressed = MainUtil.compress(uncompressed, byteStore2.get(), null);
return compressed;
}
private byte[] getChunkBytes(int cx, int cz) throws Exception {
MCAChunk mca = getCachedChunk(cx, cz);
if (mca == null) {
int offset = getOffset(cx, cz);
if (offset == 0) {
return null;
}
return getChunkCompressedBytes(offset);
}
return toBytes(mca);
}
private void writeSafe(RandomAccessFile raf, int offset, byte[] data) throws IOException {
int len = data.length + 5;
raf.seek(offset);
if (raf.length() - offset < len) {
raf.setLength(((offset + len + 4095) / 4096) * 4096);
}
// Length of remaining data
raf.writeInt(data.length + 1);
// Compression type
raf.write(2);
raf.write(data);
}
private void writeHeader(RandomAccessFile raf, int cx, int cz, int offsetMedium, int sizeByte, boolean writeTime) throws IOException {
int i = getIndex(cx, cz);
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));
raf.write((offsetMedium >> 0));
raf.write(sizeByte);
raf.seek(i + 4096);
if (offsetMedium == 0 && sizeByte == 0) {
raf.writeInt(0);
} else {
raf.writeInt((int) (System.currentTimeMillis() / 1000L));
}
}
public void close() {
if (raf == null) return;
synchronized (raf) {
if (raf != null) {
flush(true);
try {
raf.close();
} catch (IOException e) {
e.printStackTrace();
}
raf = null;
}
}
}
public boolean isModified() {
if (isDeleted()) {
return true;
}
for (int i = 0; i < chunks.length; i++) {
MCAChunk chunk = chunks[i];
if (chunk != null && this.chunkInitialized[i]) {
if (chunk.isModified() || chunk.isDeleted()) {
return true;
}
}
}
return false;
}
/**
* Write the chunk to the file
* @param wait - If the flush method needs to wait for the pool
*/
public void flush(boolean wait) {
synchronized (raf) {
// If the file is marked as deleted, nothing is written
if (isDeleted()) {
clear();
file.delete();
return;
}
// Chunks that need to be relocated
Int2ObjectOpenHashMap<byte[]> relocate = new Int2ObjectOpenHashMap<>();
// The position of each chunk
final Int2ObjectOpenHashMap<Integer> offsetMap = new Int2ObjectOpenHashMap<>(); // Offset -> <byte cx, byte cz, short size>
// The data of each modified chunk
final Int2ObjectOpenHashMap<byte[]> compressedMap = new Int2ObjectOpenHashMap<>();
// The data of each chunk that needs to be moved
final Int2ObjectOpenHashMap<byte[]> append = new Int2ObjectOpenHashMap<>();
boolean[] modified = new boolean[1];
// Get the current time for the chunk timestamp
long now = System.currentTimeMillis();
// Load the chunks into the append or compressed map
final ForkJoinPool finalPool = this.pool;
forEachCachedChunk(chunk -> {
if (chunk.isModified() || chunk.isDeleted()) {
modified[0] = true;
chunk.setLastUpdate(now);
if (!chunk.isDeleted()) {
MCAFile.this.pool.submit(() -> {
try {
byte[] compressed = toBytes(chunk);
int pair = MathMan.pair((short) (chunk.getX() & 31), (short) (chunk.getZ() & 31));
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();
}
});
}
}
});
// If any changes were detected
if (modified[0]) {
file.setLastModified(now);
// Load the offset data into the offset map
forEachChunk(new RunnableVal4<Integer, Integer, Integer, Integer>() {
@Override
public void run(Integer cx, Integer cz, Integer offset, Integer size) {
short pair1 = MathMan.pairByte((byte) (cx & 31), (byte) (cz & 31));
short pair2 = (short) (size >> 12);
offsetMap.put((int) offset, (Integer) MathMan.pair(pair1, pair2));
}
});
// Wait for previous tasks
pool.awaitQuiescence(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
int start = 8192;
int written = start;
int end = 8192;
int nextOffset = 8192;
try {
for (int count = 0; count < offsetMap.size(); count++) {
// Get the previous position of the next chunk
Integer loc = offsetMap.get(nextOffset);
while (loc == null) {
nextOffset += 4096;
loc = offsetMap.get(nextOffset);
}
int offset = nextOffset;
// Get the x/z from the paired location
short cxz = MathMan.unpairX(loc);
int cx = MathMan.unpairShortX(cxz);
int cz = MathMan.unpairShortY(cxz);
// Get the size from the pair
int size = MathMan.unpairY(loc) << 12;
nextOffset += size;
end = Math.min(start + size, end);
int pair = getIndex(cx, cz);
byte[] newBytes = relocate.get(pair);
// newBytes is null if the chunk isn't modified or marked for moving
if (newBytes == null) {
MCAChunk cached = getCachedChunk(cx, cz);
// If the previous offset marks the current write position (start) then we only write the header
if (offset == start) {
if (cached == null || !cached.isModified()) {
writeHeader(raf, cx, cz, start >> 12, size >> 12, true);
start += size;
written = start + size;
continue;
} else {
newBytes = compressedMap.get(pair);
}
} else {
// The chunk needs to be moved, fetch the data if necessary
newBytes = compressedMap.get(pair);
if (newBytes == null) {
if (cached == null || !cached.isDeleted()) {
newBytes = getChunkCompressedBytes(getOffset(cx, cz));
}
}
}
}
if (newBytes == null) {
writeHeader(raf, cx, cz, 0, 0, false);
continue;
}
// The length to be written (compressed data + 5 byte chunk header)
int len = newBytes.length + 5;
int oldSize = (size + 4095) >> 12;
int newSize = (len + 4095) >> 12;
int nextOffset2 = end;
// If the current write position (start) + length of data to write (len) are longer than the position of the next chunk, we need to move the next chunks
while (start + len > end) {
Integer nextLoc = offsetMap.get(nextOffset2);
if (nextLoc != null) {
short nextCXZ = MathMan.unpairX(nextLoc);
int nextCX = MathMan.unpairShortX(nextCXZ);
int nextCZ = MathMan.unpairShortY(nextCXZ);
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);
}
int nextSize = MathMan.unpairY(nextLoc) << 12;
end += nextSize;
nextOffset2 += nextSize;
} else {
end += 4096;
nextOffset2 += 4096;
}
}
// Write the chunk + chunk header
writeSafe(raf, start, newBytes);
// Write the location data (beginning of file)
writeHeader(raf, cx, cz, start >> 12, newSize, true);
written = start + newBytes.length + 5;
start += newSize << 12;
}
// Write all the chunks which need to be appended
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;
}
}
// Round the file length, since the vanilla server doesn't like it for some reason
raf.setLength(4096 * ((written + 4095) / 4096));
if (raf instanceof BufferedRandomAccessFile) {
((BufferedRandomAccessFile) raf).flush();
}
raf.close();
} catch (Throwable e) {
e.printStackTrace();
}
if (wait) {
pool.awaitQuiescence(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
}
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More