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>
This commit is contained in:
Hannes Greule 2021-05-29 00:47:46 +02:00 committed by GitHub
parent 04610822a2
commit 53681ccc59
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
54 changed files with 92 additions and 6829 deletions

View File

@ -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;

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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;

View File

@ -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

View File

@ -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);
}
}
};

View File

@ -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);
}

View File

@ -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;
}
}
}*/
}
}

View File

@ -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;
}
}
}

View File

@ -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);

View File

@ -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.
*

View File

@ -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();

View File

@ -23,8 +23,6 @@ public interface IFawe {
return null;
}
public default void registerPacketListener() {}
String getPlatform();
UUID getUUID(String name);

View File

@ -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;
}
}

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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));
}
}
}
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}
}

View File

@ -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() {}
}

View File

@ -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);
}
}

View 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

View 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.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();

View File

@ -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;

View 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.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;
}
}

View File

@ -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) {

View 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.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"));
}

View File

@ -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;

View File

@ -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();
}
}

View File

@ -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) {
}
}

View File

@ -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();
}
}

View File

@ -1,7 +0,0 @@
package com.boydti.fawe.object.brush.visualization;
public enum VisualMode {
NONE,
POINT,
OUTLINE
}

View File

@ -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);
}
}
}
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}
}

View File

@ -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();
}
}
}

View File

@ -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
}
}

View File

@ -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

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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);
}
}
}
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}
}
}
}

View File

@ -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"},

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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()) {

View File

@ -507,6 +507,10 @@ public interface Extent extends InputExtent, OutputExtent {
return true;
}
default int getMinY() {
return 0;
}
default int getMaxY() {
return 255;
}

View File

@ -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());
}
/**