geforkt von Mirrors/FastAsyncWorldEdit
Feature/unsafe over reflections (#1082)
* Use Unsafe to replace Lock * Start cleaning up everything that has to do with CleanableThreadLocal * Make cancellation work Co-authored-by: NotMyFault <mc.cache@web.de>
Dieser Commit ist enthalten in:
Ursprung
04610822a2
Commit
53681ccc59
@ -9,8 +9,6 @@ import com.boydti.fawe.beta.implementation.queue.QueueHandler;
|
|||||||
import com.boydti.fawe.bukkit.adapter.BukkitQueueHandler;
|
import com.boydti.fawe.bukkit.adapter.BukkitQueueHandler;
|
||||||
import com.boydti.fawe.bukkit.adapter.NMSAdapter;
|
import com.boydti.fawe.bukkit.adapter.NMSAdapter;
|
||||||
import com.boydti.fawe.bukkit.listener.BrushListener;
|
import com.boydti.fawe.bukkit.listener.BrushListener;
|
||||||
import com.boydti.fawe.bukkit.listener.BukkitImageListener;
|
|
||||||
import com.boydti.fawe.bukkit.listener.CFIPacketListener;
|
|
||||||
import com.boydti.fawe.bukkit.listener.ChunkListener9;
|
import com.boydti.fawe.bukkit.listener.ChunkListener9;
|
||||||
import com.boydti.fawe.bukkit.listener.RenderListener;
|
import com.boydti.fawe.bukkit.listener.RenderListener;
|
||||||
import com.boydti.fawe.bukkit.regions.GriefPreventionFeature;
|
import com.boydti.fawe.bukkit.regions.GriefPreventionFeature;
|
||||||
@ -57,8 +55,6 @@ public class FaweBukkit implements IFawe, Listener {
|
|||||||
private ItemUtil itemUtil;
|
private ItemUtil itemUtil;
|
||||||
|
|
||||||
private boolean listeningImages;
|
private boolean listeningImages;
|
||||||
private BukkitImageListener imageListener;
|
|
||||||
private CFIPacketListener packetListener;
|
|
||||||
private final boolean chunksStretched;
|
private final boolean chunksStretched;
|
||||||
private final FAWEPlatformAdapterImpl platformAdapter;
|
private final FAWEPlatformAdapterImpl platformAdapter;
|
||||||
|
|
||||||
@ -102,26 +98,14 @@ public class FaweBukkit implements IFawe, Listener {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override // Please don't delete this again, it's WIP
|
|
||||||
public void registerPacketListener() {
|
|
||||||
PluginManager manager = Bukkit.getPluginManager();
|
|
||||||
if (packetListener == null && manager.getPlugin("ProtocolLib") != null) {
|
|
||||||
packetListener = new CFIPacketListener(plugin);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public QueueHandler getQueueHandler() {
|
@Override public QueueHandler getQueueHandler() {
|
||||||
return new BukkitQueueHandler();
|
return new BukkitQueueHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized ImageViewer getImageViewer(com.sk89q.worldedit.entity.Player player) {
|
public synchronized ImageViewer getImageViewer(com.sk89q.worldedit.entity.Player player) {
|
||||||
if (listeningImages && imageListener == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
listeningImages = true;
|
listeningImages = true;
|
||||||
registerPacketListener();
|
|
||||||
PluginManager manager = Bukkit.getPluginManager();
|
PluginManager manager = Bukkit.getPluginManager();
|
||||||
|
|
||||||
if (manager.getPlugin("PacketListenerApi") == null) {
|
if (manager.getPlugin("PacketListenerApi") == null) {
|
||||||
@ -140,11 +124,7 @@ public class FaweBukkit implements IFawe, Listener {
|
|||||||
fos.write(jarData);
|
fos.write(jarData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BukkitImageViewer viewer = new BukkitImageViewer(BukkitAdapter.adapt(player));
|
return new BukkitImageViewer(BukkitAdapter.adapt(player));
|
||||||
if (imageListener == null) {
|
|
||||||
this.imageListener = new BukkitImageListener(plugin);
|
|
||||||
}
|
|
||||||
return viewer;
|
|
||||||
} catch (Throwable ignored) {
|
} catch (Throwable ignored) {
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
@ -7,7 +7,6 @@ import com.boydti.fawe.bukkit.adapter.NMSAdapter;
|
|||||||
import com.boydti.fawe.config.Settings;
|
import com.boydti.fawe.config.Settings;
|
||||||
import com.boydti.fawe.object.collection.BitArray;
|
import com.boydti.fawe.object.collection.BitArray;
|
||||||
import com.boydti.fawe.util.MathMan;
|
import com.boydti.fawe.util.MathMan;
|
||||||
import com.boydti.fawe.util.ReflectionUtils;
|
|
||||||
import com.boydti.fawe.util.TaskManager;
|
import com.boydti.fawe.util.TaskManager;
|
||||||
import com.mojang.datafixers.util.Either;
|
import com.mojang.datafixers.util.Either;
|
||||||
import com.sk89q.worldedit.math.BlockVector3;
|
import com.sk89q.worldedit.math.BlockVector3;
|
||||||
@ -63,9 +62,6 @@ public final class BukkitAdapter_1_15_2 extends NMSAdapter {
|
|||||||
public static final Field fieldTickingBlockCount;
|
public static final Field fieldTickingBlockCount;
|
||||||
public static final Field fieldNonEmptyBlockCount;
|
public static final Field fieldNonEmptyBlockCount;
|
||||||
|
|
||||||
private static final Field fieldDirtyCount;
|
|
||||||
private static final Field fieldDirtyBits;
|
|
||||||
|
|
||||||
private static final Field fieldBiomeArray;
|
private static final Field fieldBiomeArray;
|
||||||
|
|
||||||
private final static MethodHandle methodGetVisibleChunk;
|
private final static MethodHandle methodGetVisibleChunk;
|
||||||
@ -76,6 +72,7 @@ public final class BukkitAdapter_1_15_2 extends NMSAdapter {
|
|||||||
private static final int CHUNKSECTION_SHIFT;
|
private static final int CHUNKSECTION_SHIFT;
|
||||||
|
|
||||||
private static final Field fieldLock;
|
private static final Field fieldLock;
|
||||||
|
private static final long fieldLockOffset;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
try {
|
try {
|
||||||
@ -93,11 +90,6 @@ public final class BukkitAdapter_1_15_2 extends NMSAdapter {
|
|||||||
fieldNonEmptyBlockCount = ChunkSection.class.getDeclaredField("nonEmptyBlockCount");
|
fieldNonEmptyBlockCount = ChunkSection.class.getDeclaredField("nonEmptyBlockCount");
|
||||||
fieldNonEmptyBlockCount.setAccessible(true);
|
fieldNonEmptyBlockCount.setAccessible(true);
|
||||||
|
|
||||||
fieldDirtyCount = PlayerChunk.class.getDeclaredField("dirtyCount");
|
|
||||||
fieldDirtyCount.setAccessible(true);
|
|
||||||
fieldDirtyBits = PlayerChunk.class.getDeclaredField("r");
|
|
||||||
fieldDirtyBits.setAccessible(true);
|
|
||||||
|
|
||||||
fieldBiomeArray = BiomeStorage.class.getDeclaredField("g");
|
fieldBiomeArray = BiomeStorage.class.getDeclaredField("g");
|
||||||
fieldBiomeArray.setAccessible(true);
|
fieldBiomeArray.setAccessible(true);
|
||||||
|
|
||||||
@ -109,12 +101,10 @@ public final class BukkitAdapter_1_15_2 extends NMSAdapter {
|
|||||||
declaredSetLightNibbleArray.setAccessible(true);
|
declaredSetLightNibbleArray.setAccessible(true);
|
||||||
methodSetLightNibbleArray = MethodHandles.lookup().unreflect(declaredSetLightNibbleArray);
|
methodSetLightNibbleArray = MethodHandles.lookup().unreflect(declaredSetLightNibbleArray);
|
||||||
|
|
||||||
Field tmp = DataPaletteBlock.class.getDeclaredField("j");
|
|
||||||
ReflectionUtils.setAccessibleNonFinal(tmp);
|
|
||||||
fieldLock = tmp;
|
|
||||||
fieldLock.setAccessible(true);
|
|
||||||
|
|
||||||
Unsafe unsafe = UnsafeUtils.getUNSAFE();
|
Unsafe unsafe = UnsafeUtils.getUNSAFE();
|
||||||
|
fieldLock = DataPaletteBlock.class.getDeclaredField("j");
|
||||||
|
fieldLockOffset = unsafe.objectFieldOffset(fieldLock);
|
||||||
|
|
||||||
CHUNKSECTION_BASE = unsafe.arrayBaseOffset(ChunkSection[].class);
|
CHUNKSECTION_BASE = unsafe.arrayBaseOffset(ChunkSection[].class);
|
||||||
int scale = unsafe.arrayIndexScale(ChunkSection[].class);
|
int scale = unsafe.arrayIndexScale(ChunkSection[].class);
|
||||||
if ((scale & (scale - 1)) != 0) {
|
if ((scale & (scale - 1)) != 0) {
|
||||||
@ -141,16 +131,17 @@ public final class BukkitAdapter_1_15_2 extends NMSAdapter {
|
|||||||
//todo there has to be a better way to do this. Maybe using a() in DataPaletteBlock which acquires the lock in NMS?
|
//todo there has to be a better way to do this. Maybe using a() in DataPaletteBlock which acquires the lock in NMS?
|
||||||
try {
|
try {
|
||||||
synchronized (section) {
|
synchronized (section) {
|
||||||
|
Unsafe unsafe = UnsafeUtils.getUNSAFE();
|
||||||
DataPaletteBlock<IBlockData> blocks = section.getBlocks();
|
DataPaletteBlock<IBlockData> blocks = section.getBlocks();
|
||||||
ReentrantLock currentLock = (ReentrantLock) fieldLock.get(blocks);
|
ReentrantLock currentLock = (ReentrantLock) unsafe.getObject(blocks, fieldLockOffset);
|
||||||
if (currentLock instanceof DelegateLock) {
|
if (currentLock instanceof DelegateLock) {
|
||||||
return (DelegateLock) currentLock;
|
return (DelegateLock) currentLock;
|
||||||
}
|
}
|
||||||
DelegateLock newLock = new DelegateLock(currentLock);
|
DelegateLock newLock = new DelegateLock(currentLock);
|
||||||
fieldLock.set(blocks, newLock);
|
unsafe.putObject(blocks, fieldLockOffset, newLock);
|
||||||
return newLock;
|
return newLock;
|
||||||
}
|
}
|
||||||
} catch (IllegalAccessException e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@ import com.boydti.fawe.bukkit.adapter.NMSAdapter;
|
|||||||
import com.boydti.fawe.config.Settings;
|
import com.boydti.fawe.config.Settings;
|
||||||
import com.boydti.fawe.object.collection.BitArrayUnstretched;
|
import com.boydti.fawe.object.collection.BitArrayUnstretched;
|
||||||
import com.boydti.fawe.util.MathMan;
|
import com.boydti.fawe.util.MathMan;
|
||||||
import com.boydti.fawe.util.ReflectionUtils;
|
|
||||||
import com.boydti.fawe.util.TaskManager;
|
import com.boydti.fawe.util.TaskManager;
|
||||||
import com.mojang.datafixers.util.Either;
|
import com.mojang.datafixers.util.Either;
|
||||||
import com.sk89q.worldedit.math.BlockVector3;
|
import com.sk89q.worldedit.math.BlockVector3;
|
||||||
@ -63,9 +62,6 @@ public final class BukkitAdapter_1_16_1 extends NMSAdapter {
|
|||||||
public static final Field fieldTickingBlockCount;
|
public static final Field fieldTickingBlockCount;
|
||||||
public static final Field fieldNonEmptyBlockCount;
|
public static final Field fieldNonEmptyBlockCount;
|
||||||
|
|
||||||
private static final Field fieldDirtyCount;
|
|
||||||
private static final Field fieldDirtyBits;
|
|
||||||
|
|
||||||
private static final Field fieldBiomeArray;
|
private static final Field fieldBiomeArray;
|
||||||
|
|
||||||
private final static MethodHandle methodGetVisibleChunk;
|
private final static MethodHandle methodGetVisibleChunk;
|
||||||
@ -74,6 +70,7 @@ public final class BukkitAdapter_1_16_1 extends NMSAdapter {
|
|||||||
private static final int CHUNKSECTION_SHIFT;
|
private static final int CHUNKSECTION_SHIFT;
|
||||||
|
|
||||||
private static final Field fieldLock;
|
private static final Field fieldLock;
|
||||||
|
private static final long fieldLockOffset;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
try {
|
try {
|
||||||
@ -94,11 +91,6 @@ public final class BukkitAdapter_1_16_1 extends NMSAdapter {
|
|||||||
fieldNonEmptyBlockCount = ChunkSection.class.getDeclaredField("nonEmptyBlockCount");
|
fieldNonEmptyBlockCount = ChunkSection.class.getDeclaredField("nonEmptyBlockCount");
|
||||||
fieldNonEmptyBlockCount.setAccessible(true);
|
fieldNonEmptyBlockCount.setAccessible(true);
|
||||||
|
|
||||||
fieldDirtyCount = PlayerChunk.class.getDeclaredField("dirtyCount");
|
|
||||||
fieldDirtyCount.setAccessible(true);
|
|
||||||
fieldDirtyBits = PlayerChunk.class.getDeclaredField("r");
|
|
||||||
fieldDirtyBits.setAccessible(true);
|
|
||||||
|
|
||||||
fieldBiomeArray = BiomeStorage.class.getDeclaredField("g");
|
fieldBiomeArray = BiomeStorage.class.getDeclaredField("g");
|
||||||
fieldBiomeArray.setAccessible(true);
|
fieldBiomeArray.setAccessible(true);
|
||||||
|
|
||||||
@ -106,12 +98,10 @@ public final class BukkitAdapter_1_16_1 extends NMSAdapter {
|
|||||||
declaredGetVisibleChunk.setAccessible(true);
|
declaredGetVisibleChunk.setAccessible(true);
|
||||||
methodGetVisibleChunk = MethodHandles.lookup().unreflect(declaredGetVisibleChunk);
|
methodGetVisibleChunk = MethodHandles.lookup().unreflect(declaredGetVisibleChunk);
|
||||||
|
|
||||||
Field tmp = DataPaletteBlock.class.getDeclaredField("j");
|
|
||||||
ReflectionUtils.setAccessibleNonFinal(tmp);
|
|
||||||
fieldLock = tmp;
|
|
||||||
fieldLock.setAccessible(true);
|
|
||||||
|
|
||||||
Unsafe unsafe = UnsafeUtils.getUNSAFE();
|
Unsafe unsafe = UnsafeUtils.getUNSAFE();
|
||||||
|
fieldLock = DataPaletteBlock.class.getDeclaredField("j");
|
||||||
|
fieldLockOffset = unsafe.objectFieldOffset(fieldLock);
|
||||||
|
|
||||||
CHUNKSECTION_BASE = unsafe.arrayBaseOffset(ChunkSection[].class);
|
CHUNKSECTION_BASE = unsafe.arrayBaseOffset(ChunkSection[].class);
|
||||||
int scale = unsafe.arrayIndexScale(ChunkSection[].class);
|
int scale = unsafe.arrayIndexScale(ChunkSection[].class);
|
||||||
if ((scale & (scale - 1)) != 0) {
|
if ((scale & (scale - 1)) != 0) {
|
||||||
@ -138,16 +128,17 @@ public final class BukkitAdapter_1_16_1 extends NMSAdapter {
|
|||||||
//todo there has to be a better way to do this. Maybe using a() in DataPaletteBlock which acquires the lock in NMS?
|
//todo there has to be a better way to do this. Maybe using a() in DataPaletteBlock which acquires the lock in NMS?
|
||||||
try {
|
try {
|
||||||
synchronized (section) {
|
synchronized (section) {
|
||||||
|
Unsafe unsafe = UnsafeUtils.getUNSAFE();
|
||||||
DataPaletteBlock<IBlockData> blocks = section.getBlocks();
|
DataPaletteBlock<IBlockData> blocks = section.getBlocks();
|
||||||
ReentrantLock currentLock = (ReentrantLock) fieldLock.get(blocks);
|
ReentrantLock currentLock = (ReentrantLock) unsafe.getObject(blocks, fieldLockOffset);
|
||||||
if (currentLock instanceof DelegateLock) {
|
if (currentLock instanceof DelegateLock) {
|
||||||
return (DelegateLock) currentLock;
|
return (DelegateLock) currentLock;
|
||||||
}
|
}
|
||||||
DelegateLock newLock = new DelegateLock(currentLock);
|
DelegateLock newLock = new DelegateLock(currentLock);
|
||||||
fieldLock.set(blocks, newLock);
|
unsafe.putObject(blocks, fieldLockOffset, newLock);
|
||||||
return newLock;
|
return newLock;
|
||||||
}
|
}
|
||||||
} catch (IllegalAccessException e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@ import com.boydti.fawe.bukkit.adapter.NMSAdapter;
|
|||||||
import com.boydti.fawe.config.Settings;
|
import com.boydti.fawe.config.Settings;
|
||||||
import com.boydti.fawe.object.collection.BitArrayUnstretched;
|
import com.boydti.fawe.object.collection.BitArrayUnstretched;
|
||||||
import com.boydti.fawe.util.MathMan;
|
import com.boydti.fawe.util.MathMan;
|
||||||
import com.boydti.fawe.util.ReflectionUtils;
|
|
||||||
import com.boydti.fawe.util.TaskManager;
|
import com.boydti.fawe.util.TaskManager;
|
||||||
import com.mojang.datafixers.util.Either;
|
import com.mojang.datafixers.util.Either;
|
||||||
import com.sk89q.worldedit.math.BlockVector3;
|
import com.sk89q.worldedit.math.BlockVector3;
|
||||||
@ -63,9 +62,6 @@ public final class BukkitAdapter_1_16_2 extends NMSAdapter {
|
|||||||
public static final Field fieldTickingBlockCount;
|
public static final Field fieldTickingBlockCount;
|
||||||
public static final Field fieldNonEmptyBlockCount;
|
public static final Field fieldNonEmptyBlockCount;
|
||||||
|
|
||||||
private static final Field fieldDirty;
|
|
||||||
private static final Field fieldDirtyBlocks;
|
|
||||||
|
|
||||||
private static final Field fieldBiomeArray;
|
private static final Field fieldBiomeArray;
|
||||||
|
|
||||||
private static final MethodHandle methodGetVisibleChunk;
|
private static final MethodHandle methodGetVisibleChunk;
|
||||||
@ -74,6 +70,7 @@ public final class BukkitAdapter_1_16_2 extends NMSAdapter {
|
|||||||
private static final int CHUNKSECTION_SHIFT;
|
private static final int CHUNKSECTION_SHIFT;
|
||||||
|
|
||||||
private static final Field fieldLock;
|
private static final Field fieldLock;
|
||||||
|
private static final long fieldLockOffset;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
try {
|
try {
|
||||||
@ -94,11 +91,6 @@ public final class BukkitAdapter_1_16_2 extends NMSAdapter {
|
|||||||
fieldNonEmptyBlockCount = ChunkSection.class.getDeclaredField("nonEmptyBlockCount");
|
fieldNonEmptyBlockCount = ChunkSection.class.getDeclaredField("nonEmptyBlockCount");
|
||||||
fieldNonEmptyBlockCount.setAccessible(true);
|
fieldNonEmptyBlockCount.setAccessible(true);
|
||||||
|
|
||||||
fieldDirty = PlayerChunk.class.getDeclaredField("p");
|
|
||||||
fieldDirty.setAccessible(true);
|
|
||||||
fieldDirtyBlocks = PlayerChunk.class.getDeclaredField("dirtyBlocks");
|
|
||||||
fieldDirtyBlocks.setAccessible(true);
|
|
||||||
|
|
||||||
fieldBiomeArray = BiomeStorage.class.getDeclaredField("h");
|
fieldBiomeArray = BiomeStorage.class.getDeclaredField("h");
|
||||||
fieldBiomeArray.setAccessible(true);
|
fieldBiomeArray.setAccessible(true);
|
||||||
|
|
||||||
@ -106,25 +98,16 @@ public final class BukkitAdapter_1_16_2 extends NMSAdapter {
|
|||||||
declaredGetVisibleChunk.setAccessible(true);
|
declaredGetVisibleChunk.setAccessible(true);
|
||||||
methodGetVisibleChunk = MethodHandles.lookup().unreflect(declaredGetVisibleChunk);
|
methodGetVisibleChunk = MethodHandles.lookup().unreflect(declaredGetVisibleChunk);
|
||||||
|
|
||||||
Field tmp = DataPaletteBlock.class.getDeclaredField("j");
|
|
||||||
ReflectionUtils.setAccessibleNonFinal(tmp);
|
|
||||||
fieldLock = tmp;
|
|
||||||
fieldLock.setAccessible(true);
|
|
||||||
|
|
||||||
Unsafe unsafe = UnsafeUtils.getUNSAFE();
|
Unsafe unsafe = UnsafeUtils.getUNSAFE();
|
||||||
|
fieldLock = DataPaletteBlock.class.getDeclaredField("j");
|
||||||
|
fieldLockOffset = unsafe.objectFieldOffset(fieldLock);
|
||||||
|
|
||||||
CHUNKSECTION_BASE = unsafe.arrayBaseOffset(ChunkSection[].class);
|
CHUNKSECTION_BASE = unsafe.arrayBaseOffset(ChunkSection[].class);
|
||||||
int scale = unsafe.arrayIndexScale(ChunkSection[].class);
|
int scale = unsafe.arrayIndexScale(ChunkSection[].class);
|
||||||
if ((scale & (scale - 1)) != 0) {
|
if ((scale & (scale - 1)) != 0) {
|
||||||
throw new Error("data type scale not a power of two");
|
throw new Error("data type scale not a power of two");
|
||||||
}
|
}
|
||||||
CHUNKSECTION_SHIFT = 31 - Integer.numberOfLeadingZeros(scale);
|
CHUNKSECTION_SHIFT = 31 - Integer.numberOfLeadingZeros(scale);
|
||||||
|
|
||||||
Class<?> clsShortArraySet;
|
|
||||||
try { //paper
|
|
||||||
clsShortArraySet = Class.forName(new String(new char[]{'i', 't', '.', 'u', 'n', 'i', 'm', 'i', '.', 'd', 's', 'i', '.', 'f', 'a', 's', 't', 'u', 't', 'i', 'l', '.', 's', 'h', 'o', 'r', 't', 's', '.', 'S', 'h', 'o', 'r', 't', 'A', 'r', 'r', 'a', 'y', 'S', 'e', 't'}));
|
|
||||||
} catch (Throwable t) { // still using spigot boo
|
|
||||||
clsShortArraySet = Class.forName(new String(new char[]{'o', 'r', 'g', '.', 'b', 'u', 'k', 'k', 'i', 't', '.', 'c', 'r', 'a', 'f', 't', 'b', 'u', 'k', 'k', 'i', 't', '.', 'l', 'i', 'b', 's', '.', 'i', 't', '.', 'u', 'n', 'i', 'm', 'i', '.', 'd', 's', 'i', '.', 'f', 'a', 's', 't', 'u', 't', 'i', 'l', '.', 's', 'h', 'o', 'r', 't', 's', '.', 'S', 'h', 'o', 'r', 't', 'A', 'r', 'r', 'a', 'y', 'S', 'e', 't'}));
|
|
||||||
}
|
|
||||||
} catch (RuntimeException e) {
|
} catch (RuntimeException e) {
|
||||||
throw e;
|
throw e;
|
||||||
} catch (Throwable rethrow) {
|
} catch (Throwable rethrow) {
|
||||||
@ -145,16 +128,17 @@ public final class BukkitAdapter_1_16_2 extends NMSAdapter {
|
|||||||
//todo there has to be a better way to do this. Maybe using a() in DataPaletteBlock which acquires the lock in NMS?
|
//todo there has to be a better way to do this. Maybe using a() in DataPaletteBlock which acquires the lock in NMS?
|
||||||
try {
|
try {
|
||||||
synchronized (section) {
|
synchronized (section) {
|
||||||
|
Unsafe unsafe = UnsafeUtils.getUNSAFE();
|
||||||
DataPaletteBlock<IBlockData> blocks = section.getBlocks();
|
DataPaletteBlock<IBlockData> blocks = section.getBlocks();
|
||||||
ReentrantLock currentLock = (ReentrantLock) fieldLock.get(blocks);
|
ReentrantLock currentLock = (ReentrantLock) unsafe.getObject(blocks, fieldLockOffset);
|
||||||
if (currentLock instanceof DelegateLock) {
|
if (currentLock instanceof DelegateLock) {
|
||||||
return (DelegateLock) currentLock;
|
return (DelegateLock) currentLock;
|
||||||
}
|
}
|
||||||
DelegateLock newLock = new DelegateLock(currentLock);
|
DelegateLock newLock = new DelegateLock(currentLock);
|
||||||
fieldLock.set(blocks, newLock);
|
unsafe.putObject(blocks, fieldLockOffset, newLock);
|
||||||
return newLock;
|
return newLock;
|
||||||
}
|
}
|
||||||
} catch (IllegalAccessException e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@ import com.boydti.fawe.bukkit.adapter.NMSAdapter;
|
|||||||
import com.boydti.fawe.config.Settings;
|
import com.boydti.fawe.config.Settings;
|
||||||
import com.boydti.fawe.object.collection.BitArrayUnstretched;
|
import com.boydti.fawe.object.collection.BitArrayUnstretched;
|
||||||
import com.boydti.fawe.util.MathMan;
|
import com.boydti.fawe.util.MathMan;
|
||||||
import com.boydti.fawe.util.ReflectionUtils;
|
|
||||||
import com.boydti.fawe.util.TaskManager;
|
import com.boydti.fawe.util.TaskManager;
|
||||||
import com.mojang.datafixers.util.Either;
|
import com.mojang.datafixers.util.Either;
|
||||||
import com.sk89q.worldedit.math.BlockVector3;
|
import com.sk89q.worldedit.math.BlockVector3;
|
||||||
@ -63,9 +62,6 @@ public final class BukkitAdapter_1_16_5 extends NMSAdapter {
|
|||||||
public static final Field fieldTickingBlockCount;
|
public static final Field fieldTickingBlockCount;
|
||||||
public static final Field fieldNonEmptyBlockCount;
|
public static final Field fieldNonEmptyBlockCount;
|
||||||
|
|
||||||
private static final Field fieldDirty;
|
|
||||||
private static final Field fieldDirtyBlocks;
|
|
||||||
|
|
||||||
private static final Field fieldBiomeArray;
|
private static final Field fieldBiomeArray;
|
||||||
|
|
||||||
private static final MethodHandle methodGetVisibleChunk;
|
private static final MethodHandle methodGetVisibleChunk;
|
||||||
@ -74,6 +70,7 @@ public final class BukkitAdapter_1_16_5 extends NMSAdapter {
|
|||||||
private static final int CHUNKSECTION_SHIFT;
|
private static final int CHUNKSECTION_SHIFT;
|
||||||
|
|
||||||
private static final Field fieldLock;
|
private static final Field fieldLock;
|
||||||
|
private static final long fieldLockOffset;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
try {
|
try {
|
||||||
@ -94,11 +91,6 @@ public final class BukkitAdapter_1_16_5 extends NMSAdapter {
|
|||||||
fieldNonEmptyBlockCount = ChunkSection.class.getDeclaredField("nonEmptyBlockCount");
|
fieldNonEmptyBlockCount = ChunkSection.class.getDeclaredField("nonEmptyBlockCount");
|
||||||
fieldNonEmptyBlockCount.setAccessible(true);
|
fieldNonEmptyBlockCount.setAccessible(true);
|
||||||
|
|
||||||
fieldDirty = PlayerChunk.class.getDeclaredField("p");
|
|
||||||
fieldDirty.setAccessible(true);
|
|
||||||
fieldDirtyBlocks = PlayerChunk.class.getDeclaredField("dirtyBlocks");
|
|
||||||
fieldDirtyBlocks.setAccessible(true);
|
|
||||||
|
|
||||||
fieldBiomeArray = BiomeStorage.class.getDeclaredField("h");
|
fieldBiomeArray = BiomeStorage.class.getDeclaredField("h");
|
||||||
fieldBiomeArray.setAccessible(true);
|
fieldBiomeArray.setAccessible(true);
|
||||||
|
|
||||||
@ -106,25 +98,16 @@ public final class BukkitAdapter_1_16_5 extends NMSAdapter {
|
|||||||
declaredGetVisibleChunk.setAccessible(true);
|
declaredGetVisibleChunk.setAccessible(true);
|
||||||
methodGetVisibleChunk = MethodHandles.lookup().unreflect(declaredGetVisibleChunk);
|
methodGetVisibleChunk = MethodHandles.lookup().unreflect(declaredGetVisibleChunk);
|
||||||
|
|
||||||
Field tmp = DataPaletteBlock.class.getDeclaredField("j");
|
|
||||||
ReflectionUtils.setAccessibleNonFinal(tmp);
|
|
||||||
fieldLock = tmp;
|
|
||||||
fieldLock.setAccessible(true);
|
|
||||||
|
|
||||||
Unsafe unsafe = UnsafeUtils.getUNSAFE();
|
Unsafe unsafe = UnsafeUtils.getUNSAFE();
|
||||||
|
fieldLock = DataPaletteBlock.class.getDeclaredField("j");
|
||||||
|
fieldLockOffset = unsafe.objectFieldOffset(fieldLock);
|
||||||
|
|
||||||
CHUNKSECTION_BASE = unsafe.arrayBaseOffset(ChunkSection[].class);
|
CHUNKSECTION_BASE = unsafe.arrayBaseOffset(ChunkSection[].class);
|
||||||
int scale = unsafe.arrayIndexScale(ChunkSection[].class);
|
int scale = unsafe.arrayIndexScale(ChunkSection[].class);
|
||||||
if ((scale & (scale - 1)) != 0) {
|
if ((scale & (scale - 1)) != 0) {
|
||||||
throw new Error("data type scale not a power of two");
|
throw new Error("data type scale not a power of two");
|
||||||
}
|
}
|
||||||
CHUNKSECTION_SHIFT = 31 - Integer.numberOfLeadingZeros(scale);
|
CHUNKSECTION_SHIFT = 31 - Integer.numberOfLeadingZeros(scale);
|
||||||
|
|
||||||
Class<?> clsShortArraySet;
|
|
||||||
try { //paper
|
|
||||||
clsShortArraySet = Class.forName(new String(new char[]{'i', 't', '.', 'u', 'n', 'i', 'm', 'i', '.', 'd', 's', 'i', '.', 'f', 'a', 's', 't', 'u', 't', 'i', 'l', '.', 's', 'h', 'o', 'r', 't', 's', '.', 'S', 'h', 'o', 'r', 't', 'A', 'r', 'r', 'a', 'y', 'S', 'e', 't'}));
|
|
||||||
} catch (Throwable t) { // still using spigot boo
|
|
||||||
clsShortArraySet = Class.forName(new String(new char[]{'o', 'r', 'g', '.', 'b', 'u', 'k', 'k', 'i', 't', '.', 'c', 'r', 'a', 'f', 't', 'b', 'u', 'k', 'k', 'i', 't', '.', 'l', 'i', 'b', 's', '.', 'i', 't', '.', 'u', 'n', 'i', 'm', 'i', '.', 'd', 's', 'i', '.', 'f', 'a', 's', 't', 'u', 't', 'i', 'l', '.', 's', 'h', 'o', 'r', 't', 's', '.', 'S', 'h', 'o', 'r', 't', 'A', 'r', 'r', 'a', 'y', 'S', 'e', 't'}));
|
|
||||||
}
|
|
||||||
} catch (RuntimeException e) {
|
} catch (RuntimeException e) {
|
||||||
throw e;
|
throw e;
|
||||||
} catch (Throwable rethrow) {
|
} catch (Throwable rethrow) {
|
||||||
@ -145,16 +128,17 @@ public final class BukkitAdapter_1_16_5 extends NMSAdapter {
|
|||||||
//todo there has to be a better way to do this. Maybe using a() in DataPaletteBlock which acquires the lock in NMS?
|
//todo there has to be a better way to do this. Maybe using a() in DataPaletteBlock which acquires the lock in NMS?
|
||||||
try {
|
try {
|
||||||
synchronized (section) {
|
synchronized (section) {
|
||||||
|
Unsafe unsafe = UnsafeUtils.getUNSAFE();
|
||||||
DataPaletteBlock<IBlockData> blocks = section.getBlocks();
|
DataPaletteBlock<IBlockData> blocks = section.getBlocks();
|
||||||
ReentrantLock currentLock = (ReentrantLock) fieldLock.get(blocks);
|
ReentrantLock currentLock = (ReentrantLock) unsafe.getObject(blocks, fieldLockOffset);
|
||||||
if (currentLock instanceof DelegateLock) {
|
if (currentLock instanceof DelegateLock) {
|
||||||
return (DelegateLock) currentLock;
|
return (DelegateLock) currentLock;
|
||||||
}
|
}
|
||||||
DelegateLock newLock = new DelegateLock(currentLock);
|
DelegateLock newLock = new DelegateLock(currentLock);
|
||||||
fieldLock.set(blocks, newLock);
|
unsafe.putObject(blocks, fieldLockOffset, newLock);
|
||||||
return newLock;
|
return newLock;
|
||||||
}
|
}
|
||||||
} catch (IllegalAccessException e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
@ -191,7 +175,7 @@ public final class BukkitAdapter_1_16_5 extends NMSAdapter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void sendChunk(WorldServer nmsWorld, int chunkX, int chunkZ, int mask, boolean lighting) {
|
public static void sendChunk(WorldServer nmsWorld, int chunkX, int chunkZ, boolean lighting) {
|
||||||
PlayerChunk playerChunk = getPlayerChunk(nmsWorld, chunkX, chunkZ);
|
PlayerChunk playerChunk = getPlayerChunk(nmsWorld, chunkX, chunkZ);
|
||||||
if (playerChunk == null) {
|
if (playerChunk == null) {
|
||||||
return;
|
return;
|
||||||
|
@ -689,7 +689,7 @@ public class BukkitGetBlocks_1_16_5 extends CharGetBlocks implements BukkitGetBl
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized void send(int mask, boolean lighting) {
|
public synchronized void send(int mask, boolean lighting) {
|
||||||
BukkitAdapter_1_16_5.sendChunk(world, chunkX, chunkZ, mask, lighting);
|
BukkitAdapter_1_16_5.sendChunk(world, chunkX, chunkZ, lighting);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -214,7 +214,7 @@ public class FAWEWorldNativeAccess_1_16_R3 implements WorldNativeAccess<Chunk, I
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (IntPair chunk : toSend) {
|
for (IntPair chunk : toSend) {
|
||||||
BukkitAdapter_1_16_5.sendChunk(getWorld().getWorld().getHandle(), chunk.x, chunk.z, 0, false);
|
BukkitAdapter_1_16_5.sendChunk(getWorld().getWorld().getHandle(), chunk.x, chunk.z, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -229,7 +229,7 @@ public class FAWEWorldNativeAccess_1_16_R3 implements WorldNativeAccess<Chunk, I
|
|||||||
cachedChanges.forEach(cc -> cc.chunk.setType(cc.position, cc.blockData,
|
cachedChanges.forEach(cc -> cc.chunk.setType(cc.position, cc.blockData,
|
||||||
sideEffectSet != null && sideEffectSet.shouldApply(SideEffect.UPDATE)));
|
sideEffectSet != null && sideEffectSet.shouldApply(SideEffect.UPDATE)));
|
||||||
for (IntPair chunk : cachedChunksToSend) {
|
for (IntPair chunk : cachedChunksToSend) {
|
||||||
BukkitAdapter_1_16_5.sendChunk(getWorld().getWorld().getHandle(), chunk.x, chunk.z, 0, false);
|
BukkitAdapter_1_16_5.sendChunk(getWorld().getWorld().getHandle(), chunk.x, chunk.z, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -180,7 +180,7 @@ public class TuinityRelighter_1_16_5 implements Relighter {
|
|||||||
int x = pos.x;
|
int x = pos.x;
|
||||||
int z = pos.z;
|
int z = pos.z;
|
||||||
if (delay) { // we still need to send the block changes of that chunk
|
if (delay) { // we still need to send the block changes of that chunk
|
||||||
BukkitAdapter_1_16_5.sendChunk(world, x, z, -1, false);
|
BukkitAdapter_1_16_5.sendChunk(world, x, z, false);
|
||||||
}
|
}
|
||||||
world.getChunkProvider().removeTicketAtLevel(FAWE_TICKET, pos, LIGHT_LEVEL, Unit.INSTANCE);
|
world.getChunkProvider().removeTicketAtLevel(FAWE_TICKET, pos, LIGHT_LEVEL, Unit.INSTANCE);
|
||||||
}
|
}
|
||||||
|
@ -1,292 +0,0 @@
|
|||||||
package com.boydti.fawe.bukkit.listener;
|
|
||||||
|
|
||||||
import com.boydti.fawe.bukkit.util.image.BukkitImageViewer;
|
|
||||||
import com.boydti.fawe.object.brush.visualization.cfi.HeightMapMCAGenerator;
|
|
||||||
import com.boydti.fawe.util.image.ImageViewer;
|
|
||||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
|
||||||
import com.sk89q.worldedit.bukkit.BukkitPlayer;
|
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.Location;
|
|
||||||
import org.bukkit.World;
|
|
||||||
import org.bukkit.block.Block;
|
|
||||||
import org.bukkit.entity.Entity;
|
|
||||||
import org.bukkit.entity.ItemFrame;
|
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
import org.bukkit.event.Event;
|
|
||||||
import org.bukkit.event.EventHandler;
|
|
||||||
import org.bukkit.event.EventPriority;
|
|
||||||
import org.bukkit.event.Listener;
|
|
||||||
import org.bukkit.event.block.Action;
|
|
||||||
import org.bukkit.event.entity.EntityDamageByEntityEvent;
|
|
||||||
import org.bukkit.event.hanging.HangingBreakByEntityEvent;
|
|
||||||
import org.bukkit.event.player.PlayerEvent;
|
|
||||||
import org.bukkit.event.player.PlayerInteractEntityEvent;
|
|
||||||
import org.bukkit.event.player.PlayerInteractEvent;
|
|
||||||
import org.bukkit.inventory.EquipmentSlot;
|
|
||||||
import org.bukkit.plugin.Plugin;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class BukkitImageListener implements Listener {
|
|
||||||
|
|
||||||
private Location mutable = new Location(Bukkit.getWorlds().get(0), 0, 0, 0);
|
|
||||||
|
|
||||||
public BukkitImageListener(Plugin plugin) {
|
|
||||||
Bukkit.getPluginManager().registerEvents(this, plugin);
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO Fix along with CFI code 2020-02-04
|
|
||||||
/*
|
|
||||||
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
|
|
||||||
public void onPlayerInteractEntity(AsyncPlayerChatEvent event) {
|
|
||||||
Set<Player> recipients = event.getRecipients();
|
|
||||||
Iterator<Player> iter = recipients.iterator();
|
|
||||||
while (iter.hasNext()) {
|
|
||||||
Player player = iter.next();
|
|
||||||
BukkitPlayer bukkitPlayer = BukkitAdapter.adapt(player);
|
|
||||||
CFICommands.CFISettings settings = bukkitPlayer.getMeta("CFISettings");
|
|
||||||
if (player.equals(event.getPlayer()) || !bukkitPlayer.hasMeta() || settings == null || !settings.hasGenerator()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
String name = player.getName().toLowerCase(Locale.ROOT);
|
|
||||||
if (!event.getMessage().toLowerCase(Locale.ROOT).contains(name)) {
|
|
||||||
ArrayDeque<String> buffered = bukkitPlayer.getMeta("CFIBufferedMessages");
|
|
||||||
if (buffered == null) {
|
|
||||||
bukkitPlayer.setMeta("CFIBufferedMessaged", buffered = new ArrayDeque<>());
|
|
||||||
}
|
|
||||||
String full = String.format(event.getFormat(), event.getPlayer().getDisplayName(),
|
|
||||||
event.getMessage());
|
|
||||||
buffered.add(full);
|
|
||||||
iter.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
||||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
|
|
||||||
public void onHangingBreakByEntity(HangingBreakByEntityEvent event) {
|
|
||||||
if (!(event.getRemover() instanceof Player)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
handleInteract(event, (Player) event.getRemover(), event.getEntity(), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.LOWEST)
|
|
||||||
public void onEntityDamageByEntity(EntityDamageByEntityEvent event) {
|
|
||||||
if (!(event.getDamager() instanceof Player)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
handleInteract(event, (Player) event.getDamager(), event.getEntity(), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.LOWEST)
|
|
||||||
public void onPlayerInteract(PlayerInteractEvent event) {
|
|
||||||
if (event.useItemInHand() == Event.Result.DENY) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Player player = event.getPlayer();
|
|
||||||
BukkitPlayer bukkitPlayer = BukkitAdapter.adapt(player);
|
|
||||||
if (bukkitPlayer.getMeta("CFISettings") == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
if (event.getHand() == EquipmentSlot.OFF_HAND) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} catch (NoSuchFieldError | NoSuchMethodError ignored) {
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Block> target = player.getLastTwoTargetBlocks(null, 100);
|
|
||||||
if (target.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Block targetBlock = target.get(0);
|
|
||||||
World world = player.getWorld();
|
|
||||||
mutable.setWorld(world);
|
|
||||||
mutable.setX(targetBlock.getX() + 0.5);
|
|
||||||
mutable.setY(targetBlock.getY() + 0.5);
|
|
||||||
mutable.setZ(targetBlock.getZ() + 0.5);
|
|
||||||
Collection<Entity> entities = world.getNearbyEntities(mutable, 0.46875, 0, 0.46875);
|
|
||||||
|
|
||||||
if (!entities.isEmpty()) {
|
|
||||||
Action action = event.getAction();
|
|
||||||
boolean primary =
|
|
||||||
action == Action.RIGHT_CLICK_AIR || action == Action.RIGHT_CLICK_BLOCK;
|
|
||||||
|
|
||||||
double minDist = Integer.MAX_VALUE;
|
|
||||||
ItemFrame minItemFrame = null;
|
|
||||||
|
|
||||||
for (Entity entity : entities) {
|
|
||||||
if (entity instanceof ItemFrame) {
|
|
||||||
ItemFrame itemFrame = (ItemFrame) entity;
|
|
||||||
Location loc = itemFrame.getLocation();
|
|
||||||
double dx = loc.getX() - mutable.getX();
|
|
||||||
double dy = loc.getY() - mutable.getY();
|
|
||||||
double dz = loc.getZ() - mutable.getZ();
|
|
||||||
double dist = dx * dx + dy * dy + dz * dz;
|
|
||||||
if (dist < minDist) {
|
|
||||||
minItemFrame = itemFrame;
|
|
||||||
minDist = dist;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (minItemFrame != null) {
|
|
||||||
handleInteract(event, minItemFrame, primary);
|
|
||||||
if (event.isCancelled()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.LOWEST)
|
|
||||||
public void onPlayerInteractEntity(PlayerInteractEntityEvent event) {
|
|
||||||
handleInteract(event, event.getRightClicked(), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private BukkitImageViewer get(HeightMapMCAGenerator generator) {
|
|
||||||
if (generator == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
ImageViewer viewer = generator.getImageViewer();
|
|
||||||
if (!(viewer instanceof BukkitImageViewer)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (BukkitImageViewer) viewer;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleInteract(PlayerEvent event, Entity entity, boolean primary) {
|
|
||||||
handleInteract(event, event.getPlayer(), entity, primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleInteract(Event event, Player player, Entity entity, boolean primary) {
|
|
||||||
//todo fix with cfi code 2020-02-04
|
|
||||||
/*
|
|
||||||
if (!(entity instanceof ItemFrame)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ItemFrame itemFrame = (ItemFrame) entity;
|
|
||||||
|
|
||||||
BukkitPlayer bukkitPlayer = BukkitAdapter.adapt(player);
|
|
||||||
CFICommands.CFISettings settings = bukkitPlayer.getMeta("CFISettings");
|
|
||||||
HeightMapMCAGenerator generator = settings == null ? null : settings.getGenerator();
|
|
||||||
BukkitImageViewer viewer = get(generator);
|
|
||||||
if (viewer == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (itemFrame.getRotation() != Rotation.NONE) {
|
|
||||||
itemFrame.setRotation(Rotation.NONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
LocalSession session = bukkitPlayer.getSession();
|
|
||||||
BrushTool tool;
|
|
||||||
try {
|
|
||||||
tool = session.getBrushTool(bukkitPlayer, false);
|
|
||||||
} catch (InvalidToolBindException e) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ItemFrame[][] frames = viewer.getItemFrames();
|
|
||||||
if (frames == null || tool == null) {
|
|
||||||
viewer.selectFrame(itemFrame);
|
|
||||||
player.updateInventory();
|
|
||||||
TaskManager.IMP.laterAsync(() -> viewer.view(generator), 1);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
BrushSettings context = primary ? tool.getPrimary() : tool.getSecondary();
|
|
||||||
Brush brush = context.getBrush();
|
|
||||||
if (brush == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
tool.setContext(context);
|
|
||||||
|
|
||||||
if (event instanceof Cancellable) {
|
|
||||||
((Cancellable) event).setCancelled(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
Location target = itemFrame.getLocation();
|
|
||||||
Location source = player.getLocation();
|
|
||||||
|
|
||||||
double yawRad = Math.toRadians(source.getYaw() + 90d);
|
|
||||||
double pitchRad = Math.toRadians(-source.getPitch());
|
|
||||||
|
|
||||||
double a = Math.cos(pitchRad);
|
|
||||||
double xRat = Math.cos(yawRad) * a;
|
|
||||||
double zRat = Math.sin(yawRad) * a;
|
|
||||||
|
|
||||||
BlockFace facing = itemFrame.getFacing();
|
|
||||||
double thickness = 1 / 32D + 1 / 128D;
|
|
||||||
double modX = facing.getModX();
|
|
||||||
double modZ = facing.getModZ();
|
|
||||||
double dx = source.getX() - target.getX() - modX * thickness;
|
|
||||||
double dy = source.getY() + player.getEyeHeight() - target.getY();
|
|
||||||
double dz = source.getZ() - target.getZ() - modZ * thickness;
|
|
||||||
|
|
||||||
double offset;
|
|
||||||
double localX;
|
|
||||||
if (modX != 0) {
|
|
||||||
offset = dx / xRat;
|
|
||||||
localX = (-modX) * (dz - offset * zRat);
|
|
||||||
} else {
|
|
||||||
offset = dz / zRat;
|
|
||||||
localX = (modZ) * (dx - offset * xRat);
|
|
||||||
}
|
|
||||||
double localY = dy - offset * Math.sin(pitchRad);
|
|
||||||
int localPixelX = (int) ((localX + 0.5) * 128);
|
|
||||||
int localPixelY = (int) ((localY + 0.5) * 128);
|
|
||||||
|
|
||||||
UUID uuid = itemFrame.getUniqueId();
|
|
||||||
for (int blockX = 0; blockX < frames.length; blockX++) {
|
|
||||||
for (int blockY = 0; blockY < frames[0].length; blockY++) {
|
|
||||||
if (uuid.equals(frames[blockX][blockY].getUniqueId())) {
|
|
||||||
int pixelX = localPixelX + blockX * 128;
|
|
||||||
int pixelY = (128 * frames[0].length) - (localPixelY + blockY * 128 + 1);
|
|
||||||
|
|
||||||
int width = generator.getWidth();
|
|
||||||
int length = generator.getLength();
|
|
||||||
int worldX = (int) (pixelX * width / (frames.length * 128d));
|
|
||||||
int worldZ = (int) (pixelY * length / (frames[0].length * 128d));
|
|
||||||
|
|
||||||
if (worldX < 0 || worldX > width || worldZ < 0 || worldZ > length) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
bukkitPlayer.runAction(() -> {
|
|
||||||
BlockVector3 wPos = BlockVector3.at(worldX, 0, worldZ);
|
|
||||||
viewer.refresh();
|
|
||||||
int topY = generator
|
|
||||||
.getNearestSurfaceTerrainBlock(wPos.getBlockX(), wPos.getBlockZ(), 255,
|
|
||||||
0, 255);
|
|
||||||
wPos = wPos.withY(topY);
|
|
||||||
|
|
||||||
EditSession es = new EditSessionBuilder(bukkitPlayer.getWorld()).player(bukkitPlayer)
|
|
||||||
.combineStages(false).autoQueue(false).blockBag(null).limitUnlimited()
|
|
||||||
.build();
|
|
||||||
ExtentTraverser last = new ExtentTraverser(es.getExtent()).last();
|
|
||||||
Extent extent = last.get();
|
|
||||||
if (extent instanceof IQueueExtent) {
|
|
||||||
last = last.previous();
|
|
||||||
}
|
|
||||||
last.setNext(generator);
|
|
||||||
try {
|
|
||||||
brush.build(es, wPos, context.getMaterial(), context.getSize());
|
|
||||||
} catch (WorldEditException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
es.flushQueue();
|
|
||||||
viewer.view(generator);
|
|
||||||
}, true, true);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,338 +0,0 @@
|
|||||||
package com.boydti.fawe.bukkit.listener;
|
|
||||||
|
|
||||||
import com.boydti.fawe.FaweCache;
|
|
||||||
import com.boydti.fawe.object.RunnableVal3;
|
|
||||||
import com.boydti.fawe.object.brush.visualization.VirtualWorld;
|
|
||||||
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.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.world.block.BlockState;
|
|
||||||
import com.sk89q.worldedit.world.block.BlockTypes;
|
|
||||||
import org.bukkit.Location;
|
|
||||||
import org.bukkit.Material;
|
|
||||||
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 java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The CFIPacketListener handles packets for editing the {@link VirtualWorld}.
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* The virtual world will be displayed inside the current world. Block/Chunk/Movement packets
|
|
||||||
* need to be handled properly.
|
|
||||||
* </p>
|
|
||||||
*/
|
|
||||||
public class CFIPacketListener implements Listener {
|
|
||||||
|
|
||||||
private final Plugin plugin;
|
|
||||||
private final ProtocolManager protocolmanager;
|
|
||||||
|
|
||||||
public CFIPacketListener(Plugin plugin) {
|
|
||||||
this.plugin = plugin;
|
|
||||||
this.protocolmanager = ProtocolLibrary.getProtocolManager();
|
|
||||||
|
|
||||||
// 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: {
|
|
||||||
BlockState 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
|
|
||||||
|
|
||||||
protocolmanager.addPacketListener(new PacketAdapter(plugin, ListenerPriority.NORMAL, PacketType.Play.Server.ENTITY_VELOCITY) {
|
|
||||||
@Override
|
|
||||||
public void onPacketSending(PacketEvent event) {
|
|
||||||
if (!event.isServerPacket()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Player player = event.getPlayer();
|
|
||||||
Location pos = player.getLocation();
|
|
||||||
VirtualWorld gen = getGenerator(event);
|
|
||||||
if (gen != null) {
|
|
||||||
BlockVector3 origin = gen.getOrigin().toBlockPoint();
|
|
||||||
BlockVector3 pt = BlockVector3.at(pos.getBlockX(), pos.getBlockY(), pos.getBlockZ());
|
|
||||||
|
|
||||||
StructureModifier<Integer> ints = event.getPacket().getIntegers();
|
|
||||||
int id = ints.read(0);
|
|
||||||
int mx = ints.read(1);
|
|
||||||
int my = ints.read(2);
|
|
||||||
int mz = ints.read(3);
|
|
||||||
|
|
||||||
if (gen.contains(pt.subtract(origin)) && mx == 0 && my == 0 && mz == 0) {
|
|
||||||
event.setCancelled(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
protocolmanager.addPacketListener(new PacketAdapter(plugin, ListenerPriority.NORMAL, PacketType.Play.Server.POSITION) {
|
|
||||||
@Override
|
|
||||||
public void onPacketSending(PacketEvent event) {
|
|
||||||
if (!event.isServerPacket()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Player player = event.getPlayer();
|
|
||||||
Location pos = player.getLocation();
|
|
||||||
VirtualWorld gen = getGenerator(event);
|
|
||||||
if (gen != null) {
|
|
||||||
BlockVector3 origin = gen.getOrigin().toBlockPoint();
|
|
||||||
BlockVector3 from = BlockVector3.at(pos.getBlockX(), pos.getBlockY(), pos.getBlockZ());
|
|
||||||
|
|
||||||
PacketContainer packet = event.getPacket();
|
|
||||||
StructureModifier<Double> doubles = packet.getDoubles();
|
|
||||||
BlockVector3 to = BlockVector3.at(doubles.read(0), doubles.read(1), doubles.read(2));
|
|
||||||
if (gen.contains(to.subtract(origin)) && from.distanceSq(to) < 8) {
|
|
||||||
int id = packet.getIntegers().read(0);
|
|
||||||
PacketContainer reply = new PacketContainer(PacketType.Play.Client.TELEPORT_ACCEPT);
|
|
||||||
reply.getIntegers().write(0, id);
|
|
||||||
try {
|
|
||||||
protocolmanager.recieveClientPacket(player, reply);
|
|
||||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
event.setCancelled(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
protocolmanager.addPacketListener(new PacketAdapter(plugin, ListenerPriority.NORMAL, PacketType.Play.Server.MULTI_BLOCK_CHANGE) {
|
|
||||||
@Override
|
|
||||||
public void onPacketSending(PacketEvent event) {
|
|
||||||
if (!event.isServerPacket()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
VirtualWorld gen = getGenerator(event);
|
|
||||||
if (gen != null) {
|
|
||||||
PacketContainer packet = event.getPacket();
|
|
||||||
ChunkCoordIntPair chunk = packet.getChunkCoordIntPairs().read(0);
|
|
||||||
BlockVector3 origin = gen.getOrigin().toBlockPoint();
|
|
||||||
int cx = chunk.getChunkX() - (origin.getBlockX() >> 4);
|
|
||||||
int cz = chunk.getChunkZ() - (origin.getBlockX() >> 4);
|
|
||||||
if (gen.contains(BlockVector3.at(cx << 4, 0, cz << 4))) {
|
|
||||||
event.setCancelled(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler
|
|
||||||
public void onTeleport(PlayerTeleportEvent event) {
|
|
||||||
final Player player = event.getPlayer();
|
|
||||||
VirtualWorld gen = getGenerator(player);
|
|
||||||
if (gen != null) {
|
|
||||||
Location from = event.getFrom();
|
|
||||||
Location to = event.getTo();
|
|
||||||
if (to.getWorld().equals(from.getWorld()) && to.distanceSquared(from) < 8) {
|
|
||||||
event.setTo(player.getLocation());
|
|
||||||
event.setCancelled(true);
|
|
||||||
player.setVelocity(player.getVelocity());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean sendBlockChange(Player plr, VirtualWorld gen, BlockVector3 pt, Interaction action) {
|
|
||||||
PlatformManager platform = WorldEdit.getInstance().getPlatformManager();
|
|
||||||
com.sk89q.worldedit.entity.Player actor = BukkitAdapter.adapt(plr);
|
|
||||||
com.sk89q.worldedit.util.Location location = new com.sk89q.worldedit.util.Location(actor.getWorld(), pt.toVector3());
|
|
||||||
BlockInteractEvent toCall = new BlockInteractEvent(actor, location, action);
|
|
||||||
platform.handleBlockInteract(toCall);
|
|
||||||
if (toCall.isCancelled() || action == Interaction.OPEN) {
|
|
||||||
BlockVector3 realPos = pt.add(gen.getOrigin().toBlockPoint());
|
|
||||||
BlockState block = gen.getBlock(pt);
|
|
||||||
sendBlockChange(plr, realPos, block);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void sendBlockChange(Player plr, BlockVector3 pt, BlockState block) {
|
|
||||||
plr.sendBlockChange(new Location(plr.getWorld(), pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()), BukkitAdapter.adapt(block));
|
|
||||||
}
|
|
||||||
|
|
||||||
private VirtualWorld getGenerator(PacketEvent event) {
|
|
||||||
return getGenerator(event.getPlayer());
|
|
||||||
}
|
|
||||||
|
|
||||||
private VirtualWorld getGenerator(Player player) {
|
|
||||||
BukkitPlayer bukkitPlayer = BukkitAdapter.adapt(player);
|
|
||||||
VirtualWorld vw = bukkitPlayer.getSession().getVirtualWorld();
|
|
||||||
if (vw != null) {
|
|
||||||
return vw;
|
|
||||||
}
|
|
||||||
// CFICommands.CFISettings settings = bukkitPlayer.getMeta("CFISettings");
|
|
||||||
// if (settings != null && settings.hasGenerator() && settings.getGenerator().hasPacketViewer()) {
|
|
||||||
// return settings.getGenerator();
|
|
||||||
// }
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private BlockVector3 getRelPos(PacketEvent event, VirtualWorld generator) {
|
|
||||||
PacketContainer packet = event.getPacket();
|
|
||||||
StructureModifier<BlockPosition> position = packet.getBlockPositionModifier();
|
|
||||||
BlockPosition loc = position.readSafely(0);
|
|
||||||
if (loc == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
BlockVector3 origin = generator.getOrigin().toBlockPoint();
|
|
||||||
return BlockVector3.at(loc.getX() - origin.getBlockX(), loc.getY() - origin.getBlockY(), loc.getZ() - origin.getBlockZ());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleBlockEvent(PacketEvent event, boolean relative, RunnableVal3<PacketEvent, VirtualWorld, BlockVector3> task) {
|
|
||||||
VirtualWorld gen = getGenerator(event);
|
|
||||||
if (gen != null) {
|
|
||||||
BlockVector3 pt = getRelPos(event, gen);
|
|
||||||
if (pt != null) {
|
|
||||||
if (relative) {
|
|
||||||
pt = getRelative(event, pt);
|
|
||||||
}
|
|
||||||
if (gen.contains(pt)) {
|
|
||||||
event.setCancelled(true);
|
|
||||||
task.run(event, gen, pt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void registerBlockEvent(PacketType type, boolean relative, RunnableVal3<PacketEvent, VirtualWorld, BlockVector3> task) {
|
|
||||||
protocolmanager.addPacketListener(new PacketAdapter(plugin, ListenerPriority.NORMAL, type) {
|
|
||||||
@Override
|
|
||||||
public void onPacketReceiving(final PacketEvent event) {
|
|
||||||
if (type.isClient() || event.isServerPacket()) {
|
|
||||||
handleBlockEvent(event, relative, task);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPacketSending(PacketEvent event) {
|
|
||||||
onPacketReceiving(event);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private BlockVector3 getRelative(PacketEvent container, BlockVector3 pt) {
|
|
||||||
PacketContainer packet = container.getPacket();
|
|
||||||
StructureModifier<EnumWrappers.Direction> dirs = packet.getDirections();
|
|
||||||
EnumWrappers.Direction dir = dirs.readSafely(0);
|
|
||||||
if (dir == null) {
|
|
||||||
return pt;
|
|
||||||
}
|
|
||||||
switch (dir.ordinal()) {
|
|
||||||
case 0: return pt.add(0, -1, 0);
|
|
||||||
case 1: return pt.add(0, 1, 0);
|
|
||||||
case 2: return pt.add(0, 0, -1);
|
|
||||||
case 3: return pt.add(0, 0, 1);
|
|
||||||
case 4: return pt.add(-1, 0, 0);
|
|
||||||
case 5: return pt.add(1, 0, 0);
|
|
||||||
default: return pt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -41,33 +41,6 @@ public class BukkitReflectionUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get class for name. Replace {nms} to net.minecraft.server.V*. Replace {cb} to org.bukkit.craftbukkit.V*. Replace
|
|
||||||
* {nm} to net.minecraft
|
|
||||||
*
|
|
||||||
* @param classes possible class paths
|
|
||||||
* @return RefClass object
|
|
||||||
* @throws RuntimeException if no class found
|
|
||||||
*/
|
|
||||||
public static ReflectionUtils.RefClass getRefClass(final String... classes)
|
|
||||||
throws RuntimeException {
|
|
||||||
if (preClassM == null) {
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
for (String className : classes) {
|
|
||||||
try {
|
|
||||||
className = className.replace("{cb}", preClassB).replace("{nms}", preClassM)
|
|
||||||
.replace("{nm}", "net.minecraft");
|
|
||||||
return ReflectionUtils.getRefClass(Class.forName(className));
|
|
||||||
} catch (final ClassNotFoundException ignored) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new RuntimeException(
|
|
||||||
"no class found: " + classes[0].replace("{cb}", preClassB).replace("{nms}", preClassM)
|
|
||||||
.replace("{nm}", "net.minecraft"));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Class<?> getNmsClass(final String name) {
|
public static Class<?> getNmsClass(final String name) {
|
||||||
final String className = "net.minecraft.server." + getVersion() + "." + name;
|
final String className = "net.minecraft.server." + getVersion() + "." + name;
|
||||||
return ReflectionUtils.getClass(className);
|
return ReflectionUtils.getClass(className);
|
||||||
|
@ -2,7 +2,6 @@ package com.boydti.fawe;
|
|||||||
|
|
||||||
import com.boydti.fawe.beta.implementation.queue.QueueHandler;
|
import com.boydti.fawe.beta.implementation.queue.QueueHandler;
|
||||||
import com.boydti.fawe.config.Settings;
|
import com.boydti.fawe.config.Settings;
|
||||||
import com.boydti.fawe.object.brush.visualization.VisualQueue;
|
|
||||||
import com.boydti.fawe.util.CachedTextureUtil;
|
import com.boydti.fawe.util.CachedTextureUtil;
|
||||||
import com.boydti.fawe.util.CleanTextureUtil;
|
import com.boydti.fawe.util.CleanTextureUtil;
|
||||||
import com.boydti.fawe.util.FaweTimer;
|
import com.boydti.fawe.util.FaweTimer;
|
||||||
@ -82,7 +81,6 @@ public class Fawe {
|
|||||||
*/
|
*/
|
||||||
private final FaweTimer timer;
|
private final FaweTimer timer;
|
||||||
private FaweVersion version;
|
private FaweVersion version;
|
||||||
private VisualQueue visualQueue;
|
|
||||||
private TextureUtil textures;
|
private TextureUtil textures;
|
||||||
|
|
||||||
|
|
||||||
@ -148,7 +146,6 @@ public class Fawe {
|
|||||||
// Delayed worldedit setup
|
// Delayed worldedit setup
|
||||||
TaskManager.IMP.later(() -> {
|
TaskManager.IMP.later(() -> {
|
||||||
try {
|
try {
|
||||||
visualQueue = new VisualQueue(3);
|
|
||||||
WEManager.IMP.managers.addAll(Fawe.this.implementation.getMaskManagers());
|
WEManager.IMP.managers.addAll(Fawe.this.implementation.getMaskManagers());
|
||||||
} catch (Throwable ignored) {
|
} catch (Throwable ignored) {
|
||||||
}
|
}
|
||||||
@ -208,13 +205,6 @@ public class Fawe {
|
|||||||
return timer;
|
return timer;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the visual queue.
|
|
||||||
*/
|
|
||||||
public VisualQueue getVisualQueue() {
|
|
||||||
return visualQueue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The FAWE version.
|
* The FAWE version.
|
||||||
*
|
*
|
||||||
|
@ -9,7 +9,6 @@ import com.boydti.fawe.config.Settings;
|
|||||||
import com.boydti.fawe.object.collection.BitArray;
|
import com.boydti.fawe.object.collection.BitArray;
|
||||||
import com.boydti.fawe.object.collection.BitArrayUnstretched;
|
import com.boydti.fawe.object.collection.BitArrayUnstretched;
|
||||||
import com.boydti.fawe.object.collection.CleanableThreadLocal;
|
import com.boydti.fawe.object.collection.CleanableThreadLocal;
|
||||||
import com.boydti.fawe.object.collection.VariableThreadLocal;
|
|
||||||
import com.boydti.fawe.object.exception.FaweBlockBagException;
|
import com.boydti.fawe.object.exception.FaweBlockBagException;
|
||||||
import com.boydti.fawe.object.exception.FaweChunkLoadException;
|
import com.boydti.fawe.object.exception.FaweChunkLoadException;
|
||||||
import com.boydti.fawe.object.exception.FaweException;
|
import com.boydti.fawe.object.exception.FaweException;
|
||||||
@ -80,9 +79,6 @@ public enum FaweCache implements Trimable {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized boolean trim(boolean aggressive) {
|
public synchronized boolean trim(boolean aggressive) {
|
||||||
if (aggressive) {
|
|
||||||
CleanableThreadLocal.cleanAll();
|
|
||||||
} else {
|
|
||||||
CHUNK_FLAG.clean();
|
CHUNK_FLAG.clean();
|
||||||
BYTE_BUFFER_8192.clean();
|
BYTE_BUFFER_8192.clean();
|
||||||
BLOCK_TO_PALETTE.clean();
|
BLOCK_TO_PALETTE.clean();
|
||||||
@ -96,7 +92,6 @@ public enum FaweCache implements Trimable {
|
|||||||
MUTABLE_VECTOR3.clean();
|
MUTABLE_VECTOR3.clean();
|
||||||
MUTABLE_BLOCKVECTOR3.clean();
|
MUTABLE_BLOCKVECTOR3.clean();
|
||||||
SECTION_BITS_TO_CHAR.clean();
|
SECTION_BITS_TO_CHAR.clean();
|
||||||
}
|
|
||||||
for (Entry<Class<? extends IChunkSet>, Pool<? extends IChunkSet>> entry : REGISTERED_POOLS.entrySet()) {
|
for (Entry<Class<? extends IChunkSet>, Pool<? extends IChunkSet>> entry : REGISTERED_POOLS.entrySet()) {
|
||||||
Pool<? extends IChunkSet> pool = entry.getValue();
|
Pool<? extends IChunkSet> pool = entry.getValue();
|
||||||
pool.clear();
|
pool.clear();
|
||||||
@ -158,12 +153,9 @@ public enum FaweCache implements Trimable {
|
|||||||
*/
|
*/
|
||||||
public final CleanableThreadLocal<AtomicBoolean> CHUNK_FLAG = new CleanableThreadLocal<>(AtomicBoolean::new); // resets to false
|
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<byte[]> BYTE_BUFFER_8192 = new CleanableThreadLocal<>(() -> new byte[8192]);
|
||||||
|
|
||||||
public final VariableThreadLocal BYTE_BUFFER_VAR = new VariableThreadLocal();
|
|
||||||
|
|
||||||
public final CleanableThreadLocal<int[]> BLOCK_TO_PALETTE = new CleanableThreadLocal<>(() -> {
|
public final CleanableThreadLocal<int[]> BLOCK_TO_PALETTE = new CleanableThreadLocal<>(() -> {
|
||||||
int[] result = new int[BlockTypesCache.states.length];
|
int[] result = new int[BlockTypesCache.states.length];
|
||||||
Arrays.fill(result, Integer.MAX_VALUE);
|
Arrays.fill(result, Integer.MAX_VALUE);
|
||||||
@ -186,8 +178,6 @@ public enum FaweCache implements Trimable {
|
|||||||
|
|
||||||
public final CleanableThreadLocal<int[]> INDEX_STORE = new CleanableThreadLocal<>(() -> new int[256]);
|
public final CleanableThreadLocal<int[]> INDEX_STORE = new CleanableThreadLocal<>(() -> new int[256]);
|
||||||
|
|
||||||
public final CleanableThreadLocal<int[]> HEIGHT_STORE = new CleanableThreadLocal<>(() -> new int[256]);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds data for a palette used in a chunk section
|
* Holds data for a palette used in a chunk section
|
||||||
*/
|
*/
|
||||||
@ -314,14 +304,6 @@ public enum FaweCache implements Trimable {
|
|||||||
return toPaletteUnstretched(layerOffset, null, blocks);
|
return toPaletteUnstretched(layerOffset, null, blocks);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert raw int array to unstretched palette (1.16)
|
|
||||||
* @return palette
|
|
||||||
*/
|
|
||||||
public Palette toPaletteUnstretched(int layerOffset, int[] blocks) {
|
|
||||||
return toPaletteUnstretched(layerOffset, blocks, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Palette toPaletteUnstretched(int layerOffset, int[] blocksInts, char[] blocksChars) {
|
private Palette toPaletteUnstretched(int layerOffset, int[] blocksInts, char[] blocksChars) {
|
||||||
int[] blockToPalette = BLOCK_TO_PALETTE.get();
|
int[] blockToPalette = BLOCK_TO_PALETTE.get();
|
||||||
int[] paletteToBlock = PALETTE_TO_BLOCK.get();
|
int[] paletteToBlock = PALETTE_TO_BLOCK.get();
|
||||||
|
@ -23,8 +23,6 @@ public interface IFawe {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public default void registerPacketListener() {}
|
|
||||||
|
|
||||||
String getPlatform();
|
String getPlatform();
|
||||||
|
|
||||||
UUID getUUID(String name);
|
UUID getUUID(String name);
|
||||||
|
@ -1,164 +0,0 @@
|
|||||||
package com.boydti.fawe.beta;
|
|
||||||
|
|
||||||
import com.boydti.fawe.FaweCache;
|
|
||||||
import com.sk89q.jnbt.CompoundTag;
|
|
||||||
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 java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
public class CombinedBlocks implements IBlocks {
|
|
||||||
private final IBlocks secondary;
|
|
||||||
private final IBlocks primary;
|
|
||||||
private final int addMask;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO Add a constructor here to satisfy checkstyle.
|
|
||||||
*
|
|
||||||
* @param addMask - bitMask for force sending sections, else 0 to send the primary ones
|
|
||||||
*/
|
|
||||||
public CombinedBlocks(IBlocks secondary, IBlocks primary, int addMask) {
|
|
||||||
this.secondary = secondary;
|
|
||||||
this.primary = primary;
|
|
||||||
this.addMask = addMask == 0 ? 0 : addMask & secondary.getBitMask();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getBitMask() {
|
|
||||||
int bitMask = addMask;
|
|
||||||
for (int layer = 0; layer < FaweCache.IMP.CHUNK_LAYERS; layer++) {
|
|
||||||
if (primary.hasSection(layer)) {
|
|
||||||
bitMask |= (1 << layer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return bitMask;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void removeSectionLighting(int layer, boolean sky) {
|
|
||||||
primary.removeSectionLighting(layer, sky);
|
|
||||||
secondary.removeSectionLighting(layer, sky);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasSection(int layer) {
|
|
||||||
return primary.hasSection(layer) || secondary.hasSection(layer);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public char[] load(int layer) {
|
|
||||||
if (primary.hasSection(layer)) {
|
|
||||||
char[] blocks = primary.load(layer);
|
|
||||||
if (secondary.hasSection(layer) && primary != secondary) {
|
|
||||||
int i = 0;
|
|
||||||
for (; i < 4096; i++) {
|
|
||||||
if (blocks[i] == 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (i != 4096) {
|
|
||||||
char[] fallback = secondary.load(layer);
|
|
||||||
for (; i < 4096; i++) {
|
|
||||||
if (blocks[i] == 0) {
|
|
||||||
blocks[i] = fallback[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return blocks;
|
|
||||||
}
|
|
||||||
return secondary.load(layer);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BlockState getBlock(int x, int y, int z) {
|
|
||||||
BlockState block = primary.getBlock(x, y, z);
|
|
||||||
if (block.getBlockType() == BlockTypes.__RESERVED__) {
|
|
||||||
return secondary.getBlock(x, y, z);
|
|
||||||
}
|
|
||||||
return block;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Map<BlockVector3, CompoundTag> getTiles() {
|
|
||||||
Map<BlockVector3, CompoundTag> tiles = primary.getTiles();
|
|
||||||
if (primary != secondary) {
|
|
||||||
if (tiles.isEmpty()) {
|
|
||||||
return secondary.getTiles();
|
|
||||||
}
|
|
||||||
Map<BlockVector3, CompoundTag> otherTiles = secondary.getTiles();
|
|
||||||
if (!otherTiles.isEmpty()) {
|
|
||||||
HashMap<BlockVector3, CompoundTag> copy = null;
|
|
||||||
for (Map.Entry<BlockVector3, CompoundTag> entry : otherTiles.entrySet()) {
|
|
||||||
BlockVector3 pos = entry.getKey();
|
|
||||||
BlockState block = primary.getBlock(pos.getX(), pos.getY(), pos.getZ());
|
|
||||||
if (block.getBlockType() == BlockTypes.__RESERVED__) {
|
|
||||||
if (copy == null) {
|
|
||||||
copy = new HashMap<>(tiles);
|
|
||||||
}
|
|
||||||
copy.put(pos, entry.getValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (copy != null) {
|
|
||||||
return copy;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return tiles;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CompoundTag getTile(int x, int y, int z) {
|
|
||||||
CompoundTag tile = primary.getTile(x, y, z);
|
|
||||||
if (tile != null) {
|
|
||||||
return tile;
|
|
||||||
}
|
|
||||||
return secondary.getTile(x, y, z);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Set<CompoundTag> getEntities() {
|
|
||||||
Set<CompoundTag> joined = primary.getEntities();
|
|
||||||
if (primary != secondary) {
|
|
||||||
Set<CompoundTag> ents2 = secondary.getEntities();
|
|
||||||
if (joined.isEmpty()) {
|
|
||||||
return ents2;
|
|
||||||
}
|
|
||||||
if (ents2.isEmpty()) {
|
|
||||||
return joined;
|
|
||||||
}
|
|
||||||
joined = new HashSet<>(joined);
|
|
||||||
joined.addAll(ents2);
|
|
||||||
}
|
|
||||||
return joined;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BiomeType getBiomeType(int x, int y, int z) {
|
|
||||||
BiomeType biome = primary.getBiomeType(x, y, z);
|
|
||||||
if (biome == null) {
|
|
||||||
return secondary.getBiomeType(x, y, z);
|
|
||||||
}
|
|
||||||
return biome;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IBlocks reset() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean trim(boolean aggressive) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean trim(boolean aggressive, int layer) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
@ -75,12 +75,12 @@ public interface IQueueExtent<T extends IChunk> extends Flushable, Trimable, ICh
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
default BlockVector3 getMinimumPoint() {
|
default BlockVector3 getMinimumPoint() {
|
||||||
return BlockVector3.at(-30000000, 0, -30000000);
|
return BlockVector3.at(-30000000, getMinY(), -30000000);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
default BlockVector3 getMaximumPoint() {
|
default BlockVector3 getMaximumPoint() {
|
||||||
return BlockVector3.at(30000000, FaweCache.IMP.WORLD_MAX_Y, 30000000);
|
return BlockVector3.at(30000000, getMaxY(), 30000000);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setFastMode(boolean fastMode);
|
void setFastMode(boolean fastMode);
|
||||||
|
@ -1,89 +0,0 @@
|
|||||||
package com.boydti.fawe.beta.implementation.processors;
|
|
||||||
|
|
||||||
import com.boydti.fawe.beta.CombinedBlocks;
|
|
||||||
import com.boydti.fawe.beta.IBatchProcessor;
|
|
||||||
import com.boydti.fawe.beta.IBlocks;
|
|
||||||
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.Collection;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
import java.util.concurrent.Future;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
public class ChunkSendProcessor implements IBatchProcessor {
|
|
||||||
private final Supplier<Collection<Player>> players;
|
|
||||||
private final World world;
|
|
||||||
private final boolean full;
|
|
||||||
|
|
||||||
public ChunkSendProcessor(World world, Supplier<Collection<Player>> players) {
|
|
||||||
this(world, players, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ChunkSendProcessor(World world, Supplier<Collection<Player>> players, boolean full) {
|
|
||||||
this.players = players;
|
|
||||||
this.world = world;
|
|
||||||
this.full = full;
|
|
||||||
}
|
|
||||||
|
|
||||||
public World getWorld() {
|
|
||||||
return world;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Supplier<Collection<Player>> getPlayers() {
|
|
||||||
return players;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) {
|
|
||||||
int chunkX = chunk.getX();
|
|
||||||
int chunkZ = chunk.getZ();
|
|
||||||
IBlocks blocks;
|
|
||||||
boolean full = this.full;
|
|
||||||
if (full) {
|
|
||||||
blocks = set;
|
|
||||||
} else {
|
|
||||||
blocks = combine(chunk, get, set);
|
|
||||||
if (set.hasBiomes()) {
|
|
||||||
full = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ChunkPacket packet = new ChunkPacket(chunkX, chunkZ, () -> blocks, full);
|
|
||||||
Collection<Player> stream = this.players.get();
|
|
||||||
if (stream == null) {
|
|
||||||
world.sendFakeChunk(null, packet);
|
|
||||||
} else {
|
|
||||||
for (Player player : stream) {
|
|
||||||
if (player.getWorld().equals(world)) {
|
|
||||||
world.sendFakeChunk(player, packet);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return set;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Future<IChunkSet> postProcessSet(IChunk chunk, IChunkGet get, IChunkSet set) {
|
|
||||||
// Doesn't need to do anything
|
|
||||||
return CompletableFuture.completedFuture(set);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IBlocks combine(IChunk chunk, IChunkGet get, IChunkSet set) {
|
|
||||||
return new CombinedBlocks(get, set, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Extent construct(Extent child) {
|
|
||||||
throw new UnsupportedOperationException("Processing only");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ProcessorScope getScope() {
|
|
||||||
return ProcessorScope.READING_SET_BLOCKS;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,88 +0,0 @@
|
|||||||
package com.boydti.fawe.beta.implementation.processors;
|
|
||||||
|
|
||||||
import com.boydti.fawe.beta.CombinedBlocks;
|
|
||||||
import com.boydti.fawe.beta.IBlocks;
|
|
||||||
import com.boydti.fawe.beta.IChunk;
|
|
||||||
import com.boydti.fawe.beta.IChunkGet;
|
|
||||||
import com.boydti.fawe.beta.IChunkSet;
|
|
||||||
import com.boydti.fawe.beta.implementation.IChunkExtent;
|
|
||||||
import com.boydti.fawe.beta.implementation.packet.ChunkPacket;
|
|
||||||
import com.boydti.fawe.util.MathMan;
|
|
||||||
import com.sk89q.worldedit.entity.Player;
|
|
||||||
import com.sk89q.worldedit.math.BlockVector2;
|
|
||||||
import com.sk89q.worldedit.world.World;
|
|
||||||
import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
|
|
||||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
public class PersistentChunkSendProcessor extends ChunkSendProcessor {
|
|
||||||
private final Long2ObjectLinkedOpenHashMap<Character> current;
|
|
||||||
@Nullable
|
|
||||||
private Long2ObjectLinkedOpenHashMap<Character> previous;
|
|
||||||
private IChunkExtent queue;
|
|
||||||
|
|
||||||
public PersistentChunkSendProcessor(World world, PersistentChunkSendProcessor previous, Supplier<Collection<Player>> players) {
|
|
||||||
super(world, players);
|
|
||||||
this.current = new Long2ObjectLinkedOpenHashMap<>();
|
|
||||||
this.previous = previous != null ? previous.current : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void init(IChunkExtent queue) {
|
|
||||||
this.queue = queue;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IBlocks combine(IChunk chunk, IChunkGet get, IChunkSet set) {
|
|
||||||
int chunkX = chunk.getX();
|
|
||||||
int chunkZ = chunk.getZ();
|
|
||||||
long pair = MathMan.pairInt(chunkX, chunkZ);
|
|
||||||
char bitMask = (char) (set.hasBiomes() ? Character.MAX_VALUE : set.getBitMask());
|
|
||||||
synchronized (this) {
|
|
||||||
current.put(pair, (Character) bitMask);
|
|
||||||
if (previous != null) {
|
|
||||||
Character lastValue = previous.remove(pair);
|
|
||||||
if (lastValue != null) {
|
|
||||||
bitMask |= lastValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return new CombinedBlocks(get, set, bitMask);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void flush() {
|
|
||||||
clear(previous);
|
|
||||||
previous = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void clear() {
|
|
||||||
if (queue == null) {
|
|
||||||
throw new IllegalStateException("Queue is not provided");
|
|
||||||
}
|
|
||||||
clear(current);
|
|
||||||
current.clear();
|
|
||||||
queue = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void clear(Long2ObjectLinkedOpenHashMap<Character> current) {
|
|
||||||
if (current != null && !current.isEmpty()) {
|
|
||||||
Collection<Player> players = getPlayers().get();
|
|
||||||
for (Long2ObjectMap.Entry<Character> entry : current.long2ObjectEntrySet()) {
|
|
||||||
long pair = entry.getLongKey();
|
|
||||||
int chunkX = MathMan.unpairIntX(pair);
|
|
||||||
int chunkZ = MathMan.unpairIntY(pair);
|
|
||||||
BlockVector2 pos = BlockVector2.at(chunkX, chunkZ);
|
|
||||||
Supplier<IBlocks> chunk = () -> queue.getOrCreateChunk(pos.getX(), pos.getZ());
|
|
||||||
ChunkPacket packet = new ChunkPacket(pos.getX(), pos.getZ(), chunk, true);
|
|
||||||
char bitMask = entry.getValue();
|
|
||||||
if (players == null) {
|
|
||||||
getWorld().sendFakeChunk(null, packet);
|
|
||||||
} else {
|
|
||||||
players.forEach(player -> getWorld().sendFakeChunk(player, packet));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,44 +0,0 @@
|
|||||||
package com.boydti.fawe.jnbt.anvil;
|
|
||||||
|
|
||||||
import com.sk89q.jnbt.CompoundTag;
|
|
||||||
import com.sk89q.jnbt.NBTInputStream;
|
|
||||||
import com.sk89q.jnbt.NBTOutputStream;
|
|
||||||
import it.unimi.dsi.fastutil.io.FastBufferedInputStream;
|
|
||||||
import it.unimi.dsi.fastutil.io.FastBufferedOutputStream;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.zip.GZIPInputStream;
|
|
||||||
import java.util.zip.GZIPOutputStream;
|
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkArgument;
|
|
||||||
|
|
||||||
public class LevelDat {
|
|
||||||
private final File file;
|
|
||||||
private CompoundTag tag;
|
|
||||||
|
|
||||||
public LevelDat(File file) {
|
|
||||||
checkArgument(file.exists());
|
|
||||||
this.file = file;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void load() throws IOException {
|
|
||||||
try (NBTInputStream nis = new NBTInputStream(new FastBufferedInputStream(new GZIPInputStream(new FastBufferedInputStream(new FileInputStream(file)))))) {
|
|
||||||
this.tag = (CompoundTag) nis.readNamedTag().getTag();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void save() throws IOException {
|
|
||||||
if (this.tag != null) {
|
|
||||||
try (NBTOutputStream nos = new NBTOutputStream(new FastBufferedOutputStream(new GZIPOutputStream(new FastBufferedOutputStream(new FileOutputStream(file)))))) {
|
|
||||||
nos.writeNamedTag("", tag);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public CompoundTag getTag() {
|
|
||||||
return tag;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,651 +0,0 @@
|
|||||||
package com.boydti.fawe.jnbt.anvil;
|
|
||||||
|
|
||||||
import com.boydti.fawe.FaweCache;
|
|
||||||
import com.boydti.fawe.beta.Filter;
|
|
||||||
import com.boydti.fawe.beta.IChunk;
|
|
||||||
import com.boydti.fawe.beta.IChunkSet;
|
|
||||||
import com.boydti.fawe.beta.IQueueExtent;
|
|
||||||
import com.boydti.fawe.beta.implementation.filter.block.ChunkFilterBlock;
|
|
||||||
import com.boydti.fawe.beta.implementation.lighting.HeightMapType;
|
|
||||||
import com.boydti.fawe.jnbt.streamer.StreamDelegate;
|
|
||||||
import com.boydti.fawe.jnbt.streamer.ValueReader;
|
|
||||||
import com.boydti.fawe.object.collection.BitArray;
|
|
||||||
import com.boydti.fawe.object.collection.BlockVector3ChunkMap;
|
|
||||||
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.internal.util.LogManagerCompat;
|
|
||||||
import com.sk89q.worldedit.math.BlockVector3;
|
|
||||||
import com.sk89q.worldedit.regions.Region;
|
|
||||||
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.BaseBlock;
|
|
||||||
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 it.unimi.dsi.fastutil.io.FastByteArrayOutputStream;
|
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
|
|
||||||
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.Locale;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.concurrent.Future;
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
public class MCAChunk implements IChunk {
|
|
||||||
|
|
||||||
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
|
||||||
|
|
||||||
public final boolean[] hasSections = new boolean[16];
|
|
||||||
|
|
||||||
public boolean hasBiomes = false;
|
|
||||||
public final BiomeType[] biomes = new BiomeType[1024];
|
|
||||||
|
|
||||||
public final char[] blocks = new char[65536];
|
|
||||||
|
|
||||||
public final BlockVector3ChunkMap<CompoundTag> tiles = new BlockVector3ChunkMap<>();
|
|
||||||
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;
|
|
||||||
public int chunkZ;
|
|
||||||
|
|
||||||
private boolean createCopy = false;
|
|
||||||
|
|
||||||
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);
|
|
||||||
BitArray bitArray = new BitArray(bitsPerEntry, 4096, section.blocks);
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <V extends IChunk> void init(IQueueExtent<V> extent, int x, int z) {
|
|
||||||
if (x != chunkX || z != chunkZ) {
|
|
||||||
throw new UnsupportedOperationException("Not reuse capable");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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<Object> 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] = BiomeTypes.getLegacy(value));
|
|
||||||
|
|
||||||
return root;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getX() {
|
|
||||||
return chunkX;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getZ() {
|
|
||||||
return chunkZ;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasSection(int layer) {
|
|
||||||
return hasSections[layer];
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPosition(int X, int Z) {
|
|
||||||
this.chunkX = X;
|
|
||||||
this.chunkZ = Z;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MCAChunk reset() {
|
|
||||||
return this.reset(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public MCAChunk reset(boolean full) {
|
|
||||||
if (!tiles.isEmpty()) {
|
|
||||||
tiles.clear();
|
|
||||||
}
|
|
||||||
if (!entities.isEmpty()) {
|
|
||||||
entities.clear();
|
|
||||||
}
|
|
||||||
modified = 0;
|
|
||||||
deleted = false;
|
|
||||||
hasBiomes = false;
|
|
||||||
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 {
|
|
||||||
int[] blockToPalette = FaweCache.IMP.BLOCK_TO_PALETTE.get();
|
|
||||||
int[] paletteToBlock = FaweCache.IMP.PALETTE_TO_BLOCK.get();
|
|
||||||
long[] blockstates = FaweCache.IMP.BLOCK_STATES.get();
|
|
||||||
int[] blocksCopy = FaweCache.IMP.SECTION_BLOCKS.get();
|
|
||||||
|
|
||||||
nbtOut.writeNamedTagName("", NBTConstants.TYPE_COMPOUND);
|
|
||||||
nbtOut.writeNamedTag("DataVersion", 1631);
|
|
||||||
nbtOut.writeLazyCompoundTag("Level", out -> {
|
|
||||||
out.writeNamedTag("Status", "decorated");
|
|
||||||
out.writeNamedTag("xPos", getX());
|
|
||||||
out.writeNamedTag("zPos", getZ());
|
|
||||||
if (entities.isEmpty()) {
|
|
||||||
out.writeNamedEmptyList("Entities");
|
|
||||||
} else {
|
|
||||||
out.writeNamedTag("Entities", new ListTag(CompoundTag.class, new ArrayList<>(entities.values())));
|
|
||||||
}
|
|
||||||
if (tiles.isEmpty()) {
|
|
||||||
out.writeNamedEmptyList("TileEntities");
|
|
||||||
} else {
|
|
||||||
out.writeNamedTag("TileEntities", new ListTag(CompoundTag.class,
|
|
||||||
new ArrayList<>(tiles.values())));
|
|
||||||
}
|
|
||||||
out.writeNamedTag("InhabitedTime", inhabitedTime);
|
|
||||||
out.writeNamedTag("LastUpdate", lastUpdate);
|
|
||||||
if (hasBiomes) {
|
|
||||||
int type = NBTConstants.TYPE_BYTE_ARRAY;
|
|
||||||
out.writeNamedTagName("Biomes", type);
|
|
||||||
out.writeInt(biomes.length);
|
|
||||||
for (BiomeType biome : biomes) {
|
|
||||||
out.write(biome.getLegacyId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
int len = 0;
|
|
||||||
for (boolean hasSection : hasSections) {
|
|
||||||
if (hasSection) {
|
|
||||||
len++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
out.writeNamedTagName("Sections", NBTConstants.TYPE_LIST);
|
|
||||||
nbtOut.writeByte(NBTConstants.TYPE_COMPOUND);
|
|
||||||
nbtOut.writeInt(len);
|
|
||||||
|
|
||||||
for (int layer = 0; layer < hasSections.length; layer++) {
|
|
||||||
if (!hasSections[layer]) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
out.writeNamedTag("Y", (byte) layer);
|
|
||||||
|
|
||||||
int blockIndexStart = layer << 12;
|
|
||||||
int blockIndexEnd = blockIndexStart + 4096;
|
|
||||||
int num_palette = 0;
|
|
||||||
try {
|
|
||||||
for (int i = blockIndexStart, j = 0; i < blockIndexEnd; i++, j++) {
|
|
||||||
int ordinal = blocks[i];
|
|
||||||
int palette = blockToPalette[ordinal];
|
|
||||||
if (palette == Integer.MAX_VALUE) {
|
|
||||||
//BlockState state = BlockTypesCache.states[ordinal];
|
|
||||||
blockToPalette[ordinal] = palette = num_palette;
|
|
||||||
paletteToBlock[num_palette] = ordinal;
|
|
||||||
num_palette++;
|
|
||||||
}
|
|
||||||
blocksCopy[j] = palette;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < num_palette; i++) {
|
|
||||||
blockToPalette[paletteToBlock[i]] = Integer.MAX_VALUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
out.writeNamedTagName("Palette", NBTConstants.TYPE_LIST);
|
|
||||||
out.writeByte(NBTConstants.TYPE_COMPOUND);
|
|
||||||
out.writeInt(num_palette);
|
|
||||||
|
|
||||||
for (int i = 0; i < num_palette; i++) {
|
|
||||||
int ordinal = paletteToBlock[i];
|
|
||||||
BlockState state = BlockTypesCache.states[ordinal];
|
|
||||||
BlockType type = state.getBlockType();
|
|
||||||
out.writeNamedTag("Name", type.getId());
|
|
||||||
|
|
||||||
// Has no properties
|
|
||||||
if (type.getDefaultState() != state) {
|
|
||||||
// Write properties
|
|
||||||
out.writeNamedTagName("Properties", NBTConstants.TYPE_COMPOUND);
|
|
||||||
for (Property<?> property : type.getProperties()) {
|
|
||||||
String key = property.getName();
|
|
||||||
Object value = state.getState(property);
|
|
||||||
String valueStr = value.toString();
|
|
||||||
if (Character.isUpperCase(valueStr.charAt(0))) {
|
|
||||||
LOGGER.warn("Invalid uppercase value {}", value);
|
|
||||||
valueStr = valueStr.toLowerCase(Locale.ROOT);
|
|
||||||
}
|
|
||||||
out.writeNamedTag(key, valueStr);
|
|
||||||
}
|
|
||||||
out.writeEndTag();
|
|
||||||
}
|
|
||||||
out.writeEndTag();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// BlockStates
|
|
||||||
int bitsPerEntry = MathMan.log2nlz(num_palette - 1);
|
|
||||||
int blockBitArrayEnd = (bitsPerEntry * 4096) >> 6;
|
|
||||||
if (num_palette == 1) {
|
|
||||||
// Set a value, because minecraft needs it for some reason
|
|
||||||
blockstates[0] = 0;
|
|
||||||
blockBitArrayEnd = 1;
|
|
||||||
} else {
|
|
||||||
BitArray bitArray = new BitArray(bitsPerEntry, 4096, blockstates);
|
|
||||||
bitArray.fromRaw(blocksCopy);
|
|
||||||
}
|
|
||||||
|
|
||||||
out.writeNamedTagName("BlockStates", NBTConstants.TYPE_LONG_ARRAY);
|
|
||||||
out.writeInt(blockBitArrayEnd);
|
|
||||||
for (int i = 0; i < blockBitArrayEnd; i++) {
|
|
||||||
out.writeLong(blockstates[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* 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();
|
|
||||||
|
|
||||||
// cleanup
|
|
||||||
} catch (Throwable e) {
|
|
||||||
Arrays.fill(blockToPalette, Integer.MAX_VALUE);
|
|
||||||
e.printStackTrace();
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
nbtOut.writeEndTag();
|
|
||||||
}
|
|
||||||
|
|
||||||
public FastByteArrayOutputStream toBytes(byte[] buffer) throws IOException {
|
|
||||||
if (buffer == null) {
|
|
||||||
buffer = new byte[8192];
|
|
||||||
}
|
|
||||||
FastByteArrayOutputStream buffered = new FastByteArrayOutputStream(buffer);
|
|
||||||
try (NBTOutputStream nbtOut = new NBTOutputStream(buffered)) {
|
|
||||||
write(nbtOut);
|
|
||||||
}
|
|
||||||
return buffered;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getInhabitedTime() {
|
|
||||||
return inhabitedTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getLastUpdate() {
|
|
||||||
return lastUpdate;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setInhabitedTime(long inhabitedTime) {
|
|
||||||
this.inhabitedTime = inhabitedTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setLastUpdate(long lastUpdate) {
|
|
||||||
this.lastUpdate = lastUpdate;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDeleted(boolean deleted) {
|
|
||||||
setModified();
|
|
||||||
this.deleted = deleted;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isDeleted() {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getModified() {
|
|
||||||
return modified;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final void setModified() {
|
|
||||||
this.modified++;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getBitMask() {
|
|
||||||
int bitMask = 0;
|
|
||||||
for (int section = 0; section < hasSections.length; section++) {
|
|
||||||
if (hasSections[section]) {
|
|
||||||
bitMask += 1 << section;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return bitMask;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean setTile(int x, int y, int z, CompoundTag tile) {
|
|
||||||
setModified();
|
|
||||||
if (tile != null) {
|
|
||||||
tiles.put(x, y, z, tile);
|
|
||||||
} else {
|
|
||||||
return tiles.remove(x, y, z) != null;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public void setBlockLight(int x, int y, int z, int value) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public void setSkyLight(int x, int y, int z, int value) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public void setHeightMap(HeightMapType type, int[] heightMap) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public void removeSectionLighting(int layer, boolean sky) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public void setFullBright(int layer) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setLightLayer(int layer, char[] toSet) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setSkyLightLayer(int layer, char[] toSet) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setEntity(CompoundTag entityTag) {
|
|
||||||
setModified();
|
|
||||||
long least = entityTag.getLong("UUIDLeast");
|
|
||||||
long most = entityTag.getLong("UUIDMost");
|
|
||||||
entities.put(new UUID(most, least), entityTag);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BiomeType getBiomeType(int x, int y, int z) {
|
|
||||||
return this.biomes[(y >> 2) << 4 | (z >> 2) << 2 | x >> 2];
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BiomeType[] getBiomes() {
|
|
||||||
return this.biomes;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public char[][] getLight() {
|
|
||||||
return new char[0][];
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public char[][] getSkyLight() {
|
|
||||||
return new char[0][];
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean setBiome(BlockVector3 pos, BiomeType biome) {
|
|
||||||
return this.setBiome(pos.getX(), pos.getY(), pos.getZ(), biome);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean setBiome(int x, int y, int z, BiomeType biome) {
|
|
||||||
setModified();
|
|
||||||
biomes[(y >> 2) << 4 | (z >> 2) << 2 | x >> 2] = biome;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Set<CompoundTag> getEntities() {
|
|
||||||
return new HashSet<>(entities.values());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Map<BlockVector3, CompoundTag> getTiles() {
|
|
||||||
return tiles == null ? Collections.emptyMap() : tiles;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CompoundTag getTile(int x, int y, int z) {
|
|
||||||
if (tiles == null || tiles.isEmpty()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
short pair = MathMan.tripleBlockCoord(x, y, z);
|
|
||||||
return tiles.get(pair);
|
|
||||||
}
|
|
||||||
|
|
||||||
private final int getIndex(int x, int y, int z) {
|
|
||||||
return x | (z << 4) | (y << 8);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getBlockOrdinal(int x, int y, int z) {
|
|
||||||
return blocks[x | (z << 4) | (y << 8)];
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BlockState getBlock(int x, int y, int z) {
|
|
||||||
int ordinal = getBlockOrdinal(x, y, z);
|
|
||||||
return BlockState.getFromOrdinal(ordinal);
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO implement lighting
|
|
||||||
@Override public int getSkyLight(int x, int y, int z) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public int getEmmittedLight(int x, int y, int z) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public int[] getHeightMap(HeightMapType type) {
|
|
||||||
return new int[256];
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BaseBlock getFullBlock(int x, int y, int z) {
|
|
||||||
BlockState block = getBlock(x, y, z);
|
|
||||||
return block.toBaseBlock(this, x, y, z);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Set<UUID> getEntityRemoves() {
|
|
||||||
return new HashSet<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <B extends BlockStateHolder<B>> boolean setBlock(int x, int y, int z, B holder) {
|
|
||||||
setBlock(x, y, z, holder.getOrdinalChar());
|
|
||||||
holder.applyTileEntity(this, x, y, z);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setBlocks(int layer, char[] data) {
|
|
||||||
int offset = layer << 12;
|
|
||||||
System.arraycopy(data, 0, blocks, offset, 4096);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public char[] load(int layer) {
|
|
||||||
char[] tmp = FaweCache.IMP.SECTION_BITS_TO_CHAR.get();
|
|
||||||
int offset = layer << 12;
|
|
||||||
System.arraycopy(blocks, offset, tmp, 0, 4096);
|
|
||||||
return tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setBlock(int x, int y, int z, char ordinal) {
|
|
||||||
blocks[getIndex(x, y, z)] = ordinal;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setBiome(BiomeType biome) {
|
|
||||||
Arrays.fill(this.biomes, biome);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void removeEntity(UUID uuid) {
|
|
||||||
entities.remove(uuid);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean trim(boolean aggressive) {
|
|
||||||
return isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean trim(boolean aggressive, int layer) {
|
|
||||||
return hasSection(layer);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CompoundTag getEntity(UUID uuid) {
|
|
||||||
return this.entities.get(uuid);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public void setCreateCopy(boolean createCopy) {
|
|
||||||
this.createCopy = createCopy;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public boolean isCreateCopy() {
|
|
||||||
return createCopy;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setLightingToGet(char[][] lighting) {}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setSkyLightingToGet(char[][] lighting) {}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setHeightmapToGet(HeightMapType type, int[] data) {}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Future call(IChunkSet set, Runnable finalize) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void filterBlocks(Filter filter, ChunkFilterBlock block, @Nullable Region region, boolean full) {
|
|
||||||
try {
|
|
||||||
block.filter(this, this, this, filter, region, full);
|
|
||||||
} finally {
|
|
||||||
filter.finishChunk(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,715 +0,0 @@
|
|||||||
package com.boydti.fawe.jnbt.anvil;
|
|
||||||
|
|
||||||
import com.boydti.fawe.FaweCache;
|
|
||||||
import com.boydti.fawe.beta.Trimable;
|
|
||||||
import com.boydti.fawe.beta.implementation.IChunkExtent;
|
|
||||||
import com.boydti.fawe.beta.implementation.processors.ExtentBatchProcessorHolder;
|
|
||||||
import com.boydti.fawe.object.RunnableVal4;
|
|
||||||
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.CompoundTag;
|
|
||||||
import com.sk89q.jnbt.NBTInputStream;
|
|
||||||
import com.sk89q.worldedit.WorldEditException;
|
|
||||||
import com.sk89q.worldedit.internal.util.LogManagerCompat;
|
|
||||||
import com.sk89q.worldedit.math.BlockVector3;
|
|
||||||
import com.sk89q.worldedit.world.World;
|
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
|
||||||
import it.unimi.dsi.fastutil.io.FastByteArrayOutputStream;
|
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
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.ForkJoinTask;
|
|
||||||
import java.util.concurrent.Future;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
import java.util.stream.IntStream;
|
|
||||||
import java.util.zip.Deflater;
|
|
||||||
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 extends ExtentBatchProcessorHolder implements Trimable, IChunkExtent {
|
|
||||||
|
|
||||||
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
|
||||||
|
|
||||||
private static Field fieldBuf2;
|
|
||||||
|
|
||||||
static {
|
|
||||||
try {
|
|
||||||
fieldBuf2 = InflaterInputStream.class.getDeclaredField("buf");
|
|
||||||
fieldBuf2.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;
|
|
||||||
private int Z;
|
|
||||||
private MCAChunk[] chunks;
|
|
||||||
private boolean[] chunkInitialized;
|
|
||||||
private Object[] locks;
|
|
||||||
private Deflater deflater = new Deflater(1, false);
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BlockVector3 getMinimumPoint() {
|
|
||||||
return BlockVector3.at(this.X << 9, 0, this.Z << 9);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BlockVector3 getMaximumPoint() {
|
|
||||||
return BlockVector3.at((this.X << 9) + 511, FaweCache.IMP.WORLD_MAX_Y, (this.Z << 9) + 511);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean setTile(int x, int y, int z, CompoundTag tile) throws WorldEditException {
|
|
||||||
// final IChunk chunk = getChunk(x >> 4, z >> 4);
|
|
||||||
// return chunk.setTile(x & 15, y, z & 15, tile);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MCAChunk getOrCreateChunk(int chunkX, int chunkZ) {
|
|
||||||
try {
|
|
||||||
return getChunk(chunkX, chunkZ);
|
|
||||||
} catch (IOException e) {
|
|
||||||
// TODO generate?
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
//TODO multithreaded
|
|
||||||
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 {
|
|
||||||
LOGGER.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 ignored) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 FastByteArrayInputStream 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 = FaweCache.IMP.BYTE_BUFFER_VAR.get(size);
|
|
||||||
raf.readFully(data, 0, size);
|
|
||||||
FastByteArrayInputStream result = new FastByteArrayInputStream(data, 0, size);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private NBTInputStream getChunkIS(int offset) throws IOException {
|
|
||||||
try {
|
|
||||||
return getChunkIS(getChunkCompressedBytes(offset));
|
|
||||||
} catch (IllegalAccessException unlikely) {
|
|
||||||
unlikely.printStackTrace();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private NBTInputStream getChunkIS(InputStream is) throws IllegalAccessException {
|
|
||||||
InflaterInputStream iis = new InflaterInputStream(is, new Inflater(), 1);
|
|
||||||
fieldBuf2.set(iis, FaweCache.IMP.BYTE_BUFFER_8192.get());
|
|
||||||
BufferedInputStream bis = new BufferedInputStream(iis);
|
|
||||||
NBTInputStream nis = new NBTInputStream(bis);
|
|
||||||
return nis;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @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 = (int) IntStream.range(0, chunks.length)
|
|
||||||
.filter(i -> chunks[i] != null && this.chunkInitialized[i]).count();
|
|
||||||
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 FastByteArrayOutputStream toBytes(MCAChunk chunk) throws IOException {
|
|
||||||
if (chunk.isDeleted()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
byte[] writeBuffer = FaweCache.IMP.BYTE_BUFFER_VAR.get(4096);
|
|
||||||
FastByteArrayOutputStream uncompressed = chunk.toBytes(writeBuffer);
|
|
||||||
if (uncompressed.array.length > writeBuffer.length) {
|
|
||||||
FaweCache.IMP.BYTE_BUFFER_VAR.set(uncompressed.array);
|
|
||||||
}
|
|
||||||
writeBuffer = uncompressed.array;
|
|
||||||
byte[] buffer = FaweCache.IMP.BYTE_BUFFER_8192.get();
|
|
||||||
int length = uncompressed.length;
|
|
||||||
uncompressed.reset();
|
|
||||||
// cheat, reusing the same buffer to read/write
|
|
||||||
int compressedLength = MainUtil.compress(uncompressed.array, length, buffer, uncompressed, deflater);
|
|
||||||
return uncompressed;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void writeSafe(RandomAccessFile raf, int offset, byte[] data, int length) throws IOException {
|
|
||||||
int len = length + 5;
|
|
||||||
raf.seek(offset);
|
|
||||||
if (raf.length() - offset < len) {
|
|
||||||
raf.setLength(((offset + len + 4095) / 4096) * 4096);
|
|
||||||
}
|
|
||||||
// Length of remaining data
|
|
||||||
raf.writeInt(length + 1);
|
|
||||||
// Compression type
|
|
||||||
raf.write(2);
|
|
||||||
raf.write(data, 0, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
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<Future<byte[]>> compressedMap = new Int2ObjectOpenHashMap<>();
|
|
||||||
// The data of each chunk that needs to be moved
|
|
||||||
final Int2ObjectOpenHashMap<Future<byte[]>> append = new Int2ObjectOpenHashMap<>();
|
|
||||||
// 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;
|
|
||||||
|
|
||||||
|
|
||||||
boolean modified = false;
|
|
||||||
for (int i = 0; i < chunks.length; i++) {
|
|
||||||
if (this.chunkInitialized[i]) {
|
|
||||||
MCAChunk chunk = chunks[i];
|
|
||||||
if (chunk != null && chunk.isModified() && !chunk.isDeleted()) {
|
|
||||||
modified = true;
|
|
||||||
ForkJoinTask<byte[]> future = pool.submit(() -> {
|
|
||||||
FastByteArrayOutputStream compressed = toBytes(chunk);
|
|
||||||
return Arrays.copyOf(compressed.array, compressed.length);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//
|
|
||||||
// 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 (!modified) {
|
|
||||||
// Not modified, do nothing
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// If any changes were detected
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
Future<byte[]> future = null;
|
|
||||||
byte[] newBytes = relocate.get(pair);
|
|
||||||
int newBytesLength = 0;
|
|
||||||
|
|
||||||
// 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 {
|
|
||||||
future = compressedMap.get(pair);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// The chunk needs to be moved, fetch the data if necessary
|
|
||||||
future = compressedMap.get(pair);
|
|
||||||
if (future == null) {
|
|
||||||
if (cached == null || !cached.isDeleted()) {
|
|
||||||
FastByteArrayInputStream result = getChunkCompressedBytes(getOffset(cx, cz));
|
|
||||||
newBytes = result.array;
|
|
||||||
newBytesLength = result.length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
newBytesLength = newBytes.length;
|
|
||||||
}
|
|
||||||
if (future != null) {
|
|
||||||
newBytes = future.get();
|
|
||||||
newBytesLength = newBytes.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newBytes == null) {
|
|
||||||
writeHeader(raf, cx, cz, 0, 0, false);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The length to be written (compressed data + 5 byte chunk header)
|
|
||||||
int len = newBytesLength + 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()) {
|
|
||||||
FastByteArrayInputStream tmp = getChunkCompressedBytes(nextOffset2);
|
|
||||||
byte[] nextBytes = Arrays.copyOf(tmp.array, tmp.length);
|
|
||||||
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, newBytesLength);
|
|
||||||
// Write the location data (beginning of file)
|
|
||||||
writeHeader(raf, cx, cz, start >> 12, newSize, true);
|
|
||||||
|
|
||||||
written = start + newBytesLength + 5;
|
|
||||||
start += newSize << 12;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write all the chunks which need to be appended
|
|
||||||
if (!append.isEmpty()) {
|
|
||||||
for (Int2ObjectMap.Entry<Future<byte[]>> entry : append.int2ObjectEntrySet()) {
|
|
||||||
int pair = entry.getIntKey();
|
|
||||||
short cx = MathMan.unpairX(pair);
|
|
||||||
short cz = MathMan.unpairY(pair);
|
|
||||||
byte[] bytes = entry.getValue().get();
|
|
||||||
int len = bytes.length + 5;
|
|
||||||
int newSize = (len + 4095) >> 12;
|
|
||||||
writeSafe(raf, start, bytes, bytes.length);
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,109 +0,0 @@
|
|||||||
package com.boydti.fawe.jnbt.anvil;
|
|
||||||
|
|
||||||
import com.boydti.fawe.beta.IChunkGet;
|
|
||||||
import com.boydti.fawe.beta.implementation.packet.ChunkPacket;
|
|
||||||
import com.sk89q.jnbt.CompoundTag;
|
|
||||||
import com.sk89q.worldedit.EditSession;
|
|
||||||
import com.sk89q.worldedit.MaxChangedBlocksException;
|
|
||||||
import com.sk89q.worldedit.WorldEditException;
|
|
||||||
import com.sk89q.worldedit.blocks.BaseItemStack;
|
|
||||||
import com.sk89q.worldedit.entity.Player;
|
|
||||||
import com.sk89q.worldedit.math.BlockVector3;
|
|
||||||
import com.sk89q.worldedit.math.Vector3;
|
|
||||||
import com.sk89q.worldedit.regions.Region;
|
|
||||||
import com.sk89q.worldedit.util.SideEffect;
|
|
||||||
import com.sk89q.worldedit.util.SideEffectSet;
|
|
||||||
import com.sk89q.worldedit.util.TreeGenerator;
|
|
||||||
import com.sk89q.worldedit.world.AbstractWorld;
|
|
||||||
import com.sk89q.worldedit.world.block.BlockState;
|
|
||||||
import com.sk89q.worldedit.world.block.BlockStateHolder;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Set;
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkArgument;
|
|
||||||
|
|
||||||
public class MCAWorld extends AbstractWorld {
|
|
||||||
private final File path;
|
|
||||||
|
|
||||||
public MCAWorld(File path) {
|
|
||||||
checkArgument(path.isDirectory());
|
|
||||||
this.path = path;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getName() {
|
|
||||||
return path.getName();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <B extends BlockStateHolder<B>> boolean setBlock(BlockVector3 position, B block, SideEffectSet sideEffects) throws WorldEditException {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean setTile(int x, int y, int z, CompoundTag tile) throws WorldEditException {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean notifyAndLightBlock(BlockVector3 position, BlockState previousType) throws WorldEditException {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Set<SideEffect> applySideEffects(BlockVector3 position, BlockState previousType, SideEffectSet sideEffectSet) throws WorldEditException {
|
|
||||||
return SideEffectSet.none().getSideEffectsToApply();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean clearContainerBlockContents(BlockVector3 position) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void dropItem(Vector3 position, BaseItemStack item) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void simulateBlockMine(BlockVector3 position) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean regenerate(Region region, EditSession editSession) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean generateTree(TreeGenerator.TreeType type, EditSession editSession, BlockVector3 position) throws MaxChangedBlocksException {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BlockVector3 getSpawnPosition() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void refreshChunk(int chunkX, int chunkZ) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IChunkGet get(int x, int z) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sendFakeChunk(@Nullable Player player, ChunkPacket packet) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void flush() {}
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
package com.boydti.fawe.jnbt.anvil.mcatest;
|
|
||||||
|
|
||||||
import com.sk89q.worldedit.extent.clipboard.Clipboard;
|
|
||||||
import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormats;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
public class MCATest {
|
|
||||||
public MCATest() throws IOException {
|
|
||||||
File file = new File("plugins/FastAsyncWorldEdit/tobitower.schematic");
|
|
||||||
Clipboard loaded = ClipboardFormats.findByFile(file).load(file);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +1,6 @@
|
|||||||
package com.boydti.fawe.object.brush;
|
package com.boydti.fawe.object.brush;
|
||||||
|
|
||||||
import com.boydti.fawe.config.Caption;
|
import com.boydti.fawe.config.Caption;
|
||||||
import com.boydti.fawe.object.brush.visualization.VisualExtent;
|
|
||||||
import com.sk89q.worldedit.EditSession;
|
import com.sk89q.worldedit.EditSession;
|
||||||
import com.sk89q.worldedit.MaxChangedBlocksException;
|
import com.sk89q.worldedit.MaxChangedBlocksException;
|
||||||
import com.sk89q.worldedit.WorldEditException;
|
import com.sk89q.worldedit.WorldEditException;
|
||||||
@ -35,16 +34,13 @@ public class CatenaryBrush implements Brush, ResettableTool {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void build(EditSession editSession, BlockVector3 pos2, final Pattern pattern, double size) throws MaxChangedBlocksException {
|
public void build(EditSession editSession, BlockVector3 pos2, final Pattern pattern, double size) throws MaxChangedBlocksException {
|
||||||
boolean visual = editSession.getExtent() instanceof VisualExtent;
|
|
||||||
Player player = editSession.getPlayer();
|
Player player = editSession.getPlayer();
|
||||||
if (player == null) {
|
if (player == null) {
|
||||||
return; //todo throw error
|
return; //todo throw error
|
||||||
}
|
}
|
||||||
if (pos1 == null || pos2.equals(pos1)) {
|
if (pos1 == null || pos2.equals(pos1)) {
|
||||||
if (!visual) {
|
|
||||||
pos1 = pos2;
|
pos1 = pos2;
|
||||||
player.print(Caption.of("fawe.worldedit.brush.brush.line.primary", pos2));
|
player.print(Caption.of("fawe.worldedit.brush.brush.line.primary", pos2));
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this.vertex == null) {
|
if (this.vertex == null) {
|
||||||
@ -67,7 +63,6 @@ public class CatenaryBrush implements Brush, ResettableTool {
|
|||||||
} catch (WorldEditException e) {
|
} catch (WorldEditException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
if (!visual) {
|
|
||||||
player.print(Caption.of("fawe.worldedit.brush.brush.line.secondary"));
|
player.print(Caption.of("fawe.worldedit.brush.brush.line.secondary"));
|
||||||
if (!select) {
|
if (!select) {
|
||||||
pos1 = null;
|
pos1 = null;
|
||||||
@ -75,7 +70,6 @@ public class CatenaryBrush implements Brush, ResettableTool {
|
|||||||
}
|
}
|
||||||
pos1 = pos2;
|
pos1 = pos2;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean reset() {
|
public boolean reset() {
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package com.boydti.fawe.object.brush;
|
package com.boydti.fawe.object.brush;
|
||||||
|
|
||||||
import com.boydti.fawe.config.Caption;
|
import com.boydti.fawe.config.Caption;
|
||||||
import com.boydti.fawe.object.brush.visualization.VisualExtent;
|
|
||||||
import com.boydti.fawe.object.clipboard.ResizableClipboardBuilder;
|
import com.boydti.fawe.object.clipboard.ResizableClipboardBuilder;
|
||||||
import com.boydti.fawe.object.function.NullRegionFunction;
|
import com.boydti.fawe.object.function.NullRegionFunction;
|
||||||
import com.boydti.fawe.object.function.mask.AbstractDelegateMask;
|
import com.boydti.fawe.object.function.mask.AbstractDelegateMask;
|
||||||
@ -56,9 +55,6 @@ public class CopyPastaBrush implements Brush, ResettableTool {
|
|||||||
}
|
}
|
||||||
ClipboardHolder clipboard = session.getExistingClipboard();
|
ClipboardHolder clipboard = session.getExistingClipboard();
|
||||||
if (clipboard == null) {
|
if (clipboard == null) {
|
||||||
if (editSession.getExtent() instanceof VisualExtent) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Mask mask = editSession.getMask();
|
Mask mask = editSession.getMask();
|
||||||
if (mask == null) {
|
if (mask == null) {
|
||||||
mask = Masks.alwaysTrue();
|
mask = Masks.alwaysTrue();
|
||||||
|
@ -4,7 +4,6 @@ import com.boydti.fawe.config.Caption;
|
|||||||
import com.boydti.fawe.object.brush.heightmap.HeightMap;
|
import com.boydti.fawe.object.brush.heightmap.HeightMap;
|
||||||
import com.boydti.fawe.object.brush.heightmap.RotatableHeightMap;
|
import com.boydti.fawe.object.brush.heightmap.RotatableHeightMap;
|
||||||
import com.boydti.fawe.object.brush.heightmap.ScalableHeightMap;
|
import com.boydti.fawe.object.brush.heightmap.ScalableHeightMap;
|
||||||
import com.boydti.fawe.object.brush.visualization.cfi.HeightMapMCAGenerator;
|
|
||||||
import com.boydti.fawe.object.exception.FaweException;
|
import com.boydti.fawe.object.exception.FaweException;
|
||||||
import com.boydti.fawe.util.MathMan;
|
import com.boydti.fawe.util.MathMan;
|
||||||
import com.sk89q.worldedit.EditSession;
|
import com.sk89q.worldedit.EditSession;
|
||||||
@ -74,84 +73,6 @@ public class HeightBrush implements Brush {
|
|||||||
HeightMap map = getHeightMap();
|
HeightMap map = getHeightMap();
|
||||||
map.setSize(size);
|
map.setSize(size);
|
||||||
|
|
||||||
Extent queue = editSession.getExtent();
|
|
||||||
// Optimized application of height map
|
|
||||||
if (queue instanceof HeightMapMCAGenerator) {
|
|
||||||
HeightMapMCAGenerator hmmg = (HeightMapMCAGenerator) queue;
|
|
||||||
|
|
||||||
byte[] metaHeight = hmmg.getMetaData().getMeta("PRECISION_HEIGHT");
|
|
||||||
if (metaHeight == null) {
|
|
||||||
hmmg.getMetaData().setMeta("PRECISION_HEIGHT", metaHeight = new byte[hmmg.getArea()]);
|
|
||||||
}
|
|
||||||
|
|
||||||
int bx = position.getBlockX();
|
|
||||||
int bz = position.getBlockZ();
|
|
||||||
|
|
||||||
int minIndex = -(size * 2) - 1;
|
|
||||||
int width = hmmg.getWidth();
|
|
||||||
|
|
||||||
int minX = Math.max(-size, -bx);
|
|
||||||
int minZ = Math.max(-size, -bz);
|
|
||||||
int maxX = Math.min(size, hmmg.getWidth() - 1 - bx);
|
|
||||||
int maxZ = Math.min(size, hmmg.getLength() - 1 - bz);
|
|
||||||
|
|
||||||
int zIndex = (bz + minZ) * width;
|
|
||||||
for (int z = minZ; z <= maxZ; z++, zIndex += width) {
|
|
||||||
int zz = bz + z;
|
|
||||||
int index = zIndex + (bx + minX);
|
|
||||||
if (index < minIndex) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (index >= metaHeight.length) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
for (int x = minX; x <= maxX; x++, index++) {
|
|
||||||
if (index < 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (index >= metaHeight.length) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
int xx = bx + x;
|
|
||||||
int currentBlockHeight = hmmg.getHeight(index);
|
|
||||||
int currentLayer = metaHeight[index] & 0xFF;
|
|
||||||
|
|
||||||
double addHeight = heightMap.getHeight(x, z) * yscale;
|
|
||||||
int addBlockHeight = (int) addHeight;
|
|
||||||
int addLayer = (int) ((addHeight - addBlockHeight) * 256);
|
|
||||||
|
|
||||||
int newLayer = addLayer + currentLayer;
|
|
||||||
int newBlockHeight = currentBlockHeight + addBlockHeight;
|
|
||||||
|
|
||||||
int newLayerAbs = MathMan.absByte(newLayer);
|
|
||||||
|
|
||||||
if (newLayerAbs >= 256) {
|
|
||||||
int newLayerBlocks = (newLayer >> 8);
|
|
||||||
newBlockHeight += newLayerBlocks;
|
|
||||||
newLayer -= newLayerBlocks << 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
hmmg.setHeight(index, newBlockHeight);
|
|
||||||
metaHeight[index] = (byte) newLayer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (smooth) {
|
|
||||||
BlockVector2 min = BlockVector2.at(Math.max(0, bx - size), Math.max(0, bz - size));
|
|
||||||
BlockVector2 max = BlockVector2.at(Math.min(hmmg.getWidth() - 1, bx + size), Math.min(hmmg.getLength() - 1, bz + size));
|
|
||||||
hmmg.smooth(min, max, 8, 1);
|
|
||||||
|
|
||||||
if (size > 20) {
|
|
||||||
int smoothSize = size + 8;
|
|
||||||
min = BlockVector2.at(Math.max(0, bx - smoothSize), Math.max(0, bz - smoothSize));
|
|
||||||
max = BlockVector2.at(Math.min(hmmg.getWidth() - 1, bx + smoothSize), Math.min(hmmg.getLength() - 1, bz + smoothSize));
|
|
||||||
hmmg.smooth(min, max, 1, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Mask mask = editSession.getMask();
|
Mask mask = editSession.getMask();
|
||||||
if (mask == Masks.alwaysTrue() || mask == Masks.alwaysTrue2D()) {
|
if (mask == Masks.alwaysTrue() || mask == Masks.alwaysTrue2D()) {
|
||||||
mask = null;
|
mask = null;
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package com.boydti.fawe.object.brush;
|
package com.boydti.fawe.object.brush;
|
||||||
|
|
||||||
import com.boydti.fawe.config.Caption;
|
import com.boydti.fawe.config.Caption;
|
||||||
import com.boydti.fawe.object.brush.visualization.VisualExtent;
|
|
||||||
import com.sk89q.worldedit.EditSession;
|
import com.sk89q.worldedit.EditSession;
|
||||||
import com.sk89q.worldedit.MaxChangedBlocksException;
|
import com.sk89q.worldedit.MaxChangedBlocksException;
|
||||||
import com.sk89q.worldedit.command.tool.brush.Brush;
|
import com.sk89q.worldedit.command.tool.brush.Brush;
|
||||||
@ -23,16 +22,12 @@ public class LineBrush implements Brush, ResettableTool {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void build(EditSession editSession, BlockVector3 position, Pattern pattern, double size) throws MaxChangedBlocksException {
|
public void build(EditSession editSession, BlockVector3 position, Pattern pattern, double size) throws MaxChangedBlocksException {
|
||||||
boolean visual = editSession.getExtent() instanceof VisualExtent;
|
|
||||||
if (pos1 == null) {
|
if (pos1 == null) {
|
||||||
if (!visual) {
|
|
||||||
pos1 = position;
|
pos1 = position;
|
||||||
editSession.getPlayer().print(Caption.of("fawe.worldedit.brush.brush.line.primary", position));
|
editSession.getPlayer().print(Caption.of("fawe.worldedit.brush.brush.line.primary", position));
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
editSession.drawLine(pattern, pos1, position, size, !shell, flat);
|
editSession.drawLine(pattern, pos1, position, size, !shell, flat);
|
||||||
if (!visual) {
|
|
||||||
editSession.getPlayer().print(Caption.of("fawe.worldedit.brush.brush.line.secondary"));
|
editSession.getPlayer().print(Caption.of("fawe.worldedit.brush.brush.line.secondary"));
|
||||||
if (!select) {
|
if (!select) {
|
||||||
pos1 = null;
|
pos1 = null;
|
||||||
@ -40,7 +35,6 @@ public class LineBrush implements Brush, ResettableTool {
|
|||||||
pos1 = position;
|
pos1 = position;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean reset() {
|
public boolean reset() {
|
||||||
|
@ -2,7 +2,6 @@ package com.boydti.fawe.object.brush;
|
|||||||
|
|
||||||
import com.boydti.fawe.FaweCache;
|
import com.boydti.fawe.FaweCache;
|
||||||
import com.boydti.fawe.config.Caption;
|
import com.boydti.fawe.config.Caption;
|
||||||
import com.boydti.fawe.object.brush.visualization.VisualExtent;
|
|
||||||
import com.boydti.fawe.object.mask.IdMask;
|
import com.boydti.fawe.object.mask.IdMask;
|
||||||
import com.boydti.fawe.object.visitor.DFSRecursiveVisitor;
|
import com.boydti.fawe.object.visitor.DFSRecursiveVisitor;
|
||||||
import com.sk89q.worldedit.EditSession;
|
import com.sk89q.worldedit.EditSession;
|
||||||
@ -53,11 +52,6 @@ public class SplineBrush implements Brush, ResettableTool {
|
|||||||
} else {
|
} else {
|
||||||
mask = new MaskIntersection(mask, new IdMask(editSession));
|
mask = new MaskIntersection(mask, new IdMask(editSession));
|
||||||
}
|
}
|
||||||
boolean visualization = editSession.getExtent() instanceof VisualExtent;
|
|
||||||
if (visualization && positionSets.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
int originalSize = numSplines;
|
|
||||||
boolean newPos = !position.equals(this.position);
|
boolean newPos = !position.equals(this.position);
|
||||||
this.position = position;
|
this.position = position;
|
||||||
if (newPos) {
|
if (newPos) {
|
||||||
@ -94,10 +88,8 @@ public class SplineBrush implements Brush, ResettableTool {
|
|||||||
}
|
}
|
||||||
this.positionSets.add(points);
|
this.positionSets.add(points);
|
||||||
player.print(Caption.of("fawe.worldedit.brush.spline.primary.2"));
|
player.print(Caption.of("fawe.worldedit.brush.spline.primary.2"));
|
||||||
if (!visualization) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (positionSets.size() < 2) {
|
if (positionSets.size() < 2) {
|
||||||
player.print(Caption.of("fawe.worldedit.brush.brush.spline.secondary.error"));
|
player.print(Caption.of("fawe.worldedit.brush.brush.spline.secondary.error"));
|
||||||
return;
|
return;
|
||||||
@ -110,7 +102,6 @@ public class SplineBrush implements Brush, ResettableTool {
|
|||||||
double tension = 0;
|
double tension = 0;
|
||||||
double bias = 0;
|
double bias = 0;
|
||||||
double continuity = 0;
|
double continuity = 0;
|
||||||
double quality = 10;
|
|
||||||
|
|
||||||
final List<Node> nodes = new ArrayList<>(centroids.size());
|
final List<Node> nodes = new ArrayList<>(centroids.size());
|
||||||
|
|
||||||
@ -121,7 +112,6 @@ public class SplineBrush implements Brush, ResettableTool {
|
|||||||
n.setContinuity(continuity);
|
n.setContinuity(continuity);
|
||||||
nodes.add(n);
|
nodes.add(n);
|
||||||
}
|
}
|
||||||
int samples = numSplines;
|
|
||||||
for (int i = 0; i < numSplines; i++) {
|
for (int i = 0; i < numSplines; i++) {
|
||||||
List<BlockVector3> currentSpline = new ArrayList<>();
|
List<BlockVector3> currentSpline = new ArrayList<>();
|
||||||
for (ArrayList<BlockVector3> points : positionSets) {
|
for (ArrayList<BlockVector3> points : positionSets) {
|
||||||
@ -132,14 +122,9 @@ public class SplineBrush implements Brush, ResettableTool {
|
|||||||
editSession.drawSpline(pattern, currentSpline, 0, 0, 0, 10, 0, true);
|
editSession.drawSpline(pattern, currentSpline, 0, 0, 0, 10, 0, true);
|
||||||
}
|
}
|
||||||
player.print(Caption.of("fawe.worldedit.brush.spline.secondary"));
|
player.print(Caption.of("fawe.worldedit.brush.spline.secondary"));
|
||||||
if (visualization) {
|
|
||||||
numSplines = originalSize;
|
|
||||||
positionSets.remove(positionSets.size() - 1);
|
|
||||||
} else {
|
|
||||||
positionSets.clear();
|
positionSets.clear();
|
||||||
numSplines = 0;
|
numSplines = 0;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private Vector3 getCentroid(Collection<BlockVector3> points) {
|
private Vector3 getCentroid(Collection<BlockVector3> points) {
|
||||||
MutableVector3 sum = new MutableVector3();
|
MutableVector3 sum = new MutableVector3();
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package com.boydti.fawe.object.brush;
|
package com.boydti.fawe.object.brush;
|
||||||
|
|
||||||
import com.boydti.fawe.config.Caption;
|
import com.boydti.fawe.config.Caption;
|
||||||
import com.boydti.fawe.object.brush.visualization.VisualExtent;
|
|
||||||
import com.boydti.fawe.object.collection.LocalBlockVectorSet;
|
import com.boydti.fawe.object.collection.LocalBlockVectorSet;
|
||||||
import com.boydti.fawe.util.MathMan;
|
import com.boydti.fawe.util.MathMan;
|
||||||
import com.sk89q.worldedit.EditSession;
|
import com.sk89q.worldedit.EditSession;
|
||||||
@ -36,7 +35,6 @@ public class SurfaceSpline implements Brush {
|
|||||||
@Override
|
@Override
|
||||||
public void build(EditSession editSession, BlockVector3 pos, Pattern pattern, double radius) throws MaxChangedBlocksException {
|
public void build(EditSession editSession, BlockVector3 pos, Pattern pattern, double radius) throws MaxChangedBlocksException {
|
||||||
int maxY = editSession.getMaxY();
|
int maxY = editSession.getMaxY();
|
||||||
boolean vis = editSession.getExtent() instanceof VisualExtent;
|
|
||||||
if (path.isEmpty() || !pos.equals(path.get(path.size() - 1))) {
|
if (path.isEmpty() || !pos.equals(path.get(path.size() - 1))) {
|
||||||
int max = editSession.getNearestSurfaceTerrainBlock(pos.getBlockX(), pos.getBlockZ(), pos.getBlockY(), 0, editSession.getMaxY());
|
int max = editSession.getNearestSurfaceTerrainBlock(pos.getBlockX(), pos.getBlockZ(), pos.getBlockY(), 0, editSession.getMaxY());
|
||||||
if (max == -1) {
|
if (max == -1) {
|
||||||
@ -44,10 +42,8 @@ public class SurfaceSpline implements Brush {
|
|||||||
}
|
}
|
||||||
path.add(BlockVector3.at(pos.getBlockX(), max, pos.getBlockZ()));
|
path.add(BlockVector3.at(pos.getBlockX(), max, pos.getBlockZ()));
|
||||||
editSession.getPlayer().print(Caption.of("fawe.worldedit.brush.spline.primary.2"));
|
editSession.getPlayer().print(Caption.of("fawe.worldedit.brush.spline.primary.2"));
|
||||||
if (!vis) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
final List<Node> nodes = new ArrayList<>(path.size());
|
final List<Node> nodes = new ArrayList<>(path.size());
|
||||||
final KochanekBartelsInterpolation interpol = new KochanekBartelsInterpolation();
|
final KochanekBartelsInterpolation interpol = new KochanekBartelsInterpolation();
|
||||||
|
|
||||||
@ -103,10 +99,8 @@ public class SurfaceSpline implements Brush {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
editSession.setBlocks(newSet, pattern);
|
editSession.setBlocks(newSet, pattern);
|
||||||
if (!vis) {
|
|
||||||
path.clear();
|
path.clear();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
editSession.getPlayer().print(Caption.of("fawe.worldedit.brush.spline.secondary"));
|
editSession.getPlayer().print(Caption.of("fawe.worldedit.brush.spline.secondary"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ package com.boydti.fawe.object.brush.sweep;
|
|||||||
|
|
||||||
import com.boydti.fawe.config.Caption;
|
import com.boydti.fawe.config.Caption;
|
||||||
import com.boydti.fawe.object.brush.ResettableTool;
|
import com.boydti.fawe.object.brush.ResettableTool;
|
||||||
import com.boydti.fawe.object.brush.visualization.VisualExtent;
|
|
||||||
import com.sk89q.worldedit.EditSession;
|
import com.sk89q.worldedit.EditSession;
|
||||||
import com.sk89q.worldedit.EmptyClipboardException;
|
import com.sk89q.worldedit.EmptyClipboardException;
|
||||||
import com.sk89q.worldedit.LocalSession;
|
import com.sk89q.worldedit.LocalSession;
|
||||||
@ -40,10 +39,6 @@ public class SweepBrush implements Brush, ResettableTool {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void build(EditSession editSession, BlockVector3 position, Pattern pattern, double size) throws MaxChangedBlocksException {
|
public void build(EditSession editSession, BlockVector3 position, Pattern pattern, double size) throws MaxChangedBlocksException {
|
||||||
boolean visualization = editSession.getExtent() instanceof VisualExtent;
|
|
||||||
if (visualization && positions.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean newPos = !position.equals(this.position);
|
boolean newPos = !position.equals(this.position);
|
||||||
this.position = position;
|
this.position = position;
|
||||||
|
@ -1,93 +0,0 @@
|
|||||||
package com.boydti.fawe.object.brush.visualization;
|
|
||||||
|
|
||||||
import com.sk89q.worldedit.EditSession;
|
|
||||||
import com.sk89q.worldedit.MaxChangedBlocksException;
|
|
||||||
import com.sk89q.worldedit.WorldEditException;
|
|
||||||
import com.sk89q.worldedit.blocks.BaseItemStack;
|
|
||||||
import com.sk89q.worldedit.math.BlockVector3;
|
|
||||||
import com.sk89q.worldedit.math.Vector3;
|
|
||||||
import com.sk89q.worldedit.regions.Region;
|
|
||||||
import com.sk89q.worldedit.util.TreeGenerator;
|
|
||||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
|
||||||
import com.sk89q.worldedit.world.biome.BiomeTypes;
|
|
||||||
import com.sk89q.worldedit.world.block.BlockStateHolder;
|
|
||||||
import com.sk89q.worldedit.world.weather.WeatherType;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
public abstract class ImmutableVirtualWorld implements VirtualWorld {
|
|
||||||
@Override
|
|
||||||
public int getMaxY() {
|
|
||||||
return 255;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean regenerateChunk(int x, int z, @Nullable BiomeType biome, @Nullable Long seed) {
|
|
||||||
return unsupported();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BiomeType getBiome(BlockVector3 position) {
|
|
||||||
return BiomeTypes.FOREST;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getName() {
|
|
||||||
return Integer.toString(hashCode());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <B extends BlockStateHolder<B>> boolean setBlock(BlockVector3 position, B block, boolean notifyAndLight) throws WorldEditException {
|
|
||||||
return setBlock(position, block);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getBlockLightLevel(BlockVector3 position) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean clearContainerBlockContents(BlockVector3 position) {
|
|
||||||
return unsupported();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void dropItem(Vector3 position, BaseItemStack item) {
|
|
||||||
unsupported();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean generateTree(TreeGenerator.TreeType type, EditSession editSession, BlockVector3 position) throws MaxChangedBlocksException {
|
|
||||||
return unsupported();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean regenerate(Region region, EditSession editSession) {
|
|
||||||
return unsupported();
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean unsupported() {
|
|
||||||
throw new UnsupportedOperationException("World is immutable");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <B extends BlockStateHolder<B>> boolean setBlock(BlockVector3 pt, B block) throws WorldEditException {
|
|
||||||
return unsupported();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void simulateBlockMine(BlockVector3 position) {
|
|
||||||
unsupported();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setWeather(WeatherType weatherType) {
|
|
||||||
unsupported();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setWeather(WeatherType weatherType, long duration) {
|
|
||||||
unsupported();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,46 +0,0 @@
|
|||||||
package com.boydti.fawe.object.brush.visualization;
|
|
||||||
|
|
||||||
import com.sk89q.worldedit.WorldEditException;
|
|
||||||
import com.sk89q.worldedit.entity.Player;
|
|
||||||
import com.sk89q.worldedit.event.platform.BlockInteractEvent;
|
|
||||||
import com.sk89q.worldedit.event.platform.PlayerInputEvent;
|
|
||||||
import com.sk89q.worldedit.math.BlockVector3;
|
|
||||||
import com.sk89q.worldedit.math.Vector3;
|
|
||||||
import com.sk89q.worldedit.world.SimpleWorld;
|
|
||||||
import com.sk89q.worldedit.world.block.BaseBlock;
|
|
||||||
import com.sk89q.worldedit.world.block.BlockStateHolder;
|
|
||||||
|
|
||||||
import java.io.Closeable;
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
public interface VirtualWorld extends SimpleWorld, Closeable {
|
|
||||||
Vector3 getOrigin();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
default BaseBlock getFullBlock(int x, int y, int z) {
|
|
||||||
return getBlock(x, y, z).toBaseBlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
int getMaxY();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
<B extends BlockStateHolder<B>> boolean setBlock(BlockVector3 pt, B block) throws WorldEditException;
|
|
||||||
|
|
||||||
Player getPlayer();
|
|
||||||
|
|
||||||
void update();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
default void close() throws IOException {
|
|
||||||
close(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void close(boolean update) throws IOException;
|
|
||||||
|
|
||||||
default void handleBlockInteract(Player player, BlockVector3 pos, BlockInteractEvent event) {
|
|
||||||
}
|
|
||||||
|
|
||||||
default void handlePlayerInput(Player player, PlayerInputEvent event) {
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,70 +0,0 @@
|
|||||||
package com.boydti.fawe.object.brush.visualization;
|
|
||||||
|
|
||||||
import com.boydti.fawe.beta.IQueueExtent;
|
|
||||||
import com.sk89q.worldedit.WorldEditException;
|
|
||||||
import com.sk89q.worldedit.entity.Player;
|
|
||||||
import com.sk89q.worldedit.extent.AbstractDelegateExtent;
|
|
||||||
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.world.biome.BiomeType;
|
|
||||||
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;
|
|
||||||
|
|
||||||
public class VisualExtent extends AbstractDelegateExtent {
|
|
||||||
public static final BlockType VISUALIZE_BLOCK_DEFAULT = BlockTypes.BLACK_STAINED_GLASS;
|
|
||||||
private final BlockType visualizeBlock;
|
|
||||||
private final Player player;
|
|
||||||
|
|
||||||
public VisualExtent(Extent parent, Player player) {
|
|
||||||
this(parent, player, VISUALIZE_BLOCK_DEFAULT);
|
|
||||||
}
|
|
||||||
|
|
||||||
public VisualExtent(Extent parent, Player player, BlockType visualizeBlock) {
|
|
||||||
super(parent);
|
|
||||||
this.visualizeBlock = visualizeBlock;
|
|
||||||
this.player = player;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean setBlock(BlockVector3 location, BlockStateHolder block) throws WorldEditException {
|
|
||||||
return setBlock(location.getBlockX(), location.getBlockY(), location.getBlockZ(), block);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean setBlock(int x, int y, int z, BlockStateHolder block) throws WorldEditException {
|
|
||||||
if (block.getMaterial().isAir()) {
|
|
||||||
return super.setBlock(x, y, z, block);
|
|
||||||
} else {
|
|
||||||
return super.setBlock(x, y, z, visualizeBlock.getDefaultState());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public Operation commit() {
|
|
||||||
IQueueExtent queue = (IQueueExtent) getExtent();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean setBiome(BlockVector2 position, BiomeType biome) {
|
|
||||||
// Do nothing
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean setBiome(BlockVector3 position, BiomeType biome) {
|
|
||||||
// Do nothing
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void clear() {
|
|
||||||
IQueueExtent queue = (IQueueExtent) getExtent();
|
|
||||||
queue.cancel();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
package com.boydti.fawe.object.brush.visualization;
|
|
||||||
|
|
||||||
public enum VisualMode {
|
|
||||||
NONE,
|
|
||||||
POINT,
|
|
||||||
OUTLINE
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
package com.boydti.fawe.object.brush.visualization;
|
|
||||||
|
|
||||||
import com.boydti.fawe.object.task.SingleThreadIntervalQueue;
|
|
||||||
import com.sk89q.worldedit.LocalSession;
|
|
||||||
import com.sk89q.worldedit.WorldEdit;
|
|
||||||
import com.sk89q.worldedit.command.tool.BrushTool;
|
|
||||||
import com.sk89q.worldedit.command.tool.Tool;
|
|
||||||
import com.sk89q.worldedit.entity.Player;
|
|
||||||
|
|
||||||
public class VisualQueue extends SingleThreadIntervalQueue<Player> {
|
|
||||||
|
|
||||||
public VisualQueue(int interval) {
|
|
||||||
super(interval);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void operate(Player player) {
|
|
||||||
LocalSession session = player.getSession();
|
|
||||||
Tool tool = session.getTool(player);
|
|
||||||
if (tool instanceof BrushTool) {
|
|
||||||
BrushTool brushTool = (BrushTool) tool;
|
|
||||||
if (brushTool.getVisualMode() != VisualMode.NONE) {
|
|
||||||
try {
|
|
||||||
brushTool.visualize(BrushTool.BrushAction.PRIMARY, player);
|
|
||||||
} catch (Throwable e) {
|
|
||||||
WorldEdit.getInstance().getPlatformManager().handleThrowable(e, player);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,146 +0,0 @@
|
|||||||
package com.boydti.fawe.object.brush.visualization.cfi;
|
|
||||||
|
|
||||||
import com.boydti.fawe.Fawe;
|
|
||||||
import com.boydti.fawe.util.MathMan;
|
|
||||||
import com.boydti.fawe.util.TextureUtil;
|
|
||||||
import com.sk89q.worldedit.world.block.BlockID;
|
|
||||||
import com.sk89q.worldedit.world.block.BlockTypes;
|
|
||||||
|
|
||||||
import java.awt.image.BufferedImage;
|
|
||||||
import java.awt.image.DataBufferInt;
|
|
||||||
import java.util.concurrent.ForkJoinPool;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
public final class CFIDrawer {
|
|
||||||
private final HeightMapMCAGenerator gen;
|
|
||||||
private final TextureUtil tu;
|
|
||||||
private final ForkJoinPool pool;
|
|
||||||
|
|
||||||
public CFIDrawer(HeightMapMCAGenerator generator, TextureUtil textureUtil) {
|
|
||||||
this.gen = generator;
|
|
||||||
this.tu = textureUtil;
|
|
||||||
this.pool = new ForkJoinPool();
|
|
||||||
}
|
|
||||||
|
|
||||||
public CFIDrawer(HeightMapMCAGenerator generator) {
|
|
||||||
this(generator, Fawe.get().getCachedTextureUtil(false, 0, 100));
|
|
||||||
}
|
|
||||||
|
|
||||||
public BufferedImage draw() {
|
|
||||||
BufferedImage img = new BufferedImage(gen.getWidth(), gen.getLength(), BufferedImage.TYPE_INT_RGB);
|
|
||||||
final char[] overlay = gen.overlay == null ? gen.floor.get() : gen.overlay.get();
|
|
||||||
final char[] floor = gen.floor.get();
|
|
||||||
final char[] main = gen.main.get();
|
|
||||||
final byte[] heights = gen.heights.get();
|
|
||||||
final byte[] biomes = gen.biomes.get();
|
|
||||||
final int waterHeight = gen.primitives.waterHeight;
|
|
||||||
final int width = gen.getWidth();
|
|
||||||
final int length = gen.getLength();
|
|
||||||
|
|
||||||
int[] raw = ((DataBufferInt) img.getRaster().getDataBuffer()).getData();
|
|
||||||
|
|
||||||
int parallelism = pool.getParallelism();
|
|
||||||
int size = (heights.length + parallelism - 1) / parallelism;
|
|
||||||
for (int i = 0; i < parallelism; i++) {
|
|
||||||
int start = i * size;
|
|
||||||
int end = Math.min(heights.length, start + size);
|
|
||||||
pool.submit((Runnable) () -> {
|
|
||||||
for (int index = start; index < end; index++) {
|
|
||||||
int height = (heights[index] & 0xFF);
|
|
||||||
char ordinal;
|
|
||||||
if ((ordinal = overlay[index]) == 0) {
|
|
||||||
height--;
|
|
||||||
ordinal = floor[index];
|
|
||||||
if (ordinal == 0) {
|
|
||||||
height--;
|
|
||||||
ordinal = main[index];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// draw ordinal
|
|
||||||
int color;
|
|
||||||
switch (ordinal >> 4) {
|
|
||||||
case 2:
|
|
||||||
color = getAverageBiomeColor(biomes, width, index);
|
|
||||||
break;
|
|
||||||
case 78:
|
|
||||||
color = (0xDD << 16) + (0xDD << 8) + (0xDD << 0);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
color = tu.getColor(BlockTypes.getFromStateOrdinal(ordinal));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
int slope = getSlope(heights, width, index, height);
|
|
||||||
if (slope != 0) {
|
|
||||||
slope = (slope << 3) + (slope << 2);
|
|
||||||
int r = MathMan.clamp(((color >> 16) & 0xFF) + slope, 0, 255);
|
|
||||||
int g = MathMan.clamp(((color >> 8) & 0xFF) + slope, 0, 255);
|
|
||||||
int b = MathMan.clamp(((color >> 0) & 0xFF) + slope, 0, 255);
|
|
||||||
color = (r << 16) + (g << 8) + (b << 0);
|
|
||||||
}
|
|
||||||
if (height + 1 < waterHeight) {
|
|
||||||
char waterId = gen.primitives.waterOrdinal;
|
|
||||||
int waterColor = 0;
|
|
||||||
switch (waterId) {
|
|
||||||
case BlockID.WATER:
|
|
||||||
color = tu.averageColor((0x11 << 16) + (0x66 << 8) + (0xCC), color);
|
|
||||||
break;
|
|
||||||
case BlockID.LAVA:
|
|
||||||
color = (0xCC << 16) + (0x33 << 8) + (0);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
color = tu.getColor(BlockTypes.getFromStateOrdinal(waterId));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
raw[index] = color;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
pool.awaitQuiescence(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
|
|
||||||
pool.shutdownNow();
|
|
||||||
return img;
|
|
||||||
}
|
|
||||||
|
|
||||||
private final int getAverageBiomeColor(byte[] biomes, int width, int index) {
|
|
||||||
int c0 = tu.getBiome(biomes[index] & 0xFF).grassCombined;
|
|
||||||
int c2 = getBiome(biomes, index + 1 + width, index);
|
|
||||||
int c1 = getBiome(biomes, index - 1 - width, index);
|
|
||||||
// int c3 = getBiome(biomes, index + width, index);
|
|
||||||
// int c4 = getBiome(biomes, index - width, index);
|
|
||||||
int r = ((c0 >> 16) & 0xFF) + ((c1 >> 16) & 0xFF) + ((c2 >> 16) & 0xFF); // + ((c3 >> 16) & 0xFF) + ((c4 >> 16) & 0xFF);
|
|
||||||
int g = ((c0 >> 8) & 0xFF) + ((c1 >> 8) & 0xFF) + ((c2 >> 8) & 0xFF); // + ((c3 >> 8) & 0xFF) + ((c4 >> 8) & 0xFF);
|
|
||||||
int b = ((c0) & 0xFF) + ((c1) & 0xFF) + ((c2) & 0xFF); // + ((c3) & 0xFF) + ((c4) & 0xFF);
|
|
||||||
r = r * 85 >> 8;
|
|
||||||
g = g * 85 >> 8;
|
|
||||||
b = b * 85 >> 8;
|
|
||||||
return (r << 16) + (g << 8) + (b);
|
|
||||||
}
|
|
||||||
|
|
||||||
private final int getBiome(byte[] biomes, int newIndex, int index) {
|
|
||||||
if (newIndex < 0 || newIndex >= biomes.length) {
|
|
||||||
newIndex = index;
|
|
||||||
}
|
|
||||||
int biome = biomes[newIndex] & 0xFF;
|
|
||||||
return tu.getBiome(biome).grassCombined;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getSlope(byte[] heights, int width, int index, int height) {
|
|
||||||
return (
|
|
||||||
+ getHeight(heights, index + 1, height)
|
|
||||||
// + getHeight(heights, index + width, height)
|
|
||||||
+ getHeight(heights, index + width + 1, height)
|
|
||||||
- getHeight(heights, index - 1, height)
|
|
||||||
// - getHeight(heights, index - width, height)
|
|
||||||
- getHeight(heights, index - width - 1, height)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getHeight(byte[] heights, int index, int height) {
|
|
||||||
if (index < 0 || index >= heights.length) {
|
|
||||||
return height;
|
|
||||||
}
|
|
||||||
return heights[index] & 0xFF;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
Datei-Diff unterdrückt, da er zu groß ist
Diff laden
@ -1,210 +0,0 @@
|
|||||||
package com.boydti.fawe.object.brush.visualization.cfi;
|
|
||||||
|
|
||||||
import com.boydti.fawe.jnbt.anvil.MCAChunk;
|
|
||||||
import com.boydti.fawe.object.collection.CleanableThreadLocal;
|
|
||||||
import com.boydti.fawe.object.io.BufferedRandomAccessFile;
|
|
||||||
import com.boydti.fawe.util.MainUtil;
|
|
||||||
import com.sk89q.worldedit.extent.Extent;
|
|
||||||
import com.sk89q.worldedit.world.block.BlockID;
|
|
||||||
import it.unimi.dsi.fastutil.io.FastByteArrayOutputStream;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.concurrent.ForkJoinPool;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.zip.Deflater;
|
|
||||||
|
|
||||||
public abstract class MCAWriter implements Extent {
|
|
||||||
private File folder;
|
|
||||||
private final int length;
|
|
||||||
private final int width;
|
|
||||||
private final int area;
|
|
||||||
private int OX;
|
|
||||||
private int OZ;
|
|
||||||
|
|
||||||
|
|
||||||
public MCAWriter(int width, int length, File regionFolder) {
|
|
||||||
this.folder = regionFolder;
|
|
||||||
this.width = width;
|
|
||||||
this.length = length;
|
|
||||||
this.area = width * length;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final File getFolder() {
|
|
||||||
return folder;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setFolder(File folder) {
|
|
||||||
this.folder = folder;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final int getWidth() {
|
|
||||||
return width;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final int getLength() {
|
|
||||||
return length;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the MCA file offset (each mca file is 512 blocks)
|
|
||||||
* - A negative value will shift the map negative
|
|
||||||
* - This only applies to generation, not block get/set
|
|
||||||
*
|
|
||||||
* @param mcaOX
|
|
||||||
* @param mcaOZ
|
|
||||||
*/
|
|
||||||
public void setMCAOffset(int mcaOX, int mcaOZ) {
|
|
||||||
OX = mcaOX << 9;
|
|
||||||
OZ = mcaOZ << 9;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getOffsetX() {
|
|
||||||
return OX;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getOffsetZ() {
|
|
||||||
return OZ;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final int getArea() {
|
|
||||||
return area;
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract boolean shouldWrite(int chunkX, int chunkZ);
|
|
||||||
|
|
||||||
public abstract MCAChunk write(MCAChunk input, int startX, int endX, int startZ, int endZ);
|
|
||||||
|
|
||||||
private static CleanableThreadLocal<MCAChunk> createCache() {
|
|
||||||
return new CleanableThreadLocal<>(() -> {
|
|
||||||
MCAChunk chunk = new MCAChunk();
|
|
||||||
Arrays.fill(chunk.blocks, (char) BlockID.AIR);
|
|
||||||
// Arrays.fill(chunk.skyLight, (byte) 255);
|
|
||||||
return chunk;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void generate() throws IOException {
|
|
||||||
if (!folder.exists()) {
|
|
||||||
folder.mkdirs();
|
|
||||||
}
|
|
||||||
final ForkJoinPool pool = new ForkJoinPool();
|
|
||||||
int tcx = (width - 1) >> 4;
|
|
||||||
int tcz = (length - 1) >> 4;
|
|
||||||
try (CleanableThreadLocal<MCAChunk> chunkStore = createCache()) {
|
|
||||||
final ThreadLocal<byte[]> byteStore1 = ThreadLocal.withInitial(() -> new byte[500000]);
|
|
||||||
final ThreadLocal<byte[]> byteStore2 = ThreadLocal.withInitial(() -> new byte[500000]);
|
|
||||||
final ThreadLocal<Deflater> deflateStore = ThreadLocal
|
|
||||||
.withInitial(() -> new Deflater(Deflater.BEST_SPEED, false));
|
|
||||||
byte[] fileBuf = new byte[1 << 16];
|
|
||||||
int mcaXMin = 0;
|
|
||||||
int mcaZMin = 0;
|
|
||||||
int mcaXMax = mcaXMin + ((width - 1) >> 9);
|
|
||||||
int mcaZMax = mcaZMin + ((length - 1) >> 9);
|
|
||||||
|
|
||||||
final byte[] header = new byte[4096];
|
|
||||||
|
|
||||||
for (int mcaZ = mcaXMin; mcaZ <= mcaZMax; mcaZ++) {
|
|
||||||
for (int mcaX = mcaXMin; mcaX <= mcaXMax; mcaX++) {
|
|
||||||
File file = new File(folder, "r." + (mcaX + (getOffsetX() >> 9)) + "." + (mcaZ + (getOffsetZ() >> 9)) + ".mca");
|
|
||||||
if (!file.exists()) {
|
|
||||||
file.createNewFile();
|
|
||||||
}
|
|
||||||
final BufferedRandomAccessFile raf = new BufferedRandomAccessFile(file, "rw", fileBuf);
|
|
||||||
final byte[][] compressed = new byte[1024][];
|
|
||||||
int bx = mcaX << 9;
|
|
||||||
int bz = mcaZ << 9;
|
|
||||||
int scx = bx >> 4;
|
|
||||||
int ecx = Math.min(scx + 31, tcx);
|
|
||||||
int scz = bz >> 4;
|
|
||||||
int ecz = Math.min(scz + 31, tcz);
|
|
||||||
for (int cz = scz; cz <= ecz; cz++) {
|
|
||||||
final int csz = cz << 4;
|
|
||||||
final int cez = Math.min(csz + 15, length - 1);
|
|
||||||
for (int cx = scx; cx <= ecx; cx++) {
|
|
||||||
final int csx = cx << 4;
|
|
||||||
final int cex = Math.min(csx + 15, width - 1);
|
|
||||||
final int fcx = cx;
|
|
||||||
final int fcz = cz;
|
|
||||||
if (shouldWrite(cx, cz)) {
|
|
||||||
pool.submit(() -> {
|
|
||||||
try {
|
|
||||||
MCAChunk chunk = chunkStore.get();
|
|
||||||
chunk.reset();
|
|
||||||
chunk.setPosition(fcx, fcz);
|
|
||||||
chunk = write(chunk, csx, cex, csz, cez);
|
|
||||||
if (chunk != null) {
|
|
||||||
// Generation offset
|
|
||||||
chunk.setPosition(fcx + (getOffsetX() >> 4), fcz + (getOffsetZ() >> 4));
|
|
||||||
|
|
||||||
// Compress
|
|
||||||
FastByteArrayOutputStream uncompressed = chunk.toBytes(byteStore1.get());
|
|
||||||
int len = uncompressed.length;
|
|
||||||
uncompressed.reset();
|
|
||||||
MainUtil.compress(uncompressed.array, len , byteStore2.get(), uncompressed, deflateStore.get());
|
|
||||||
byte[] clone = Arrays.copyOf(uncompressed.array, uncompressed.length);
|
|
||||||
|
|
||||||
// TODO optimize (avoid cloning) by add a synchronized block and write to the RAF here instead of below
|
|
||||||
compressed[((fcx & 31)) + ((fcz & 31) << 5)] = clone;
|
|
||||||
}
|
|
||||||
} catch (Throwable e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pool.awaitQuiescence(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
|
|
||||||
pool.submit(() -> {
|
|
||||||
try {
|
|
||||||
int totalLength = 8192;
|
|
||||||
for (byte[] compressedBytes : compressed) {
|
|
||||||
if (compressedBytes != null) {
|
|
||||||
int blocks = ((4095 + compressedBytes.length + 5) / 4096) * 4096;
|
|
||||||
totalLength += blocks;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
raf.setLength(totalLength);
|
|
||||||
int offset = 8192;
|
|
||||||
for (int i = 0; i < compressed.length; i++) {
|
|
||||||
byte[] compressedBytes = compressed[i];
|
|
||||||
if (compressedBytes != null) {
|
|
||||||
// Set header
|
|
||||||
int index = i << 2;
|
|
||||||
int offsetMedium = offset >> 12;
|
|
||||||
int blocks = ((4095 + compressedBytes.length + 5) / 4096);
|
|
||||||
header[index] = (byte) (offsetMedium >> 16);
|
|
||||||
header[index + 1] = (byte) ((offsetMedium >> 8));
|
|
||||||
header[index + 2] = (byte) ((offsetMedium >> 0));
|
|
||||||
header[index + 3] = (byte) (blocks);
|
|
||||||
// Write bytes
|
|
||||||
raf.seek(offset);
|
|
||||||
raf.writeInt(compressedBytes.length + 1);
|
|
||||||
raf.write(2);
|
|
||||||
raf.write(compressedBytes);
|
|
||||||
offset += blocks * 4096;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
raf.seek(0);
|
|
||||||
raf.write(header);
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
} finally {
|
|
||||||
try {
|
|
||||||
raf.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pool.awaitQuiescence(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
|
|
||||||
pool.shutdown();
|
|
||||||
CleanableThreadLocal.clean(byteStore1);
|
|
||||||
CleanableThreadLocal.clean(byteStore2);
|
|
||||||
CleanableThreadLocal.clean(deflateStore);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,61 +0,0 @@
|
|||||||
package com.boydti.fawe.object.change;
|
|
||||||
|
|
||||||
import com.boydti.fawe.object.brush.visualization.cfi.HeightMapMCAGenerator;
|
|
||||||
import com.boydti.fawe.util.ExtentTraverser;
|
|
||||||
import com.sk89q.worldedit.WorldEditException;
|
|
||||||
import com.sk89q.worldedit.history.UndoContext;
|
|
||||||
import com.sk89q.worldedit.history.change.Change;
|
|
||||||
import com.sk89q.worldedit.internal.util.LogManagerCompat;
|
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
|
||||||
|
|
||||||
public class CFIChange implements Change {
|
|
||||||
|
|
||||||
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
|
||||||
|
|
||||||
private final File file;
|
|
||||||
|
|
||||||
public CFIChange(File file) {
|
|
||||||
checkNotNull(file);
|
|
||||||
this.file = file;
|
|
||||||
}
|
|
||||||
|
|
||||||
private HeightMapMCAGenerator getQueue(UndoContext context) {
|
|
||||||
ExtentTraverser<HeightMapMCAGenerator> found = new ExtentTraverser<>(context.getExtent()).find(HeightMapMCAGenerator.class);
|
|
||||||
if (found != null) {
|
|
||||||
return found.get();
|
|
||||||
}
|
|
||||||
LOGGER.debug("FAWE does not support: " + context.getExtent() + " for " + getClass() + " (bug Empire92)");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void undo(UndoContext context) throws WorldEditException {
|
|
||||||
HeightMapMCAGenerator queue = getQueue(context);
|
|
||||||
if (queue != null) {
|
|
||||||
try {
|
|
||||||
queue.undoChanges(file);
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
queue.update();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void redo(UndoContext context) throws WorldEditException {
|
|
||||||
HeightMapMCAGenerator queue = getQueue(context);
|
|
||||||
if (queue != null) {
|
|
||||||
try {
|
|
||||||
queue.redoChanges(file);
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
queue.update();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,105 +0,0 @@
|
|||||||
package com.boydti.fawe.object.changeset;
|
|
||||||
|
|
||||||
import com.boydti.fawe.Fawe;
|
|
||||||
import com.boydti.fawe.config.Settings;
|
|
||||||
import com.boydti.fawe.object.brush.visualization.cfi.HeightMapMCAGenerator;
|
|
||||||
import com.boydti.fawe.object.change.CFIChange;
|
|
||||||
import com.boydti.fawe.util.MainUtil;
|
|
||||||
import com.sk89q.jnbt.CompoundTag;
|
|
||||||
import com.sk89q.worldedit.history.change.Change;
|
|
||||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
|
|
||||||
public class CFIChangeSet extends AbstractChangeSet {
|
|
||||||
|
|
||||||
private static final Map<UUID, Map<String, Integer>> NEXT_INDEX = new ConcurrentHashMap<>();
|
|
||||||
|
|
||||||
private final File file;
|
|
||||||
|
|
||||||
public CFIChangeSet(HeightMapMCAGenerator hmmg, UUID uuid) throws IOException {
|
|
||||||
super(hmmg);
|
|
||||||
final String hmmgId = hmmg.getId();
|
|
||||||
final File folder = MainUtil.getFile(Fawe.imp().getDirectory(), Settings.IMP.PATHS.HISTORY + File.separator + uuid + File.separator + "CFI" + File.separator + hmmgId);
|
|
||||||
|
|
||||||
final int max = NEXT_INDEX.computeIfAbsent(uuid, _uuid -> new HashMap<>())
|
|
||||||
.compute(hmmgId, (_hmmgId, id) -> (id == null ? MainUtil.getMaxFileId(folder) : id) + 1) - 1;
|
|
||||||
|
|
||||||
this.file = new File(folder, max + ".cfi");
|
|
||||||
File parent = this.file.getParentFile();
|
|
||||||
if (!parent.exists()) {
|
|
||||||
this.file.getParentFile().mkdirs();
|
|
||||||
}
|
|
||||||
if (!this.file.exists()) {
|
|
||||||
this.file.createNewFile();
|
|
||||||
}
|
|
||||||
hmmg.flushChanges(file);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void closeAsync() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void add(int x, int y, int z, int combinedFrom, int combinedTo) {
|
|
||||||
throw new UnsupportedOperationException("Only CFI operations are supported");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addTileCreate(CompoundTag tag) {
|
|
||||||
throw new UnsupportedOperationException("Only CFI operations are supported");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addTileRemove(CompoundTag tag) {
|
|
||||||
throw new UnsupportedOperationException("Only CFI operations are supported");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addEntityRemove(CompoundTag tag) {
|
|
||||||
throw new UnsupportedOperationException("Only CFI operations are supported");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addEntityCreate(CompoundTag tag) {
|
|
||||||
throw new UnsupportedOperationException("Only CFI operations are supported");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addBiomeChange(int x, int y, int z, BiomeType from, BiomeType to) {
|
|
||||||
throw new UnsupportedOperationException("Only CFI operations are supported");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Iterator<Change> getIterator(boolean redo) {
|
|
||||||
return Collections.<Change>singleton(new CFIChange(file)).iterator();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int size() {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isRecordingChanges() {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setRecordChanges(boolean recordChanges) {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -16,10 +16,10 @@ import java.util.function.Consumer;
|
|||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
public class CleanableThreadLocal<T> extends ThreadLocal<T> implements Closeable {
|
public class CleanableThreadLocal<T> extends ThreadLocal<T> implements AutoCloseable {
|
||||||
private final Supplier<T> supplier;
|
private final Supplier<T> supplier;
|
||||||
private final Function<T, T> modifier;
|
private final Function<T, T> modifier;
|
||||||
private LongAdder count = new LongAdder();
|
private final LongAdder count = new LongAdder(); // what is that supposed to do?
|
||||||
|
|
||||||
public CleanableThreadLocal(Supplier<T> supplier) {
|
public CleanableThreadLocal(Supplier<T> supplier) {
|
||||||
this(supplier, Function.identity());
|
this(supplier, Function.identity());
|
||||||
@ -51,123 +51,7 @@ public class CleanableThreadLocal<T> extends ThreadLocal<T> implements Closeable
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void clean() {
|
public void clean() {
|
||||||
if (count.sumThenReset() > 0) {
|
remove();
|
||||||
CleanableThreadLocal.clean(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<T> getAll() {
|
|
||||||
List<T> list = new ArrayList<>();
|
|
||||||
iterate(this, new Consumer<Object>() {
|
|
||||||
Method methodGetEntry;
|
|
||||||
Field fieldValue;
|
|
||||||
@Override
|
|
||||||
public void accept(Object tlm) {
|
|
||||||
try {
|
|
||||||
if (methodGetEntry == null) {
|
|
||||||
methodGetEntry = tlm.getClass().getDeclaredMethod("getEntry", ThreadLocal.class);
|
|
||||||
methodGetEntry.setAccessible(true);
|
|
||||||
}
|
|
||||||
Object entry = methodGetEntry.invoke(tlm, CleanableThreadLocal.this);
|
|
||||||
if (entry != null) {
|
|
||||||
if (fieldValue == null) {
|
|
||||||
fieldValue = entry.getClass().getDeclaredField("value");
|
|
||||||
fieldValue.setAccessible(true);
|
|
||||||
}
|
|
||||||
Object value = fieldValue.get(entry);
|
|
||||||
if (value != null) {
|
|
||||||
list.add((T) value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | NoSuchFieldException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <L> void iterate(ThreadLocal<L> instance, Consumer<Object> withMap) {
|
|
||||||
try {
|
|
||||||
Thread[] threads = MainUtil.getThreads();
|
|
||||||
Field tl = Thread.class.getDeclaredField("threadLocals");
|
|
||||||
tl.setAccessible(true);
|
|
||||||
for (Thread thread : threads) {
|
|
||||||
if (thread != null) {
|
|
||||||
Object tlm = tl.get(thread);
|
|
||||||
if (tlm != null) {
|
|
||||||
withMap.accept(tlm);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <L> void clean(ThreadLocal<L> instance) {
|
|
||||||
iterate(instance, new Consumer<Object>() {
|
|
||||||
Method methodRemove;
|
|
||||||
@Override
|
|
||||||
public void accept(Object tlm) {
|
|
||||||
try {
|
|
||||||
if (methodRemove == null) {
|
|
||||||
methodRemove = tlm.getClass().getDeclaredMethod("remove", ThreadLocal.class);
|
|
||||||
methodRemove.setAccessible(true);
|
|
||||||
}
|
|
||||||
if (methodRemove != null) {
|
|
||||||
try {
|
|
||||||
methodRemove.invoke(tlm, instance);
|
|
||||||
} catch (Throwable ignored) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (NoSuchMethodException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void cleanAll() {
|
|
||||||
try {
|
|
||||||
// Get a reference to the thread locals table of the current thread
|
|
||||||
Thread thread = Thread.currentThread();
|
|
||||||
Field threadLocalsField = Thread.class.getDeclaredField("threadLocals");
|
|
||||||
threadLocalsField.setAccessible(true);
|
|
||||||
Object threadLocalTable = threadLocalsField.get(thread);
|
|
||||||
|
|
||||||
// Get a reference to the array holding the thread local variables inside the
|
|
||||||
// ThreadLocalMap of the current thread
|
|
||||||
Class<?> threadLocalMapClass = Class.forName("java.lang.ThreadLocal$ThreadLocalMap");
|
|
||||||
Field tableField = threadLocalMapClass.getDeclaredField("table");
|
|
||||||
tableField.setAccessible(true);
|
|
||||||
Object table = tableField.get(threadLocalTable);
|
|
||||||
|
|
||||||
// The key to the ThreadLocalMap is a WeakReference object. The referent field of this object
|
|
||||||
// is a reference to the actual ThreadLocal variable
|
|
||||||
Field referentField = Reference.class.getDeclaredField("referent");
|
|
||||||
referentField.setAccessible(true);
|
|
||||||
|
|
||||||
for (int i = 0; i < Array.getLength(table); i++) {
|
|
||||||
// Each entry in the table array of ThreadLocalMap is an Entry object
|
|
||||||
// representing the thread local reference and its value
|
|
||||||
Object entry = Array.get(table, i);
|
|
||||||
if (entry != null) {
|
|
||||||
// Get a reference to the thread local object and remove it from the table
|
|
||||||
ThreadLocal threadLocal = (ThreadLocal)referentField.get(entry);
|
|
||||||
clean(threadLocal);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
// We will tolerate an exception here and just log it
|
|
||||||
throw new IllegalStateException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void finalize() throws Throwable {
|
|
||||||
clean(this);
|
|
||||||
super.finalize();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1,54 +0,0 @@
|
|||||||
package com.boydti.fawe.object.collection;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.concurrent.ConcurrentLinkedDeque;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
public class IterableThreadLocal<T> extends ThreadLocal<T> implements Iterable<T> {
|
|
||||||
private final ConcurrentLinkedDeque<T> allValues = new ConcurrentLinkedDeque<>();
|
|
||||||
private final Supplier<T> supplier;
|
|
||||||
|
|
||||||
public IterableThreadLocal(Supplier<T> supplier) {
|
|
||||||
this.supplier = supplier;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected final T initialValue() {
|
|
||||||
T value = init();
|
|
||||||
if (value != null) {
|
|
||||||
synchronized (this) {
|
|
||||||
allValues.add(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final Iterator<T> iterator() {
|
|
||||||
return getAll().iterator();
|
|
||||||
}
|
|
||||||
|
|
||||||
public T init() {
|
|
||||||
return supplier.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void clean() {
|
|
||||||
if (!allValues.isEmpty()) {
|
|
||||||
synchronized (this) {
|
|
||||||
CleanableThreadLocal.clean(this);
|
|
||||||
allValues.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public final Collection<T> getAll() {
|
|
||||||
return Collections.unmodifiableCollection(allValues);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void finalize() throws Throwable {
|
|
||||||
CleanableThreadLocal.clean(this);
|
|
||||||
super.finalize();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
package com.boydti.fawe.object.collection;
|
|
||||||
|
|
||||||
public class VariableThreadLocal extends CleanableThreadLocal<byte[]> {
|
|
||||||
public VariableThreadLocal() {
|
|
||||||
super(() -> null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] get(int size) {
|
|
||||||
byte[] existing = get();
|
|
||||||
if (existing == null || existing.length < size) {
|
|
||||||
int padded = ((size + 4095) / 4096) * 4096;
|
|
||||||
existing = new byte[padded];
|
|
||||||
set(existing);
|
|
||||||
}
|
|
||||||
return existing;
|
|
||||||
}
|
|
||||||
}
|
|
@ -18,7 +18,6 @@ import com.boydti.fawe.object.HistoryExtent;
|
|||||||
import com.boydti.fawe.object.NullChangeSet;
|
import com.boydti.fawe.object.NullChangeSet;
|
||||||
import com.boydti.fawe.object.RegionWrapper;
|
import com.boydti.fawe.object.RegionWrapper;
|
||||||
import com.boydti.fawe.object.RelightMode;
|
import com.boydti.fawe.object.RelightMode;
|
||||||
import com.boydti.fawe.object.brush.visualization.VirtualWorld;
|
|
||||||
import com.boydti.fawe.object.changeset.AbstractChangeSet;
|
import com.boydti.fawe.object.changeset.AbstractChangeSet;
|
||||||
import com.boydti.fawe.object.changeset.BlockBagChangeSet;
|
import com.boydti.fawe.object.changeset.BlockBagChangeSet;
|
||||||
import com.boydti.fawe.object.changeset.DiskStorageHistory;
|
import com.boydti.fawe.object.changeset.DiskStorageHistory;
|
||||||
@ -379,7 +378,7 @@ public class EditSessionBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (allowedRegions == null) {
|
if (allowedRegions == null) {
|
||||||
if (player != null && !player.hasPermission("fawe.bypass") && !player.hasPermission("fawe.bypass.regions") && !(root instanceof VirtualWorld)) {
|
if (player != null && !player.hasPermission("fawe.bypass") && !player.hasPermission("fawe.bypass.regions")) {
|
||||||
allowedRegions = player.getCurrentRegions();
|
allowedRegions = player.getCurrentRegions();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,16 +4,12 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
|
|
||||||
import java.lang.invoke.MethodHandles;
|
import java.lang.invoke.MethodHandles;
|
||||||
import java.lang.reflect.AccessibleObject;
|
import java.lang.reflect.AccessibleObject;
|
||||||
import java.lang.reflect.Constructor;
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class ReflectionUtils {
|
public class ReflectionUtils {
|
||||||
|
|
||||||
@ -21,10 +17,6 @@ public class ReflectionUtils {
|
|||||||
return t.isInstance(o) ? t.cast(o) : null;
|
return t.isInstance(o) ? t.cast(o) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T extends Enum<?>> T addEnum(Class<T> enumType, String enumName) {
|
|
||||||
return ReflectionUtils9.addEnum(enumType, enumName);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void setAccessibleNonFinal(Field field) throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
|
public static void setAccessibleNonFinal(Field field) throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
|
||||||
// let's make the field accessible
|
// let's make the field accessible
|
||||||
field.setAccessible(true);
|
field.setAccessible(true);
|
||||||
@ -52,21 +44,6 @@ public class ReflectionUtils {
|
|||||||
field.set(target, value);
|
field.set(target, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void blankField(Class<?> enumClass, String fieldName) throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
|
|
||||||
for (Field field : Class.class.getDeclaredFields()) {
|
|
||||||
if (field.getName().contains(fieldName)) {
|
|
||||||
AccessibleObject.setAccessible(new Field[] { field }, true);
|
|
||||||
setFailsafeFieldValue(field, enumClass, null);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void cleanEnumCache(Class<?> enumClass) throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
|
|
||||||
blankField(enumClass, "enumConstantDirectory"); // Sun (Oracle?!?) JDK 1.5/6
|
|
||||||
blankField(enumClass, "enumConstants"); // IBM JDK
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Object getHandle(Object wrapper) {
|
public static Object getHandle(Object wrapper) {
|
||||||
final Method getHandle = makeMethod(wrapper.getClass(), "getHandle");
|
final Method getHandle = makeMethod(wrapper.getClass(), "getHandle");
|
||||||
return callMethod(getHandle, wrapper);
|
return callMethod(getHandle, wrapper);
|
||||||
@ -98,108 +75,6 @@ public class ReflectionUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public static <T> Constructor<T> makeConstructor(Class<?> clazz, Class<?>... parameterTypes) {
|
|
||||||
try {
|
|
||||||
return (Constructor<T>) clazz.getConstructor(parameterTypes);
|
|
||||||
} catch (NoSuchMethodException ex) {
|
|
||||||
return null;
|
|
||||||
} catch (Exception ex) {
|
|
||||||
throw new RuntimeException(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <T> T callConstructor(Constructor<T> constructor, Object... parameters) {
|
|
||||||
if (constructor == null) {
|
|
||||||
throw new RuntimeException("No such constructor");
|
|
||||||
}
|
|
||||||
constructor.setAccessible(true);
|
|
||||||
try {
|
|
||||||
return constructor.newInstance(parameters);
|
|
||||||
} catch (InvocationTargetException ex) {
|
|
||||||
throw new RuntimeException(ex.getCause());
|
|
||||||
} catch (Exception ex) {
|
|
||||||
throw new RuntimeException(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Field makeField(Class<?> clazz, String name) {
|
|
||||||
try {
|
|
||||||
return clazz.getDeclaredField(name);
|
|
||||||
} catch (NoSuchFieldException ex) {
|
|
||||||
return null;
|
|
||||||
} catch (Exception ex) {
|
|
||||||
throw new RuntimeException(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Field findField(Class<?> clazz, Class<?> type, int hasMods, int noMods) {
|
|
||||||
for (Field field : clazz.getDeclaredFields()) {
|
|
||||||
if (type == null || type.isAssignableFrom(field.getType())) {
|
|
||||||
int mods = field.getModifiers();
|
|
||||||
if ((mods & hasMods) == hasMods && (mods & noMods) == 0) {
|
|
||||||
return setAccessible(field);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Field findField(Class<?> clazz, Class<?> type) {
|
|
||||||
for (Field field : clazz.getDeclaredFields()) {
|
|
||||||
if (field.getType() == type) {
|
|
||||||
return setAccessible(field);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Method findMethod(Class<?> clazz, Class<?> returnType, Class<?>... params) {
|
|
||||||
return findMethod(clazz, 0, returnType, params);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Method findMethod(Class<?> clazz, int index, int hasMods, int noMods, Class<?> returnType, Class<?>... params) {
|
|
||||||
outer:
|
|
||||||
for (Method method : sortMethods(clazz.getDeclaredMethods())) {
|
|
||||||
if (returnType == null || method.getReturnType() == returnType) {
|
|
||||||
Class<?>[] mp = method.getParameterTypes();
|
|
||||||
int mods = method.getModifiers();
|
|
||||||
if ((mods & hasMods) != hasMods || (mods & noMods) != 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (params == null) {
|
|
||||||
if (index-- == 0) {
|
|
||||||
return setAccessible(method);
|
|
||||||
} else {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (mp.length == params.length) {
|
|
||||||
for (int i = 0; i < mp.length; i++) {
|
|
||||||
if (mp[i] != params[i]) {
|
|
||||||
continue outer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (index-- == 0) {
|
|
||||||
return setAccessible(method);
|
|
||||||
} else {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Method findMethod(Class<?> clazz, int index, Class<?> returnType, Class<?>... params) {
|
|
||||||
return findMethod(clazz, index, 0, 0, returnType, params);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Method[] sortMethods(Method[] methods) {
|
|
||||||
Arrays.sort(methods, Comparator.comparing(Method::getName));
|
|
||||||
return methods;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Field[] sortFields(Field[] fields) {
|
public static Field[] sortFields(Field[] fields) {
|
||||||
Arrays.sort(fields, Comparator.comparing(Field::getName));
|
Arrays.sort(fields, Comparator.comparing(Field::getName));
|
||||||
return fields;
|
return fields;
|
||||||
@ -220,15 +95,6 @@ public class ReflectionUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void setField(@NotNull Field field, Object instance, Object value) {
|
|
||||||
field.setAccessible(true);
|
|
||||||
try {
|
|
||||||
field.set(instance, value);
|
|
||||||
} catch (Exception ex) {
|
|
||||||
throw new RuntimeException(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Class<?> getClass(String name) {
|
public static Class<?> getClass(String name) {
|
||||||
try {
|
try {
|
||||||
return Class.forName(name);
|
return Class.forName(name);
|
||||||
@ -244,450 +110,4 @@ public class ReflectionUtils {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a {@link RefClass} object by its original {@link Class}.
|
|
||||||
*
|
|
||||||
* @param clazz class
|
|
||||||
* @return RefClass based on passed class
|
|
||||||
*/
|
|
||||||
public static RefClass getRefClass(Class<?> clazz) {
|
|
||||||
return new RefClass(clazz);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A utility to simplify work with reflections.
|
|
||||||
*/
|
|
||||||
public static class RefClass {
|
|
||||||
|
|
||||||
private final Class<?> clazz;
|
|
||||||
|
|
||||||
private RefClass(Class<?> clazz) {
|
|
||||||
this.clazz = clazz;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Class<?> getClazz() {
|
|
||||||
return this.clazz;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* See {@link Class#isInstance(Object)}.
|
|
||||||
*
|
|
||||||
* @param object the object to check
|
|
||||||
* @return true if object is an instance of this class
|
|
||||||
*/
|
|
||||||
public boolean isInstance(Object object) {
|
|
||||||
return this.clazz.isInstance(object);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get an existing method by name and types. The {@code types} parameter accepts both {@link Class}
|
|
||||||
* and {@link RefClass} objects.
|
|
||||||
*
|
|
||||||
* @param name name
|
|
||||||
* @param types method parameters
|
|
||||||
* @return RefMethod object
|
|
||||||
* @throws RuntimeException if method not found
|
|
||||||
*/
|
|
||||||
public RefMethod getMethod(String name, Object... types) {
|
|
||||||
try {
|
|
||||||
final Class[] classes = new Class[types.length];
|
|
||||||
int i = 0;
|
|
||||||
for (Object e : types) {
|
|
||||||
if (e instanceof Class) {
|
|
||||||
classes[i++] = (Class) e;
|
|
||||||
} else if (e instanceof RefClass) {
|
|
||||||
classes[i++] = ((RefClass) e).getClazz();
|
|
||||||
} else {
|
|
||||||
classes[i++] = e.getClass();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return new RefMethod(this.clazz.getMethod(name, classes));
|
|
||||||
} catch (NoSuchMethodException ignored) {
|
|
||||||
return new RefMethod(this.clazz.getDeclaredMethod(name, classes));
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get an existing constructor by types.The {@code types} parameter accepts both {@link Class}
|
|
||||||
* and {@link RefClass} objects.
|
|
||||||
*
|
|
||||||
* @param types parameters
|
|
||||||
* @return RefMethod object
|
|
||||||
* @throws RuntimeException if constructor not found
|
|
||||||
*/
|
|
||||||
public RefConstructor getConstructor(Object... types) {
|
|
||||||
try {
|
|
||||||
final Class[] classes = new Class[types.length];
|
|
||||||
int i = 0;
|
|
||||||
for (Object e : types) {
|
|
||||||
if (e instanceof Class) {
|
|
||||||
classes[i++] = (Class) e;
|
|
||||||
} else if (e instanceof RefClass) {
|
|
||||||
classes[i++] = ((RefClass) e).getClazz();
|
|
||||||
} else {
|
|
||||||
classes[i++] = e.getClass();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return new RefConstructor(this.clazz.getConstructor(classes));
|
|
||||||
} catch (NoSuchMethodException ignored) {
|
|
||||||
return new RefConstructor(this.clazz.getDeclaredConstructor(classes));
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find a method by type parameters. The {@code types} parameter accepts both {@link Class}
|
|
||||||
* and {@link RefClass} objects.
|
|
||||||
*
|
|
||||||
* @param types parameters
|
|
||||||
* @return RefMethod object
|
|
||||||
* @throws RuntimeException if method not found
|
|
||||||
*/
|
|
||||||
public RefMethod findMethod(Object... types) {
|
|
||||||
final Class[] classes = new Class[types.length];
|
|
||||||
int t = 0;
|
|
||||||
for (Object e : types) {
|
|
||||||
if (e instanceof Class) {
|
|
||||||
classes[t++] = (Class) e;
|
|
||||||
} else if (e instanceof RefClass) {
|
|
||||||
classes[t++] = ((RefClass) e).getClazz();
|
|
||||||
} else {
|
|
||||||
classes[t++] = e.getClass();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
final List<Method> methods = new ArrayList<>();
|
|
||||||
Collections.addAll(methods, this.clazz.getMethods());
|
|
||||||
Collections.addAll(methods, this.clazz.getDeclaredMethods());
|
|
||||||
findMethod:
|
|
||||||
for (Method m : methods) {
|
|
||||||
final Class<?>[] methodTypes = m.getParameterTypes();
|
|
||||||
if (methodTypes.length != classes.length) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
for (Class aClass : classes) {
|
|
||||||
if (!Arrays.equals(classes, methodTypes)) {
|
|
||||||
continue findMethod;
|
|
||||||
}
|
|
||||||
return new RefMethod(m);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new RuntimeException("no such method");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find a method by name.
|
|
||||||
*
|
|
||||||
* @param names possible names of method
|
|
||||||
* @return RefMethod object
|
|
||||||
* @throws RuntimeException if method not found
|
|
||||||
*/
|
|
||||||
public RefMethod findMethodByName(String... names) {
|
|
||||||
final List<Method> methods = new ArrayList<>();
|
|
||||||
Collections.addAll(methods, this.clazz.getMethods());
|
|
||||||
Collections.addAll(methods, this.clazz.getDeclaredMethods());
|
|
||||||
for (Method m : methods) {
|
|
||||||
for (String name : names) {
|
|
||||||
if (m.getName().equals(name)) {
|
|
||||||
return new RefMethod(m);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new RuntimeException("no such method");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find a method by return value.
|
|
||||||
*
|
|
||||||
* @param type type of returned value
|
|
||||||
* @return RefMethod
|
|
||||||
* @throws RuntimeException if method not found
|
|
||||||
*/
|
|
||||||
public RefMethod findMethodByReturnType(RefClass type) {
|
|
||||||
return this.findMethodByReturnType(type.clazz);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find a method by return value.
|
|
||||||
*
|
|
||||||
* @param type type of returned value
|
|
||||||
* @return RefMethod
|
|
||||||
* @throws RuntimeException if method not found
|
|
||||||
*/
|
|
||||||
public RefMethod findMethodByReturnType(Class<?> type) {
|
|
||||||
if (type == null) {
|
|
||||||
type = void.class.getComponentType();
|
|
||||||
}
|
|
||||||
final List<Method> methods = new ArrayList<>();
|
|
||||||
Collections.addAll(methods, this.clazz.getMethods());
|
|
||||||
Collections.addAll(methods, this.clazz.getDeclaredMethods());
|
|
||||||
for (Method m : methods) {
|
|
||||||
if (type.equals(m.getReturnType())) {
|
|
||||||
return new RefMethod(m);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new RuntimeException("no such method");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find the constructor by the number of arguments.
|
|
||||||
*
|
|
||||||
* @param number number of arguments
|
|
||||||
* @return RefConstructor
|
|
||||||
* @throws RuntimeException if constructor not found
|
|
||||||
*/
|
|
||||||
public RefConstructor findConstructor(int number) {
|
|
||||||
final List<Constructor<?>> constructors = new ArrayList<>();
|
|
||||||
Collections.addAll(constructors, this.clazz.getConstructors());
|
|
||||||
Collections.addAll(constructors, this.clazz.getDeclaredConstructors());
|
|
||||||
for (Constructor<?> m : constructors) {
|
|
||||||
if (m.getParameterTypes().length == number) {
|
|
||||||
return new RefConstructor(m);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new RuntimeException("no such constructor");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the field by name.
|
|
||||||
*
|
|
||||||
* @param name field name
|
|
||||||
* @return RefField
|
|
||||||
* @throws RuntimeException if field not found
|
|
||||||
*/
|
|
||||||
public RefField getField(String name) {
|
|
||||||
try {
|
|
||||||
try {
|
|
||||||
return new RefField(this.clazz.getField(name));
|
|
||||||
} catch (NoSuchFieldException ignored) {
|
|
||||||
return new RefField(this.clazz.getDeclaredField(name));
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find the field by type.
|
|
||||||
*
|
|
||||||
* @param type field type
|
|
||||||
* @return RefField
|
|
||||||
* @throws NoSuchFieldException if field not found
|
|
||||||
*/
|
|
||||||
public RefField findField(RefClass type) throws NoSuchFieldException {
|
|
||||||
return this.findField(type.clazz);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find the field by type.
|
|
||||||
*
|
|
||||||
* @param type field type
|
|
||||||
* @return RefField
|
|
||||||
* @throws RuntimeException if field not found
|
|
||||||
*/
|
|
||||||
public RefField findField(Class<?> type) throws NoSuchFieldException {
|
|
||||||
if (type == null) {
|
|
||||||
type = void.class;
|
|
||||||
}
|
|
||||||
final List<Field> fields = new ArrayList<>();
|
|
||||||
Collections.addAll(fields, this.clazz.getFields());
|
|
||||||
Collections.addAll(fields, this.clazz.getDeclaredFields());
|
|
||||||
for (Field f : fields) {
|
|
||||||
if (type.equals(f.getType())) {
|
|
||||||
return new RefField(f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new NoSuchFieldException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method reflection wrapper.
|
|
||||||
*/
|
|
||||||
public static class RefMethod {
|
|
||||||
|
|
||||||
private final Method method;
|
|
||||||
|
|
||||||
private RefMethod(Method method) {
|
|
||||||
this.method = method;
|
|
||||||
method.setAccessible(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Method getMethod() {
|
|
||||||
return this.method;
|
|
||||||
}
|
|
||||||
|
|
||||||
public RefClass getRefClass() {
|
|
||||||
return new RefClass(this.method.getDeclaringClass());
|
|
||||||
}
|
|
||||||
|
|
||||||
public RefClass getReturnRefClass() {
|
|
||||||
return new RefClass(this.method.getReturnType());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Apply method to object.
|
|
||||||
*
|
|
||||||
* @param e object to which the method is applied
|
|
||||||
* @return RefExecutor with method call(...)
|
|
||||||
*/
|
|
||||||
public RefExecutor of(Object e) {
|
|
||||||
return new RefExecutor(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Call static method.
|
|
||||||
*
|
|
||||||
* @param params sent parameters
|
|
||||||
* @return return value
|
|
||||||
*/
|
|
||||||
public Object call(Object... params) {
|
|
||||||
try {
|
|
||||||
return this.method.invoke(null, params);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class RefExecutor {
|
|
||||||
|
|
||||||
final Object executor;
|
|
||||||
|
|
||||||
public RefExecutor(Object executor) {
|
|
||||||
this.executor = executor;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Invokes the method on the selected object.
|
|
||||||
*
|
|
||||||
* @param params sent parameters
|
|
||||||
* @return return value
|
|
||||||
* @throws RuntimeException if something went wrong
|
|
||||||
*/
|
|
||||||
public Object call(Object... params) {
|
|
||||||
try {
|
|
||||||
return RefMethod.this.method.invoke(this.executor, params);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor wrapper.
|
|
||||||
*/
|
|
||||||
public static class RefConstructor {
|
|
||||||
|
|
||||||
private final Constructor<?> constructor;
|
|
||||||
|
|
||||||
private RefConstructor(Constructor<?> constructor) {
|
|
||||||
this.constructor = constructor;
|
|
||||||
constructor.setAccessible(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Constructor<?> getConstructor() {
|
|
||||||
return this.constructor;
|
|
||||||
}
|
|
||||||
|
|
||||||
public RefClass getRefClass() {
|
|
||||||
return new RefClass(this.constructor.getDeclaringClass());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create and initialize a new instance of constructor's declaring class.
|
|
||||||
*
|
|
||||||
* @param params parameters for constructor
|
|
||||||
* @return new object
|
|
||||||
* @throws RuntimeException if something went wrong
|
|
||||||
*/
|
|
||||||
public Object create(Object... params) {
|
|
||||||
try {
|
|
||||||
return this.constructor.newInstance(params);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static class RefField {
|
|
||||||
|
|
||||||
private final Field field;
|
|
||||||
|
|
||||||
private RefField(Field field) {
|
|
||||||
this.field = field;
|
|
||||||
field.setAccessible(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Field getField() {
|
|
||||||
return this.field;
|
|
||||||
}
|
|
||||||
|
|
||||||
public RefClass getRefClass() {
|
|
||||||
return new RefClass(this.field.getDeclaringClass());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a wrapper to the class of the field's returning type.
|
|
||||||
*/
|
|
||||||
public RefClass getFieldRefClass() {
|
|
||||||
return new RefClass(this.field.getType());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Apply the field on object.
|
|
||||||
*
|
|
||||||
* @param e applied object
|
|
||||||
* @return RefExecutor with getter and setter
|
|
||||||
*/
|
|
||||||
public RefExecutor of(Object e) {
|
|
||||||
return new RefExecutor(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
public class RefExecutor {
|
|
||||||
|
|
||||||
final Object executor;
|
|
||||||
|
|
||||||
public RefExecutor(Object e) {
|
|
||||||
this.executor = e;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set field value for applied object.
|
|
||||||
*
|
|
||||||
* @param param value
|
|
||||||
*/
|
|
||||||
public void set(Object param) {
|
|
||||||
try {
|
|
||||||
RefField.this.field.set(this.executor, param);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get field value for the applied object.
|
|
||||||
*
|
|
||||||
* @return value of field
|
|
||||||
*/
|
|
||||||
public Object get() {
|
|
||||||
try {
|
|
||||||
return RefField.this.field.get(this.executor);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,77 +0,0 @@
|
|||||||
package com.boydti.fawe.util;
|
|
||||||
|
|
||||||
import sun.misc.Unsafe;
|
|
||||||
|
|
||||||
import java.lang.reflect.AccessibleObject;
|
|
||||||
import java.lang.reflect.Array;
|
|
||||||
import java.lang.reflect.Constructor;
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class ReflectionUtils9 {
|
|
||||||
public static <T extends Enum<?>> T addEnum(Class<T> enumType, String enumName) {
|
|
||||||
|
|
||||||
// 0. Sanity checks
|
|
||||||
if (!Enum.class.isAssignableFrom(enumType)) {
|
|
||||||
throw new RuntimeException("class " + enumType + " is not an instance of Enum");
|
|
||||||
}
|
|
||||||
// 1. Lookup "$VALUES" holder in enum class and get previous enum instances
|
|
||||||
Field valuesField = null;
|
|
||||||
Field[] fields = enumType.getDeclaredFields();
|
|
||||||
for (Field field : fields) {
|
|
||||||
if (field.getName().contains("$VALUES")) {
|
|
||||||
valuesField = field;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
AccessibleObject.setAccessible(new Field[]{valuesField}, true);
|
|
||||||
|
|
||||||
try {
|
|
||||||
|
|
||||||
// 2. Copy it
|
|
||||||
T[] previousValues = (T[]) valuesField.get(enumType);
|
|
||||||
List<T> values = new ArrayList<>(Arrays.asList(previousValues));
|
|
||||||
|
|
||||||
// 3. build new enum
|
|
||||||
T newValue = (T) makeEnum(enumType, // The target enum class
|
|
||||||
enumName, // THE NEW ENUM INSTANCE TO BE DYNAMICALLY ADDED
|
|
||||||
values.size()); // can be used to pass values to the enum constructor
|
|
||||||
|
|
||||||
// 4. add new value
|
|
||||||
values.add(newValue);
|
|
||||||
|
|
||||||
// 5. Set new values field
|
|
||||||
try {
|
|
||||||
ReflectionUtils.setFailsafeFieldValue(valuesField, null,
|
|
||||||
values.toArray((T[]) Array.newInstance(enumType, 0)));
|
|
||||||
} catch (Throwable e) {
|
|
||||||
Field ordinalField = Enum.class.getDeclaredField("ordinal");
|
|
||||||
ReflectionUtils.setFailsafeFieldValue(ordinalField, newValue, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 6. Clean enum cache
|
|
||||||
ReflectionUtils.cleanEnumCache(enumType);
|
|
||||||
return newValue;
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
throw new RuntimeException(e.getMessage(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Object makeEnum(Class<?> enumClass, String value, int ordinal) throws Exception {
|
|
||||||
Constructor<?> constructor = Unsafe.class.getDeclaredConstructors()[0];
|
|
||||||
constructor.setAccessible(true);
|
|
||||||
Unsafe unsafe = (Unsafe) constructor.newInstance();
|
|
||||||
Object instance = unsafe.allocateInstance(enumClass);
|
|
||||||
|
|
||||||
Field ordinalField = Enum.class.getDeclaredField("ordinal");
|
|
||||||
ReflectionUtils.setFailsafeFieldValue(ordinalField, instance, 0);
|
|
||||||
|
|
||||||
Field nameField = Enum.class.getDeclaredField("name");
|
|
||||||
ReflectionUtils.setFailsafeFieldValue(nameField, instance, value);
|
|
||||||
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
}
|
|
@ -24,7 +24,6 @@ import com.boydti.fawe.config.Settings;
|
|||||||
import com.boydti.fawe.object.FaweInputStream;
|
import com.boydti.fawe.object.FaweInputStream;
|
||||||
import com.boydti.fawe.object.FaweLimit;
|
import com.boydti.fawe.object.FaweLimit;
|
||||||
import com.boydti.fawe.object.FaweOutputStream;
|
import com.boydti.fawe.object.FaweOutputStream;
|
||||||
import com.boydti.fawe.object.brush.visualization.VirtualWorld;
|
|
||||||
import com.boydti.fawe.object.changeset.DiskStorageHistory;
|
import com.boydti.fawe.object.changeset.DiskStorageHistory;
|
||||||
import com.boydti.fawe.object.clipboard.MultiClipboardHolder;
|
import com.boydti.fawe.object.clipboard.MultiClipboardHolder;
|
||||||
import com.boydti.fawe.object.collection.SparseBitSet;
|
import com.boydti.fawe.object.collection.SparseBitSet;
|
||||||
@ -80,6 +79,8 @@ import com.sk89q.worldedit.world.item.ItemTypes;
|
|||||||
import com.sk89q.worldedit.world.snapshot.experimental.Snapshot;
|
import com.sk89q.worldedit.world.snapshot.experimental.Snapshot;
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
@ -92,15 +93,12 @@ import java.util.LinkedList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.ListIterator;
|
import java.util.ListIterator;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Random;
|
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.concurrent.locks.Lock;
|
import java.util.concurrent.locks.Lock;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
@ -159,7 +157,6 @@ public class LocalSession implements TextureHolder {
|
|||||||
private transient UUID uuid;
|
private transient UUID uuid;
|
||||||
private transient volatile long historySize = 0;
|
private transient volatile long historySize = 0;
|
||||||
|
|
||||||
private transient VirtualWorld virtual;
|
|
||||||
private transient BlockVector3 cuiTemporaryBlock;
|
private transient BlockVector3 cuiTemporaryBlock;
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
private transient EditSession.ReorderMode reorderMode = EditSession.ReorderMode.MULTI_STAGE;
|
private transient EditSession.ReorderMode reorderMode = EditSession.ReorderMode.MULTI_STAGE;
|
||||||
@ -752,35 +749,6 @@ public class LocalSession implements TextureHolder {
|
|||||||
return selector.getRegion();
|
return selector.getRegion();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public VirtualWorld getVirtualWorld() {
|
|
||||||
synchronized (dirty) {
|
|
||||||
return virtual;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setVirtualWorld(@Nullable VirtualWorld world) {
|
|
||||||
VirtualWorld tmp;
|
|
||||||
synchronized (dirty) {
|
|
||||||
tmp = this.virtual;
|
|
||||||
if (tmp == world) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.virtual = world;
|
|
||||||
}
|
|
||||||
if (tmp != null) {
|
|
||||||
try {
|
|
||||||
tmp.close(world == null);
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (world != null) {
|
|
||||||
Fawe.imp().registerPacketListener();
|
|
||||||
world.update();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the selection world.
|
* Get the selection world.
|
||||||
*
|
*
|
||||||
@ -1234,10 +1202,6 @@ public class LocalSession implements TextureHolder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (player != null && previous instanceof BrushTool) {
|
|
||||||
BrushTool brushTool = (BrushTool) previous;
|
|
||||||
brushTool.clear(player);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1723,14 +1687,4 @@ public class LocalSession implements TextureHolder {
|
|||||||
this.transform = transform;
|
this.transform = transform;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void unregisterTools(Player player) {
|
|
||||||
synchronized (tools) {
|
|
||||||
for (Tool tool : tools.values()) {
|
|
||||||
if (tool instanceof BrushTool) {
|
|
||||||
((BrushTool) tool).clear(player);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,6 @@ import com.boydti.fawe.config.Caption;
|
|||||||
import com.boydti.fawe.object.brush.BrushSettings;
|
import com.boydti.fawe.object.brush.BrushSettings;
|
||||||
import com.boydti.fawe.object.brush.TargetMode;
|
import com.boydti.fawe.object.brush.TargetMode;
|
||||||
import com.boydti.fawe.object.brush.scroll.Scroll;
|
import com.boydti.fawe.object.brush.scroll.Scroll;
|
||||||
import com.boydti.fawe.object.brush.visualization.VisualMode;
|
|
||||||
import com.boydti.fawe.util.MathMan;
|
import com.boydti.fawe.util.MathMan;
|
||||||
import com.boydti.fawe.util.StringMan;
|
import com.boydti.fawe.util.StringMan;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
@ -231,31 +230,6 @@ public class ToolUtilCommands {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Command(
|
|
||||||
name = "visualize",
|
|
||||||
aliases = {"visual", "vis", "/visualize", "/visual", "/vis"},
|
|
||||||
desc = "Toggle between different visualization modes",
|
|
||||||
descFooter = "Toggle between different visualization modes\n"
|
|
||||||
+ "0 = No visualization\n"
|
|
||||||
+ "1 = Single block at target position\n"
|
|
||||||
+ "2 = Glass showing what blocks will be changed"
|
|
||||||
)
|
|
||||||
@CommandPermissions("worldedit.brush.visualize")
|
|
||||||
public void visual(Player player, LocalSession session,
|
|
||||||
@Arg(name = "mode", desc = "int", def = "0")
|
|
||||||
@Range(from = 0, to = 2)
|
|
||||||
int mode) throws WorldEditException {
|
|
||||||
BrushTool tool = session.getBrushTool(player, false);
|
|
||||||
if (tool == null) {
|
|
||||||
player.print(Caption.of("fawe.worldedit.brush.brush.none"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
VisualMode[] modes = VisualMode.values();
|
|
||||||
VisualMode newMode = modes[MathMan.wrap(mode, 0, modes.length - 1)];
|
|
||||||
tool.setVisualMode(player, newMode);
|
|
||||||
player.print(Caption.of("fawe.worldedit.brush.brush.visual.mode.set", newMode));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Command(
|
@Command(
|
||||||
name = "target",
|
name = "target",
|
||||||
aliases = {"tar", "/target", "/tar"},
|
aliases = {"tar", "/target", "/tar"},
|
||||||
|
@ -19,10 +19,8 @@
|
|||||||
|
|
||||||
package com.sk89q.worldedit.command.tool;
|
package com.sk89q.worldedit.command.tool;
|
||||||
|
|
||||||
import com.boydti.fawe.Fawe;
|
|
||||||
import com.boydti.fawe.beta.implementation.IChunkExtent;
|
import com.boydti.fawe.beta.implementation.IChunkExtent;
|
||||||
import com.boydti.fawe.beta.implementation.processors.NullProcessor;
|
import com.boydti.fawe.beta.implementation.processors.NullProcessor;
|
||||||
import com.boydti.fawe.beta.implementation.processors.PersistentChunkSendProcessor;
|
|
||||||
import com.boydti.fawe.config.Caption;
|
import com.boydti.fawe.config.Caption;
|
||||||
import com.boydti.fawe.object.brush.BrushSettings;
|
import com.boydti.fawe.object.brush.BrushSettings;
|
||||||
import com.boydti.fawe.object.brush.MovableTool;
|
import com.boydti.fawe.object.brush.MovableTool;
|
||||||
@ -30,8 +28,6 @@ import com.boydti.fawe.object.brush.ResettableTool;
|
|||||||
import com.boydti.fawe.object.brush.TargetMode;
|
import com.boydti.fawe.object.brush.TargetMode;
|
||||||
import com.boydti.fawe.object.brush.scroll.Scroll;
|
import com.boydti.fawe.object.brush.scroll.Scroll;
|
||||||
import com.boydti.fawe.object.brush.scroll.ScrollTool;
|
import com.boydti.fawe.object.brush.scroll.ScrollTool;
|
||||||
import com.boydti.fawe.object.brush.visualization.VisualExtent;
|
|
||||||
import com.boydti.fawe.object.brush.visualization.VisualMode;
|
|
||||||
import com.boydti.fawe.object.extent.ResettableExtent;
|
import com.boydti.fawe.object.extent.ResettableExtent;
|
||||||
import com.boydti.fawe.object.mask.MaskedTargetBlock;
|
import com.boydti.fawe.object.mask.MaskedTargetBlock;
|
||||||
import com.boydti.fawe.object.pattern.PatternTraverser;
|
import com.boydti.fawe.object.pattern.PatternTraverser;
|
||||||
@ -93,7 +89,6 @@ public class BrushTool
|
|||||||
protected static int MAX_RANGE = 500;
|
protected static int MAX_RANGE = 500;
|
||||||
protected static int DEFAULT_RANGE = 240; // 500 is laggy as the default
|
protected static int DEFAULT_RANGE = 240; // 500 is laggy as the default
|
||||||
protected int range = -1;
|
protected int range = -1;
|
||||||
private VisualMode visualMode = VisualMode.NONE;
|
|
||||||
private TargetMode targetMode = TargetMode.TARGET_BLOCK_RANGE;
|
private TargetMode targetMode = TargetMode.TARGET_BLOCK_RANGE;
|
||||||
private Mask traceMask = null;
|
private Mask traceMask = null;
|
||||||
private int targetOffset;
|
private int targetOffset;
|
||||||
@ -102,8 +97,6 @@ public class BrushTool
|
|||||||
private transient BrushSettings secondary = new BrushSettings();
|
private transient BrushSettings secondary = new BrushSettings();
|
||||||
private transient BrushSettings context = primary;
|
private transient BrushSettings context = primary;
|
||||||
|
|
||||||
private transient PersistentChunkSendProcessor visualExtent;
|
|
||||||
|
|
||||||
private transient BaseItem holder;
|
private transient BaseItem holder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -517,26 +510,6 @@ public class BrushTool
|
|||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setVisualMode(Player player, VisualMode visualMode) {
|
|
||||||
if (visualMode == null) {
|
|
||||||
visualMode = VisualMode.NONE;
|
|
||||||
}
|
|
||||||
if (this.visualMode != visualMode) {
|
|
||||||
if (this.visualMode != VisualMode.NONE) {
|
|
||||||
clear(player);
|
|
||||||
}
|
|
||||||
this.visualMode = visualMode;
|
|
||||||
if (visualMode != VisualMode.NONE) {
|
|
||||||
try {
|
|
||||||
queueVisualization(player);
|
|
||||||
} catch (Throwable e) {
|
|
||||||
WorldEdit.getInstance().getPlatformManager().handleThrowable(e, player);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
|
|
||||||
public TargetMode getTargetMode() {
|
public TargetMode getTargetMode() {
|
||||||
return targetMode;
|
return targetMode;
|
||||||
}
|
}
|
||||||
@ -545,114 +518,19 @@ public class BrushTool
|
|||||||
return targetOffset;
|
return targetOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
public VisualMode getVisualMode() {
|
|
||||||
return visualMode;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean increment(Player player, int amount) {
|
public boolean increment(Player player, int amount) {
|
||||||
BrushSettings current = getContext();
|
BrushSettings current = getContext();
|
||||||
Scroll tmp = current.getScrollAction();
|
Scroll tmp = current.getScrollAction();
|
||||||
if (tmp != null) {
|
if (tmp != null) {
|
||||||
tmp.setTool(this);
|
tmp.setTool(this);
|
||||||
if (tmp.increment(player, amount)) {
|
return tmp.increment(player, amount);
|
||||||
if (visualMode != VisualMode.NONE) {
|
|
||||||
try {
|
|
||||||
queueVisualization(player);
|
|
||||||
} catch (Throwable e) {
|
|
||||||
WorldEdit.getInstance().getPlatformManager().handleThrowable(e, player);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (visualMode != VisualMode.NONE) {
|
|
||||||
clear(player);
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void queueVisualization(Player player) {
|
|
||||||
Fawe.get().getVisualQueue().queue(player);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
public synchronized void visualize(BrushTool.BrushAction action, Player player)
|
|
||||||
throws WorldEditException {
|
|
||||||
VisualMode mode = getVisualMode();
|
|
||||||
if (mode == VisualMode.NONE) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
BrushSettings current = getContext();
|
|
||||||
Brush brush = current.getBrush();
|
|
||||||
if (brush == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
EditSessionBuilder builder =
|
|
||||||
new EditSessionBuilder(player.getWorld()).command(current.toString()).player(player)
|
|
||||||
.allowedRegionsEverywhere().autoQueue(false).blockBag(null).changeSetNull()
|
|
||||||
.fastmode(true).combineStages(true);
|
|
||||||
EditSession editSession = builder.build();
|
|
||||||
|
|
||||||
World world = editSession.getWorld();
|
|
||||||
Supplier<Collection<Player>> players = () -> Collections.singleton(player);
|
|
||||||
|
|
||||||
PersistentChunkSendProcessor newVisualExtent =
|
|
||||||
new PersistentChunkSendProcessor(world, this.visualExtent, players);
|
|
||||||
ExtentTraverser<IChunkExtent> traverser =
|
|
||||||
new ExtentTraverser<>(editSession).find(IChunkExtent.class);
|
|
||||||
if (traverser == null) {
|
|
||||||
throw new IllegalStateException("No queue found");
|
|
||||||
}
|
|
||||||
|
|
||||||
IChunkExtent chunkExtent = traverser.get();
|
|
||||||
if (this.visualExtent != null) {
|
|
||||||
this.visualExtent.init(chunkExtent);
|
|
||||||
}
|
|
||||||
newVisualExtent.init(chunkExtent);
|
|
||||||
|
|
||||||
editSession.addProcessor(newVisualExtent);
|
|
||||||
editSession.addProcessor(NullProcessor.getInstance());
|
|
||||||
|
|
||||||
BlockVector3 position = getPosition(editSession, player);
|
|
||||||
if (position != null) {
|
|
||||||
switch (mode) {
|
|
||||||
case POINT:
|
|
||||||
editSession.setBlock(position, VisualExtent.VISUALIZE_BLOCK_DEFAULT);
|
|
||||||
break;
|
|
||||||
case OUTLINE: {
|
|
||||||
new PatternTraverser(current).reset(editSession);
|
|
||||||
brush.build(editSession, position, current.getMaterial(), current.getSize());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
throw new IllegalStateException("Unexpected value: " + mode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
editSession.flushQueue();
|
|
||||||
|
|
||||||
if (visualExtent != null) {
|
|
||||||
// clear old data
|
|
||||||
visualExtent.flush();
|
|
||||||
}
|
|
||||||
visualExtent = newVisualExtent;
|
|
||||||
newVisualExtent.flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void clear(Player player) {
|
|
||||||
Fawe.get().getVisualQueue().dequeue(player);
|
|
||||||
if (visualExtent != null) {
|
|
||||||
visualExtent.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean move(Player player) {
|
public boolean move(Player player) {
|
||||||
if (visualMode != VisualMode.NONE) {
|
|
||||||
queueVisualization(player);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,6 @@ package com.sk89q.worldedit.entity;
|
|||||||
import com.boydti.fawe.Fawe;
|
import com.boydti.fawe.Fawe;
|
||||||
import com.boydti.fawe.config.Caption;
|
import com.boydti.fawe.config.Caption;
|
||||||
import com.boydti.fawe.config.Settings;
|
import com.boydti.fawe.config.Settings;
|
||||||
import com.boydti.fawe.object.brush.visualization.VirtualWorld;
|
|
||||||
import com.boydti.fawe.object.clipboard.DiskOptimizedClipboard;
|
import com.boydti.fawe.object.clipboard.DiskOptimizedClipboard;
|
||||||
import com.boydti.fawe.regions.FaweMaskManager;
|
import com.boydti.fawe.regions.FaweMaskManager;
|
||||||
import com.boydti.fawe.util.MainUtil;
|
import com.boydti.fawe.util.MainUtil;
|
||||||
@ -385,14 +384,6 @@ public interface Player extends Entity, Actor {
|
|||||||
* @return Editing world
|
* @return Editing world
|
||||||
*/
|
*/
|
||||||
default World getWorldForEditing() {
|
default World getWorldForEditing() {
|
||||||
VirtualWorld virtual = getSession().getVirtualWorld();
|
|
||||||
if (virtual != null) {
|
|
||||||
return virtual;
|
|
||||||
}
|
|
||||||
// CFICommands.CFISettings cfi = getMeta("CFISettings");
|
|
||||||
// if (cfi != null && cfi.hasGenerator() && cfi.getGenerator().hasPacketViewer()) {
|
|
||||||
// return cfi.getGenerator();
|
|
||||||
// }
|
|
||||||
return WorldEdit.getInstance().getPlatformManager().getWorldForEditing(getWorld());
|
return WorldEdit.getInstance().getPlatformManager().getWorldForEditing(getWorld());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -404,7 +395,6 @@ public interface Player extends Entity, Actor {
|
|||||||
getSession().setClipboard(null);
|
getSession().setClipboard(null);
|
||||||
if (Settings.IMP.HISTORY.DELETE_ON_LOGOUT) {
|
if (Settings.IMP.HISTORY.DELETE_ON_LOGOUT) {
|
||||||
getSession().clearHistory();
|
getSession().clearHistory();
|
||||||
getSession().unregisterTools(this);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,7 +22,6 @@ package com.sk89q.worldedit.extension.platform;
|
|||||||
import com.boydti.fawe.Fawe;
|
import com.boydti.fawe.Fawe;
|
||||||
import com.boydti.fawe.config.Settings;
|
import com.boydti.fawe.config.Settings;
|
||||||
import com.boydti.fawe.object.FaweLimit;
|
import com.boydti.fawe.object.FaweLimit;
|
||||||
import com.boydti.fawe.object.brush.visualization.VirtualWorld;
|
|
||||||
import com.boydti.fawe.util.task.InterruptableCondition;
|
import com.boydti.fawe.util.task.InterruptableCondition;
|
||||||
import com.sk89q.worldedit.EditSession;
|
import com.sk89q.worldedit.EditSession;
|
||||||
import com.sk89q.worldedit.entity.MapMetadatable;
|
import com.sk89q.worldedit.entity.MapMetadatable;
|
||||||
@ -242,22 +241,6 @@ public interface Actor extends Identifiable, SessionOwner, Subject, MapMetadatab
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
VirtualWorld world = getSession().getVirtualWorld();
|
|
||||||
if (world != null) {
|
|
||||||
if (close) {
|
|
||||||
try {
|
|
||||||
world.close(false);
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
world.close(false);
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return cancelled;
|
return cancelled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,7 +21,6 @@ package com.sk89q.worldedit.extension.platform;
|
|||||||
|
|
||||||
import com.boydti.fawe.config.Caption;
|
import com.boydti.fawe.config.Caption;
|
||||||
import com.boydti.fawe.config.Settings;
|
import com.boydti.fawe.config.Settings;
|
||||||
import com.boydti.fawe.object.brush.visualization.VirtualWorld;
|
|
||||||
import com.boydti.fawe.object.exception.FaweException;
|
import com.boydti.fawe.object.exception.FaweException;
|
||||||
import com.boydti.fawe.object.pattern.PatternTraverser;
|
import com.boydti.fawe.object.pattern.PatternTraverser;
|
||||||
import com.boydti.fawe.wrappers.LocationMaskedPlayerWrapper;
|
import com.boydti.fawe.wrappers.LocationMaskedPlayerWrapper;
|
||||||
@ -347,18 +346,6 @@ public class PlatformManager {
|
|||||||
try {
|
try {
|
||||||
Vector3 vector = location.toVector();
|
Vector3 vector = location.toVector();
|
||||||
|
|
||||||
VirtualWorld virtual = session.getVirtualWorld();
|
|
||||||
if (virtual != null) {
|
|
||||||
if (Settings.IMP.EXPERIMENTAL.OTHER) {
|
|
||||||
LOGGER.info("virtualWorld was not null in handlePlayerInput()");
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual.handleBlockInteract(player, vector.toBlockPoint(), event);
|
|
||||||
if (event.isCancelled()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event.getType() == Interaction.HIT) {
|
if (event.getType() == Interaction.HIT) {
|
||||||
// superpickaxe is special because its primary interaction is a left click, not a right click
|
// superpickaxe is special because its primary interaction is a left click, not a right click
|
||||||
// in addition, it is implicitly bound to all pickaxe items, not just a single tool item
|
// in addition, it is implicitly bound to all pickaxe items, not just a single tool item
|
||||||
@ -421,16 +408,6 @@ public class PlatformManager {
|
|||||||
// making changes to the world
|
// making changes to the world
|
||||||
Player player = createProxyActor(event.getPlayer());
|
Player player = createProxyActor(event.getPlayer());
|
||||||
LocalSession session = worldEdit.getSessionManager().get(player);
|
LocalSession session = worldEdit.getSessionManager().get(player);
|
||||||
VirtualWorld virtual = session.getVirtualWorld();
|
|
||||||
if (virtual != null) {
|
|
||||||
if (Settings.IMP.EXPERIMENTAL.OTHER) {
|
|
||||||
LOGGER.info("virtualWorld was not null in handlePlayerInput()");
|
|
||||||
}
|
|
||||||
virtual.handlePlayerInput(player, event);
|
|
||||||
if (event.isCancelled()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
switch (event.getInputType()) {
|
switch (event.getInputType()) {
|
||||||
|
@ -507,6 +507,10 @@ public interface Extent extends InputExtent, OutputExtent {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default int getMinY() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
default int getMaxY() {
|
default int getMaxY() {
|
||||||
return 255;
|
return 255;
|
||||||
}
|
}
|
||||||
|
@ -19,22 +19,25 @@
|
|||||||
|
|
||||||
package com.sk89q.worldedit.session.request;
|
package com.sk89q.worldedit.session.request;
|
||||||
|
|
||||||
import com.boydti.fawe.object.collection.CleanableThreadLocal;
|
|
||||||
import com.sk89q.worldedit.EditSession;
|
import com.sk89q.worldedit.EditSession;
|
||||||
import com.sk89q.worldedit.LocalSession;
|
import com.sk89q.worldedit.LocalSession;
|
||||||
import com.sk89q.worldedit.extension.platform.Actor;
|
import com.sk89q.worldedit.extension.platform.Actor;
|
||||||
import com.sk89q.worldedit.extent.Extent;
|
import com.sk89q.worldedit.extent.Extent;
|
||||||
import com.sk89q.worldedit.world.World;
|
import com.sk89q.worldedit.world.World;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes the current request using a {@link ThreadLocal}.
|
* Describes the current request using a {@link ThreadLocal}.
|
||||||
*/
|
*/
|
||||||
public final class Request {
|
public final class Request {
|
||||||
|
|
||||||
private static final CleanableThreadLocal<Request> threadLocal = new CleanableThreadLocal<>(Request::new);
|
private static final ThreadLocal<Request> threadLocal = ThreadLocal.withInitial(Request::new);
|
||||||
|
// TODO any better way to deal with this?
|
||||||
|
private static final Map<Thread, Request> requests = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private World world;
|
private World world;
|
||||||
@ -49,10 +52,11 @@ public final class Request {
|
|||||||
private boolean valid;
|
private boolean valid;
|
||||||
|
|
||||||
private Request() {
|
private Request() {
|
||||||
|
requests.put(Thread.currentThread(), this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<Request> getAll() {
|
public static Collection<Request> getAll() {
|
||||||
return threadLocal.getAll();
|
return requests.values();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -154,6 +158,7 @@ public final class Request {
|
|||||||
public static void reset() {
|
public static void reset() {
|
||||||
request().invalidate();
|
request().invalidate();
|
||||||
threadLocal.remove();
|
threadLocal.remove();
|
||||||
|
requests.remove(Thread.currentThread());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren