Mirror von
https://github.com/IntellectualSites/FastAsyncWorldEdit.git
synchronisiert 2024-09-06 16:12:51 +02:00
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.NMSAdapter;
|
||||
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.RenderListener;
|
||||
import com.boydti.fawe.bukkit.regions.GriefPreventionFeature;
|
||||
@ -57,8 +55,6 @@ public class FaweBukkit implements IFawe, Listener {
|
||||
private ItemUtil itemUtil;
|
||||
|
||||
private boolean listeningImages;
|
||||
private BukkitImageListener imageListener;
|
||||
private CFIPacketListener packetListener;
|
||||
private final boolean chunksStretched;
|
||||
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() {
|
||||
return new BukkitQueueHandler();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized ImageViewer getImageViewer(com.sk89q.worldedit.entity.Player player) {
|
||||
if (listeningImages && imageListener == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
listeningImages = true;
|
||||
registerPacketListener();
|
||||
PluginManager manager = Bukkit.getPluginManager();
|
||||
|
||||
if (manager.getPlugin("PacketListenerApi") == null) {
|
||||
@ -140,11 +124,7 @@ public class FaweBukkit implements IFawe, Listener {
|
||||
fos.write(jarData);
|
||||
}
|
||||
}
|
||||
BukkitImageViewer viewer = new BukkitImageViewer(BukkitAdapter.adapt(player));
|
||||
if (imageListener == null) {
|
||||
this.imageListener = new BukkitImageListener(plugin);
|
||||
}
|
||||
return viewer;
|
||||
return new BukkitImageViewer(BukkitAdapter.adapt(player));
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
return null;
|
||||
|
@ -7,7 +7,6 @@ import com.boydti.fawe.bukkit.adapter.NMSAdapter;
|
||||
import com.boydti.fawe.config.Settings;
|
||||
import com.boydti.fawe.object.collection.BitArray;
|
||||
import com.boydti.fawe.util.MathMan;
|
||||
import com.boydti.fawe.util.ReflectionUtils;
|
||||
import com.boydti.fawe.util.TaskManager;
|
||||
import com.mojang.datafixers.util.Either;
|
||||
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 fieldNonEmptyBlockCount;
|
||||
|
||||
private static final Field fieldDirtyCount;
|
||||
private static final Field fieldDirtyBits;
|
||||
|
||||
private static final Field fieldBiomeArray;
|
||||
|
||||
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 Field fieldLock;
|
||||
private static final long fieldLockOffset;
|
||||
|
||||
static {
|
||||
try {
|
||||
@ -93,11 +90,6 @@ public final class BukkitAdapter_1_15_2 extends NMSAdapter {
|
||||
fieldNonEmptyBlockCount = ChunkSection.class.getDeclaredField("nonEmptyBlockCount");
|
||||
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.setAccessible(true);
|
||||
|
||||
@ -109,12 +101,10 @@ public final class BukkitAdapter_1_15_2 extends NMSAdapter {
|
||||
declaredSetLightNibbleArray.setAccessible(true);
|
||||
methodSetLightNibbleArray = MethodHandles.lookup().unreflect(declaredSetLightNibbleArray);
|
||||
|
||||
Field tmp = DataPaletteBlock.class.getDeclaredField("j");
|
||||
ReflectionUtils.setAccessibleNonFinal(tmp);
|
||||
fieldLock = tmp;
|
||||
fieldLock.setAccessible(true);
|
||||
|
||||
Unsafe unsafe = UnsafeUtils.getUNSAFE();
|
||||
fieldLock = DataPaletteBlock.class.getDeclaredField("j");
|
||||
fieldLockOffset = unsafe.objectFieldOffset(fieldLock);
|
||||
|
||||
CHUNKSECTION_BASE = unsafe.arrayBaseOffset(ChunkSection[].class);
|
||||
int scale = unsafe.arrayIndexScale(ChunkSection[].class);
|
||||
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?
|
||||
try {
|
||||
synchronized (section) {
|
||||
Unsafe unsafe = UnsafeUtils.getUNSAFE();
|
||||
DataPaletteBlock<IBlockData> blocks = section.getBlocks();
|
||||
ReentrantLock currentLock = (ReentrantLock) fieldLock.get(blocks);
|
||||
ReentrantLock currentLock = (ReentrantLock) unsafe.getObject(blocks, fieldLockOffset);
|
||||
if (currentLock instanceof DelegateLock) {
|
||||
return (DelegateLock) currentLock;
|
||||
}
|
||||
DelegateLock newLock = new DelegateLock(currentLock);
|
||||
fieldLock.set(blocks, newLock);
|
||||
unsafe.putObject(blocks, fieldLockOffset, newLock);
|
||||
return newLock;
|
||||
}
|
||||
} catch (IllegalAccessException e) {
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
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.object.collection.BitArrayUnstretched;
|
||||
import com.boydti.fawe.util.MathMan;
|
||||
import com.boydti.fawe.util.ReflectionUtils;
|
||||
import com.boydti.fawe.util.TaskManager;
|
||||
import com.mojang.datafixers.util.Either;
|
||||
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 fieldNonEmptyBlockCount;
|
||||
|
||||
private static final Field fieldDirtyCount;
|
||||
private static final Field fieldDirtyBits;
|
||||
|
||||
private static final Field fieldBiomeArray;
|
||||
|
||||
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 Field fieldLock;
|
||||
private static final long fieldLockOffset;
|
||||
|
||||
static {
|
||||
try {
|
||||
@ -94,11 +91,6 @@ public final class BukkitAdapter_1_16_1 extends NMSAdapter {
|
||||
fieldNonEmptyBlockCount = ChunkSection.class.getDeclaredField("nonEmptyBlockCount");
|
||||
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.setAccessible(true);
|
||||
|
||||
@ -106,12 +98,10 @@ public final class BukkitAdapter_1_16_1 extends NMSAdapter {
|
||||
declaredGetVisibleChunk.setAccessible(true);
|
||||
methodGetVisibleChunk = MethodHandles.lookup().unreflect(declaredGetVisibleChunk);
|
||||
|
||||
Field tmp = DataPaletteBlock.class.getDeclaredField("j");
|
||||
ReflectionUtils.setAccessibleNonFinal(tmp);
|
||||
fieldLock = tmp;
|
||||
fieldLock.setAccessible(true);
|
||||
|
||||
Unsafe unsafe = UnsafeUtils.getUNSAFE();
|
||||
fieldLock = DataPaletteBlock.class.getDeclaredField("j");
|
||||
fieldLockOffset = unsafe.objectFieldOffset(fieldLock);
|
||||
|
||||
CHUNKSECTION_BASE = unsafe.arrayBaseOffset(ChunkSection[].class);
|
||||
int scale = unsafe.arrayIndexScale(ChunkSection[].class);
|
||||
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?
|
||||
try {
|
||||
synchronized (section) {
|
||||
Unsafe unsafe = UnsafeUtils.getUNSAFE();
|
||||
DataPaletteBlock<IBlockData> blocks = section.getBlocks();
|
||||
ReentrantLock currentLock = (ReentrantLock) fieldLock.get(blocks);
|
||||
ReentrantLock currentLock = (ReentrantLock) unsafe.getObject(blocks, fieldLockOffset);
|
||||
if (currentLock instanceof DelegateLock) {
|
||||
return (DelegateLock) currentLock;
|
||||
}
|
||||
DelegateLock newLock = new DelegateLock(currentLock);
|
||||
fieldLock.set(blocks, newLock);
|
||||
unsafe.putObject(blocks, fieldLockOffset, newLock);
|
||||
return newLock;
|
||||
}
|
||||
} catch (IllegalAccessException e) {
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
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.object.collection.BitArrayUnstretched;
|
||||
import com.boydti.fawe.util.MathMan;
|
||||
import com.boydti.fawe.util.ReflectionUtils;
|
||||
import com.boydti.fawe.util.TaskManager;
|
||||
import com.mojang.datafixers.util.Either;
|
||||
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 fieldNonEmptyBlockCount;
|
||||
|
||||
private static final Field fieldDirty;
|
||||
private static final Field fieldDirtyBlocks;
|
||||
|
||||
private static final Field fieldBiomeArray;
|
||||
|
||||
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 Field fieldLock;
|
||||
private static final long fieldLockOffset;
|
||||
|
||||
static {
|
||||
try {
|
||||
@ -94,11 +91,6 @@ public final class BukkitAdapter_1_16_2 extends NMSAdapter {
|
||||
fieldNonEmptyBlockCount = ChunkSection.class.getDeclaredField("nonEmptyBlockCount");
|
||||
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.setAccessible(true);
|
||||
|
||||
@ -106,25 +98,16 @@ public final class BukkitAdapter_1_16_2 extends NMSAdapter {
|
||||
declaredGetVisibleChunk.setAccessible(true);
|
||||
methodGetVisibleChunk = MethodHandles.lookup().unreflect(declaredGetVisibleChunk);
|
||||
|
||||
Field tmp = DataPaletteBlock.class.getDeclaredField("j");
|
||||
ReflectionUtils.setAccessibleNonFinal(tmp);
|
||||
fieldLock = tmp;
|
||||
fieldLock.setAccessible(true);
|
||||
|
||||
Unsafe unsafe = UnsafeUtils.getUNSAFE();
|
||||
fieldLock = DataPaletteBlock.class.getDeclaredField("j");
|
||||
fieldLockOffset = unsafe.objectFieldOffset(fieldLock);
|
||||
|
||||
CHUNKSECTION_BASE = unsafe.arrayBaseOffset(ChunkSection[].class);
|
||||
int scale = unsafe.arrayIndexScale(ChunkSection[].class);
|
||||
if ((scale & (scale - 1)) != 0) {
|
||||
throw new Error("data type scale not a power of two");
|
||||
}
|
||||
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) {
|
||||
throw e;
|
||||
} 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?
|
||||
try {
|
||||
synchronized (section) {
|
||||
Unsafe unsafe = UnsafeUtils.getUNSAFE();
|
||||
DataPaletteBlock<IBlockData> blocks = section.getBlocks();
|
||||
ReentrantLock currentLock = (ReentrantLock) fieldLock.get(blocks);
|
||||
ReentrantLock currentLock = (ReentrantLock) unsafe.getObject(blocks, fieldLockOffset);
|
||||
if (currentLock instanceof DelegateLock) {
|
||||
return (DelegateLock) currentLock;
|
||||
}
|
||||
DelegateLock newLock = new DelegateLock(currentLock);
|
||||
fieldLock.set(blocks, newLock);
|
||||
unsafe.putObject(blocks, fieldLockOffset, newLock);
|
||||
return newLock;
|
||||
}
|
||||
} catch (IllegalAccessException e) {
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
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.object.collection.BitArrayUnstretched;
|
||||
import com.boydti.fawe.util.MathMan;
|
||||
import com.boydti.fawe.util.ReflectionUtils;
|
||||
import com.boydti.fawe.util.TaskManager;
|
||||
import com.mojang.datafixers.util.Either;
|
||||
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 fieldNonEmptyBlockCount;
|
||||
|
||||
private static final Field fieldDirty;
|
||||
private static final Field fieldDirtyBlocks;
|
||||
|
||||
private static final Field fieldBiomeArray;
|
||||
|
||||
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 Field fieldLock;
|
||||
private static final long fieldLockOffset;
|
||||
|
||||
static {
|
||||
try {
|
||||
@ -94,11 +91,6 @@ public final class BukkitAdapter_1_16_5 extends NMSAdapter {
|
||||
fieldNonEmptyBlockCount = ChunkSection.class.getDeclaredField("nonEmptyBlockCount");
|
||||
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.setAccessible(true);
|
||||
|
||||
@ -106,25 +98,16 @@ public final class BukkitAdapter_1_16_5 extends NMSAdapter {
|
||||
declaredGetVisibleChunk.setAccessible(true);
|
||||
methodGetVisibleChunk = MethodHandles.lookup().unreflect(declaredGetVisibleChunk);
|
||||
|
||||
Field tmp = DataPaletteBlock.class.getDeclaredField("j");
|
||||
ReflectionUtils.setAccessibleNonFinal(tmp);
|
||||
fieldLock = tmp;
|
||||
fieldLock.setAccessible(true);
|
||||
|
||||
Unsafe unsafe = UnsafeUtils.getUNSAFE();
|
||||
fieldLock = DataPaletteBlock.class.getDeclaredField("j");
|
||||
fieldLockOffset = unsafe.objectFieldOffset(fieldLock);
|
||||
|
||||
CHUNKSECTION_BASE = unsafe.arrayBaseOffset(ChunkSection[].class);
|
||||
int scale = unsafe.arrayIndexScale(ChunkSection[].class);
|
||||
if ((scale & (scale - 1)) != 0) {
|
||||
throw new Error("data type scale not a power of two");
|
||||
}
|
||||
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) {
|
||||
throw e;
|
||||
} 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?
|
||||
try {
|
||||
synchronized (section) {
|
||||
Unsafe unsafe = UnsafeUtils.getUNSAFE();
|
||||
DataPaletteBlock<IBlockData> blocks = section.getBlocks();
|
||||
ReentrantLock currentLock = (ReentrantLock) fieldLock.get(blocks);
|
||||
ReentrantLock currentLock = (ReentrantLock) unsafe.getObject(blocks, fieldLockOffset);
|
||||
if (currentLock instanceof DelegateLock) {
|
||||
return (DelegateLock) currentLock;
|
||||
}
|
||||
DelegateLock newLock = new DelegateLock(currentLock);
|
||||
fieldLock.set(blocks, newLock);
|
||||
unsafe.putObject(blocks, fieldLockOffset, newLock);
|
||||
return newLock;
|
||||
}
|
||||
} catch (IllegalAccessException e) {
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
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);
|
||||
if (playerChunk == null) {
|
||||
return;
|
||||
|
@ -689,7 +689,7 @@ public class BukkitGetBlocks_1_16_5 extends CharGetBlocks implements BukkitGetBl
|
||||
|
||||
@Override
|
||||
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
|
||||
|
@ -214,7 +214,7 @@ public class FAWEWorldNativeAccess_1_16_R3 implements WorldNativeAccess<Chunk, I
|
||||
return;
|
||||
}
|
||||
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,
|
||||
sideEffectSet != null && sideEffectSet.shouldApply(SideEffect.UPDATE)));
|
||||
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 z = pos.z;
|
||||
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);
|
||||
}
|
||||
|
@ -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) {
|
||||
final String className = "net.minecraft.server." + getVersion() + "." + name;
|
||||
return ReflectionUtils.getClass(className);
|
||||
|
@ -2,7 +2,6 @@ package com.boydti.fawe;
|
||||
|
||||
import com.boydti.fawe.beta.implementation.queue.QueueHandler;
|
||||
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.CleanTextureUtil;
|
||||
import com.boydti.fawe.util.FaweTimer;
|
||||
@ -82,7 +81,6 @@ public class Fawe {
|
||||
*/
|
||||
private final FaweTimer timer;
|
||||
private FaweVersion version;
|
||||
private VisualQueue visualQueue;
|
||||
private TextureUtil textures;
|
||||
|
||||
|
||||
@ -148,7 +146,6 @@ public class Fawe {
|
||||
// Delayed worldedit setup
|
||||
TaskManager.IMP.later(() -> {
|
||||
try {
|
||||
visualQueue = new VisualQueue(3);
|
||||
WEManager.IMP.managers.addAll(Fawe.this.implementation.getMaskManagers());
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
@ -208,13 +205,6 @@ public class Fawe {
|
||||
return timer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the visual queue.
|
||||
*/
|
||||
public VisualQueue getVisualQueue() {
|
||||
return visualQueue;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.BitArrayUnstretched;
|
||||
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.FaweChunkLoadException;
|
||||
import com.boydti.fawe.object.exception.FaweException;
|
||||
@ -80,23 +79,19 @@ public enum FaweCache implements Trimable {
|
||||
|
||||
@Override
|
||||
public synchronized boolean trim(boolean aggressive) {
|
||||
if (aggressive) {
|
||||
CleanableThreadLocal.cleanAll();
|
||||
} else {
|
||||
CHUNK_FLAG.clean();
|
||||
BYTE_BUFFER_8192.clean();
|
||||
BLOCK_TO_PALETTE.clean();
|
||||
PALETTE_TO_BLOCK.clean();
|
||||
BLOCK_STATES.clean();
|
||||
SECTION_BLOCKS.clean();
|
||||
PALETTE_CACHE.clean();
|
||||
PALETTE_TO_BLOCK_CHAR.clean();
|
||||
INDEX_STORE.clean();
|
||||
CHUNK_FLAG.clean();
|
||||
BYTE_BUFFER_8192.clean();
|
||||
BLOCK_TO_PALETTE.clean();
|
||||
PALETTE_TO_BLOCK.clean();
|
||||
BLOCK_STATES.clean();
|
||||
SECTION_BLOCKS.clean();
|
||||
PALETTE_CACHE.clean();
|
||||
PALETTE_TO_BLOCK_CHAR.clean();
|
||||
INDEX_STORE.clean();
|
||||
|
||||
MUTABLE_VECTOR3.clean();
|
||||
MUTABLE_BLOCKVECTOR3.clean();
|
||||
SECTION_BITS_TO_CHAR.clean();
|
||||
}
|
||||
MUTABLE_VECTOR3.clean();
|
||||
MUTABLE_BLOCKVECTOR3.clean();
|
||||
SECTION_BITS_TO_CHAR.clean();
|
||||
for (Entry<Class<? extends IChunkSet>, Pool<? extends IChunkSet>> entry : REGISTERED_POOLS.entrySet()) {
|
||||
Pool<? extends IChunkSet> pool = entry.getValue();
|
||||
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<long[]> LONG_BUFFER_1024 = new CleanableThreadLocal<>(() -> new long[1024]);
|
||||
|
||||
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<>(() -> {
|
||||
int[] result = new int[BlockTypesCache.states.length];
|
||||
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[]> HEIGHT_STORE = new CleanableThreadLocal<>(() -> new int[256]);
|
||||
|
||||
/**
|
||||
* Holds data for a palette used in a chunk section
|
||||
*/
|
||||
@ -314,14 +304,6 @@ public enum FaweCache implements Trimable {
|
||||
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) {
|
||||
int[] blockToPalette = BLOCK_TO_PALETTE.get();
|
||||
int[] paletteToBlock = PALETTE_TO_BLOCK.get();
|
||||
|
@ -23,8 +23,6 @@ public interface IFawe {
|
||||
return null;
|
||||
}
|
||||
|
||||
public default void registerPacketListener() {}
|
||||
|
||||
String getPlatform();
|
||||
|
||||
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
|
||||
default BlockVector3 getMinimumPoint() {
|
||||
return BlockVector3.at(-30000000, 0, -30000000);
|
||||
return BlockVector3.at(-30000000, getMinY(), -30000000);
|
||||
}
|
||||
|
||||
@Override
|
||||
default BlockVector3 getMaximumPoint() {
|
||||
return BlockVector3.at(30000000, FaweCache.IMP.WORLD_MAX_Y, 30000000);
|
||||
return BlockVector3.at(30000000, getMaxY(), 30000000);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
import com.boydti.fawe.config.Caption;
|
||||
import com.boydti.fawe.object.brush.visualization.VisualExtent;
|
||||
import com.sk89q.worldedit.EditSession;
|
||||
import com.sk89q.worldedit.MaxChangedBlocksException;
|
||||
import com.sk89q.worldedit.WorldEditException;
|
||||
@ -35,16 +34,13 @@ public class CatenaryBrush implements Brush, ResettableTool {
|
||||
|
||||
@Override
|
||||
public void build(EditSession editSession, BlockVector3 pos2, final Pattern pattern, double size) throws MaxChangedBlocksException {
|
||||
boolean visual = editSession.getExtent() instanceof VisualExtent;
|
||||
Player player = editSession.getPlayer();
|
||||
if (player == null) {
|
||||
return; //todo throw error
|
||||
}
|
||||
if (pos1 == null || pos2.equals(pos1)) {
|
||||
if (!visual) {
|
||||
pos1 = pos2;
|
||||
player.print(Caption.of("fawe.worldedit.brush.brush.line.primary", pos2));
|
||||
}
|
||||
pos1 = pos2;
|
||||
player.print(Caption.of("fawe.worldedit.brush.brush.line.primary", pos2));
|
||||
return;
|
||||
}
|
||||
if (this.vertex == null) {
|
||||
@ -67,14 +63,12 @@ public class CatenaryBrush implements Brush, ResettableTool {
|
||||
} catch (WorldEditException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (!visual) {
|
||||
player.print(Caption.of("fawe.worldedit.brush.brush.line.secondary"));
|
||||
if (!select) {
|
||||
pos1 = null;
|
||||
return;
|
||||
}
|
||||
pos1 = pos2;
|
||||
player.print(Caption.of("fawe.worldedit.brush.brush.line.secondary"));
|
||||
if (!select) {
|
||||
pos1 = null;
|
||||
return;
|
||||
}
|
||||
pos1 = pos2;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,7 +1,6 @@
|
||||
package com.boydti.fawe.object.brush;
|
||||
|
||||
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.function.NullRegionFunction;
|
||||
import com.boydti.fawe.object.function.mask.AbstractDelegateMask;
|
||||
@ -56,9 +55,6 @@ public class CopyPastaBrush implements Brush, ResettableTool {
|
||||
}
|
||||
ClipboardHolder clipboard = session.getExistingClipboard();
|
||||
if (clipboard == null) {
|
||||
if (editSession.getExtent() instanceof VisualExtent) {
|
||||
return;
|
||||
}
|
||||
Mask mask = editSession.getMask();
|
||||
if (mask == null) {
|
||||
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.RotatableHeightMap;
|
||||
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.util.MathMan;
|
||||
import com.sk89q.worldedit.EditSession;
|
||||
@ -74,84 +73,6 @@ public class HeightBrush implements Brush {
|
||||
HeightMap map = getHeightMap();
|
||||
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();
|
||||
if (mask == Masks.alwaysTrue() || mask == Masks.alwaysTrue2D()) {
|
||||
mask = null;
|
||||
|
@ -1,7 +1,6 @@
|
||||
package com.boydti.fawe.object.brush;
|
||||
|
||||
import com.boydti.fawe.config.Caption;
|
||||
import com.boydti.fawe.object.brush.visualization.VisualExtent;
|
||||
import com.sk89q.worldedit.EditSession;
|
||||
import com.sk89q.worldedit.MaxChangedBlocksException;
|
||||
import com.sk89q.worldedit.command.tool.brush.Brush;
|
||||
@ -23,22 +22,17 @@ public class LineBrush implements Brush, ResettableTool {
|
||||
|
||||
@Override
|
||||
public void build(EditSession editSession, BlockVector3 position, Pattern pattern, double size) throws MaxChangedBlocksException {
|
||||
boolean visual = editSession.getExtent() instanceof VisualExtent;
|
||||
if (pos1 == null) {
|
||||
if (!visual) {
|
||||
pos1 = position;
|
||||
editSession.getPlayer().print(Caption.of("fawe.worldedit.brush.brush.line.primary", position));
|
||||
}
|
||||
pos1 = position;
|
||||
editSession.getPlayer().print(Caption.of("fawe.worldedit.brush.brush.line.primary", position));
|
||||
return;
|
||||
}
|
||||
editSession.drawLine(pattern, pos1, position, size, !shell, flat);
|
||||
if (!visual) {
|
||||
editSession.getPlayer().print(Caption.of("fawe.worldedit.brush.brush.line.secondary"));
|
||||
if (!select) {
|
||||
pos1 = null;
|
||||
} else {
|
||||
pos1 = position;
|
||||
}
|
||||
editSession.getPlayer().print(Caption.of("fawe.worldedit.brush.brush.line.secondary"));
|
||||
if (!select) {
|
||||
pos1 = null;
|
||||
} else {
|
||||
pos1 = position;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,6 @@ package com.boydti.fawe.object.brush;
|
||||
|
||||
import com.boydti.fawe.FaweCache;
|
||||
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.visitor.DFSRecursiveVisitor;
|
||||
import com.sk89q.worldedit.EditSession;
|
||||
@ -53,11 +52,6 @@ public class SplineBrush implements Brush, ResettableTool {
|
||||
} else {
|
||||
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);
|
||||
this.position = position;
|
||||
if (newPos) {
|
||||
@ -94,9 +88,7 @@ public class SplineBrush implements Brush, ResettableTool {
|
||||
}
|
||||
this.positionSets.add(points);
|
||||
player.print(Caption.of("fawe.worldedit.brush.spline.primary.2"));
|
||||
if (!visualization) {
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (positionSets.size() < 2) {
|
||||
player.print(Caption.of("fawe.worldedit.brush.brush.spline.secondary.error"));
|
||||
@ -110,7 +102,6 @@ public class SplineBrush implements Brush, ResettableTool {
|
||||
double tension = 0;
|
||||
double bias = 0;
|
||||
double continuity = 0;
|
||||
double quality = 10;
|
||||
|
||||
final List<Node> nodes = new ArrayList<>(centroids.size());
|
||||
|
||||
@ -121,7 +112,6 @@ public class SplineBrush implements Brush, ResettableTool {
|
||||
n.setContinuity(continuity);
|
||||
nodes.add(n);
|
||||
}
|
||||
int samples = numSplines;
|
||||
for (int i = 0; i < numSplines; i++) {
|
||||
List<BlockVector3> currentSpline = new ArrayList<>();
|
||||
for (ArrayList<BlockVector3> points : positionSets) {
|
||||
@ -132,13 +122,8 @@ public class SplineBrush implements Brush, ResettableTool {
|
||||
editSession.drawSpline(pattern, currentSpline, 0, 0, 0, 10, 0, true);
|
||||
}
|
||||
player.print(Caption.of("fawe.worldedit.brush.spline.secondary"));
|
||||
if (visualization) {
|
||||
numSplines = originalSize;
|
||||
positionSets.remove(positionSets.size() - 1);
|
||||
} else {
|
||||
positionSets.clear();
|
||||
numSplines = 0;
|
||||
}
|
||||
positionSets.clear();
|
||||
numSplines = 0;
|
||||
}
|
||||
|
||||
private Vector3 getCentroid(Collection<BlockVector3> points) {
|
||||
|
@ -1,7 +1,6 @@
|
||||
package com.boydti.fawe.object.brush;
|
||||
|
||||
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.util.MathMan;
|
||||
import com.sk89q.worldedit.EditSession;
|
||||
@ -36,7 +35,6 @@ public class SurfaceSpline implements Brush {
|
||||
@Override
|
||||
public void build(EditSession editSession, BlockVector3 pos, Pattern pattern, double radius) throws MaxChangedBlocksException {
|
||||
int maxY = editSession.getMaxY();
|
||||
boolean vis = editSession.getExtent() instanceof VisualExtent;
|
||||
if (path.isEmpty() || !pos.equals(path.get(path.size() - 1))) {
|
||||
int max = editSession.getNearestSurfaceTerrainBlock(pos.getBlockX(), pos.getBlockZ(), pos.getBlockY(), 0, editSession.getMaxY());
|
||||
if (max == -1) {
|
||||
@ -44,9 +42,7 @@ public class SurfaceSpline implements Brush {
|
||||
}
|
||||
path.add(BlockVector3.at(pos.getBlockX(), max, pos.getBlockZ()));
|
||||
editSession.getPlayer().print(Caption.of("fawe.worldedit.brush.spline.primary.2"));
|
||||
if (!vis) {
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
final List<Node> nodes = new ArrayList<>(path.size());
|
||||
final KochanekBartelsInterpolation interpol = new KochanekBartelsInterpolation();
|
||||
@ -103,9 +99,7 @@ public class SurfaceSpline implements Brush {
|
||||
}
|
||||
}
|
||||
editSession.setBlocks(newSet, pattern);
|
||||
if (!vis) {
|
||||
path.clear();
|
||||
}
|
||||
path.clear();
|
||||
}
|
||||
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.object.brush.ResettableTool;
|
||||
import com.boydti.fawe.object.brush.visualization.VisualExtent;
|
||||
import com.sk89q.worldedit.EditSession;
|
||||
import com.sk89q.worldedit.EmptyClipboardException;
|
||||
import com.sk89q.worldedit.LocalSession;
|
||||
@ -40,10 +39,6 @@ public class SweepBrush implements Brush, ResettableTool {
|
||||
|
||||
@Override
|
||||
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);
|
||||
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.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 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) {
|
||||
this(supplier, Function.identity());
|
||||
@ -51,123 +51,7 @@ public class CleanableThreadLocal<T> extends ThreadLocal<T> implements Closeable
|
||||
}
|
||||
|
||||
public void clean() {
|
||||
if (count.sumThenReset() > 0) {
|
||||
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();
|
||||
remove();
|
||||
}
|
||||
|
||||
@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.RegionWrapper;
|
||||
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.BlockBagChangeSet;
|
||||
import com.boydti.fawe.object.changeset.DiskStorageHistory;
|
||||
@ -379,7 +378,7 @@ public class EditSessionBuilder {
|
||||
}
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
@ -4,16 +4,12 @@ import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.reflect.AccessibleObject;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
public class ReflectionUtils {
|
||||
|
||||
@ -21,10 +17,6 @@ public class ReflectionUtils {
|
||||
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 {
|
||||
// let's make the field accessible
|
||||
field.setAccessible(true);
|
||||
@ -52,21 +44,6 @@ public class ReflectionUtils {
|
||||
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) {
|
||||
final Method getHandle = makeMethod(wrapper.getClass(), "getHandle");
|
||||
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) {
|
||||
Arrays.sort(fields, Comparator.comparing(Field::getName));
|
||||
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) {
|
||||
try {
|
||||
return Class.forName(name);
|
||||
@ -244,450 +110,4 @@ public class ReflectionUtils {
|
||||
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.FaweLimit;
|
||||
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.clipboard.MultiClipboardHolder;
|
||||
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 it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
@ -92,15 +93,12 @@ import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.TimeZone;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
@ -159,7 +157,6 @@ public class LocalSession implements TextureHolder {
|
||||
private transient UUID uuid;
|
||||
private transient volatile long historySize = 0;
|
||||
|
||||
private transient VirtualWorld virtual;
|
||||
private transient BlockVector3 cuiTemporaryBlock;
|
||||
@SuppressWarnings("unused")
|
||||
private transient EditSession.ReorderMode reorderMode = EditSession.ReorderMode.MULTI_STAGE;
|
||||
@ -752,35 +749,6 @@ public class LocalSession implements TextureHolder {
|
||||
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.
|
||||
*
|
||||
@ -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;
|
||||
}
|
||||
|
||||
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.TargetMode;
|
||||
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.StringMan;
|
||||
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(
|
||||
name = "target",
|
||||
aliases = {"tar", "/target", "/tar"},
|
||||
|
@ -19,10 +19,8 @@
|
||||
|
||||
package com.sk89q.worldedit.command.tool;
|
||||
|
||||
import com.boydti.fawe.Fawe;
|
||||
import com.boydti.fawe.beta.implementation.IChunkExtent;
|
||||
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.object.brush.BrushSettings;
|
||||
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.scroll.Scroll;
|
||||
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.mask.MaskedTargetBlock;
|
||||
import com.boydti.fawe.object.pattern.PatternTraverser;
|
||||
@ -93,7 +89,6 @@ public class BrushTool
|
||||
protected static int MAX_RANGE = 500;
|
||||
protected static int DEFAULT_RANGE = 240; // 500 is laggy as the default
|
||||
protected int range = -1;
|
||||
private VisualMode visualMode = VisualMode.NONE;
|
||||
private TargetMode targetMode = TargetMode.TARGET_BLOCK_RANGE;
|
||||
private Mask traceMask = null;
|
||||
private int targetOffset;
|
||||
@ -102,8 +97,6 @@ public class BrushTool
|
||||
private transient BrushSettings secondary = new BrushSettings();
|
||||
private transient BrushSettings context = primary;
|
||||
|
||||
private transient PersistentChunkSendProcessor visualExtent;
|
||||
|
||||
private transient BaseItem holder;
|
||||
|
||||
/**
|
||||
@ -517,26 +510,6 @@ public class BrushTool
|
||||
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() {
|
||||
return targetMode;
|
||||
}
|
||||
@ -545,114 +518,19 @@ public class BrushTool
|
||||
return targetOffset;
|
||||
}
|
||||
|
||||
public VisualMode getVisualMode() {
|
||||
return visualMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean increment(Player player, int amount) {
|
||||
BrushSettings current = getContext();
|
||||
Scroll tmp = current.getScrollAction();
|
||||
if (tmp != null) {
|
||||
tmp.setTool(this);
|
||||
if (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 tmp.increment(player, amount);
|
||||
}
|
||||
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
|
||||
public boolean move(Player player) {
|
||||
if (visualMode != VisualMode.NONE) {
|
||||
queueVisualization(player);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,6 @@ package com.sk89q.worldedit.entity;
|
||||
import com.boydti.fawe.Fawe;
|
||||
import com.boydti.fawe.config.Caption;
|
||||
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.regions.FaweMaskManager;
|
||||
import com.boydti.fawe.util.MainUtil;
|
||||
@ -385,14 +384,6 @@ public interface Player extends Entity, Actor {
|
||||
* @return Editing world
|
||||
*/
|
||||
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());
|
||||
}
|
||||
|
||||
@ -404,7 +395,6 @@ public interface Player extends Entity, Actor {
|
||||
getSession().setClipboard(null);
|
||||
if (Settings.IMP.HISTORY.DELETE_ON_LOGOUT) {
|
||||
getSession().clearHistory();
|
||||
getSession().unregisterTools(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,6 @@ package com.sk89q.worldedit.extension.platform;
|
||||
import com.boydti.fawe.Fawe;
|
||||
import com.boydti.fawe.config.Settings;
|
||||
import com.boydti.fawe.object.FaweLimit;
|
||||
import com.boydti.fawe.object.brush.visualization.VirtualWorld;
|
||||
import com.boydti.fawe.util.task.InterruptableCondition;
|
||||
import com.sk89q.worldedit.EditSession;
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,6 @@ package com.sk89q.worldedit.extension.platform;
|
||||
|
||||
import com.boydti.fawe.config.Caption;
|
||||
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.pattern.PatternTraverser;
|
||||
import com.boydti.fawe.wrappers.LocationMaskedPlayerWrapper;
|
||||
@ -347,18 +346,6 @@ public class PlatformManager {
|
||||
try {
|
||||
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) {
|
||||
// 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
|
||||
@ -421,16 +408,6 @@ public class PlatformManager {
|
||||
// making changes to the world
|
||||
Player player = createProxyActor(event.getPlayer());
|
||||
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 {
|
||||
switch (event.getInputType()) {
|
||||
|
@ -507,6 +507,10 @@ public interface Extent extends InputExtent, OutputExtent {
|
||||
return true;
|
||||
}
|
||||
|
||||
default int getMinY() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
default int getMaxY() {
|
||||
return 255;
|
||||
}
|
||||
|
@ -19,22 +19,25 @@
|
||||
|
||||
package com.sk89q.worldedit.session.request;
|
||||
|
||||
import com.boydti.fawe.object.collection.CleanableThreadLocal;
|
||||
import com.sk89q.worldedit.EditSession;
|
||||
import com.sk89q.worldedit.LocalSession;
|
||||
import com.sk89q.worldedit.extension.platform.Actor;
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
import com.sk89q.worldedit.world.World;
|
||||
|
||||
import java.util.List;
|
||||
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}.
|
||||
*/
|
||||
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
|
||||
private World world;
|
||||
@ -49,10 +52,11 @@ public final class Request {
|
||||
private boolean valid;
|
||||
|
||||
private Request() {
|
||||
requests.put(Thread.currentThread(), this);
|
||||
}
|
||||
|
||||
public static List<Request> getAll() {
|
||||
return threadLocal.getAll();
|
||||
public static Collection<Request> getAll() {
|
||||
return requests.values();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -154,6 +158,7 @@ public final class Request {
|
||||
public static void reset() {
|
||||
request().invalidate();
|
||||
threadLocal.remove();
|
||||
requests.remove(Thread.currentThread());
|
||||
}
|
||||
|
||||
/**
|
||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren