From 395b46c842c4fef9f682815310125e88fcd20847 Mon Sep 17 00:00:00 2001 From: Jesse Boyd Date: Tue, 26 Dec 2017 22:15:24 +1100 Subject: [PATCH] Various major New building mode (using CFI): - CFI world is generated around the player in realtime - Use WorldEdit/VS brushes while editing - This is still being optimized - Use a 1K radius height brush with near instant results - https://github.com/boy0001/FastAsyncWorldedit/wiki/CreateFromImage Early example: https://www.youtube.com/watch?v=jp1SjanWN70 --- .../com/boydti/fawe/bukkit/FaweBukkit.java | 13 +- .../BukkitImageListener.java | 35 +- .../bukkit/listener/CFIPacketListener.java | 329 +++ .../bukkit/util/image/BukkitImageViewer.java | 43 +- .../fawe/bukkit/v0/BukkitChunk_All.java | 5 +- .../boydti/fawe/bukkit/v0/BukkitQueue_0.java | 101 + .../fawe/bukkit/v0/BukkitQueue_All.java | 31 +- .../fawe/bukkit/v1_12/BukkitQueue_1_12.java | 39 + .../bukkit/v1_12/packet/FaweChunkPacket.java | 95 +- .../bukkit/v1_12/packet/MCAChunkPacket.java | 83 + .../fawe/bukkit/v1_7/BukkitQueue17.java | 4 +- .../fawe/bukkit/wrapper/AsyncWorld.java | 2 +- bukkit/src/main/resources/plugin.yml | 1 + .../com/boydti/fawe/command/CFICommand.java | 17 +- .../com/boydti/fawe/command/CFICommands.java | 85 +- .../java/com/boydti/fawe/command/Cancel.java | 3 +- .../java/com/boydti/fawe/config/Settings.java | 8 +- .../boydti/fawe/example/MappedFaweQueue.java | 165 +- .../fawe/example/SimpleCharFaweChunk.java | 40 + .../fawe/jnbt/anvil/HeightMapMCADrawer.java | 15 +- .../jnbt/anvil/HeightMapMCAGenerator.java | 1779 ++++++++++++----- .../com/boydti/fawe/jnbt/anvil/MCAQueue.java | 10 +- .../fawe/object/ChangeSetFaweQueue.java | 2 +- .../com/boydti/fawe/object/FaweChunk.java | 10 +- .../boydti/fawe/object/FaweInputStream.java | 22 + .../boydti/fawe/object/FaweOutputStream.java | 23 + .../com/boydti/fawe/object/FawePlayer.java | 65 +- .../com/boydti/fawe/object/FaweQueue.java | 331 ++- .../boydti/fawe/object/MaskedFaweQueue.java | 2 +- .../com/boydti/fawe/object/Metadatable.java | 4 + .../boydti/fawe/object/brush/HeightBrush.java | 81 +- .../object/brush/heightmap/HeightMap.java | 6 +- .../brush/heightmap/ScalableHeightMap.java | 2 +- .../boydti/fawe/object/change/CFIChange.java | 60 + .../fawe/object/change/StreamChange.java | 51 + .../fawe/object/changeset/CFIChangeSet.java | 81 + .../fawe/object/changeset/FaweChangeSet.java | 1 - .../clipboard/MultiClipboardHolder.java | 2 +- .../object/collection/DifferentialArray.java | 237 +++ .../collection/DifferentialBlockBuffer.java | 212 ++ .../collection/DifferentialCollection.java | 7 + .../collection/IterableThreadLocal.java | 6 +- .../object/extent/FastWorldEditExtent.java | 5 + .../fawe/object/io/serialize/Serialize.java | 12 + .../fawe/object/io/serialize/Serializer.java | 50 + .../fawe/object/mask/MaskedTargetBlock.java | 9 +- .../fawe/object/queue/DelegateFaweQueue.java | 25 + .../object/queue/FaweQueueDelegateExtent.java | 1 - .../fawe/object/queue/IDelegateFaweQueue.java | 515 +++++ .../fawe/object/queue/LazyFaweChunk.java | 219 ++ .../boydti/fawe/util/CleanTextureUtil.java | 4 +- .../boydti/fawe/util/DelegateFaweQueue.java | 465 ----- .../boydti/fawe/util/EditSessionBuilder.java | 2 +- .../java/com/boydti/fawe/util/MainUtil.java | 12 +- .../java/com/boydti/fawe/util/MathMan.java | 4 + .../com/boydti/fawe/util/ReflectionUtils.java | 7 +- .../java/com/boydti/fawe/util/SetQueue.java | 3 + .../com/boydti/fawe/util/image/Drawable.java | 7 + .../boydti/fawe/util/image/ImageViewer.java | 3 +- .../boydti/fawe/wrappers/PlayerWrapper.java | 6 +- .../java/com/sk89q/worldedit/EditSession.java | 87 +- .../com/sk89q/worldedit/LocalSession.java | 26 +- .../worldedit/command/BrushCommands.java | 9 +- .../worldedit/command/BrushProcessor.java | 8 +- .../worldedit/command/ClipboardCommands.java | 3 - .../worldedit/command/RegionCommands.java | 7 +- .../extension/platform/PlatformManager.java | 21 +- .../extension/platform/PlayerProxy.java | 12 +- .../extent/AbstractDelegateExtent.java | 18 +- .../com/sk89q/worldedit/extent/Extent.java | 17 +- .../internal/command/WorldEditBinding.java | 5 +- .../worldedit/math/convolution/HeightMap.java | 50 +- .../com/sk89q/worldedit/util/TargetBlock.java | 214 ++ .../sk89q/worldedit/world/SimpleWorld.java | 186 ++ .../com/thevoxelbox/voxelsniper/Sniper.java | 3 +- 75 files changed, 4771 insertions(+), 1352 deletions(-) rename bukkit/src/main/java/com/boydti/fawe/bukkit/{util/image => listener}/BukkitImageListener.java (92%) create mode 100644 bukkit/src/main/java/com/boydti/fawe/bukkit/listener/CFIPacketListener.java create mode 100644 bukkit/src/main/java/com/boydti/fawe/bukkit/v1_12/packet/MCAChunkPacket.java create mode 100644 core/src/main/java/com/boydti/fawe/example/SimpleCharFaweChunk.java create mode 100644 core/src/main/java/com/boydti/fawe/object/change/CFIChange.java create mode 100644 core/src/main/java/com/boydti/fawe/object/change/StreamChange.java create mode 100644 core/src/main/java/com/boydti/fawe/object/changeset/CFIChangeSet.java create mode 100644 core/src/main/java/com/boydti/fawe/object/collection/DifferentialArray.java create mode 100644 core/src/main/java/com/boydti/fawe/object/collection/DifferentialBlockBuffer.java create mode 100644 core/src/main/java/com/boydti/fawe/object/collection/DifferentialCollection.java create mode 100644 core/src/main/java/com/boydti/fawe/object/io/serialize/Serialize.java create mode 100644 core/src/main/java/com/boydti/fawe/object/io/serialize/Serializer.java create mode 100644 core/src/main/java/com/boydti/fawe/object/queue/DelegateFaweQueue.java create mode 100644 core/src/main/java/com/boydti/fawe/object/queue/IDelegateFaweQueue.java create mode 100644 core/src/main/java/com/boydti/fawe/object/queue/LazyFaweChunk.java delete mode 100644 core/src/main/java/com/boydti/fawe/util/DelegateFaweQueue.java create mode 100644 core/src/main/java/com/boydti/fawe/util/image/Drawable.java create mode 100644 core/src/main/java/com/sk89q/worldedit/util/TargetBlock.java create mode 100644 core/src/main/java/com/sk89q/worldedit/world/SimpleWorld.java diff --git a/bukkit/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java b/bukkit/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java index 4bea196fe6..66c991d325 100644 --- a/bukkit/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java +++ b/bukkit/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java @@ -4,6 +4,7 @@ import com.boydti.fawe.IFawe; import com.boydti.fawe.bukkit.chat.BukkitChatManager; import com.boydti.fawe.bukkit.listener.BrushListener; +import com.boydti.fawe.bukkit.listener.CFIPacketListener; import com.boydti.fawe.bukkit.listener.RenderListener; import com.boydti.fawe.bukkit.regions.FactionsFeature; import com.boydti.fawe.bukkit.regions.FactionsOneFeature; @@ -19,7 +20,7 @@ import com.boydti.fawe.bukkit.util.VaultUtil; import com.boydti.fawe.bukkit.util.cui.CUIListener; import com.boydti.fawe.bukkit.util.cui.StructureCUI; -import com.boydti.fawe.bukkit.util.image.BukkitImageListener; +import com.boydti.fawe.bukkit.listener.BukkitImageListener; import com.boydti.fawe.bukkit.util.image.BukkitImageViewer; import com.boydti.fawe.bukkit.v0.BukkitQueue_0; import com.boydti.fawe.bukkit.v0.BukkitQueue_All; @@ -80,6 +81,7 @@ public class FaweBukkit implements IFawe, Listener { private boolean listeningImages; private BukkitImageListener imageListener; + private CFIPacketListener packetListener; private boolean listeningCui; private CUIListener cuiListener; @@ -136,10 +138,15 @@ public FaweBukkit(BukkitMain plugin) { MainUtil.handleError(e); Bukkit.getServer().shutdown(); } + + // Registered delayed Event Listeners TaskManager.IMP.task(new Runnable() { @Override public void run() { + // This class Bukkit.getPluginManager().registerEvents(FaweBukkit.this, FaweBukkit.this.plugin); + + // The tick limiter new ChunkListener(); } }); @@ -169,6 +176,10 @@ public synchronized ImageViewer getImageViewer(FawePlayer fp) { try { listeningImages = true; PluginManager manager = Bukkit.getPluginManager(); + if (manager.getPlugin("ProtocolLib") != null) { + packetListener = new CFIPacketListener(plugin); + } + if (manager.getPlugin("PacketListenerApi") == null) { File output = new File(plugin.getDataFolder().getParentFile(), "PacketListenerAPI_v3.6.0-SNAPSHOT.jar"); byte[] jarData = Jars.PL_v3_6_0.download(); diff --git a/bukkit/src/main/java/com/boydti/fawe/bukkit/util/image/BukkitImageListener.java b/bukkit/src/main/java/com/boydti/fawe/bukkit/listener/BukkitImageListener.java similarity index 92% rename from bukkit/src/main/java/com/boydti/fawe/bukkit/util/image/BukkitImageListener.java rename to bukkit/src/main/java/com/boydti/fawe/bukkit/listener/BukkitImageListener.java index caacf90027..2454a50e5e 100644 --- a/bukkit/src/main/java/com/boydti/fawe/bukkit/util/image/BukkitImageListener.java +++ b/bukkit/src/main/java/com/boydti/fawe/bukkit/listener/BukkitImageListener.java @@ -1,5 +1,6 @@ -package com.boydti.fawe.bukkit.util.image; +package com.boydti.fawe.bukkit.listener; +import com.boydti.fawe.bukkit.util.image.BukkitImageViewer; import com.boydti.fawe.command.CFICommands; import com.boydti.fawe.jnbt.anvil.HeightMapMCAGenerator; import com.boydti.fawe.object.FawePlayer; @@ -60,17 +61,21 @@ public void onPlayerInteractEntity(AsyncPlayerChatEvent event) { Iterator iter = recipients.iterator(); while (iter.hasNext()) { Player player = iter.next(); + if (player.equals(event.getPlayer())) continue; + FawePlayer fp = FawePlayer.wrap(player); + if (!fp.hasMeta()) continue; + CFICommands.CFISettings settings = fp.getMeta("CFISettings"); - if (settings != null && settings.hasGenerator()) { - String name = player.getName().toLowerCase(); - if (!event.getMessage().toLowerCase().contains(name)) { - ArrayDeque buffered = fp.getMeta("CFIBufferedMessages"); - if (buffered == null) fp.setMeta("CFIBufferedMessaged", buffered = new ArrayDeque()); - String full = String.format(event.getFormat(), event.getPlayer().getDisplayName(), event.getMessage()); - buffered.add(full); - iter.remove(); - } + if (settings == null || !settings.hasGenerator()) continue; + + String name = player.getName().toLowerCase(); + if (!event.getMessage().toLowerCase().contains(name)) { + ArrayDeque buffered = fp.getMeta("CFIBufferedMessages"); + if (buffered == null) fp.setMeta("CFIBufferedMessaged", buffered = new ArrayDeque()); + String full = String.format(event.getFormat(), event.getPlayer().getDisplayName(), event.getMessage()); + buffered.add(full); + iter.remove(); } } } @@ -183,7 +188,7 @@ private void handleInteract(Event event, Player player, Entity entity, boolean p TaskManager.IMP.laterAsync(new Runnable() { @Override public void run() { - viewer.view(generator.draw()); + viewer.view(generator); } }, 1); return; @@ -263,7 +268,7 @@ public void run() { e.printStackTrace(); } es.flushQueue(); - viewer.view(generator.draw()); + viewer.view(generator); } }, true, true); @@ -274,11 +279,5 @@ public void run() { } } } - - - - - - } } \ No newline at end of file diff --git a/bukkit/src/main/java/com/boydti/fawe/bukkit/listener/CFIPacketListener.java b/bukkit/src/main/java/com/boydti/fawe/bukkit/listener/CFIPacketListener.java new file mode 100644 index 0000000000..2edbef6b77 --- /dev/null +++ b/bukkit/src/main/java/com/boydti/fawe/bukkit/listener/CFIPacketListener.java @@ -0,0 +1,329 @@ +package com.boydti.fawe.bukkit.listener; + +import com.boydti.fawe.FaweCache; +import com.boydti.fawe.command.CFICommands; +import com.boydti.fawe.jnbt.anvil.HeightMapMCAGenerator; +import com.boydti.fawe.object.FaweChunk; +import com.boydti.fawe.object.FawePlayer; +import com.boydti.fawe.object.FaweQueue; +import com.boydti.fawe.object.RunnableVal3; +import com.boydti.fawe.util.SetQueue; +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.injector.netty.WirePacket; +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.comphenix.protocol.wrappers.WrappedBlockData; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.event.platform.BlockInteractEvent; +import com.sk89q.worldedit.event.platform.Interaction; +import com.sk89q.worldedit.extension.platform.PlatformManager; +import java.lang.reflect.InvocationTargetException; +import java.util.List; +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; + +/** + * The CFIPacketListener handles packets for editing the HeightMapMCAGenerator + * The generator is a virtual world which only the creator can see + * - The virtual world is displayed inside the current world + * - Block/Chunk/Movement packets need to be handled properly + */ +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() { + @Override + public void run(PacketEvent event, HeightMapMCAGenerator gen, Vector pt) { + try { + Player plr = event.getPlayer(); + Vector realPos = pt.add(gen.getOrigin()); + if (!sendBlockChange(plr, gen, pt, Interaction.HIT)) { + gen.setBlock(pt, EditSession.nullBlock); + } + } catch (WorldEditException e) { + e.printStackTrace(); + } + } + }); + + // Direct placing to the virtual world + RunnableVal3 placeTask = new RunnableVal3() { + @Override + public void run(PacketEvent event, HeightMapMCAGenerator gen, Vector pt) { + try { + Player plr = event.getPlayer(); + List 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 != null && hand.getType().isBlock() && hand.getTypeId() != 0) { + BaseBlock block = FaweCache.getBlock(hand.getTypeId(), hand.getDurability()); + gen.setBlock(pt, block); + } else { + 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() { + @Override + public void run(PacketEvent event, HeightMapMCAGenerator gen, Vector pt) { + // Do nothing + } + }); + + // Modify chunk packets where the real world overlaps with the virtual one + protocolmanager.addPacketListener(new PacketAdapter(plugin, ListenerPriority.NORMAL, PacketType.Play.Server.MAP_CHUNK) { + @Override + public void onPacketSending(PacketEvent event) { + if (!event.isServerPacket()) return; + + HeightMapMCAGenerator gen = getGenerator(event); + if (gen != null) { + Vector origin = gen.getOrigin(); + PacketContainer packet = event.getPacket(); + StructureModifier 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.contain(new Vector((cx - ocx) << 4, 0, (cz - ocz) << 4))) { + event.setCancelled(true); + + Player plr = event.getPlayer(); + + FaweQueue queue = SetQueue.IMP.getNewQueue(plr.getWorld().getName(), true, false); + + FaweChunk toSend = gen.getSnapshot(cx - ocx, cz - ocz); + toSend.setLoc(gen, cx, cz); + queue.sendChunkUpdate(toSend, FawePlayer.wrap(plr)); + } + } + } + }); + + // 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(); + HeightMapMCAGenerator gen = getGenerator(event); + if (gen != null) { + Vector origin = gen.getOrigin(); + Vector pt = new Vector(pos.getBlockX(), pos.getBlockY(), pos.getBlockZ()); + + StructureModifier 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.contain(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(); + HeightMapMCAGenerator gen = getGenerator(event); + if (gen != null) { + Vector origin = gen.getOrigin(); + Vector from = new Vector(pos.getBlockX(), pos.getBlockY(), pos.getBlockZ()); + + PacketContainer packet = event.getPacket(); + StructureModifier doubles = packet.getDoubles(); + Vector to = new Vector(doubles.read(0), doubles.read(1), doubles.read(2)); + if (gen.contain(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 e) { + e.printStackTrace(); + } catch (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; + + HeightMapMCAGenerator gen = getGenerator(event); + if (gen != null) { + PacketContainer packet = event.getPacket(); + ChunkCoordIntPair chunk = packet.getChunkCoordIntPairs().read(0); + Vector origin = gen.getOrigin(); + int cx = chunk.getChunkX() - (origin.getBlockX() >> 4); + int cz = chunk.getChunkZ() - (origin.getBlockX() >> 4); + if (gen.contain(new Vector(cx << 4, 0, cz << 4))) { + event.setCancelled(true); + } + } + } + }); + } + + @EventHandler + public void onTeleport(PlayerTeleportEvent event) { + final Player player = event.getPlayer(); + HeightMapMCAGenerator 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, HeightMapMCAGenerator gen, Vector pt, Interaction action) { + PlatformManager platform = WorldEdit.getInstance().getPlatformManager(); + com.sk89q.worldedit.entity.Player actor = FawePlayer.wrap(plr).getPlayer(); + com.sk89q.worldedit.util.Location location = new com.sk89q.worldedit.util.Location(actor.getWorld(), pt); + BlockInteractEvent toCall = new BlockInteractEvent(actor, location, action); + platform.handleBlockInteract(toCall); + if (toCall.isCancelled() || action == Interaction.OPEN) { + Vector realPos = pt.add(gen.getOrigin()); + BaseBlock block = gen.getBlock(pt); + sendBlockChange(plr, realPos, block); + return true; + } + return false; + } + + private void sendBlockChange(Player plr, Vector pt, BaseBlock block) { + PacketContainer container = new PacketContainer(PacketType.Play.Server.BLOCK_CHANGE); + // Block position + // block combined id + container.getBlockPositionModifier().write(0, new BlockPosition(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ())); + WrappedBlockData bd = WrappedBlockData.createData(Material.getMaterial(block.getId()), block.getData()); + container.getBlockData().write(0, bd); + try { + protocolmanager.sendWirePacket(plr, WirePacket.fromPacket(container)); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } + } + + private HeightMapMCAGenerator getGenerator(PacketEvent event) { + return getGenerator(event.getPlayer()); + } + + private HeightMapMCAGenerator getGenerator(Player player) { + CFICommands.CFISettings settings = FawePlayer.wrap(player).getMeta("CFISettings"); + if (settings != null && settings.hasGenerator() && settings.getGenerator().hasPacketViewer()) { + return settings.getGenerator(); + } + return null; + } + + private Vector getRelPos(PacketEvent event, HeightMapMCAGenerator generator) { + PacketContainer packet = event.getPacket(); + StructureModifier position = packet.getBlockPositionModifier(); + BlockPosition loc = position.readSafely(0); + if (loc == null) return null; + Vector origin = generator.getOrigin(); + Vector pt = new Vector(loc.getX() - origin.getBlockX(), loc.getY() - origin.getBlockY(), loc.getZ() - origin.getBlockZ()); + return pt; + } + + private void handleBlockEvent(PacketEvent event, boolean relative, RunnableVal3 task) { + HeightMapMCAGenerator gen = getGenerator(event); + if (gen != null) { + Vector pt = getRelPos(event, gen); + if (pt != null) { + if (relative) pt = getRelative(event, pt); + if (gen.contain(pt)) { + event.setCancelled(true); + task.run(event, gen, pt); + } + } + } + } + + private void registerBlockEvent(PacketType type, boolean relative, RunnableVal3 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 Vector getRelative(PacketEvent container, Vector pt) { + PacketContainer packet = container.getPacket(); + StructureModifier 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; + } + } +} diff --git a/bukkit/src/main/java/com/boydti/fawe/bukkit/util/image/BukkitImageViewer.java b/bukkit/src/main/java/com/boydti/fawe/bukkit/util/image/BukkitImageViewer.java index c42e0ecc43..b49a35b3fd 100644 --- a/bukkit/src/main/java/com/boydti/fawe/bukkit/util/image/BukkitImageViewer.java +++ b/bukkit/src/main/java/com/boydti/fawe/bukkit/util/image/BukkitImageViewer.java @@ -1,19 +1,24 @@ package com.boydti.fawe.bukkit.util.image; +import com.boydti.fawe.util.image.Drawable; import com.boydti.fawe.util.image.ImageUtil; import com.boydti.fawe.util.image.ImageViewer; import java.awt.RenderingHints; import java.awt.image.BufferedImage; import java.io.IOException; import java.util.Collection; +import javax.annotation.Nullable; import org.bukkit.Bukkit; import org.bukkit.Location; +import org.bukkit.Material; import org.bukkit.Rotation; import org.bukkit.World; import org.bukkit.block.BlockFace; import org.bukkit.entity.Entity; import org.bukkit.entity.ItemFrame; import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.PlayerInventory; import org.inventivetalent.mapmanager.MapManagerPlugin; import org.inventivetalent.mapmanager.controller.MapController; import org.inventivetalent.mapmanager.controller.MultiMapController; @@ -27,7 +32,6 @@ public class BukkitImageViewer implements ImageViewer { private ItemFrame[][] frames; private boolean reverse; - public BukkitImageViewer(Player player) { mapManager = ((MapManagerPlugin) Bukkit.getPluginManager().getPlugin("MapManager")).getMapManager(); this.player = player; @@ -124,9 +128,17 @@ private ItemFrame[][] find(Location pos1, Location pos2, BlockFace facing) { } @Override - public void view(BufferedImage image) { - last = image; + public void view(Drawable drawable) { + view(null, drawable); + } + + private void view(@Nullable BufferedImage image, @Nullable Drawable drawable) { + if (image == null && drawable == null) throw new IllegalArgumentException("An image or drawable must be provided. Both cannot be null"); + boolean initializing = last == null; + if (this.frames != null) { + if (image == null && drawable != null) image = drawable.draw(); + last = image; int width = frames.length; int height = frames[0].length; BufferedImage scaled = ImageUtil.getScaledInstance(image, 128 * width, 128 * height, RenderingHints.VALUE_INTERPOLATION_BILINEAR, false); @@ -136,6 +148,18 @@ public void view(BufferedImage image) { controller.sendContent(player); controller.showInFrames(player, frames, true); } else { + int slot = getMapSlot(player); + if (slot == -1) { + if (initializing) { + player.getInventory().setItemInMainHand(new ItemStack(Material.MAP)); + } else { + return; + } + } else if (player.getInventory().getHeldItemSlot() != slot) { + player.getInventory().setHeldItemSlot(slot); + } + if (image == null && drawable != null) image = drawable.draw(); + last = image; BufferedImage scaled = ImageUtil.getScaledInstance(image, 128, 128, RenderingHints.VALUE_INTERPOLATION_BILINEAR, false); MapWrapper mapWrapper = mapManager.wrapImage(scaled); MapController controller = mapWrapper.getController(); @@ -145,8 +169,19 @@ public void view(BufferedImage image) { } } + private int getMapSlot(Player player) { + PlayerInventory inventory = player.getInventory(); + for (int i = 0; i < 9; i++) { + ItemStack item = inventory.getItem(i); + if (item != null && item.getType() == Material.MAP) { + return i; + } + } + return -1; + } + public void refresh() { - if (last != null) view(last); + if (last != null) view(last, null); } @Override diff --git a/bukkit/src/main/java/com/boydti/fawe/bukkit/v0/BukkitChunk_All.java b/bukkit/src/main/java/com/boydti/fawe/bukkit/v0/BukkitChunk_All.java index 6329c4fbfb..4d8506b55f 100644 --- a/bukkit/src/main/java/com/boydti/fawe/bukkit/v0/BukkitChunk_All.java +++ b/bukkit/src/main/java/com/boydti/fawe/bukkit/v0/BukkitChunk_All.java @@ -56,14 +56,11 @@ public CharFaweChunk copy(boolean shallow) { if (shallow) { copy = new BukkitChunk_All(getParent(), getX(), getZ(), ids, count, air, heightMap); copy.biomes = biomes; - copy.chunk = chunk; } else { copy = new BukkitChunk_All(getParent(), getX(), getZ(), (char[][]) MainUtil.copyNd(ids), count.clone(), air.clone(), heightMap.clone()); - copy.biomes = biomes; - copy.chunk = chunk; copy.biomes = biomes.clone(); - copy.chunk = chunk; } + copy.chunk = chunk; return copy; } diff --git a/bukkit/src/main/java/com/boydti/fawe/bukkit/v0/BukkitQueue_0.java b/bukkit/src/main/java/com/boydti/fawe/bukkit/v0/BukkitQueue_0.java index 7f5a315aec..96d487be20 100644 --- a/bukkit/src/main/java/com/boydti/fawe/bukkit/v0/BukkitQueue_0.java +++ b/bukkit/src/main/java/com/boydti/fawe/bukkit/v0/BukkitQueue_0.java @@ -4,14 +4,23 @@ import com.boydti.fawe.FaweCache; import com.boydti.fawe.bukkit.BukkitPlayer; import com.boydti.fawe.bukkit.FaweBukkit; +import com.boydti.fawe.bukkit.v1_12.packet.FaweChunkPacket; +import com.boydti.fawe.bukkit.v1_12.packet.MCAChunkPacket; import com.boydti.fawe.example.CharFaweChunk; import com.boydti.fawe.example.NMSMappedFaweQueue; +import com.boydti.fawe.jnbt.anvil.MCAChunk; import com.boydti.fawe.object.FaweChunk; import com.boydti.fawe.object.FawePlayer; import com.boydti.fawe.object.RunnableVal; +import com.boydti.fawe.object.queue.LazyFaweChunk; import com.boydti.fawe.object.visitor.FaweChunkVisitor; import com.boydti.fawe.util.MathMan; +import com.boydti.fawe.util.ReflectionUtils; import com.boydti.fawe.util.TaskManager; +import com.comphenix.protocol.PacketType; +import com.comphenix.protocol.ProtocolLibrary; +import com.comphenix.protocol.ProtocolManager; +import com.comphenix.protocol.injector.netty.WirePacket; import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; @@ -36,14 +45,26 @@ import org.bukkit.event.world.ChunkLoadEvent; import org.bukkit.event.world.ChunkUnloadEvent; import org.bukkit.event.world.WorldInitEvent; +import org.bukkit.plugin.Plugin; public abstract class BukkitQueue_0 extends NMSMappedFaweQueue implements Listener { + protected static boolean PAPER = true; private static BukkitImplAdapter adapter; private static FaweAdapter_All backupAdaper; private static Method methodToNative; private static Method methodFromNative; private static boolean setupAdapter = false; + private static Method methodGetHandle; + + static { + Class classCraftChunk = ReflectionUtils.getCbClass("CraftChunk"); + try { + methodGetHandle = ReflectionUtils.setAccessible(classCraftChunk.getDeclaredMethod("getHandle")); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } + } public BukkitQueue_0(final com.sk89q.worldedit.world.World world) { super(world); @@ -63,6 +84,86 @@ public BukkitQueue_0(String world) { } } + @Override + public boolean supports(Capability capability) { + switch (capability) { + case CHUNK_PACKETS: + Plugin plib = Bukkit.getPluginManager().getPlugin("ProtocolLib"); + return plib != null && plib.isEnabled(); + } + return super.supports(capability); + } + + @Override + public void sendChunkUpdate(FaweChunk chunk, FawePlayer... players) { + if (supports(Capability.CHUNK_PACKETS)) { + sendChunkUpdatePLIB(chunk, players); + } else { + sendBlockUpdate(chunk, players); + } + } + + public void sendChunkUpdatePLIB(FaweChunk chunk, FawePlayer... players) { + ProtocolManager manager = ProtocolLibrary.getProtocolManager(); + WirePacket packet = null; + int viewDistance = Bukkit.getViewDistance(); + try { + for (int i = 0; i < players.length; i++) { + int cx = chunk.getX(); + int cz = chunk.getZ(); + + Player player = ((BukkitPlayer) players[i]).parent; + Location loc = player.getLocation(); + + if (Math.abs((loc.getBlockX() >> 4) - cx) <= viewDistance && Math.abs((loc.getBlockZ() >> 4) - cz) <= viewDistance) { + if (packet == null) { + byte[] data; + byte[] buffer = new byte[8192]; + if (chunk instanceof LazyFaweChunk) { + chunk = (FaweChunk) chunk.getChunk(); + } + if (chunk instanceof MCAChunk) { + data = new MCAChunkPacket((MCAChunk) chunk, true, true, hasSky()).apply(buffer); + } else { + data = new FaweChunkPacket(chunk, true, true, hasSky()).apply(buffer); + } + packet = new WirePacket(PacketType.Play.Server.MAP_CHUNK, data); + } + manager.sendWirePacket(player, packet); + } + } + } catch (InvocationTargetException e) { + throw new RuntimeException(e); + } + } + + @Override + public boolean queueChunkLoad(int cx, int cz, RunnableVal operation) { + if (PAPER) { + try { + getImpWorld().getChunkAtAsync(cx, cz, new World.ChunkLoadCallback() { + @Override + public void onLoad(Chunk bukkitChunk) { + try { + CHUNK chunk = (CHUNK) methodGetHandle.invoke(bukkitChunk); + try { + operation.run(chunk); + } catch (Throwable e) { + e.printStackTrace(); + } + } catch (Throwable e) { + PAPER = false; + } + } + }); + return true; + } catch (Throwable ignore) { + PAPER = false; + } + } + return super.queueChunkLoad(cx, cz); + } + public static BukkitImplAdapter getAdapter() { if (adapter == null) setupAdapter(null); if (adapter == null) return backupAdaper; diff --git a/bukkit/src/main/java/com/boydti/fawe/bukkit/v0/BukkitQueue_All.java b/bukkit/src/main/java/com/boydti/fawe/bukkit/v0/BukkitQueue_All.java index b5ce8f3a07..fd89e1b8ad 100644 --- a/bukkit/src/main/java/com/boydti/fawe/bukkit/v0/BukkitQueue_All.java +++ b/bukkit/src/main/java/com/boydti/fawe/bukkit/v0/BukkitQueue_All.java @@ -30,7 +30,6 @@ public class BukkitQueue_All extends BukkitQueue_0 { public static int ALLOCATE; - private static int LIGHT_MASK = 0x739C0; private ConcurrentMap chunkCache = new MapMaker() .weakValues() .makeMap(); @@ -53,6 +52,29 @@ public BukkitQueue_All(String world) { } } + @Override + public boolean queueChunkLoad(int cx, int cz, RunnableVal operation) { + if (PAPER) { + try { + getImpWorld().getChunkAtAsync(cx, cz, new World.ChunkLoadCallback() { + @Override + public void onLoad(Chunk chunk) { + try { + ChunkSnapshot snapshot = chunk.getChunkSnapshot(); + operation.run(snapshot); + } catch (Throwable e) { + e.printStackTrace(); + } + } + }); + return true; + } catch (Throwable ignore) { + PAPER = false; + } + } + return super.queueChunkLoad(cx, cz); + } + @Override public Relighter getRelighter() { return NullRelighter.INSTANCE; @@ -307,8 +329,11 @@ public FaweChunk getFaweChunk(int x, int z) { } @Override - public boolean supportsChangeTask() { - return getAdapter() != null; + public boolean supports(Capability capability) { + switch (capability) { + case CHANGE_TASKS: return getAdapter() != null; + } + return super.supports(capability); } private int skip; diff --git a/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_12/BukkitQueue_1_12.java b/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_12/BukkitQueue_1_12.java index b92e919fff..4cc409070c 100644 --- a/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_12/BukkitQueue_1_12.java +++ b/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_12/BukkitQueue_1_12.java @@ -4,18 +4,26 @@ import com.boydti.fawe.FaweCache; import com.boydti.fawe.bukkit.BukkitPlayer; import com.boydti.fawe.bukkit.v0.BukkitQueue_0; +import com.boydti.fawe.bukkit.v1_12.packet.FaweChunkPacket; +import com.boydti.fawe.bukkit.v1_12.packet.MCAChunkPacket; import com.boydti.fawe.example.CharFaweChunk; +import com.boydti.fawe.jnbt.anvil.MCAChunk; import com.boydti.fawe.object.FaweChunk; import com.boydti.fawe.object.FawePlayer; import com.boydti.fawe.object.RegionWrapper; import com.boydti.fawe.object.RunnableVal; import com.boydti.fawe.object.brush.visualization.VisualChunk; +import com.boydti.fawe.object.queue.LazyFaweChunk; import com.boydti.fawe.object.visitor.FaweChunkVisitor; import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.MathMan; import com.boydti.fawe.util.ReflectionUtils; import com.boydti.fawe.util.SetQueue; import com.boydti.fawe.util.TaskManager; +import com.comphenix.protocol.PacketType; +import com.comphenix.protocol.ProtocolLibrary; +import com.comphenix.protocol.ProtocolManager; +import com.comphenix.protocol.injector.netty.WirePacket; import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.StringTag; import com.sk89q.jnbt.Tag; @@ -566,6 +574,37 @@ public void sendChunk(int x, int z, int bitMask) { } } + @Override + public void sendChunkUpdatePLIB(FaweChunk chunk, FawePlayer... players) { + PlayerChunkMap playerManager = ((CraftWorld) getWorld()).getHandle().getPlayerChunkMap(); + ProtocolManager manager = ProtocolLibrary.getProtocolManager(); + WirePacket packet = null; + try { + for (int i = 0; i < players.length; i++) { + CraftPlayer bukkitPlayer = ((CraftPlayer) ((BukkitPlayer) players[i]).parent); + EntityPlayer player = bukkitPlayer.getHandle(); + if (playerManager.a(player, chunk.getX(), chunk.getZ())) { + if (packet == null) { + byte[] data; + byte[] buffer = new byte[8192]; + if (chunk instanceof LazyFaweChunk) { + chunk = (FaweChunk) chunk.getChunk(); + } + if (chunk instanceof MCAChunk) { + data = new MCAChunkPacket((MCAChunk) chunk, true, true, hasSky()).apply(buffer); + } else { + data = new FaweChunkPacket(chunk, true, true, hasSky()).apply(buffer); + } + packet = new WirePacket(PacketType.Play.Server.MAP_CHUNK, data); + } + manager.sendWirePacket(bukkitPlayer, packet); + } + } + } catch (InvocationTargetException e) { + throw new RuntimeException(e); + } + } + @Override public void sendBlockUpdate(FaweChunk chunk, FawePlayer... players) { try { diff --git a/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_12/packet/FaweChunkPacket.java b/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_12/packet/FaweChunkPacket.java index f9424c3eab..c14c7bf1a5 100644 --- a/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_12/packet/FaweChunkPacket.java +++ b/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_12/packet/FaweChunkPacket.java @@ -1,81 +1,92 @@ package com.boydti.fawe.bukkit.v1_12.packet; -import com.boydti.fawe.FaweCache; -import com.boydti.fawe.jnbt.anvil.MCAChunk; +import com.boydti.fawe.object.FaweChunk; import com.boydti.fawe.object.FaweOutputStream; import com.boydti.fawe.object.io.FastByteArrayOutputStream; -import com.comphenix.protocol.events.PacketContainer; -import com.comphenix.protocol.reflect.StructureModifier; -import com.comphenix.protocol.wrappers.nbt.NbtBase; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; +import java.util.function.Function; -public class FaweChunkPacket { +public class FaweChunkPacket implements Function { - private final MCAChunk chunk; + private final FaweChunk chunk; private final boolean full; private final boolean biomes; private final boolean sky; - public FaweChunkPacket(MCAChunk fc, boolean replaceAllSections, boolean sendBiomeData, boolean hasSky) { + public FaweChunkPacket(FaweChunk fc, boolean replaceAllSections, boolean sendBiomeData, boolean hasSky) { this.chunk = fc; this.full = replaceAllSections; this.biomes = sendBiomeData; this.sky = hasSky; } - public void write(PacketContainer packet) throws IOException { + @Override + public byte[] apply(byte[] buffer) { try { - StructureModifier ints = packet.getIntegers(); - StructureModifier byteArray = packet.getByteArrays(); - StructureModifier bools = packet.getBooleans(); - ints.write(0, this.chunk.getX()); - ints.write(1, this.chunk.getZ()); + FastByteArrayOutputStream baos = new FastByteArrayOutputStream(); + FaweOutputStream fos = new FaweOutputStream(baos); - bools.write(0, this.full); - ints.write(2, this.chunk.getBitMask()); // writeVarInt + fos.writeInt(this.chunk.getX()); + fos.writeInt(this.chunk.getZ()); - FastByteArrayOutputStream fbaos = new FastByteArrayOutputStream(); - FaweOutputStream buffer = new FaweOutputStream(fbaos); - byte[][] ids = chunk.ids; + fos.writeBoolean(this.full); + fos.writeVarInt(this.chunk.getBitMask()); // writeVarInt + + FastByteArrayOutputStream sectionByteArray = new FastByteArrayOutputStream(buffer); + FaweOutputStream sectionWriter = new FaweOutputStream(sectionByteArray); + + char[][] ids = chunk.getCombinedIdArrays(); + byte[][] blockLight = chunk.getBlockLightArray(); + byte[][] skyLight = chunk.getSkyLightArray(); for (int layer = 0; layer < ids.length; layer++) { - byte[] layerIds = ids[layer]; + char[] layerIds = ids[layer]; if (layerIds == null) { continue; } - byte[] layerData = chunk.data[layer]; - int num = 9; - buffer.write(num); // num blocks, anything > 8 - doesn't need to be accurate - buffer.writeVarInt(0); // varint 0 - data palette global + int num = 13; + sectionWriter.write(num); // num blocks, anything > 8 - doesn't need to be accurate + sectionWriter.writeVarInt(0); // varint 0 - data palette global BitArray bits = new BitArray(num, 4096); for (int i = 0; i < 4096; i++) { - int id = layerIds[i]; - if (id != 0) { - int data = FaweCache.hasData(id) ? chunk.getNibble(i, layerData) : 0; - int combined = FaweCache.getCombined(id, data); - bits.setAt(i, combined); + char combinedId = layerIds[i]; + if (combinedId != 0) { + bits.setAt(i, combinedId); } } - buffer.write(bits.getBackingLongArray()); + sectionWriter.write(bits.getBackingLongArray()); - buffer.write(chunk.blockLight[layer]); + if (blockLight != null && blockLight[layer] != null) { + sectionWriter.write(blockLight[layer]); + } else { + sectionWriter.write(0, 2048); + } if (sky) { - buffer.write(chunk.skyLight[layer]); + if (skyLight != null && skyLight[layer] != null) { + sectionWriter.write(skyLight[layer]); + } else { + sectionWriter.write((byte) 255, 2048); + } + } + } + + if (this.biomes) { + byte[] biomeArr = chunk.getBiomeArray(); + if (biomeArr != null) { + sectionWriter.write(biomeArr); } } - if (this.biomes && chunk.biomes != null) { - buffer.write(chunk.biomes); + fos.writeVarInt(sectionByteArray.getSize()); + for (byte[] arr : sectionByteArray.toByteArrays()) { + fos.write(arr); } + fos.writeVarInt(0); - byteArray.write(0, fbaos.toByteArray()); - // TODO - empty - StructureModifier>> list = packet.getListNbtModifier(); - list.write(0, new ArrayList<>()); + fos.close(); + sectionWriter.close(); + return baos.toByteArray(); } catch (Throwable e) { - e.printStackTrace(); + throw new RuntimeException(e); } } } diff --git a/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_12/packet/MCAChunkPacket.java b/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_12/packet/MCAChunkPacket.java new file mode 100644 index 0000000000..fabc8ddca1 --- /dev/null +++ b/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_12/packet/MCAChunkPacket.java @@ -0,0 +1,83 @@ +package com.boydti.fawe.bukkit.v1_12.packet; + +import com.boydti.fawe.FaweCache; +import com.boydti.fawe.jnbt.anvil.MCAChunk; +import com.boydti.fawe.object.FaweOutputStream; +import com.boydti.fawe.object.io.FastByteArrayOutputStream; +import java.util.function.Function; + +public class MCAChunkPacket implements Function { + + private final MCAChunk chunk; + private final boolean full; + private final boolean biomes; + private final boolean sky; + + public MCAChunkPacket(MCAChunk fc, boolean replaceAllSections, boolean sendBiomeData, boolean hasSky) { + this.chunk = fc; + this.full = replaceAllSections; + this.biomes = sendBiomeData; + this.sky = hasSky; + } + + @Override + public byte[] apply(byte[] buffer) { + try { + FastByteArrayOutputStream baos = new FastByteArrayOutputStream(); + FaweOutputStream fos = new FaweOutputStream(baos); + + fos.writeInt(this.chunk.getX()); + fos.writeInt(this.chunk.getZ()); + + fos.writeBoolean(this.full); + fos.writeVarInt(this.chunk.getBitMask()); // writeVarInt + + FastByteArrayOutputStream sectionByteArray = new FastByteArrayOutputStream(buffer); + FaweOutputStream sectionWriter = new FaweOutputStream(sectionByteArray); + byte[][] ids = chunk.ids; + + for (int layer = 0; layer < ids.length; layer++) { + byte[] layerIds = ids[layer]; + if (layerIds == null) { + continue; + } + byte[] layerData = chunk.data[layer]; + int num = 13; + sectionWriter.write(num); // num blocks, anything > 8 - doesn't need to be accurate + sectionWriter.writeVarInt(0); // varint 0 - data palette global + BitArray bits = new BitArray(num, 4096); + bits.setAt(0, 0); + for (int i = 0; i < 4096; i++) { + int id = layerIds[i] & 0xFF; + if (id != 0) { + int data = FaweCache.hasData(id) ? chunk.getNibble(i, layerData) : 0; + int combined = FaweCache.getCombined(id, data); + bits.setAt(i, combined); + } + } + sectionWriter.write(bits.getBackingLongArray()); + + sectionWriter.write(chunk.blockLight[layer]); + if (sky) { + sectionWriter.write(chunk.skyLight[layer]); + } + } + + if (this.biomes && chunk.biomes != null) { + sectionWriter.write(chunk.biomes); + } + + fos.writeVarInt(sectionByteArray.getSize()); + for (byte[] arr : sectionByteArray.toByteArrays()) { + fos.write(arr); + } + fos.writeVarInt(0); + + fos.close(); + sectionWriter.close(); + return baos.toByteArray(); + } catch (Throwable e) { + throw new RuntimeException(e); + } + } +} diff --git a/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_7/BukkitQueue17.java b/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_7/BukkitQueue17.java index 88d3d8d26c..006770b056 100644 --- a/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_7/BukkitQueue17.java +++ b/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_7/BukkitQueue17.java @@ -517,11 +517,11 @@ public void run(Object value) { for (EntityPlayer player : players) { int currentVersion = player.playerConnection.networkManager.getVersion(); if (mask == 0 || mask == 65535 && hasEntities(nmsChunk)) { - PacketPlayOutMapChunk packet = new PacketPlayOutMapChunk(nmsChunk, true, 65280, currentVersion); + PacketPlayOutMapChunk packet = new PacketPlayOutMapChunk(nmsChunk, false, 65280, currentVersion); player.playerConnection.sendPacket(packet); mask = 255; } - PacketPlayOutMapChunk packet = new PacketPlayOutMapChunk(nmsChunk, true, mask, currentVersion); + PacketPlayOutMapChunk packet = new PacketPlayOutMapChunk(nmsChunk, false, mask, currentVersion); player.playerConnection.sendPacket(packet); } if (empty) { diff --git a/bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncWorld.java b/bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncWorld.java index 95883b45a2..1e01702d34 100644 --- a/bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncWorld.java +++ b/bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncWorld.java @@ -5,7 +5,7 @@ import com.boydti.fawe.object.FaweQueue; import com.boydti.fawe.object.HasFaweQueue; import com.boydti.fawe.object.RunnableVal; -import com.boydti.fawe.util.DelegateFaweQueue; +import com.boydti.fawe.object.queue.DelegateFaweQueue; import com.boydti.fawe.util.SetQueue; import com.boydti.fawe.util.StringMan; import com.boydti.fawe.util.TaskManager; diff --git a/bukkit/src/main/resources/plugin.yml b/bukkit/src/main/resources/plugin.yml index 398ff8e4f1..801f4d485a 100644 --- a/bukkit/src/main/resources/plugin.yml +++ b/bukkit/src/main/resources/plugin.yml @@ -6,6 +6,7 @@ authors: [Empire92] loadbefore: [WorldEdit,AsyncWorldEdit,AsyncWorldEditInjector] load: STARTUP database: false +softdepend: [ProtocolLib] #softdepend: [WorldGuard, PlotSquared, MCore, Factions, GriefPrevention, Residence, Towny, PlotMe, PreciousStones] commands: fcancel: diff --git a/core/src/main/java/com/boydti/fawe/command/CFICommand.java b/core/src/main/java/com/boydti/fawe/command/CFICommand.java index 5640840633..9429715673 100644 --- a/core/src/main/java/com/boydti/fawe/command/CFICommand.java +++ b/core/src/main/java/com/boydti/fawe/command/CFICommand.java @@ -1,15 +1,19 @@ package com.boydti.fawe.command; import com.boydti.fawe.config.Commands; +import com.boydti.fawe.jnbt.anvil.HeightMapMCAGenerator; import com.boydti.fawe.object.FawePlayer; +import com.boydti.fawe.object.changeset.CFIChangeSet; import com.sk89q.minecraft.util.commands.Command; import com.sk89q.minecraft.util.commands.CommandContext; import com.sk89q.minecraft.util.commands.CommandException; import com.sk89q.minecraft.util.commands.CommandPermissions; +import com.sk89q.worldedit.LocalSession; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.command.MethodCommands; import com.sk89q.worldedit.util.command.SimpleDispatcher; import com.sk89q.worldedit.util.command.parametric.ParametricBuilder; +import java.io.IOException; public class CFICommand extends MethodCommands { @@ -32,9 +36,20 @@ public CFICommand(WorldEdit worldEdit, ParametricBuilder builder) { desc = "Start CreateFromImage" ) @CommandPermissions("worldedit.anvil.cfi") - public void cfi(FawePlayer fp, CommandContext context) throws CommandException { + public void cfi(FawePlayer fp, CommandContext context) throws CommandException, IOException { CFICommands.CFISettings settings = child.getSettings(fp); settings.popMessages(fp); + dispatch(fp, settings, context); + HeightMapMCAGenerator gen = settings.getGenerator(); + if (gen != null && gen.isModified()) { + gen.update(); + CFIChangeSet set = new CFIChangeSet(gen, fp.getUUID()); + LocalSession session = fp.getSession(); + session.remember(fp.getPlayer(), gen, set, fp.getLimit()); + } + } + + private void dispatch(FawePlayer fp, CFICommands.CFISettings settings, CommandContext context) throws CommandException { if (!settings.hasGenerator()) { switch (context.argsLength()) { case 0: { diff --git a/core/src/main/java/com/boydti/fawe/command/CFICommands.java b/core/src/main/java/com/boydti/fawe/command/CFICommands.java index cce463a892..f94c7e21f4 100644 --- a/core/src/main/java/com/boydti/fawe/command/CFICommands.java +++ b/core/src/main/java/com/boydti/fawe/command/CFICommands.java @@ -34,11 +34,13 @@ import com.sk89q.minecraft.util.commands.CommandException; import com.sk89q.minecraft.util.commands.CommandPermissions; import com.sk89q.worldedit.EmptyClipboardException; +import com.sk89q.worldedit.LocalSession; import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.command.MethodCommands; +import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.input.InputParseException; import com.sk89q.worldedit.extension.input.ParserContext; import com.sk89q.worldedit.extent.clipboard.Clipboard; @@ -64,6 +66,7 @@ import java.util.ArrayDeque; import java.util.HashSet; import java.util.Set; +import java.util.UUID; import javax.imageio.ImageIO; @Command(aliases = {"/cfi"}, desc = "Create a world from images: [More Info](https://git.io/v5iDy)") @@ -81,6 +84,10 @@ public CFICommands(WorldEdit worldEdit, Dispatcher dispatcher) { this.dispathcer= dispatcher; } + private File getFolder(String worldName) { + return new File(PS.imp().getWorldContainer(), worldName + File.separator + "region"); + } + @Command( aliases = {"heightmap"}, usage = "", @@ -88,7 +95,7 @@ public CFICommands(WorldEdit worldEdit, Dispatcher dispatcher) { ) @CommandPermissions("worldedit.anvil.cfi") public void heightmap(FawePlayer fp, BufferedImage image) { - HeightMapMCAGenerator generator = new HeightMapMCAGenerator(image, null); + HeightMapMCAGenerator generator = new HeightMapMCAGenerator(image, getFolder("CFI-" + UUID.randomUUID())); setup(generator, fp); } @@ -99,14 +106,16 @@ public void heightmap(FawePlayer fp, BufferedImage image) { ) @CommandPermissions("worldedit.anvil.cfi") public void heightmap(FawePlayer fp, int width, int length) { - HeightMapMCAGenerator generator = new HeightMapMCAGenerator(width, length, null); + HeightMapMCAGenerator generator = new HeightMapMCAGenerator(width, length, getFolder("CFI-" + UUID.randomUUID())); setup(generator, fp); } private void setup(HeightMapMCAGenerator generator, FawePlayer fp) { CFISettings settings = getSettings(fp); settings.remove().setGenerator(generator).bind(); + generator.setPacketViewer(fp); generator.setImageViewer(Fawe.imp().getImageViewer(fp)); + generator.update(); mainMenu(fp); } @@ -202,12 +211,14 @@ public void run(Plot o) { }); if (plot == null) return; - File folder = new File(PS.imp().getWorldContainer(), plot.getWorldName() + File.separator + "region"); + File folder = getFolder(plot.getWorldName()); HeightMapMCAGenerator generator = settings.getGenerator(); generator.setFolder(folder); fp.sendMessage(BBC.getPrefix() + "Generating"); generator.generate(); + generator.setPacketViewer(null); + generator.setImageViewer(null); settings.remove(); fp.sendMessage(BBC.getPrefix() + "Done!"); TaskManager.IMP.sync(new RunnableVal() { @@ -555,6 +566,60 @@ public void waterId(FawePlayer fp, BaseBlock block) throws ParameterException, W component(fp); } + @Command( + aliases = {"baseid", "bedrockid"}, + usage = "", + desc = "Change the block used for the base\n" + + "e.g. Bedrock" + ) + @CommandPermissions("worldedit.anvil.cfi") + public void baseId(FawePlayer fp, BaseBlock block) throws ParameterException, WorldEditException { + CFISettings settings = assertSettings(fp); + settings.getGenerator().setBedrockId(block.getId()); + msg("Set base id!").send(fp); + settings.resetComponent(); + component(fp); + } + + @Command( + aliases = {"thickness", "width", "floorthickness"}, + usage = "", + desc = "Set the thickness of the generated world from the floor\n" + + " - A value of 0 is the default and will only set the top block" + ) + @CommandPermissions("worldedit.anvil.cfi") + public void floorthickness(FawePlayer fp, int height) throws ParameterException, WorldEditException { + assertSettings(fp).getGenerator().setFloorThickness(height); + msg("Set world thickness!").send(fp); + component(fp); + } + + @Command( + aliases = {"update", "refresh", "resend"}, + desc = "Resend the CFI chunks" + ) + @CommandPermissions("worldedit.anvil.cfi") + public void update(FawePlayer fp) throws ParameterException, WorldEditException { + assertSettings(fp).getGenerator().update(); + msg("Chunks refreshed!").send(fp); + mainMenu(fp); + } + + @Command( + aliases = {"tp", "visit", "home"}, + desc = "Teleport to the CFI virtual world" + ) + @CommandPermissions("worldedit.anvil.cfi") + public void tp(FawePlayer fp) throws ParameterException, WorldEditException { + HeightMapMCAGenerator gen = assertSettings(fp).getGenerator(); + msg("Teleporting...").send(fp); + Vector origin = gen.getOrigin(); + Player player = fp.getPlayer(); + player.setPosition(origin.subtract(16, 0, 16)); + player.findFreePosition(); + mainMenu(fp); + } + @Command( aliases = {"waterheight", "sealevel", "setwaterheight"}, usage = "", @@ -565,7 +630,7 @@ public void waterId(FawePlayer fp, BaseBlock block) throws ParameterException, W @CommandPermissions("worldedit.anvil.cfi") public void waterheight(FawePlayer fp, int height) throws ParameterException, WorldEditException { assertSettings(fp).getGenerator().setWaterHeight(height); - msg("Set height!").send(fp); + msg("Set water height!").send(fp); component(fp); } @@ -877,6 +942,8 @@ public void component(FawePlayer fp) throws ParameterException{ .newline() .text("&7[&aWaterHeight&7]").suggestTip(alias() + " " + alias("waterheight") + " 60").text(" - Sea level for whole map") .newline() + .text("&7[&aFloorThickness&7]").suggestTip(alias() + " " + alias("floorthickness") + " 60").text(" - Floor thickness of entire map") + .newline() .text("&7[&aSnow&7]").suggestTip(alias() + " " + alias("snow") + maskArgs).text(" - Set snow in the masked areas") .newline(); @@ -884,6 +951,7 @@ public void component(FawePlayer fp) throws ParameterException{ String disabled = "You must specify a pattern"; msg .text("&7[&cWaterId&7]").tooltip(disabled).newline() + .text("&7[&cBedrockId&7]").tooltip(disabled).newline() .text("&7[&cFloor&7]").tooltip(disabled).newline() .text("&7[&cMain&7]").tooltip(disabled).newline() .text("&7[&cColumn&7]").tooltip(disabled).newline() @@ -894,6 +962,7 @@ public void component(FawePlayer fp) throws ParameterException{ msg .text("&7[&aWaterId&7]").cmdTip(alias() + " waterId " + pattern).text(" - Water id for whole map").newline() + .text("&7[&aBedrockId&7]").cmdTip(alias() + " baseId " + pattern).text(" - Bedrock id for whole map").newline() .text("&7[&aFloor&7]").cmdTip(alias() + " floor" + compArgs).text(" - Set the floor in the masked areas").newline() .text("&7[&aMain&7]").cmdTip(alias() + " main" + compArgs).text(" - Set the main block in the masked areas").newline() .text("&7[&aColumn&7]").cmdTip(alias() + " column" + compArgs).text(" - Set the columns in the masked areas").newline() @@ -1012,8 +1081,14 @@ public void popMessages(FawePlayer fp) { } public CFISettings remove() { - popMessages(fp); fp.deleteMeta("CFISettings"); + HeightMapMCAGenerator gen = this.generator; + if (gen != null) { + gen.close(); + LocalSession session = fp.getSession(); + session.clearHistory(); + } + popMessages(fp); generator = null; image = null; imageArg = null; diff --git a/core/src/main/java/com/boydti/fawe/command/Cancel.java b/core/src/main/java/com/boydti/fawe/command/Cancel.java index 12f336b53e..dba98c0e21 100644 --- a/core/src/main/java/com/boydti/fawe/command/Cancel.java +++ b/core/src/main/java/com/boydti/fawe/command/Cancel.java @@ -7,7 +7,6 @@ import com.boydti.fawe.util.SetQueue; import com.sk89q.worldedit.EditSession; import java.util.Collection; -import java.util.Set; import java.util.UUID; public class Cancel extends FaweCommand { @@ -26,7 +25,7 @@ public boolean execute(final FawePlayer player, final String... args) { int cancelled = 0; player.clearActions(); for (FaweQueue queue : queues) { - Set sessions = queue.getEditSessions(); + Collection sessions = queue.getEditSessions(); for (EditSession session : sessions) { FawePlayer currentPlayer = session.getPlayer(); if (currentPlayer == player) { diff --git a/core/src/main/java/com/boydti/fawe/config/Settings.java b/core/src/main/java/com/boydti/fawe/config/Settings.java index 3e876efc99..9b2fe6e65c 100644 --- a/core/src/main/java/com/boydti/fawe/config/Settings.java +++ b/core/src/main/java/com/boydti/fawe/config/Settings.java @@ -290,7 +290,9 @@ public static class PROGRESS { } @Comment({ - "Experimental options, use at your own risk" + "Experimental options, use at your own risk", + " - UNSAFE = Can cause permanent damage to the server", + " - SAFE = Can be buggy but unlikely to cause any damage" }) public static class EXPERIMENTAL { @Comment({ @@ -307,11 +309,11 @@ public static class EXPERIMENTAL { }) public boolean DYNAMIC_CHUNK_RENDERING = false; @Comment({ - "Allows brushes to be persistent", + "[SAFE] Allows brushes to be persistent", }) public boolean PERSISTENT_BRUSHES = false; @Comment({ - "Enable CUI without needing the mod installed (Requires ProtocolLib)", + "[SAFE] Enable CUI without needing the mod installed (Requires ProtocolLib)", }) public boolean VANILLA_CUI = false; diff --git a/core/src/main/java/com/boydti/fawe/example/MappedFaweQueue.java b/core/src/main/java/com/boydti/fawe/example/MappedFaweQueue.java index 5d78eda780..f3ced2177c 100644 --- a/core/src/main/java/com/boydti/fawe/example/MappedFaweQueue.java +++ b/core/src/main/java/com/boydti/fawe/example/MappedFaweQueue.java @@ -1,12 +1,14 @@ package com.boydti.fawe.example; import com.boydti.fawe.Fawe; +import com.boydti.fawe.FaweAPI; import com.boydti.fawe.FaweCache; import com.boydti.fawe.config.Settings; import com.boydti.fawe.object.FaweChunk; import com.boydti.fawe.object.FaweQueue; import com.boydti.fawe.object.IntegerPair; import com.boydti.fawe.object.RunnableVal; +import com.boydti.fawe.object.RunnableVal2; import com.boydti.fawe.object.exception.FaweException; import com.boydti.fawe.object.extent.LightingExtent; import com.boydti.fawe.util.MainUtil; @@ -14,17 +16,20 @@ import com.boydti.fawe.util.SetQueue; import com.boydti.fawe.util.TaskManager; import com.sk89q.jnbt.CompoundTag; +import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.blocks.BlockMaterial; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.biome.BaseBiome; import com.sk89q.worldedit.world.registry.BundledBlockData; import java.util.ArrayDeque; import java.util.Collection; +import java.util.HashSet; import java.util.UUID; +import java.util.concurrent.ConcurrentLinkedDeque; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.TimeUnit; -public abstract class MappedFaweQueue extends FaweQueue implements LightingExtent { +public abstract class MappedFaweQueue implements LightingExtent, FaweQueue { private WORLD impWorld; @@ -37,6 +42,17 @@ public abstract class MappedFaweQueue exte public CHUNKSECTIONS lastChunkSections; public SECTION lastSection; + + private World weWorld; + private String world; + private ConcurrentLinkedDeque sessions; + private long modified = System.currentTimeMillis(); + private RunnableVal2 changeTask; + private RunnableVal2 progressTask; + private SetQueue.QueueStage stage; + private Settings settings = Settings.IMP; + public ConcurrentLinkedDeque tasks = new ConcurrentLinkedDeque<>(); + private CHUNK cachedLoadChunk; public final RunnableVal loadChunk = new RunnableVal() { @@ -55,12 +71,12 @@ public MappedFaweQueue(final World world) { } public MappedFaweQueue(final String world) { - super(world); + this.world = world; map = Settings.IMP.PREVENT_CRASHES ? new WeakFaweQueueMap(this) : new DefaultFaweQueueMap(this); } public MappedFaweQueue(final String world, IFaweQueueMap map) { - super(world); + this.world = world; if (map == null) { map = Settings.IMP.PREVENT_CRASHES ? new WeakFaweQueueMap(this) : new DefaultFaweQueueMap(this); } @@ -68,13 +84,19 @@ public MappedFaweQueue(final String world, IFaweQueueMap map) { } public MappedFaweQueue(final World world, IFaweQueueMap map) { - super(world); + this.weWorld = world; + if (world != null) this.world = Fawe.imp().getWorldName(world); if (map == null) { map = Settings.IMP.PREVENT_CRASHES ? new WeakFaweQueueMap(this) : new DefaultFaweQueueMap(this); } this.map = map; } + @Override + public int getMaxY() { + return weWorld == null ? 255 : weWorld.getMaxY(); + } + public IFaweQueueMap getFaweQueueMap() { return map; } @@ -212,7 +234,26 @@ public void end(FaweChunk chunk) { @Override public void runTasks() { - super.runTasks(); + synchronized (this) { + this.notifyAll(); + } + if (getProgressTask() != null) { + try { + getProgressTask().run(ProgressType.DONE, 1); + } catch (Throwable e) { + e.printStackTrace(); + } + } + while (!tasks.isEmpty()) { + Runnable task = tasks.poll(); + if (task != null) { + try { + task.run(); + } catch (Throwable e) { + MainUtil.handleError(e); + } + } + } if (getProgressTask() != null) { try { getProgressTask().run(ProgressType.DONE, 1); @@ -231,6 +272,94 @@ public void runTasks() { } } + public Settings getSettings() { + return settings; + } + + public void setSettings(Settings settings) { + this.settings = settings == null ? Settings.IMP : settings; + } + + public void setWorld(String world) { + this.world = world; + this.weWorld = null; + } + + public World getWEWorld() { + return weWorld != null ? weWorld : (weWorld = FaweAPI.getWorld(world)); + } + + public String getWorldName() { + return world; + } + + @Override + public Collection getEditSessions() { + Collection tmp = sessions; + if (tmp == null) tmp = new HashSet<>(); + return tmp; + } + + @Override + public void addEditSession(EditSession session) { + ConcurrentLinkedDeque tmp = sessions; + if (tmp == null) tmp = new ConcurrentLinkedDeque<>(); + tmp.add(session); + this.sessions = tmp; + } + + @Override + public boolean supports(Capability capability) { + switch (capability) { + case CHANGE_TASKS: return true; + } + return false; + } + + public void setSessions(ConcurrentLinkedDeque sessions) { + this.sessions = sessions; + } + + public long getModified() { + return modified; + } + + public void setModified(long modified) { + this.modified = modified; + } + + public RunnableVal2 getProgressTask() { + return progressTask; + } + + public void setProgressTask(RunnableVal2 progressTask) { + this.progressTask = progressTask; + } + + public void setChangeTask(RunnableVal2 changeTask) { + this.changeTask = changeTask; + } + + public RunnableVal2 getChangeTask() { + return changeTask; + } + + public SetQueue.QueueStage getStage() { + return stage; + } + + public void setStage(SetQueue.QueueStage stage) { + this.stage = stage; + } + + public void addNotifyTask(Runnable runnable) { + this.tasks.add(runnable); + } + + public void addTask(Runnable whenFree) { + tasks.add(whenFree); + } + @Override public int size() { int size = map.size(); @@ -262,6 +391,15 @@ public SECTION getCachedSection(CHUNKSECTIONS chunk, int cy) { public abstract int getCombinedId4Data(SECTION section, int x, int y, int z); + public int getLocalCombinedId4Data(CHUNK chunk, int x, int y, int z) { + CHUNKSECTIONS sections = getSections(lastChunk); + SECTION section = getCachedSection(sections, y >> 4); + if (section == null) { + return 0; + } + return getCombinedId4Data(lastSection, x, y, z); + } + public abstract int getBiome(CHUNK chunk, int x, int z); public abstract CompoundTag getTileEntity(CHUNK chunk, int x, int y, int z); @@ -299,6 +437,23 @@ public void run() { return false; } + public boolean queueChunkLoad(final int cx, final int cz, RunnableVal operation) { + operation.value = getCachedChunk(getWorld(), cx, cz); + if (operation.value == null) { + SetQueue.IMP.addTask(new Runnable() { + @Override + public void run() { + operation.value = loadChunk(getWorld(), cx, cz, true); + if (operation.value != null) TaskManager.IMP.async(operation); + } + }); + return true; + } else { + TaskManager.IMP.async(operation); + } + return false; + } + @Override public boolean hasBlock(int x, int y, int z) throws FaweException.FaweChunkLoadException { int cx = x >> 4; diff --git a/core/src/main/java/com/boydti/fawe/example/SimpleCharFaweChunk.java b/core/src/main/java/com/boydti/fawe/example/SimpleCharFaweChunk.java new file mode 100644 index 0000000000..6ba9a0b4bc --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/example/SimpleCharFaweChunk.java @@ -0,0 +1,40 @@ +package com.boydti.fawe.example; + +import com.boydti.fawe.object.FaweChunk; +import com.boydti.fawe.object.FaweQueue; +import com.boydti.fawe.util.MainUtil; + +public class SimpleCharFaweChunk extends CharFaweChunk { + + public SimpleCharFaweChunk(FaweQueue parent, int x, int z) { + super(parent, x, z); + } + + public SimpleCharFaweChunk(FaweQueue parent, int x, int z, char[][] ids, short[] count, short[] air, byte[] heightMap) { + super(parent, x, z, ids, count, air, heightMap); + } + + @Override + public Object getNewChunk() { + return this; + } + + @Override + public CharFaweChunk copy(boolean shallow) { + SimpleCharFaweChunk copy; + if (shallow) { + copy = new SimpleCharFaweChunk(getParent(), getX(), getZ(), ids, count, air, heightMap); + copy.biomes = biomes; + } else { + copy = new SimpleCharFaweChunk(getParent(), getX(), getZ(), (char[][]) MainUtil.copyNd(ids), count.clone(), air.clone(), heightMap.clone()); + copy.biomes = biomes.clone(); + } + return copy; + } + + @Override + public FaweChunk call() { + getParent().setChunk(this); + return this; + } +} diff --git a/core/src/main/java/com/boydti/fawe/jnbt/anvil/HeightMapMCADrawer.java b/core/src/main/java/com/boydti/fawe/jnbt/anvil/HeightMapMCADrawer.java index 7ddbb4721b..9fc66234f6 100644 --- a/core/src/main/java/com/boydti/fawe/jnbt/anvil/HeightMapMCADrawer.java +++ b/core/src/main/java/com/boydti/fawe/jnbt/anvil/HeightMapMCADrawer.java @@ -27,11 +27,12 @@ public HeightMapMCADrawer(HeightMapMCAGenerator generator) { public BufferedImage draw() { BufferedImage img = new BufferedImage(gen.getWidth(), gen.getLength(), BufferedImage.TYPE_INT_RGB); - final char[] overlay = gen.overlay == null ? gen.floor : gen.overlay; - final char[] floor = gen.floor; - final char[] main = gen.main; - final byte[] heights = gen.heights; - final int waterHeight = gen.waterHeight; + 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.primtives.waterHeight; final int width = gen.getWidth(); final int length = gen.getLength(); @@ -58,7 +59,7 @@ public BufferedImage draw() { int color; switch (combined >> 4) { case 2: - color = getAverageBiomeColor(gen.biomes, width, index); + color = getAverageBiomeColor(biomes, width, index); break; case 78: color = (0xDD << 16) + (0xDD << 8) + (0xDD << 0); @@ -76,7 +77,7 @@ public BufferedImage draw() { color = (r << 16) + (g << 8) + (b << 0); } if (height + 1 < waterHeight) { - byte waterId = gen.waterId; + byte waterId = gen.primtives.waterId; int waterColor = 0; switch (waterId) { case BlockID.WATER: diff --git a/core/src/main/java/com/boydti/fawe/jnbt/anvil/HeightMapMCAGenerator.java b/core/src/main/java/com/boydti/fawe/jnbt/anvil/HeightMapMCAGenerator.java index ace9a2e6af..e3a240a8a6 100644 --- a/core/src/main/java/com/boydti/fawe/jnbt/anvil/HeightMapMCAGenerator.java +++ b/core/src/main/java/com/boydti/fawe/jnbt/anvil/HeightMapMCAGenerator.java @@ -2,41 +2,73 @@ import com.boydti.fawe.Fawe; import com.boydti.fawe.FaweCache; +import com.boydti.fawe.example.SimpleCharFaweChunk; +import com.boydti.fawe.object.FaweChunk; +import com.boydti.fawe.object.FaweInputStream; +import com.boydti.fawe.object.FaweLocation; +import com.boydti.fawe.object.FaweOutputStream; +import com.boydti.fawe.object.FawePlayer; +import com.boydti.fawe.object.FaweQueue; +import com.boydti.fawe.object.Metadatable; import com.boydti.fawe.object.PseudoRandom; +import com.boydti.fawe.object.RunnableVal2; +import com.boydti.fawe.object.change.StreamChange; +import com.boydti.fawe.object.changeset.CFIChangeSet; +import com.boydti.fawe.object.collection.DifferentialArray; +import com.boydti.fawe.object.collection.DifferentialBlockBuffer; import com.boydti.fawe.object.collection.IterableThreadLocal; import com.boydti.fawe.object.collection.LocalBlockVector2DSet; import com.boydti.fawe.object.collection.SummedAreaTable; +import com.boydti.fawe.object.exception.FaweException; +import com.boydti.fawe.object.queue.LazyFaweChunk; import com.boydti.fawe.object.schematic.Schematic; import com.boydti.fawe.util.CachedTextureUtil; -import com.boydti.fawe.util.MathMan; import com.boydti.fawe.util.RandomTextureUtil; +import com.boydti.fawe.util.ReflectionUtils; +import com.boydti.fawe.util.SetQueue; +import com.boydti.fawe.util.TaskManager; import com.boydti.fawe.util.TextureUtil; +import com.boydti.fawe.util.image.Drawable; import com.boydti.fawe.util.image.ImageViewer; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.worldedit.BlockVector; import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.MutableBlockVector; import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.Vector2D; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.blocks.BlockID; -import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.function.mask.Mask; +import com.sk89q.worldedit.function.operation.Operation; import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.math.transform.AffineTransform; import com.sk89q.worldedit.math.transform.Transform; import com.sk89q.worldedit.regions.CuboidRegion; +import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.session.ClipboardHolder; +import com.sk89q.worldedit.util.TreeGenerator; +import com.sk89q.worldedit.world.SimpleWorld; +import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.biome.BaseBiome; import com.sk89q.worldedit.world.registry.WorldData; -import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import java.awt.image.BufferedImage; +import java.io.Closeable; import java.io.File; import java.io.FileNotFoundException; +import java.io.IOException; +import java.lang.reflect.Field; import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; import java.util.List; +import java.util.UUID; +import javax.annotation.Nullable; -public class HeightMapMCAGenerator extends MCAWriter implements Extent { +public class HeightMapMCAGenerator extends MCAWriter implements SimpleWorld, FaweQueue, StreamChange, Closeable, Drawable { private final MutableBlockVector mutable = new MutableBlockVector(); private final ThreadLocal indexStore = new ThreadLocal() { @@ -46,20 +78,147 @@ protected int[] initialValue() { } }; - private final Int2ObjectOpenHashMap blocks = new Int2ObjectOpenHashMap<>(); + private final DifferentialBlockBuffer blocks; + protected final DifferentialArray heights; + protected final DifferentialArray biomes; + protected final DifferentialArray floor; + protected final DifferentialArray main; + protected DifferentialArray overlay; + + protected final CFIPrimtives primtives = new CFIPrimtives(); + private CFIPrimtives oldPrimitives = new CFIPrimtives(); + + public final class CFIPrimtives implements Cloneable { + protected int waterHeight = 0; + protected int floorThickness = 0; + protected boolean randomVariation = true; + protected int biomePriority = 0; + protected byte waterId = BlockID.STATIONARY_WATER; + protected byte bedrockId = 7; + protected boolean modifiedMain = false; - protected final byte[] heights; - protected final byte[] biomes; - protected final char[] floor; - protected final char[] main; - protected char[] overlay; - protected int waterHeight = 0; + @Override + public boolean equals(Object obj) { + if (obj == null || !(obj instanceof CFIPrimtives)) { + return false; + } + try { + for (Field field : CFIPrimtives.class.getDeclaredFields()) { + if (field.get(this) != field.get(obj)) return false; + } + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + return true; + } + + @Override + protected Object clone() throws CloneNotSupportedException { + return super.clone(); + } + } + + + protected Metadatable metaData = new Metadatable(); protected TextureUtil textureUtil; - protected boolean randomVariation = true; - protected int biomePriority = 0; - protected byte waterId = BlockID.STATIONARY_WATER; - protected boolean modifiedMain = false; + + @Override + public void flushChanges(FaweOutputStream out) throws IOException { + heights.flushChanges(out); + biomes.flushChanges(out); + floor.flushChanges(out); + main.flushChanges(out); + out.writeBoolean(overlay != null); + if (overlay != null) overlay.flushChanges(out); + try { + for (Field field : ReflectionUtils.sortFields(CFIPrimtives.class.getDeclaredFields())) { + Object now = field.get(primtives); + Object old = field.get(oldPrimitives); + boolean diff = old != now; + out.writeBoolean(diff); + if (diff) { + out.writePrimitive(old); + out.writePrimitive(now); + } + } + resetPrimtives(); + } catch (Throwable neverHappens) { neverHappens.printStackTrace(); } + + blocks.flushChanges(out); + } + + public boolean isModified() { + return blocks.isModified() || + heights.isModified() || + biomes.isModified() || + (overlay != null && overlay.isModified()) || + !primtives.equals(oldPrimitives); + } + + private void resetPrimtives() throws CloneNotSupportedException { + oldPrimitives = (CFIPrimtives) primtives.clone(); + } + + @Override + public void undoChanges(FaweInputStream in) throws IOException { + heights.undoChanges(in); + biomes.undoChanges(in); + floor.undoChanges(in); + main.undoChanges(in); + if (in.readBoolean()) overlay.undoChanges(in); + try { + for (Field field : ReflectionUtils.sortFields(CFIPrimtives.class.getDeclaredFields())) { + if (in.readBoolean()) { + field.set(primtives, in.readPrimitive(field.getType())); // old + in.readPrimitive(field.getType()); // new + } + } + resetPrimtives(); + } catch (Throwable neverHappens) { neverHappens.printStackTrace(); } + blocks.undoChanges(in); + } + + @Override + public void redoChanges(FaweInputStream in) throws IOException { + heights.redoChanges(in); + biomes.redoChanges(in); + floor.redoChanges(in); + main.redoChanges(in); + if (in.readBoolean()) overlay.redoChanges(in); + + try { + for (Field field : ReflectionUtils.sortFields(CFIPrimtives.class.getDeclaredFields())) { + if (in.readBoolean()) { + in.readPrimitive(field.getType()); // old + field.set(primtives, in.readPrimitive(field.getType())); // new + } + } + resetPrimtives(); + } catch (Throwable neverHappens) { neverHappens.printStackTrace(); } + + blocks.clearChanges(); // blocks.redoChanges(in); Unsupported + } + + @Override + public void addEditSession(EditSession session) { + session.setFastMode(true); + this.editSession = session; + } + + @Override + public boolean supports(Capability capability) { + return false; + } + + // Used for visualizing the world on a map private ImageViewer viewer; + // Used for visualizing the world by sending chunk packets + // These three variables should be set together +// private FaweQueue packetQueue; + private FawePlayer player; + private Vector2D chunkOffset = Vector2D.ZERO; + private EditSession editSession; + // end public HeightMapMCAGenerator(BufferedImage img, File regionFolder) { this(img.getWidth(), img.getHeight(), regionFolder); @@ -69,28 +228,108 @@ public HeightMapMCAGenerator(BufferedImage img, File regionFolder) { public HeightMapMCAGenerator(int width, int length, File regionFolder) { super(width, length, regionFolder); int area = getArea(); - heights = new byte[getArea()]; - biomes = new byte[getArea()]; - floor = new char[getArea()]; - main = new char[getArea()]; + + blocks = new DifferentialBlockBuffer(width, length); + heights = new DifferentialArray(new byte[getArea()]); + biomes = new DifferentialArray(new byte[getArea()]); + floor = new DifferentialArray(new char[getArea()]); + main = new DifferentialArray(new char[getArea()]); + char stone = (char) FaweCache.getCombined(1, 0); char grass = (char) FaweCache.getCombined(2, 0); - Arrays.fill(main, stone); - Arrays.fill(floor, grass); + Arrays.fill(main.getCharArray(), stone); + Arrays.fill(floor.getCharArray(), grass); + } + + public Metadatable getMetaData() { + return metaData; + } + + @Override + public FaweQueue getQueue() { + throw new UnsupportedOperationException("Not supported: Queue is not backed by a real world"); + } + + public Vector getOrigin() { + return new BlockVector(chunkOffset.getBlockX() << 4, 0, chunkOffset.getBlockZ() << 4); + } + + public boolean hasPacketViewer() { + return player != null; + } + + public void setPacketViewer(FawePlayer player) { + this.player = player; + if (player != null) { + FaweLocation pos = player.getLocation(); + this.chunkOffset = new Vector2D(pos.x >> 4, pos.z >> 4); + } + } + + public FawePlayer getOwner() { + return player; + } + + private char[][][] getChunkArray(int x, int z) { + char[][][][][] blocksData = blocks.get(); + if (blocksData == null) return null; + char[][][][] arr = blocksData[z]; + return arr != null ? arr[x] : null; } public void setImageViewer(ImageViewer viewer) { this.viewer = viewer; - update(); } public ImageViewer getImageViewer() { return viewer; } - private void update() { + public void update() { if (viewer != null) { - viewer.view(draw()); + viewer.view(this); + } + if (chunkOffset != null && player != null) { + FaweQueue packetQueue = SetQueue.IMP.getNewQueue(player.getWorld(), true, false); + + if (!packetQueue.supports(Capability.CHUNK_PACKETS)) { + return; + } + + int lenCX = (getWidth() + 15) >> 4; + int lenCZ = (getLength() + 15) >> 4; + + int OX = chunkOffset.getBlockX(); + int OZ = chunkOffset.getBlockZ(); + + FaweLocation position = player.getLocation(); + int pcx = (position.x >> 4) - OX; + int pcz = (position.z >> 4) - OZ; + + int scx = Math.max(0, pcx - 10); + int scz = Math.max(0, pcz - 10); + int ecx = Math.min(lenCX - 1, pcx + 10); + int ecz = Math.min(lenCZ - 1, pcz + 10); + + MCAChunk chunk = new MCAChunk(this, 0, 0); + for (int cz = scz; cz <= ecz; cz++) { + for (int cx = scx; cx <= ecx; cx++) { + final int finalCX = cx; + final int finalCZ = cz; + TaskManager.IMP.getPublicForkJoinPool().submit((Runnable) () -> { + try { + FaweChunk toSend = getSnapshot(finalCX, finalCZ); + toSend.setLoc(HeightMapMCAGenerator.this, finalCX + OX, finalCZ + OZ); + packetQueue.sendChunkUpdate(toSend, player); + } catch (Throwable e) { + e.printStackTrace(); + } + }); +// FaweChunk toSend = getSnapshot(chunk, finalCX, finalCZ); +// toSend.setLoc(HeightMapMCAGenerator.this, finalCX + OX, finalCZ + OZ); +// packetQueue.sendChunkUpdate(toSend, player); + } + } } } @@ -106,7 +345,7 @@ public TextureUtil getTextureUtil() { textureUtil = Fawe.get().getTextureUtil(); } try { - if (randomVariation) { + if (primtives.randomVariation) { return new RandomTextureUtil(textureUtil); } else if (textureUtil instanceof CachedTextureUtil) { return textureUtil; @@ -119,22 +358,28 @@ public TextureUtil getTextureUtil() { } } + public void setBedrockId(int bedrockId) { + this.primtives.bedrockId = (byte) bedrockId; + } + + public void setFloorThickness(int floorThickness) { + this.primtives.floorThickness = floorThickness; + } + public void setWaterHeight(int waterHeight) { - this.waterHeight = waterHeight; - update(); + this.primtives.waterHeight = waterHeight; } public void setWaterId(int waterId) { - this.waterId = (byte) waterId; - update(); + this.primtives.waterId = (byte) waterId; } public void setTextureRandomVariation(boolean randomVariation) { - this.randomVariation = randomVariation; + this.primtives.randomVariation = randomVariation; } public boolean getTextureRandomVariation() { - return this.randomVariation; + return this.primtives.randomVariation; } public void setTextureUtil(TextureUtil textureUtil) { @@ -149,100 +394,166 @@ public void smooth(Mask mask, int radius, int iterations) { smooth(null, mask, false, radius, iterations); } - private void smooth(BufferedImage img, Mask mask, boolean white, int radius, int iterations) { - char snow = 78 << 4; - long[] copy = new long[heights.length]; - char[] layers = new char[heights.length]; + public void smooth(Vector2D min, Vector2D max, int radius, int iterations) { + char snowLayer = 78 << 4; + char snowBlock = 80; + + char[] floor = this.floor.get(); + byte[] heights = this.heights.get(); + int width = getWidth(); int length = getLength(); - SummedAreaTable table = new SummedAreaTable(copy, layers, width, radius); + + int minX = min.getBlockX(); + int minZ = min.getBlockZ(); + + int maxX = max.getBlockX(); + int maxZ = max.getBlockZ(); + + int tableWidth = (maxX - minX + 1); + int tableLength = (maxZ - minZ + 1); + int smoothArea = tableWidth * tableLength; + + long[] copy = new long[smoothArea]; + char[] layers = new char[smoothArea]; + + SummedAreaTable table = new SummedAreaTable(copy, layers, tableWidth, radius); for (int j = 0; j < iterations; j++) { - for (int i = 0; i < heights.length; i++) { - char combined = floor[i]; - int id = combined >> 4; - if (id == 78 || id == 80) { - layers[i] = (char) (((heights[i] & 0xFF) << 3) + (floor[i] & 0x7) + 1); - } else { - layers[i] = (char) (((heights[i] & 0xFF) << 3) + 9); + + { // Copy to table + int localIndex = 0; + int zIndex = (minZ * getWidth()); + for (int z = minZ; z <= maxZ; z++, zIndex += getWidth()) { + int index = zIndex + minX; + for (int x = minX; x <= maxX; x++, index++, localIndex++) { + char combined = floor[index]; + int id = combined >> 4; + if (id == 78) { + layers[localIndex] = (char) (((heights[index] & 0xFF) << 3) + (floor[index] & 0x7) - 7); + } else { + layers[localIndex] = (char) (((heights[index] & 0xFF) << 3)); + } + } } } - int index = 0; + // Process table table.processSummedAreaTable(); - if (img != null) { - for (int z = 0; z < getLength(); z++) { - for (int x = 0; x < getWidth(); x++, index++) { - int height = img.getRGB(x, z) & 0xFF; - if (height == 255 || height > 0 && !white && PseudoRandom.random.nextInt(256) <= height) { - int newHeight = table.average(x, z, index) - 1; - int blockHeight = (newHeight) >> 3; - int layerHeight = (newHeight) & 0x7; - heights[index] = (byte) blockHeight; - int id = floor[index] >> 4; - if (id == 78 || id == 80) { - floor[index] = (char) (snow + layerHeight); + { // Copy from table + int localIndex = 0; + int zIndex = (minZ * getWidth()); + for (int z = minZ, localZ = 0; z <= maxZ; z++, localZ++, zIndex += getWidth()) { + int index = zIndex + minX; + for (int x = minX, localX = 0; x <= maxX; x++, localX++, index++, localIndex++) { + int y = heights[index] & 0xFF; + int newHeight = table.average(localX, localZ, localIndex); + setLayerHeight(index, newHeight); + } + } + } + } + } + + private final void setLayerHeight(int index, int height) { + int blockHeight = (height) >> 3; + int layerHeight = (height) & 0x7; + setLayerHeight(index, blockHeight, layerHeight); + } + + private final void setLayerHeight(int index, int blockHeight, int layerHeight) { + int floorId = floor.get()[index] >> 4; + if (floorId == 78 || floorId == 80) { + if (layerHeight != 0) { + this.heights.setByte(index, (byte) (blockHeight + 1)); + this.floor.setChar(index, (char) (1248 + layerHeight)); + } else { + this.heights.setByte(index, (byte) (blockHeight)); + this.floor.setChar(index, (char) (1280)); + } + } else { + this.heights.setByte(index, (byte) (blockHeight)); + + } + } + + private void smooth(BufferedImage img, Mask mask, boolean white, int radius, int iterations) { + char snowLayer = 78 << 4; + char snowBlock = 80; + + char[] floor = this.floor.get(); + byte[] heights = this.heights.get(); + + long[] copy = new long[heights.length]; + char[] layers = new char[heights.length]; + + this.floor.record(() -> HeightMapMCAGenerator.this.heights.record(() -> { + int width = getWidth(); + int length = getLength(); + SummedAreaTable table = new SummedAreaTable(copy, layers, width, radius); + for (int j = 0; j < iterations; j++) { + for (int i = 0; i < heights.length; i++) { + char combined = floor[i]; + int id = combined >> 4; + if (id == 78) { + layers[i] = (char) (((heights[i] & 0xFF) << 3) + (floor[i] & 0x7) - 7); + } else { + layers[i] = (char) (((heights[i] & 0xFF) << 3)); + } + } + int index = 0; + table.processSummedAreaTable(); + if (img != null) { + for (int z = 0; z < getLength(); z++) { + for (int x = 0; x < getWidth(); x++, index++) { + int height = img.getRGB(x, z) & 0xFF; + if (height == 255 || height > 0 && !white && PseudoRandom.random.nextInt(256) <= height) { + int newHeight = table.average(x, z, index); + setLayerHeight(index, newHeight); } } } - } - } else if (mask != null) { - for (int z = 0; z < getLength(); z++) { - mutable.mutZ(z); - for (int x = 0; x < getWidth(); x++, index++) { - int y = heights[index] & 0xFF; - mutable.mutX(x); - mutable.mutY(y); - if (mask.test(mutable)) { - int newHeight = table.average(x, z, index) - 1; - int blockHeight = (newHeight) >> 3; - int layerHeight = (newHeight) & 0x7; - heights[index] = (byte) blockHeight; - int id = floor[index] >> 4; - if (id == 78 || id == 80) { - floor[index] = (char) (snow + layerHeight); + } else if (mask != null) { + for (int z = 0; z < getLength(); z++) { + mutable.mutZ(z); + for (int x = 0; x < getWidth(); x++, index++) { + int y = heights[index] & 0xFF; + mutable.mutX(x); + mutable.mutY(y); + if (mask.test(mutable)) { + int newHeight = table.average(x, z, index); + setLayerHeight(index, newHeight); } } } - } - } else { - for (int z = 0; z < getLength(); z++) { - for (int x = 0; x < getWidth(); x++, index++) { - int y = heights[index] & 0xFF; - int newHeight = table.average(x, z, index) - 1; - int blockHeight = (newHeight) >> 3; - int layerHeight = (newHeight) & 0x7; - heights[index] = (byte) blockHeight; - int id = floor[index] >> 4; - if (id == 78 || id == 80) { - floor[index] = (char) (snow + layerHeight); + } else { + for (int z = 0; z < getLength(); z++) { + for (int x = 0; x < getWidth(); x++, index++) { + int newHeight = table.average(x, z, index); + setLayerHeight(index, newHeight); } } } } - } - update(); + })); } public void setHeight(BufferedImage img) { int index = 0; for (int z = 0; z < getLength(); z++) { for (int x = 0; x < getWidth(); x++, index++) { - heights[index] = (byte) (img.getRGB(x, z) >> 8); + heights.setByte(index, (byte) (img.getRGB(x, z) >> 8)); } } - update(); } public void addCaves() throws WorldEditException { CuboidRegion region = new CuboidRegion(new Vector(0, 0, 0), new Vector(getWidth(), 255, getLength())); addCaves(region); - update(); } @Deprecated public void addSchems(Mask mask, WorldData worldData, List clipboards, int rarity, boolean rotate) throws WorldEditException { CuboidRegion region = new CuboidRegion(new Vector(0, 0, 0), new Vector(getWidth(), 255, getLength())); addSchems(region, mask, worldData, clipboards, rarity, rotate); - update(); } public void addSchems(BufferedImage img, Mask mask, WorldData worldData, List clipboards, int rarity, int distance, boolean randomRotate) throws WorldEditException { @@ -255,7 +566,7 @@ public void addSchems(BufferedImage img, Mask mask, WorldData worldData, List height * doubleRarity) { continue; @@ -294,7 +605,6 @@ public void addSchems(BufferedImage img, Mask mask, WorldData worldData, List clipboards, int rarity, int distance, boolean randomRotate) throws WorldEditException { @@ -305,7 +615,7 @@ public void addSchems(Mask mask, WorldData worldData, List clip for (int z = 0; z < getLength(); z++) { mutable.mutZ(z); for (int x = 0; x < getWidth(); x++, index++) { - int y = heights[index]; + int y = heights.getByte(index) & 0xFF; if (PseudoRandom.random.nextInt(256) > scaledRarity) { continue; } @@ -344,18 +654,15 @@ public void addSchems(Mask mask, WorldData worldData, List clip } } } - update(); } public void addOre(Mask mask, Pattern material, int size, int frequency, int rarity, int minY, int maxY) throws WorldEditException { CuboidRegion region = new CuboidRegion(new Vector(0, 0, 0), new Vector(getWidth(), 255, getLength())); addOre(region, mask, material, size, frequency, rarity, minY, maxY); - update(); } public void addDefaultOres(Mask mask) throws WorldEditException { addOres(new CuboidRegion(new Vector(0, 0, 0), new Vector(getWidth(), 255, getLength())), mask); - update(); } @Override @@ -375,142 +682,395 @@ public boolean setBlock(Vector position, BaseBlock block) throws WorldEditExcept @Override public boolean setBiome(Vector2D position, BaseBiome biome) { - int index = position.getBlockZ() * getWidth() + position.getBlockX(); - if (index < 0 || index >= heights.length) index = Math.floorMod(index, heights.length); - biomes[index] = (byte) biome.getId(); - return true; + return this.setBiome(position.getBlockX(), position.getBlockZ(), biome); } - @Override - public boolean setBlock(int x, int y, int z, BaseBlock block) throws WorldEditException { + private boolean setBlock(int x, int y, int z, char combined) { int index = z * getWidth() + x; - if (index < 0 || index >= heights.length) index = Math.floorMod(index, heights.length); - int height = heights[index] & 0xFF; - char combined = (char) block.getCombined(); + if (index < 0 || index >= getArea()) return false; + int height = heights.getByte(index) & 0xFF; switch (y - height) { case 0: - floor[index] = combined; + floor.setChar(index, combined); return true; case 1: - char mainId = main[index]; - char floorId = floor[index]; - floor[index] = combined; - heights[index]++; + char mainId = main.getChar(index); + char floorId = floor.getChar(index); + floor.setChar(index, combined); + + byte currentHeight = heights.getByte(index); + currentHeight++; + heights.setByte(index, currentHeight); if (mainId == floorId) return true; y--; combined = floorId; default: - short chunkX = (short) (x >> 4); - short chunkZ = (short) (z >> 4); - int pair = MathMan.pair(chunkX, chunkZ); - char[][][] map = blocks.get(pair); - if (map == null) { - map = new char[256][][]; - blocks.put(pair, map); - } - char[][] yMap = map[y]; - if (yMap == null) { - map[y] = yMap = new char[16][]; - } - z = z & 15; - char[] zMap = yMap[z]; - if (zMap == null) { - yMap[z] = zMap = new char[16]; - } - zMap[x & 15] = combined != 0 ? combined : 1; - return true; + try { + short chunkX = (short) (x >> 4); + short chunkZ = (short) (z >> 4); + blocks.set(x, y, z, combined); + return true; + } catch (IndexOutOfBoundsException ignore) { + return false; + } } } @Override - public BaseBiome getBiome(Vector2D position) { - int index = position.getBlockZ() * getWidth() + position.getBlockX(); - if (index < 0 || index >= heights.length) index = Math.floorMod(index, heights.length); - return FaweCache.CACHE_BIOME[biomes[index] & 0xFF]; + public boolean setBlock(int x, int y, int z, int id, int data) { + return this.setBlock(x, y, z, (char) FaweCache.getCombined(id, data)); } @Override - public BaseBlock getBlock(Vector position) { - return getLazyBlock(position); + public void setTile(int x, int y, int z, CompoundTag tag) { + // Not implemented } @Override - public BaseBlock getLazyBlock(Vector position) { - return getLazyBlock(position.getBlockX(), position.getBlockY(), position.getBlockZ()); + public void setEntity(int x, int y, int z, CompoundTag tag) { + // Not implemented } @Override - public BaseBlock getLazyBlock(int x, int y, int z) { + public void removeEntity(int x, int y, int z, UUID uuid) { + // Not implemented + } + + @Override + public boolean setBiome(int x, int z, BaseBiome biome) { + int index = z * getWidth() + x; + if (index < 0 || index >= getArea()) return false; + biomes.setByte(index, (byte) biome.getId()); + return true; + } + + @Override + public FaweChunk getFaweChunk(int chunkX, int chunkZ) { + return new SimpleCharFaweChunk(this, chunkX, chunkZ); + } + + public FaweChunk getSnapshot(int chunkX, int chunkZ) { + return getSnapshot(null, chunkX, chunkZ); + } + + private FaweChunk getSnapshot(final MCAChunk chunk, int chunkX, int chunkZ) { + return new LazyFaweChunk(this, chunkX, chunkZ) { + @Override + public MCAChunk getChunk() { + MCAChunk tmp = chunk; + if (tmp == null) { + tmp = new MCAChunk(HeightMapMCAGenerator.this, chunkX, chunkZ); + } else { + tmp.setLoc(HeightMapMCAGenerator.this, chunkX, chunkZ); + } + int cbx = chunkX << 4; + int cbz = chunkZ << 4; + int csx = Math.max(0, cbx); + int csz = Math.max(0, cbz); + int cex = Math.min(getWidth(), cbx + 15); + int cez = Math.min(getLength(), cbz + 15); + write(tmp, csx, cex, csz, cez); + tmp.setLoc(HeightMapMCAGenerator.this, getX(), getZ()); + return tmp; + } + + @Override + public void addToQueue() { + MCAChunk cached = getCachedChunk(); + if (cached != null) setChunk(cached); + } + }; + } + + @Override + public Collection getFaweChunks() { + return Collections.emptyList(); + } + + @Override + public void setChunk(FaweChunk chunk) { + System.out.println(((SimpleCharFaweChunk) chunk).getTotalCount()); + char[][] src = chunk.getCombinedIdArrays(); + for (int i = 0; i < src.length; i++) { + if (src[i] != null) { + int bx = chunk.getX() << 4; + int bz = chunk.getZ() << 4; + int by = i << 4; + for (int layer = i; layer < src.length; layer++) { + char[] srcLayer = src[layer]; + if (srcLayer != null) { + int index = 0; + for (int y = 0; y < 16; y++) { + int yy = by + y; + for (int z = 0; z < 16; z++) { + int zz = bz + z; + for (int x = 0; x < 16; x++, index++) { + char combined = srcLayer[index]; + if (combined != 0) { + setBlock(bx + x, yy, zz, combined); + } + } + } + } + } + } + break; + } + } + } + + @Override + public File getSaveFolder() { + return getFolder(); + } + + @Override + public boolean regenerateChunk(int x, int z, @Nullable BaseBiome biome, @Nullable Long seed) { + // Unsupported + return false; + } + + @Override + public void sendBlockUpdate(FaweChunk chunk, FawePlayer... players) { + + } + + @Override + public void flush(int time) { + next(0, time); + } + + @Override + public boolean next(int amount, long time) { + EditSession curES = editSession; + if (curES != null && isModified()) { + try { + update(); + FawePlayer esPlayer = curES.getPlayer(); + UUID uuid = esPlayer != null ? esPlayer.getUUID() : EditSession.CONSOLE; + try { + curES.setRawChangeSet(new CFIChangeSet(this, uuid)); + } catch (IOException e) { + e.printStackTrace(); + } + } catch (Throwable e) { + e.printStackTrace(); + } + } + clear(); + return false; + } + + @Override + public void sendChunk(FaweChunk chunk) { + } + + @Override + public void sendChunk(int x, int z, int bitMask) { + } + + @Override + public void clear() { + this.editSession = null; + } + + @Override + public void close() { + clear(); + if (chunkOffset != null && player != null) { + FaweQueue packetQueue = SetQueue.IMP.getNewQueue(player.getWorld(), true, false); + + int lenCX = (getWidth() + 15) >> 4; + int lenCZ = (getLength() + 15) >> 4; + + int OX = chunkOffset.getBlockX(); + int OZ = chunkOffset.getBlockZ(); + + FaweLocation position = player.getLocation(); + int pcx = (position.x >> 4) - OX; + int pcz = (position.z >> 4) - OZ; + + int scx = Math.max(0, pcx - 10); + int scz = Math.max(0, pcz - 10); + int ecx = Math.min(lenCX - 1, pcx + 10); + int ecz = Math.min(lenCZ - 1, pcz + 10); + + for (int cz = scz; cz <= ecz; cz++) { + for (int cx = scx; cx <= ecx; cx++) { + packetQueue.sendChunk(cx + OX, cz + OZ, 0); + } + } + } + player = null; + chunkOffset = null; + } + + @Override + public void addNotifyTask(int x, int z, Runnable runnable) { + if (runnable != null) runnable.run(); + } + + @Override + public int getBiomeId(int x, int z) throws FaweException.FaweChunkLoadException { int index = z * getWidth() + x; - if (y < 0) return EditSession.nullBlock; - if (index < 0 || index >= heights.length) index = Math.floorMod(index, heights.length); - int height = heights[index] & 0xFF; + if (index < 0 || index >= getArea()) index = Math.floorMod(index, getArea()); + return biomes.getByte(index) & 0xFF; + } + + @Override + public int getCombinedId4Data(int x, int y, int z) throws FaweException.FaweChunkLoadException { + int index = z * getWidth() + x; + if (y < 0) return 0; + if (index < 0 || index >= getArea()) index = Math.floorMod(index, getArea()); + int height = heights.getByte(index) & 0xFF; if (y > height) { if (y == height + 1) { - return FaweCache.CACHE_BLOCK[overlay != null ? overlay[index] : 0]; + return overlay != null ? overlay.getChar(index) : 0; } - if (!blocks.isEmpty()) { + if (blocks != null) { short chunkX = (short) (x >> 4); short chunkZ = (short) (z >> 4); - int pair = MathMan.pair(chunkX, chunkZ); - char[][][] map = blocks.get(pair); + char[][][] map = getChunkArray(chunkX, chunkZ); if (map != null) { char combined = get(map, x, y, z); if (combined != 0) { - return FaweCache.CACHE_BLOCK[combined]; + return combined; } } } - if (y > waterHeight) { - return FaweCache.CACHE_BLOCK[waterId << 4]; + if (y <= primtives.waterHeight) { + return primtives.waterId << 4; } - return FaweCache.CACHE_BLOCK[0]; + return 0; } else if (y == height) { - return FaweCache.CACHE_BLOCK[floor[index]]; + return floor.getChar(index); } else { - if (!blocks.isEmpty()) { + if (blocks != null) { short chunkX = (short) (x >> 4); short chunkZ = (short) (z >> 4); - int pair = MathMan.pair(chunkX, chunkZ); - char[][][] map = blocks.get(pair); + char[][][] map = getChunkArray(chunkX, chunkZ); if (map != null) { char combined = get(map, x, y, z); if (combined != 0) { - return FaweCache.CACHE_BLOCK[combined]; + return combined; } } } - return FaweCache.CACHE_BLOCK[main[index]]; + return main.getChar(index); } } + @Override + public int getCombinedId4Data(int x, int y, int z, int def) { + return getCombinedId4Data(x, y, z); + } + + @Override + public int getCachedCombinedId4Data(int x, int y, int z) throws FaweException.FaweChunkLoadException { + return getCombinedId4Data(x, y, z); + } + + @Override + public boolean hasSky() { + return true; + } + + @Override + public int getSkyLight(int x, int y, int z) { + return getNearestSurfaceTerrainBlock(x, z, y, 0, 255) < y ? 15 : 0; + } + + @Override + public int getEmmittedLight(int x, int y, int z) { + return 0; + } + + @Override + public CompoundTag getTileEntity(int x, int y, int z) throws FaweException.FaweChunkLoadException { + return null; + } + + @Override + public int size() { + return 0; + } + + @Override + public boolean setBlock(int x, int y, int z, BaseBlock block) throws WorldEditException { + return this.setBlock(x, y, z, (char) block.getCombined()); + } + + @Override + public BaseBiome getBiome(Vector2D position) { + return FaweCache.CACHE_BIOME[getBiomeId(position.getBlockX(), position.getBlockZ())]; + } + + @Override + public BaseBlock getBlock(Vector position) { + return getLazyBlock(position); + } + + public BaseBlock getFloor(int x, int z) { + int index = z * getWidth() + x; + return FaweCache.CACHE_BLOCK[floor.getChar(index)]; + } + + public int getHeight(int x, int z) { + int index = z * getWidth() + x; + return heights.getByte(index) & 0xFF; + } + + public int getHeight(int index) { + return heights.getByte(index) & 0xFF; + } + + public void setFloor(int x, int z, BaseBlock block) { + int index = z * getWidth() + x; + floor.setChar(index, (char) block.getCombined()); + } + + @Override + public BaseBlock getLazyBlock(Vector position) { + return getLazyBlock(position.getBlockX(), position.getBlockY(), position.getBlockZ()); + } + + @Override + public BaseBlock getLazyBlock(int x, int y, int z) { + return FaweCache.CACHE_BLOCK[getCombinedId4Data(x, y, z)]; + } + @Override public int getNearestSurfaceLayer(int x, int z, int y, int minY, int maxY) { int index = z * getWidth() + x; - if (index < 0 || index >= heights.length) index = Math.floorMod(index, heights.length); - return ((heights[index] & 0xFF) << 3) + (floor[index] & 0xFF) + 1; + if (index < 0 || index >= getArea()) index = Math.floorMod(index, getArea()); + return ((heights.getByte(index) & 0xFF) << 3) + (floor.getChar(index) & 0xFF) + 1; } @Override public int getNearestSurfaceTerrainBlock(int x, int z, int y, int minY, int maxY) { int index = z * getWidth() + x; - if (index < 0 || index >= heights.length) index = Math.floorMod(index, heights.length); - return heights[index] & 0xFF; + if (index < 0 || index >= getArea()) index = Math.floorMod(index, getArea()); + return heights.getByte(index) & 0xFF; } public void setBiome(BufferedImage img, byte biome, boolean white) { if (img.getWidth() != getWidth() || img.getHeight() != getLength()) throw new IllegalArgumentException("Input image dimensions do not match the current height map!"); - int index = 0; - for (int z = 0; z < getLength(); z++) { - for (int x = 0; x < getWidth(); x++, index++) { - int height = img.getRGB(x, z) & 0xFF; - if (height == 255 || height > 0 && !white && PseudoRandom.random.nextInt(256) <= height) { - biomes[index] = biome; + biomes.record(new Runnable() { + @Override + public void run() { + byte[] biomeArr = biomes.get(); + int index = 0; + for (int z = 0; z < getLength(); z++) { + for (int x = 0; x < getWidth(); x++, index++) { + int height = img.getRGB(x, z) & 0xFF; + if (height == 255 || height > 0 && !white && PseudoRandom.random.nextInt(256) <= height) { + biomeArr[index] = biome; + } + } } } - } - update(); + }); } public BufferedImage draw() { @@ -518,11 +1078,11 @@ public BufferedImage draw() { } public void setBiomePriority(int value) { - this.biomePriority = ((value * 65536) / 100) - 32768; + this.primtives.biomePriority = ((value * 65536) / 100) - 32768; } public int getBiomePriority() { - return ((biomePriority + 32768) * 100) / 65536; + return ((primtives.biomePriority + 32768) * 100) / 65536; } public void setBlockAndBiomeColor(BufferedImage img, Mask mask, BufferedImage imgMask, boolean whiteOnly) { @@ -533,77 +1093,94 @@ public void setBlockAndBiomeColor(BufferedImage img, Mask mask, BufferedImage im if (img.getWidth() != getWidth() || img.getHeight() != getLength()) throw new IllegalArgumentException("Input image dimensions do not match the current height map!"); TextureUtil textureUtil = getTextureUtil(); - int index = 0; + int widthIndex = img.getWidth() - 1; int heightIndex = img.getHeight() - 1; - int maxIndex = biomes.length - 1; + int maxIndex = getArea() - 1; - int[] buffer = new int[2]; - for (int z = 0; z < img.getHeight(); z++) { - mutable.mutZ(z); - for (int x = 0; x < img.getWidth(); x++, index++) { - if (mask != null) { - mutable.mutX(z); - mutable.mutY(heights[index] & 0xFF); - if (!mask.test(mutable)) continue; - } - if (imgMask != null) { - int height = imgMask.getRGB(x, z) & 0xFF; - if (height != 255 && (height <= 0 || !whiteOnly || PseudoRandom.random.nextInt(256) > height)) continue; - } - int color = img.getRGB(x, z); - if (textureUtil.getIsBlockCloserThanBiome(buffer, color, biomePriority)) { - char combined = (char) buffer[0]; - main[index] = combined; - floor[index] = combined; - } - biomes[index] = (byte) buffer[1]; + biomes.record(() -> floor.record(() -> main.record(() -> { + char[] mainArr = main.get(); + char[] floorArr = floor.get(); + byte[] biomesArr = biomes.get(); + + int index = 0; + int[] buffer = new int[2]; + for (int z = 0; z < img.getHeight(); z++) { + mutable.mutZ(z); + for (int x = 0; x < img.getWidth(); x++, index++) { + if (mask != null) { + mutable.mutX(z); + mutable.mutY(heights.getByte(index) & 0xFF); + if (!mask.test(mutable)) continue; + } + if (imgMask != null) { + int height = imgMask.getRGB(x, z) & 0xFF; + if (height != 255 && (height <= 0 || !whiteOnly || PseudoRandom.random.nextInt(256) > height)) continue; + } + int color = img.getRGB(x, z); + if (textureUtil.getIsBlockCloserThanBiome(buffer, color, primtives.biomePriority)) { + char combined = (char) buffer[0]; + mainArr[index] = combined; + floorArr[index] = combined; + } + biomesArr[index] = (byte) buffer[1]; + } } - } - update(); + }))); } public void setBlockAndBiomeColor(BufferedImage img) { if (img.getWidth() != getWidth() || img.getHeight() != getLength()) throw new IllegalArgumentException("Input image dimensions do not match the current height map!"); TextureUtil textureUtil = getTextureUtil(); - int index = 0; int widthIndex = img.getWidth() - 1; int heightIndex = img.getHeight() - 1; - int maxIndex = biomes.length - 1; - - int[] buffer = new int[2]; - for (int y = 0; y < img.getHeight(); y++) { - boolean yBiome = y > 0 && y < heightIndex; - for (int x = 0; x < img.getWidth(); x++, index++) { - int color = img.getRGB(x, y); - if (textureUtil.getIsBlockCloserThanBiome(buffer, color, biomePriority)) { - char combined = (char) buffer[0]; - main[index] = combined; - floor[index] = combined; - } - biomes[index] = (byte) buffer[1]; + int maxIndex = getArea() - 1; + + biomes.record(() -> floor.record(() -> main.record(() -> { + char[] mainArr = main.get(); + char[] floorArr = floor.get(); + byte[] biomesArr = biomes.get(); + + int[] buffer = new int[2]; + int index = 0; + for (int y = 0; y < img.getHeight(); y++) { + boolean yBiome = y > 0 && y < heightIndex; + for (int x = 0; x < img.getWidth(); x++, index++) { + int color = img.getRGB(x, y); + if (textureUtil.getIsBlockCloserThanBiome(buffer, color, primtives.biomePriority)) { + char combined = (char) buffer[0]; + mainArr[index] = combined; + floorArr[index] = combined; + } + biomesArr[index] = (byte) buffer[1]; + } } - } - update(); + }))); } public void setBiomeColor(BufferedImage img) { if (img.getWidth() != getWidth() || img.getHeight() != getLength()) throw new IllegalArgumentException("Input image dimensions do not match the current height map!"); TextureUtil textureUtil = getTextureUtil(); - int index = 0; - for (int y = 0; y < img.getHeight(); y++) { - for (int x = 0; x < img.getWidth(); x++) { - int color = img.getRGB(x, y); - TextureUtil.BiomeColor biome = textureUtil.getNearestBiome(color); - if (biome != null) { - biomes[index] = (byte) biome.id; - } - index++; + + biomes.record(new Runnable() { + @Override + public void run() { + byte[] biomesArr = biomes.get(); + int index = 0; + for (int y = 0; y < img.getHeight(); y++) { + for (int x = 0; x < img.getWidth(); x++) { + int color = img.getRGB(x, y); + TextureUtil.BiomeColor biome = textureUtil.getNearestBiome(color); + if (biome != null) { + biomesArr[index] = (byte) biome.id; + } + index++; + } + } } - } - update(); + }); } public void setColor(BufferedImage img, BufferedImage mask, boolean white) { @@ -611,89 +1188,110 @@ public void setColor(BufferedImage img, BufferedImage mask, boolean white) { throw new IllegalArgumentException("Input image dimensions do not match the current height map!"); if (mask.getWidth() != getWidth() || mask.getHeight() != getLength()) throw new IllegalArgumentException("Input image dimensions do not match the current height map!"); - modifiedMain = true; + primtives.modifiedMain = true; TextureUtil textureUtil = getTextureUtil(); - int index = 0; - for (int z = 0; z < getLength(); z++) { - for (int x = 0; x < getWidth(); x++, index++) { - int height = mask.getRGB(x, z) & 0xFF; - if (height == 255 || height > 0 && !white && PseudoRandom.random.nextInt(256) <= height) { - int color = img.getRGB(x, z); - BaseBlock block = textureUtil.getNearestBlock(color); - if (block != null) { - char combined = (char) block.getCombined(); - main[index] = combined; - floor[index] = combined; + + floor.record(() -> main.record(() -> { + char[] mainArr = main.get(); + char[] floorArr = floor.get(); + + int index = 0; + for (int z = 0; z < getLength(); z++) { + for (int x = 0; x < getWidth(); x++, index++) { + int height = mask.getRGB(x, z) & 0xFF; + if (height == 255 || height > 0 && !white && PseudoRandom.random.nextInt(256) <= height) { + int color = img.getRGB(x, z); + BaseBlock block = textureUtil.getNearestBlock(color); + if (block != null) { + char combined = (char) block.getCombined(); + mainArr[index] = combined; + floorArr[index] = combined; + } } } } - } - update(); + })); } public void setColor(BufferedImage img, Mask mask) { if (img.getWidth() != getWidth() || img.getHeight() != getLength()) throw new IllegalArgumentException("Input image dimensions do not match the current height map!"); - modifiedMain = true; + primtives.modifiedMain = true; TextureUtil textureUtil = getTextureUtil(); - int index = 0; - for (int z = 0; z < getLength(); z++) { - mutable.mutZ(z); - for (int x = 0; x < getWidth(); x++, index++) { - mutable.mutX(x); - mutable.mutY(heights[index] & 0xFF); - if (mask.test(mutable)) { - int color = img.getRGB(x, z); - BaseBlock block = textureUtil.getNearestBlock(color); - if (block != null) { - char combined = (char) block.getCombined(); - main[index] = combined; - floor[index] = combined; + + + floor.record(() -> main.record(() -> { + char[] mainArr = main.get(); + char[] floorArr = floor.get(); + + int index = 0; + for (int z = 0; z < getLength(); z++) { + mutable.mutZ(z); + for (int x = 0; x < getWidth(); x++, index++) { + mutable.mutX(x); + mutable.mutY(heights.getByte(index) & 0xFF); + if (mask.test(mutable)) { + int color = img.getRGB(x, z); + BaseBlock block = textureUtil.getNearestBlock(color); + if (block != null) { + char combined = (char) block.getCombined(); + mainArr[index] = combined; + floorArr[index] = combined; + } } } } - } - update(); + })); } public void setColor(BufferedImage img) { if (img.getWidth() != getWidth() || img.getHeight() != getLength()) throw new IllegalArgumentException("Input image dimensions do not match the current height map!"); - modifiedMain = true; + primtives.modifiedMain = true; TextureUtil textureUtil = getTextureUtil(); - int index = 0; - for (int z = 0; z < img.getHeight(); z++) { - for (int x = 0; x < img.getWidth(); x++) { - int color = img.getRGB(x, z); - BaseBlock block = textureUtil.getNearestBlock(color); - if (block != null) { - char combined = (char) block.getCombined(); - main[index] = combined; - floor[index] = combined; - } - index++; + + floor.record(() -> main.record(() -> { + char[] mainArr = main.get(); + char[] floorArr = floor.get(); + + int index = 0; + for (int z = 0; z < img.getHeight(); z++) { + for (int x = 0; x < img.getWidth(); x++) { + int color = img.getRGB(x, z); + BaseBlock block = textureUtil.getNearestBlock(color); + if (block != null) { + char combined = (char) block.getCombined(); + mainArr[index] = combined; + floorArr[index] = combined; + } + index++; + } } - } - update(); + })); } public void setColorWithGlass(BufferedImage img) { if (img.getWidth() != getWidth() || img.getHeight() != getLength()) throw new IllegalArgumentException("Input image dimensions do not match the current height map!"); TextureUtil textureUtil = getTextureUtil(); - int index = 0; - for (int y = 0; y < img.getHeight(); y++) { - for (int x = 0; x < img.getWidth(); x++) { - int color = img.getRGB(x, y); - char[] layer = textureUtil.getNearestLayer(color); - if (layer != null) { - floor[index] = layer[0]; - main[index] = layer[1]; - } - index++; + + floor.record(() -> main.record(() -> { + char[] mainArr = main.get(); + char[] floorArr = floor.get(); + + int index = 0; + for (int y = 0; y < img.getHeight(); y++) { + for (int x = 0; x < img.getWidth(); x++) { + int color = img.getRGB(x, y); + char[] layer = textureUtil.getNearestLayer(color); + if (layer != null) { + floorArr[index] = layer[0]; + mainArr[index] = layer[1]; + } + index++; + } } - } - update(); + })); } public void setBiome(Mask mask, byte biome) { @@ -701,15 +1299,14 @@ public void setBiome(Mask mask, byte biome) { for (int z = 0; z < getLength(); z++) { mutable.mutZ(z); for (int x = 0; x < getWidth(); x++, index++) { - int y = heights[index] & 0xFF; + int y = heights.getByte(index) & 0xFF; mutable.mutX(x); mutable.mutY(y); if (mask.test(mutable)) { - biomes[index] = biome; + biomes.setByte(index, biome); } } } - update(); } public void setOverlay(BufferedImage img, Pattern pattern, boolean white) { @@ -718,21 +1315,27 @@ public void setOverlay(BufferedImage img, Pattern pattern, boolean white) { } else { if (img.getWidth() != getWidth() || img.getHeight() != getLength()) throw new IllegalArgumentException("Input image dimensions do not match the current height map!"); - if (overlay == null) overlay = new char[getArea()]; - int index = 0; - for (int z = 0; z < getLength(); z++) { - mutable.mutZ(z); - for (int x = 0; x < getWidth(); x++, index++) { - int height = img.getRGB(x, z) & 0xFF; - if (height == 255 || height > 0 && !white && PseudoRandom.random.nextInt(256) <= height) { - mutable.mutX(x); - mutable.mutY(height); - overlay[index] = (char) pattern.apply(mutable).getCombined(); + if (overlay == null) { + overlay = new DifferentialArray<>(new char[getArea()]); + } + + overlay.record(() -> { + char[] overlayArr = overlay.get(); + int index = 0; + for (int z = 0; z < getLength(); z++) { + mutable.mutZ(z); + for (int x = 0; x < getWidth(); x++, index++) { + int height = img.getRGB(x, z) & 0xFF; + if (height == 255 || height > 0 && !white && PseudoRandom.random.nextInt(256) <= height) { + mutable.mutX(x); + mutable.mutY(height); + overlayArr[index] = (char) pattern.apply(mutable).getCombined(); + } } } - } + }); + } - update(); } public void setMain(BufferedImage img, Pattern pattern, boolean white) { @@ -741,43 +1344,49 @@ public void setMain(BufferedImage img, Pattern pattern, boolean white) { } else { if (img.getWidth() != getWidth() || img.getHeight() != getLength()) throw new IllegalArgumentException("Input image dimensions do not match the current height map!"); - modifiedMain = true; - int index = 0; - for (int z = 0; z < getLength(); z++) { - mutable.mutZ(z); - for (int x = 0; x < getWidth(); x++, index++) { - int height = img.getRGB(x, z) & 0xFF; - if (height == 255 || height > 0 && !white && PseudoRandom.random.nextInt(256) <= height) { - mutable.mutX(x); - mutable.mutY(height); - main[index] = (char) pattern.apply(mutable).getCombined(); + primtives.modifiedMain = true; + + main.record(() -> { + char[] mainArr = main.get(); + int index = 0; + for (int z = 0; z < getLength(); z++) { + mutable.mutZ(z); + for (int x = 0; x < getWidth(); x++, index++) { + int height = img.getRGB(x, z) & 0xFF; + if (height == 255 || height > 0 && !white && PseudoRandom.random.nextInt(256) <= height) { + mutable.mutX(x); + mutable.mutY(height); + mainArr[index] = (char) pattern.apply(mutable).getCombined(); + } } } - } + }); } - update(); } - public void setFloor(BufferedImage img, Pattern pattern, boolean white) { - if (pattern instanceof BaseBlock) { - setFloor(img, (char) ((BaseBlock) pattern).getCombined(), white); - } else { - if (img.getWidth() != getWidth() || img.getHeight() != getLength()) - throw new IllegalArgumentException("Input image dimensions do not match the current height map!"); - int index = 0; - for (int z = 0; z < getLength(); z++) { - mutable.mutZ(z); - for (int x = 0; x < getWidth(); x++, index++) { - int height = img.getRGB(x, z) & 0xFF; - if (height == 255 || height > 0 && !white && PseudoRandom.random.nextInt(256) <= height) { - mutable.mutX(x); - mutable.mutY(height); - floor[index] = (char) pattern.apply(mutable).getCombined(); + public void setFloor(BufferedImage img, Pattern pattern, boolean white) { + if (pattern instanceof BaseBlock) { + setFloor(img, (char) ((BaseBlock) pattern).getCombined(), white); + } else { + if (img.getWidth() != getWidth() || img.getHeight() != getLength()) + throw new IllegalArgumentException("Input image dimensions do not match the current height map!"); + + floor.record(() -> { + char[] floorArr = floor.get(); + int index = 0; + for (int z = 0; z < getLength(); z++) { + mutable.mutZ(z); + for (int x = 0; x < getWidth(); x++, index++) { + int height = img.getRGB(x, z) & 0xFF; + if (height == 255 || height > 0 && !white && PseudoRandom.random.nextInt(256) <= height) { + mutable.mutX(x); + mutable.mutY(height); + floorArr[index] = (char) pattern.apply(mutable).getCombined(); + } } } - } + }); } - update(); } public void setColumn(BufferedImage img, Pattern pattern, boolean white) { @@ -786,23 +1395,27 @@ public void setColumn(BufferedImage img, Pattern pattern, boolean white) { } else { if (img.getWidth() != getWidth() || img.getHeight() != getLength()) throw new IllegalArgumentException("Input image dimensions do not match the current height map!"); - modifiedMain = true; - int index = 0; - for (int z = 0; z < getLength(); z++) { - mutable.mutZ(z); - for (int x = 0; x < getWidth(); x++, index++) { - int height = img.getRGB(x, z) & 0xFF; - if (height == 255 || height > 0 && !white && PseudoRandom.random.nextInt(256) <= height) { - mutable.mutX(x); - mutable.mutY(height); - char combined = (char) pattern.apply(mutable).getCombined(); - main[index] = combined; - floor[index] = combined; + primtives.modifiedMain = true; + + main.record(() -> floor.record(() -> { + char[] floorArr = floor.get(); + char[] mainArr = main.get(); + int index = 0; + for (int z = 0; z < getLength(); z++) { + mutable.mutZ(z); + for (int x = 0; x < getWidth(); x++, index++) { + int height = img.getRGB(x, z) & 0xFF; + if (height == 255 || height > 0 && !white && PseudoRandom.random.nextInt(256) <= height) { + mutable.mutX(x); + mutable.mutY(height); + char combined = (char) pattern.apply(mutable).getCombined(); + mainArr[index] = combined; + floorArr[index] = combined; + } } } - } + })); } - update(); } public void setOverlay(Mask mask, Pattern pattern) { @@ -810,20 +1423,19 @@ public void setOverlay(Mask mask, Pattern pattern) { setOverlay(mask, (char) ((BaseBlock) pattern).getCombined()); } else { int index = 0; - if (overlay == null) overlay = new char[getArea()]; + if (overlay == null) overlay = new DifferentialArray<>(new char[getArea()]); for (int z = 0; z < getLength(); z++) { mutable.mutZ(z); for (int x = 0; x < getWidth(); x++, index++) { - int y = heights[index] & 0xFF; + int y = heights.getByte(index) & 0xFF; mutable.mutX(x); mutable.mutY(y); if (mask.test(mutable)) { - overlay[index] = (char) pattern.apply(mutable).getCombined(); + overlay.setChar(index, (char) pattern.apply(mutable).getCombined()); } } } } - update(); } public void setFloor(Mask mask, Pattern pattern) { @@ -834,145 +1446,161 @@ public void setFloor(Mask mask, Pattern pattern) { for (int z = 0; z < getLength(); z++) { mutable.mutZ(z); for (int x = 0; x < getWidth(); x++, index++) { - int y = heights[index] & 0xFF; + int y = heights.getByte(index) & 0xFF; mutable.mutX(x); mutable.mutY(y); if (mask.test(mutable)) { - floor[index] = (char) pattern.apply(mutable).getCombined(); + floor.setChar(index, (char) pattern.apply(mutable).getCombined()); } } } } - update(); } public void setMain(Mask mask, Pattern pattern) { if (pattern instanceof BaseBlock) { setMain(mask, (char) ((BaseBlock) pattern).getCombined()); } else { - modifiedMain = true; + primtives.modifiedMain = true; int index = 0; for (int z = 0; z < getLength(); z++) { mutable.mutZ(z); for (int x = 0; x < getWidth(); x++, index++) { - int y = heights[index] & 0xFF; + int y = heights.getByte(index) & 0xFF; mutable.mutX(x); mutable.mutY(y); if (mask.test(mutable)) { - main[index] = (char) pattern.apply(mutable).getCombined(); + main.setChar(index, (char) pattern.apply(mutable).getCombined()); } } } } - update(); } public void setColumn(Mask mask, Pattern pattern) { if (pattern instanceof BaseBlock) { setColumn(mask, (char) ((BaseBlock) pattern).getCombined()); } else { - modifiedMain = true; + primtives.modifiedMain = true; int index = 0; for (int z = 0; z < getLength(); z++) { mutable.mutZ(z); for (int x = 0; x < getWidth(); x++, index++) { - int y = heights[index] & 0xFF; + int y = heights.getByte(index) & 0xFF; mutable.mutX(x); mutable.mutY(y); if (mask.test(mutable)) { char combined = (char) pattern.apply(mutable).getCombined(); - floor[index] = combined; - main[index] = combined; + floor.setChar(index, combined); + main.setChar(index, combined); } } } } - update(); } public void setBiome(int biome) { - Arrays.fill(biomes, (byte) biome); - update(); + biomes.record(() -> Arrays.fill(biomes.get(), (byte) biome)); } public void setFloor(Pattern value) { if (value instanceof BaseBlock) { setFloor(((BaseBlock) value).getCombined()); } else { - int index = 0; - for (int z = 0; z < getLength(); z++) { - mutable.mutZ(z); - for (int x = 0; x < getWidth(); x++, index++) { - int y = heights[index] & 0xFF; - mutable.mutX(x); - mutable.mutY(y); - floor[index] = (char) value.apply(mutable).getCombined(); + floor.record(() -> { + char[] floorArr = floor.get(); + int index = 0; + for (int z = 0; z < getLength(); z++) { + mutable.mutZ(z); + for (int x = 0; x < getWidth(); x++, index++) { + int y = heights.getByte(index) & 0xFF; + mutable.mutX(x); + mutable.mutY(y); + floorArr[index] = (char) value.apply(mutable).getCombined(); + } } - } + }); } - update(); } public void setColumn(Pattern value) { if (value instanceof BaseBlock) { setColumn(((BaseBlock) value).getCombined()); } else { - int index = 0; - for (int z = 0; z < getLength(); z++) { - mutable.mutZ(z); - for (int x = 0; x < getWidth(); x++, index++) { - int y = heights[index] & 0xFF; - mutable.mutX(x); - mutable.mutY(y); - char combined = (char) value.apply(mutable).getCombined(); - main[index] = combined; - floor[index] = combined; + main.record(() -> floor.record(() -> { + char[] floorArr = floor.get(); + char[] mainArr = main.get(); + int index = 0; + for (int z = 0; z < getLength(); z++) { + mutable.mutZ(z); + for (int x = 0; x < getWidth(); x++, index++) { + int y = heights.getByte(index) & 0xFF; + mutable.mutX(x); + mutable.mutY(y); + char combined = (char) value.apply(mutable).getCombined(); + mainArr[index] = combined; + floorArr[index] = combined; + } } - } + })); } - update(); } public void setMain(Pattern value) { if (value instanceof BaseBlock) { setMain(((BaseBlock) value).getCombined()); } else { - int index = 0; - for (int z = 0; z < getLength(); z++) { - mutable.mutZ(z); - for (int x = 0; x < getWidth(); x++, index++) { - int y = heights[index] & 0xFF; - mutable.mutX(x); - mutable.mutY(y); - main[index] = (char) value.apply(mutable).getCombined(); + main.record(() -> { + char[] mainArr = main.get(); + int index = 0; + for (int z = 0; z < getLength(); z++) { + mutable.mutZ(z); + for (int x = 0; x < getWidth(); x++, index++) { + int y = heights.getByte(index) & 0xFF; + mutable.mutX(x); + mutable.mutY(y); + mainArr[index] = (char) value.apply(mutable).getCombined(); + } } - } + }); } - update(); } public void setOverlay(Pattern value) { - if (overlay == null) overlay = new char[getArea()]; + if (overlay == null) overlay = new DifferentialArray<>(new char[getArea()]); if (value instanceof BaseBlock) { setOverlay(((BaseBlock) value).getCombined()); } else { - int index = 0; - for (int z = 0; z < getLength(); z++) { - mutable.mutZ(z); - for (int x = 0; x < getWidth(); x++, index++) { - int y = heights[index] & 0xFF; - mutable.mutX(x); - mutable.mutY(y); - overlay[index] = (char) value.apply(mutable).getCombined(); + overlay.record(() -> { + char[] overlayArr = overlay.get(); + int index = 0; + for (int z = 0; z < getLength(); z++) { + mutable.mutZ(z); + for (int x = 0; x < getWidth(); x++, index++) { + int y = heights.getByte(index) & 0xFF; + mutable.mutX(x); + mutable.mutY(y); + overlayArr[index] = (char) value.apply(mutable).getCombined(); + } } - } + }); } - update(); + } + + public void setHeight(int x, int z, int height) { + int index = z * getWidth() + x; + if (index < 0 || index >= getArea()) return; + heights.setByte(index, (byte) height); + } + + public void setHeight(int index, int height) { + heights.setByte(index, (byte) height); } public void setHeights(int value) { - Arrays.fill(heights, (byte) value); - update(); + heights.record(() -> { + Arrays.fill(heights.get(), (byte) value); + }); } @Override @@ -982,6 +1610,11 @@ public boolean shouldWrite(int chunkX, int chunkZ) { @Override public MCAChunk write(MCAChunk chunk, int csx, int cex, int csz, int cez) { + byte[] heights = this.heights.get(); + byte[] biomes = this.biomes.get(); + char[] main = this.main.get(); + char[] floor = this.floor.get(); + char[] overlay = this.overlay != null ? this.overlay.get() : null; try { int[] indexes = indexStore.get(); for (int i = 0; i < chunk.ids.length; i++) { @@ -1001,7 +1634,6 @@ public MCAChunk write(MCAChunk chunk, int csx, int cex, int csz, int cez) { index = (z & 15) << 4; for (int x = csx; x <= cex; x++, index++, globalIndex++) { indexes[index] = globalIndex; - chunk.biomes[index] = biomes[globalIndex]; int height = heights[globalIndex] & 0xFF; heightMap[index] = height; maxY = Math.max(maxY, height); @@ -1022,11 +1654,11 @@ public MCAChunk write(MCAChunk chunk, int csx, int cex, int csz, int cez) { chunk.blockLight[layer] = new byte[2048]; } } - if (waterHeight != 0) { - maxY = Math.max(maxY, waterHeight); - int maxWaterLayer = ((waterHeight + 15) >> 4); + if (primtives.waterHeight != 0) { + maxY = Math.max(maxY, primtives.waterHeight); + int maxWaterLayer = ((primtives.waterHeight + 15) >> 4); for (int layer = 0; layer < maxWaterLayer; layer++) { - boolean fillAll = (layer << 4) + 15 <= waterHeight; + boolean fillAll = (layer << 4) + 15 <= primtives.waterHeight; byte[] ids = chunk.ids[layer]; if (ids == null) { chunk.ids[layer] = ids = new byte[4096]; @@ -1036,14 +1668,15 @@ public MCAChunk write(MCAChunk chunk, int csx, int cex, int csz, int cez) { Arrays.fill(chunk.skyLight[layer], (byte) 255); } if (fillAll) { - Arrays.fill(ids, waterId); + Arrays.fill(ids, primtives.waterId); } else { - int maxIndex = maxWaterLayer << 8; - Arrays.fill(ids, 0, maxIndex, waterId); + int maxIndex = (primtives.waterHeight & 15) << 8; + Arrays.fill(ids, 0, maxIndex, primtives.waterId); } } } - if (modifiedMain) { // If the main block is modified, we can't short circuit this + + if (primtives.modifiedMain) { // If the main block is modified, we can't short circuit this for (int layer = 0; layer < fillLayers; layer++) { byte[] layerIds = chunk.ids[layer]; byte[] layerDatas = chunk.data[layer]; @@ -1071,6 +1704,7 @@ public MCAChunk write(MCAChunk chunk, int csx, int cex, int csz, int cez) { Arrays.fill(chunk.ids[layer], (byte) 1); } } + for (int layer = fillLayers; layer <= maxLayer; layer++) { Arrays.fill(chunk.skyLight[layer], (byte) 255); byte[] layerIds = chunk.ids[layer]; @@ -1133,22 +1767,24 @@ public MCAChunk write(MCAChunk chunk, int csx, int cex, int csz, int cez) { } } } + int maxYMod = 15 + (maxLayer << 4); for (int layer = (maxY >> 4) + 1; layer < 16; layer++) { chunk.ids[layer] = null; chunk.data[layer] = null; } - { // Bedrock + + if (primtives.bedrockId != 0) { // Bedrock byte[] layerIds = chunk.ids[0]; for (int z = csz; z <= cez; z++) { index = (z & 15) << 4; for (int x = csx; x <= cex; x++) { - layerIds[index++] = (byte) 7; + layerIds[index++] = primtives.bedrockId; } } } - int chunkPair = MathMan.pair((short) chunk.getX(), (short) chunk.getZ()); - char[][][] localBlocks = blocks.get(chunkPair); + + char[][][] localBlocks = getChunkArray(chunk.getX(), chunk.getZ()); if (localBlocks != null) { for (int layer = 0; layer < 16; layer++) { int by = layer << 4; @@ -1162,6 +1798,7 @@ public MCAChunk write(MCAChunk chunk, int csx, int cex, int csz, int cez) { chunk.data[layer] = new byte[2048]; chunk.skyLight[layer] = new byte[2048]; chunk.blockLight[layer] = new byte[2048]; + Arrays.fill(chunk.skyLight[layer], (byte) 255); } byte[] idsLayer = chunk.ids[layer]; byte[] dataLayer = chunk.data[layer]; @@ -1173,7 +1810,8 @@ public MCAChunk write(MCAChunk chunk, int csx, int cex, int csz, int cez) { char combined = zBlocks[x]; if (combined == 0) continue; int id = FaweCache.getId(combined); - if (!FaweCache.hasData(id)) { + int data = FaweCache.getData(combined); + if (data == 0) { chunk.setIdUnsafe(idsLayer, zIndex, (byte) id); } else { chunk.setBlockUnsafe(idsLayer, dataLayer, zIndex, (byte) id, FaweCache.getData(combined)); @@ -1185,12 +1823,88 @@ public MCAChunk write(MCAChunk chunk, int csx, int cex, int csz, int cez) { } } } + + if (primtives.floorThickness != 0) { + // Use biomes array as temporary buffer + byte[] minArr = chunk.biomes; + for (int z = csz; z <= cez; z++) { + index = (z & 15) << 4; + for (int x = csx; x <= cex; x++, index++) { + int gi = indexes[index]; + int height = heightMap[index]; + int min = height; + if (x > 0) min = Math.min(heights[gi - 1] & 0xFF, min); + if (x < getWidth() - 1) min = Math.min(heights[gi + 1] & 0xFF, min); + if (z > 0) min = Math.min(heights[gi - getWidth()] & 0xFF, min); + if (z < getLength() - 1) min = Math.min(heights[gi + getWidth()] & 0xFF, min); + minArr[index] = (byte) min; + } + } + + int minLayer = Math.max(0, (minY - primtives.floorThickness) >> 4); + for (int layer = 0; layer < minLayer; layer++) { + chunk.ids[layer] = null; + chunk.data[layer] = null; + } + for (int layer = minLayer; layer <= maxLayer; layer++) { + byte[] layerIds = chunk.ids[layer]; + byte[] layerDatas = chunk.data[layer]; + int startY = layer << 4; + int endY = startY + 15; + for (int z = csz; z <= cez; z++) { + index = (z & 15) << 4; + for (int x = csx; x <= cex; x++, index++) { + globalIndex = indexes[index]; + int height = heightMap[index]; + + int min = (minArr[index] & 0xFF) - primtives.floorThickness; + int localMin = min - startY; + if (localMin > 0) { + char floorCombined = floor[globalIndex]; + final byte id = (byte) FaweCache.getId(floorCombined); + final int data = FaweCache.getData(floorCombined); + + for (int y = 0; y < localMin; y++) { + int floorIndex = index + ((y & 15) << 8); + layerIds[floorIndex] = 0; + if (data != 0) { + chunk.setNibble(floorIndex, layerDatas, 0); + } + } + } + } + } + } + + for (int layer = fillLayers; layer <= maxLayer; layer++) { + Arrays.fill(chunk.skyLight[layer], (byte) 255); + + } + } + + for (int i = 0; i < 256; i++) { + chunk.biomes[i] = biomes[indexes[i]]; + } + + } catch (Throwable e) { e.printStackTrace(); } return chunk; } + private void setUnsafe(char[][][] map, char combined, int x, int y, int z) { + char[][] yMap = map[y]; + if (yMap == null) { + map[y] = yMap = new char[16][]; + } + char[] zMap = yMap[z]; + if (zMap == null) { + yMap[z] = zMap = new char[16]; + } + zMap[x] = combined; + } + private char get(char[][][] map, int x, int y, int z) { char[][] yMap = map[y]; if (yMap == null) { @@ -1205,15 +1919,15 @@ private char get(char[][][] map, int x, int y, int z) { private void setOverlay(Mask mask, char combined) { int index = 0; - if (overlay == null) overlay = new char[getArea()]; + if (overlay == null) overlay = new DifferentialArray<>(new char[getArea()]); for (int z = 0; z < getLength(); z++) { mutable.mutZ(z); for (int x = 0; x < getWidth(); x++, index++) { - int y = heights[index] & 0xFF; + int y = heights.getByte(index) & 0xFF; mutable.mutX(x); mutable.mutY(y); if (mask.test(mutable)) { - overlay[index] = combined; + overlay.setChar(index, combined); } } } @@ -1224,51 +1938,51 @@ private void setFloor(Mask mask, char combined) { for (int z = 0; z < getLength(); z++) { mutable.mutZ(z); for (int x = 0; x < getWidth(); x++, index++) { - int y = heights[index] & 0xFF; + int y = heights.getByte(index) & 0xFF; mutable.mutX(x); mutable.mutY(y); if (mask.test(mutable)) { - floor[index] = combined; + floor.setChar(index, combined); } } } } private void setMain(Mask mask, char combined) { - modifiedMain = true; + primtives.modifiedMain = true; int index = 0; for (int z = 0; z < getLength(); z++) { mutable.mutZ(z); for (int x = 0; x < getWidth(); x++, index++) { - int y = heights[index] & 0xFF; + int y = heights.getByte(index) & 0xFF; mutable.mutX(x); mutable.mutY(y); if (mask.test(mutable)) { - main[index] = combined; + main.setChar(index, combined); } } } } private void setColumn(Mask mask, char combined) { - modifiedMain = true; + primtives.modifiedMain = true; int index = 0; for (int z = 0; z < getLength(); z++) { mutable.mutZ(z); for (int x = 0; x < getWidth(); x++, index++) { - int y = heights[index] & 0xFF; + int y = heights.getByte(index) & 0xFF; mutable.mutX(x); mutable.mutY(y); if (mask.test(mutable)) { - floor[index] = combined; - main[index] = combined; + floor.setChar(index, combined); + main.setChar(index, combined); } } } } private void setFloor(int value) { - Arrays.fill(floor, (char) value); + floor.record(() -> Arrays.fill(floor.get(), (char) value)); } private void setColumn(int value) { @@ -1277,73 +1991,85 @@ private void setColumn(int value) { } private void setMain(int value) { - modifiedMain = true; - Arrays.fill(main, (char) value); + primtives.modifiedMain = true; + main.record(() -> Arrays.fill(main.get(), (char) value)); } private void setOverlay(int value) { - if (overlay == null) overlay = new char[getArea()]; - Arrays.fill(overlay, (char) value); + if (overlay == null) overlay = new DifferentialArray<>(new char[getArea()]); + overlay.record(() -> Arrays.fill(overlay.get(), (char) value)); } private void setOverlay(BufferedImage img, char combined, boolean white) { if (img.getWidth() != getWidth() || img.getHeight() != getLength()) throw new IllegalArgumentException("Input image dimensions do not match the current height map!"); - if (overlay == null) overlay = new char[getArea()]; - int index = 0; - for (int z = 0; z < getLength(); z++) { - for (int x = 0; x < getWidth(); x++, index++) { - int height = img.getRGB(x, z) & 0xFF; - if (height == 255 || height > 0 && white && PseudoRandom.random.nextInt(256) <= height) { - overlay[index] = combined; + if (overlay == null) overlay = new DifferentialArray<>(new char[getArea()]); + + overlay.record(() -> { + int index = 0; + for (int z = 0; z < getLength(); z++) { + for (int x = 0; x < getWidth(); x++, index++) { + int height = img.getRGB(x, z) & 0xFF; + if (height == 255 || height > 0 && white && PseudoRandom.random.nextInt(256) <= height) { + overlay.get()[index] = combined; + } } } - } + }); } private void setMain(BufferedImage img, char combined, boolean white) { if (img.getWidth() != getWidth() || img.getHeight() != getLength()) throw new IllegalArgumentException("Input image dimensions do not match the current height map!"); - modifiedMain = true; - int index = 0; - for (int z = 0; z < getLength(); z++) { - for (int x = 0; x < getWidth(); x++, index++) { - int height = img.getRGB(x, z) & 0xFF; - if (height == 255 || height > 0 && !white && PseudoRandom.random.nextInt(256) <= height) { - main[index] = combined; + primtives.modifiedMain = true; + + main.record(() -> { + int index = 0; + for (int z = 0; z < getLength(); z++) { + for (int x = 0; x < getWidth(); x++, index++) { + int height = img.getRGB(x, z) & 0xFF; + if (height == 255 || height > 0 && !white && PseudoRandom.random.nextInt(256) <= height) { + main.get()[index] = combined; + } } } - } + }); } private void setFloor(BufferedImage img, char combined, boolean white) { if (img.getWidth() != getWidth() || img.getHeight() != getLength()) throw new IllegalArgumentException("Input image dimensions do not match the current height map!"); - int index = 0; - for (int z = 0; z < getLength(); z++) { - for (int x = 0; x < getWidth(); x++, index++) { - int height = img.getRGB(x, z) & 0xFF; - if (height == 255 || height > 0 && !white && PseudoRandom.random.nextInt(256) <= height) { - floor[index] = combined; + + floor.record(() -> { + int index = 0; + for (int z = 0; z < getLength(); z++) { + for (int x = 0; x < getWidth(); x++, index++) { + int height = img.getRGB(x, z) & 0xFF; + if (height == 255 || height > 0 && !white && PseudoRandom.random.nextInt(256) <= height) { + floor.get()[index] = combined; + } } } - } + }); } private void setColumn(BufferedImage img, char combined, boolean white) { if (img.getWidth() != getWidth() || img.getHeight() != getLength()) throw new IllegalArgumentException("Input image dimensions do not match the current height map!"); - modifiedMain = true; - int index = 0; - for (int z = 0; z < getLength(); z++) { - for (int x = 0; x < getWidth(); x++, index++) { - int height = img.getRGB(x, z) & 0xFF; - if (height == 255 || height > 0 && !white && PseudoRandom.random.nextInt(256) <= height) { - main[index] = combined; - floor[index] = combined; + primtives.modifiedMain = true; + + main.record(() -> floor.record(() -> { + int index = 0; + for (int z = 0; z < getLength(); z++) { + for (int x = 0; x < getWidth(); x++, index++) { + int height = img.getRGB(x, z) & 0xFF; + if (height == 255 || height > 0 && !white && PseudoRandom.random.nextInt(256) <= height) { + main.get()[index] = combined; + floor.get()[index] = combined; + } } } - } + })); } @Override @@ -1351,4 +2077,137 @@ protected void finalize() throws Throwable { IterableThreadLocal.clean(indexStore); super.finalize(); } + + @Override + public int getMaxY() { + return SimpleWorld.super.getMaxY(); + } + + @Override + public void setWorld(String world) { + + } + + @Override + public World getWEWorld() { + return this; + } + + @Override + public String getWorldName() { + return getName(); + } + + @Override + public long getModified() { + return 0; + } + + @Override + public void setModified(long modified) { + // Unsupported + } + + @Override + public RunnableVal2 getProgressTask() { + return null; + } + + @Override + public void setProgressTask(RunnableVal2 progressTask) { + + } + + @Override + public void setChangeTask(RunnableVal2 changeTask) { + + } + + @Override + public RunnableVal2 getChangeTask() { + return null; + } + + @Override + public SetQueue.QueueStage getStage() { + return SetQueue.QueueStage.NONE; + } + + @Override + public void setStage(SetQueue.QueueStage stage) { + // Not supported + } + + @Override + public void addNotifyTask(Runnable runnable) { + runnable.run(); + } + + @Override + public void runTasks() { + + } + + @Override + public void addTask(Runnable whenFree) { + whenFree.run(); + } + + @Override + public boolean isEmpty() { + return !isModified(); + } + + @Nullable + @Override + public Operation commit() { + return null; + } + + @Override + public String getName() { + File folder = getFolder(); + if (folder != null) { + String name = folder.getName(); + if (name.equalsIgnoreCase("region")) return folder.getParentFile().getName(); + return name; + } + return Integer.toString(hashCode()); + } + + @Override + public boolean setBlock(Vector position, BaseBlock block, boolean notifyAndLight) throws WorldEditException { + return setBlock(position, block); + } + + // These aren't implemented yet... + @Override + public int getBlockLightLevel(Vector position) { + return 0; + } + + @Override + public boolean clearContainerBlockContents(Vector position) { + return false; + } + + @Override + public void dropItem(Vector position, BaseItemStack item) { + + } + + @Override + public boolean regenerate(Region region, EditSession editSession) { + return false; + } + + @Override + public boolean generateTree(TreeGenerator.TreeType type, EditSession editSession, Vector position) throws MaxChangedBlocksException { + return false; + } + + @Override + public WorldData getWorldData() { + return null; + } } diff --git a/core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAQueue.java b/core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAQueue.java index 1016066bc5..f01c080661 100644 --- a/core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAQueue.java +++ b/core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAQueue.java @@ -619,6 +619,14 @@ public void run(Path value1, RunnableVal2 value2) { }); } + @Override + public boolean supports(Capability capability) { + switch (capability) { + case CHANGE_TASKS: return false; + } + return super.supports(capability); + } + @Override public void relight(int x, int y, int z) { throw new UnsupportedOperationException("Not supported"); @@ -819,4 +827,4 @@ public void sendBlockUpdate(FaweChunk chunk, FawePlayer... players) { parentNMS.sendBlockUpdate(chunk, players); } } -} +} \ No newline at end of file diff --git a/core/src/main/java/com/boydti/fawe/object/ChangeSetFaweQueue.java b/core/src/main/java/com/boydti/fawe/object/ChangeSetFaweQueue.java index 44740314a4..cce4025549 100644 --- a/core/src/main/java/com/boydti/fawe/object/ChangeSetFaweQueue.java +++ b/core/src/main/java/com/boydti/fawe/object/ChangeSetFaweQueue.java @@ -2,7 +2,7 @@ import com.boydti.fawe.FaweCache; import com.boydti.fawe.object.changeset.FaweChangeSet; -import com.boydti.fawe.util.DelegateFaweQueue; +import com.boydti.fawe.object.queue.DelegateFaweQueue; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.world.biome.BaseBiome; diff --git a/core/src/main/java/com/boydti/fawe/object/FaweChunk.java b/core/src/main/java/com/boydti/fawe/object/FaweChunk.java index b6cd733365..2ea3ee0c6c 100644 --- a/core/src/main/java/com/boydti/fawe/object/FaweChunk.java +++ b/core/src/main/java/com/boydti/fawe/object/FaweChunk.java @@ -19,7 +19,7 @@ public abstract class FaweChunk implements Callable { private int x, z; public static int HEIGHT = 256; - private final ArrayDeque tasks = new ArrayDeque(); + private final ArrayDeque tasks = new ArrayDeque(0); /** * A FaweSections object represents a chunk and the blocks that you wish to change in it. @@ -154,6 +154,14 @@ char[] getIdArray(int layer) { return ids; } + public byte[][] getBlockLightArray() { + return null; + } + + public byte[][] getSkyLightArray() { + return null; + } + public abstract byte[] getBiomeArray(); public void forEachQueuedBlock(FaweChunkVisitor onEach) { diff --git a/core/src/main/java/com/boydti/fawe/object/FaweInputStream.java b/core/src/main/java/com/boydti/fawe/object/FaweInputStream.java index 0670d0fe5b..07ad034b8b 100644 --- a/core/src/main/java/com/boydti/fawe/object/FaweInputStream.java +++ b/core/src/main/java/com/boydti/fawe/object/FaweInputStream.java @@ -42,6 +42,28 @@ public NamedTag readNBT() throws IOException { return nbtIn.readNamedTag(); } + public Object readPrimitive(Class clazz) throws IOException { + if (clazz == long.class || clazz == Long.class) { + return readLong(); + } else if (clazz == double.class || clazz == Double.class) { + return readDouble(); + } else if (clazz == float.class || clazz == Float.class) { + return readFloat(); + } else if (clazz == int.class || clazz == Integer.class) { + return readInt(); + } else if (clazz == short.class || clazz == Short.class) { + return readShort(); + } else if (clazz == char.class || clazz == Character.class) { + return readChar(); + } else if (clazz == byte.class || clazz == Byte.class) { + return readByte(); + } else if (clazz == boolean.class || clazz == Boolean.class) { + return readBoolean(); + } else { + throw new UnsupportedOperationException("Unknown class " + clazz); + } + } + public int readVarInt() throws IOException { int i = 0; int offset = 0; diff --git a/core/src/main/java/com/boydti/fawe/object/FaweOutputStream.java b/core/src/main/java/com/boydti/fawe/object/FaweOutputStream.java index 5abdfc953a..ccdcdb3de0 100644 --- a/core/src/main/java/com/boydti/fawe/object/FaweOutputStream.java +++ b/core/src/main/java/com/boydti/fawe/object/FaweOutputStream.java @@ -61,6 +61,29 @@ public void writeNBT(String name, Tag tag) throws IOException { nbtOut.writeNamedTag(name, tag); } + public void writePrimitive(Object value) throws IOException { + Class clazz = value.getClass(); + if (clazz == long.class || clazz == Long.class) { + writeLong((long) value); + } else if (clazz == double.class || clazz == Double.class) { + writeDouble((double) value); + } else if (clazz == float.class || clazz == Float.class) { + writeFloat((float) value); + } else if (clazz == int.class || clazz == Integer.class) { + writeInt((int) value); + } else if (clazz == short.class || clazz == Short.class) { + writeShort((short) value); + } else if (clazz == char.class || clazz == Character.class) { + writeChar((char) value); + } else if (clazz == byte.class || clazz == Byte.class) { + writeByte((byte) value); + } else if (clazz == boolean.class || clazz == Boolean.class) { + writeBoolean((boolean) value); + } else { + throw new UnsupportedOperationException("Unknown class " + clazz); + } + } + @Override public void close() throws IOException { if (nbtOut != null) { diff --git a/core/src/main/java/com/boydti/fawe/object/FawePlayer.java b/core/src/main/java/com/boydti/fawe/object/FawePlayer.java index 4aa9eb2240..e803c2cb12 100644 --- a/core/src/main/java/com/boydti/fawe/object/FawePlayer.java +++ b/core/src/main/java/com/boydti/fawe/object/FawePlayer.java @@ -2,11 +2,14 @@ import com.boydti.fawe.Fawe; import com.boydti.fawe.FaweAPI; +import com.boydti.fawe.command.CFICommands; import com.boydti.fawe.config.BBC; import com.boydti.fawe.config.Settings; +import com.boydti.fawe.jnbt.anvil.HeightMapMCAGenerator; import com.boydti.fawe.object.clipboard.DiskOptimizedClipboard; import com.boydti.fawe.object.exception.FaweException; import com.boydti.fawe.regions.FaweMaskManager; +import com.boydti.fawe.util.EditSessionBuilder; import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.SetQueue; import com.boydti.fawe.util.TaskManager; @@ -26,7 +29,10 @@ import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.event.platform.CommandEvent; import com.sk89q.worldedit.extension.platform.Actor; +import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.extension.platform.CommandManager; +import com.sk89q.worldedit.extension.platform.PlatformManager; +import com.sk89q.worldedit.extension.platform.PlayerProxy; import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.regions.RegionOperationException; @@ -39,9 +45,9 @@ import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Arrays; +import java.util.Collection; import java.util.HashSet; import java.util.Map; -import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedDeque; @@ -350,8 +356,21 @@ public World getWorld() { return FaweAPI.getWorld(getLocation().world); } + public FaweQueue getFaweQueue(boolean autoQueue) { + return getFaweQueue(true, autoQueue); + } + + public FaweQueue getFaweQueue(boolean fast, boolean autoQueue) { + CFICommands.CFISettings settings = this.getMeta("CFISettings"); + if (settings != null && settings.hasGenerator()) { + return settings.getGenerator(); + } else { + return SetQueue.IMP.getNewQueue(getWorld(), true, autoQueue); + } + } + public FaweQueue getMaskedFaweQueue(boolean autoQueue) { - FaweQueue queue = SetQueue.IMP.getNewQueue(getWorld(), true, autoQueue); + FaweQueue queue = getFaweQueue(autoQueue); RegionWrapper[] allowedRegions = getCurrentRegions(); if (allowedRegions.length == 1 && allowedRegions[0].isGlobal()) { return queue; @@ -574,7 +593,43 @@ public void unregister() { * Get a new EditSession from this player */ public EditSession getNewEditSession() { - return WorldEdit.getInstance().getEditSessionFactory().getEditSession(getWorld(), -1, toWorldEditPlayer()); + return new EditSessionBuilder(getWorld()).player(this).build(); + } + + /** + * Get the World the player is editing in (may not match the world they are in)
+ * - e.g. If they are editing a CFI world.
+ * @return Editing world + */ + public World getWorldForEditing() { + CFICommands.CFISettings cfi = getMeta("CFISettings"); + if (cfi != null && cfi.hasGenerator() && cfi.getGenerator().hasPacketViewer()) { + return cfi.getGenerator(); + } + return WorldEdit.getInstance().getPlatformManager().getWorldForEditing(getWorld()); + } + + public PlayerProxy createProxy() { + Player player = getPlayer(); + World world = getWorldForEditing(); + + PlatformManager platformManager = WorldEdit.getInstance().getPlatformManager(); + + Player permActor = platformManager.queryCapability(Capability.PERMISSIONS).matchPlayer(player); + if (permActor == null) { + permActor = player; + } + + Player cuiActor = platformManager.queryCapability(Capability.WORLDEDIT_CUI).matchPlayer(player); + if (cuiActor == null) { + cuiActor = player; + } + + PlayerProxy proxy = new PlayerProxy(player, permActor, cuiActor, world); + if (world instanceof HeightMapMCAGenerator) { + proxy.setOffset(Vector.ZERO.subtract(((HeightMapMCAGenerator) world).getOrigin())); + } + return proxy; } @@ -589,7 +644,7 @@ public Map getTrackedSessions(SetQueue.QueueSt Map map = new ConcurrentHashMap<>(8, 0.9f, 1); if (requiredStage == null || requiredStage == SetQueue.QueueStage.ACTIVE) { for (FaweQueue queue : SetQueue.IMP.getActiveQueues()) { - Set sessions = queue.getEditSessions(); + Collection sessions = queue.getEditSessions(); for (EditSession session : sessions) { FawePlayer currentPlayer = session.getPlayer(); if (currentPlayer == this) { @@ -600,7 +655,7 @@ public Map getTrackedSessions(SetQueue.QueueSt } if (requiredStage == null || requiredStage == SetQueue.QueueStage.INACTIVE) { for (FaweQueue queue : SetQueue.IMP.getInactiveQueues()) { - Set sessions = queue.getEditSessions(); + Collection sessions = queue.getEditSessions(); for (EditSession session : sessions) { FawePlayer currentPlayer = session.getPlayer(); if (currentPlayer == this) { diff --git a/core/src/main/java/com/boydti/fawe/object/FaweQueue.java b/core/src/main/java/com/boydti/fawe/object/FaweQueue.java index f02d82ffab..c295e8a296 100644 --- a/core/src/main/java/com/boydti/fawe/object/FaweQueue.java +++ b/core/src/main/java/com/boydti/fawe/object/FaweQueue.java @@ -1,7 +1,6 @@ package com.boydti.fawe.object; import com.boydti.fawe.Fawe; -import com.boydti.fawe.FaweAPI; import com.boydti.fawe.FaweCache; import com.boydti.fawe.config.BBC; import com.boydti.fawe.config.Settings; @@ -27,50 +26,51 @@ import com.sk89q.worldedit.world.registry.BundledBlockData; import java.io.File; import java.util.Collection; -import java.util.HashSet; -import java.util.Set; +import java.util.Collections; import java.util.UUID; -import java.util.concurrent.ConcurrentLinkedDeque; import javax.annotation.Nullable; -public abstract class FaweQueue implements HasFaweQueue, Extent { +/** + * A queue based Extent capable of queing chunk and region changes + */ +public interface FaweQueue extends HasFaweQueue, Extent { - private World weWorld; - private String world; - private ConcurrentLinkedDeque sessions; - private long modified = System.currentTimeMillis(); - private RunnableVal2 changeTask; - private RunnableVal2 progressTask; - private SetQueue.QueueStage stage; - private Settings settings = Settings.IMP; + enum ProgressType { + QUEUE, + DISPATCH, + DONE, + } - public FaweQueue(String world) { - this.world = world; + enum RelightMode { + NONE, + OPTIMAL, + ALL, } - public FaweQueue(World world) { - if (world != null) { - this.weWorld = world; - this.world = Fawe.imp().getWorldName(world); - } + enum Capability { + // If history can be recorded in an async task by the dispatcher + CHANGE_TASKS, + // If custom chunk packets can be sent + CHUNK_PACKETS + // } - public Relighter getRelighter() { + default Relighter getRelighter() { return NullRelighter.INSTANCE; } @Override - public Vector getMinimumPoint() { + default Vector getMinimumPoint() { return new Vector(-30000000, 0, -30000000); } @Override - public Vector getMaximumPoint() { + default Vector getMaximumPoint() { return new Vector(30000000, getMaxY(), 30000000); } @Override - public BaseBlock getLazyBlock(int x, int y, int z) { + default BaseBlock getLazyBlock(int x, int y, int z) { int combinedId4Data = getCachedCombinedId4Data(x, y, z, 0); int id = FaweCache.getId(combinedId4Data); if (!FaweCache.hasNBT(id)) { @@ -90,76 +90,43 @@ public BaseBlock getLazyBlock(int x, int y, int z) { } @Override - public boolean setBlock(int x, int y, int z, BaseBlock block) throws WorldEditException { + default boolean setBlock(int x, int y, int z, BaseBlock block) throws WorldEditException { return setBlock(x, y, z, block.getId(), block.getData(), block.getNbtData()); } @Override - public BaseBlock getBlock(Vector position) { + default BaseBlock getBlock(Vector position) { return getLazyBlock(position.getBlockX(), position.getBlockY(), position.getBlockZ()); } @Override - public BaseBiome getBiome(Vector2D position) { + default BaseBiome getBiome(Vector2D position) { return null; } @Override - public boolean setBlock(Vector position, BaseBlock block) throws WorldEditException { + default boolean setBlock(Vector position, BaseBlock block) throws WorldEditException { return setBlock(position.getBlockX(), position.getBlockY(), position.getBlockZ(), block); } @Override - public boolean setBiome(Vector2D position, BaseBiome biome) { + default boolean setBiome(Vector2D position, BaseBiome biome) { return setBiome(position.getBlockX(), position.getBlockZ(), biome); } - public enum ProgressType { - QUEUE, - DISPATCH, - DONE, - } - - public enum RelightMode { - NONE, - OPTIMAL, - ALL, - } - @Override - public FaweQueue getQueue() { + default FaweQueue getQueue() { return this; } - public Settings getSettings() { - return settings; - } - - public void setSettings(Settings settings) { - this.settings = settings == null ? Settings.IMP : settings; - } - public void setWorld(String world) { - this.world = world; - this.weWorld = null; - } - public void addEditSession(EditSession session) { + default void addEditSession(EditSession session) { if (session == null) { return; } - if (this.getSessions() == null) { - setSessions(new ConcurrentLinkedDeque()); - } - getSessions().add(session); - } - - public World getWEWorld() { - return weWorld != null ? weWorld : (weWorld = FaweAPI.getWorld(world)); - } - - public String getWorldName() { - return world; + Collection sessions = getEditSessions(); + sessions.add(session); } /** @@ -169,54 +136,21 @@ public String getWorldName() { * * @param progressTask */ - public void setProgressTracker(RunnableVal2 progressTask) { + default void setProgressTracker(RunnableVal2 progressTask) { this.setProgressTask(progressTask); } - public Set getEditSessions() { - return getSessions() == null ? new HashSet() : new HashSet<>(getSessions()); - } - - public ConcurrentLinkedDeque getSessions() { - return sessions; - } - - public void setSessions(ConcurrentLinkedDeque sessions) { - this.sessions = sessions; - } - - public long getModified() { - return modified; - } - - public void setModified(long modified) { - this.modified = modified; - } - - public RunnableVal2 getProgressTask() { - return progressTask; - } - - public void setProgressTask(RunnableVal2 progressTask) { - this.progressTask = progressTask; - } - - public boolean supportsChangeTask() { - return true; - } - - public void setChangeTask(RunnableVal2 changeTask) { - this.changeTask = changeTask; + default Collection getEditSessions() { + return Collections.emptySet(); } - public RunnableVal2 getChangeTask() { - return changeTask; + default boolean supports(Capability capability) { + return false; } - public void optimize() { - } + default void optimize() {} - public int setBlocks(CuboidRegion cuboid, final int id, final int data) { + default int setBlocks(CuboidRegion cuboid, final int id, final int data) { RegionWrapper current = new RegionWrapper(cuboid.getMinimumPoint(), cuboid.getMaximumPoint()); final int minY = cuboid.getMinimumY(); final int maxY = cuboid.getMaximumY(); @@ -258,13 +192,13 @@ public void run(int[] value) { return cuboid.getArea(); } - public abstract boolean setBlock(final int x, final int y, final int z, final int id, final int data); + boolean setBlock(final int x, final int y, final int z, final int id, final int data); - public boolean setBlock(int x, int y, int z, int id) { + default boolean setBlock(int x, int y, int z, int id) { return setBlock(x, y, z, id, 0); } - public boolean setBlock(int x, int y, int z, int id, int data, CompoundTag nbt) { + default boolean setBlock(int x, int y, int z, int id, int data, CompoundTag nbt) { if (nbt != null) { if (setBlock(x, y, z, id, data)) { MainUtil.setPosition(nbt, x, y, z); @@ -277,32 +211,69 @@ public boolean setBlock(int x, int y, int z, int id, int data, CompoundTag nbt) } } - public abstract void setTile(int x, int y, int z, CompoundTag tag); + void setTile(int x, int y, int z, CompoundTag tag); - public abstract void setEntity(int x, int y, int z, CompoundTag tag); + void setEntity(int x, int y, int z, CompoundTag tag); - public abstract void removeEntity(int x, int y, int z, UUID uuid); + void removeEntity(int x, int y, int z, UUID uuid); - public abstract boolean setBiome(final int x, final int z, final BaseBiome biome); + boolean setBiome(final int x, final int z, final BaseBiome biome); - public abstract FaweChunk getFaweChunk(int x, int z); + FaweChunk getFaweChunk(int x, int z); - public abstract Collection getFaweChunks(); + Collection getFaweChunks(); - public boolean setMCA(int mcaX, int mcaZ, RegionWrapper region, Runnable whileLocked, boolean save, boolean load) { + default boolean setMCA(int mcaX, int mcaZ, RegionWrapper region, Runnable whileLocked, boolean save, boolean load) { if (whileLocked != null) whileLocked.run(); return true; } - public abstract void setChunk(final FaweChunk chunk); + void setChunk(final FaweChunk chunk); - public abstract File getSaveFolder(); + File getSaveFolder(); - public int getMaxY() { + default int getMaxY() { + World weWorld = getWEWorld(); return weWorld == null ? 255 : weWorld.getMaxY(); } - public void forEachBlockInChunk(int cx, int cz, RunnableVal2 onEach) { + default Settings getSettings() { + return Settings.IMP; + } + + default void setSettings(Settings settings) { + + } + + void setWorld(String world); + + World getWEWorld(); + + String getWorldName(); + + long getModified(); + + void setModified(long modified); + + RunnableVal2 getProgressTask(); + + void setProgressTask(RunnableVal2 progressTask); + + void setChangeTask(RunnableVal2 changeTask); + + RunnableVal2 getChangeTask(); + + SetQueue.QueueStage getStage(); + + void setStage(SetQueue.QueueStage stage); + + void addNotifyTask(Runnable runnable); + + void runTasks(); + + void addTask(Runnable whenFree); + + default void forEachBlockInChunk(int cx, int cz, RunnableVal2 onEach) { int bx = cx << 4; int bz = cz << 4; MutableBlockVector mutable = new MutableBlockVector(0, 0, 0); @@ -331,7 +302,7 @@ public void forEachBlockInChunk(int cx, int cz, RunnableVal2 } } - public void forEachTileInChunk(int cx, int cz, RunnableVal2 onEach) { + default void forEachTileInChunk(int cx, int cz, RunnableVal2 onEach) { int bx = cx << 4; int bz = cz << 4; MutableBlockVector mutable = new MutableBlockVector(0, 0, 0); @@ -359,22 +330,22 @@ public void forEachTileInChunk(int cx, int cz, RunnableVal2 o } @Deprecated - public boolean regenerateChunk(int x, int z) { + default boolean regenerateChunk(int x, int z) { return regenerateChunk(x, z, null, null); } - public abstract boolean regenerateChunk(int x, int z, @Nullable BaseBiome biome, @Nullable Long seed); + boolean regenerateChunk(int x, int z, @Nullable BaseBiome biome, @Nullable Long seed); - public void startSet(boolean parallel) { + default void startSet(boolean parallel) { } - public void endSet(boolean parallel) { + default void endSet(boolean parallel) { } - public int cancel() { + default int cancel() { clear(); int count = 0; - for (EditSession session : getSessions()) { + for (EditSession session : getEditSessions()) { if (session.cancel()) { count++; } @@ -382,10 +353,14 @@ public int cancel() { return count; } - public abstract void sendBlockUpdate(FaweChunk chunk, FawePlayer... players); + void sendBlockUpdate(FaweChunk chunk, FawePlayer... players); + + default void sendChunkUpdate(FaweChunk chunk, FawePlayer... players) { + sendBlockUpdate(chunk, players); + } @Deprecated - public boolean next() { + default boolean next() { int amount = Settings.IMP.QUEUE.PARALLEL_THREADS; long time = 20; // 30ms return next(amount, time); @@ -396,9 +371,9 @@ public boolean next() { * * @return */ - public abstract boolean next(int amount, long time); + boolean next(int amount, long time); - public void saveMemory() { + default void saveMemory() { MainUtil.sendAdmin(BBC.OOM.s()); // Set memory limited MemUtil.memoryLimitedTask(); @@ -411,28 +386,28 @@ public void saveMemory() { // Unload chunks } - public abstract void sendChunk(FaweChunk chunk); + void sendChunk(FaweChunk chunk); - public abstract void sendChunk(int x, int z, int bitMask); + void sendChunk(int x, int z, int bitMask); /** * This method is called when the server is < 1% available memory */ - public abstract void clear(); + void clear(); - public abstract void addNotifyTask(int x, int z, Runnable runnable); + void addNotifyTask(int x, int z, Runnable runnable); - public boolean hasBlock(int x, int y, int z) throws FaweException.FaweChunkLoadException { + default boolean hasBlock(int x, int y, int z) throws FaweException.FaweChunkLoadException { return getCombinedId4Data(x, y, z) != 0; } - public abstract int getBiomeId(int x, int z) throws FaweException.FaweChunkLoadException; + int getBiomeId(int x, int z) throws FaweException.FaweChunkLoadException; - public abstract int getCombinedId4Data(int x, int y, int z) throws FaweException.FaweChunkLoadException; + int getCombinedId4Data(int x, int y, int z) throws FaweException.FaweChunkLoadException; - public abstract int getCachedCombinedId4Data(int x, int y, int z) throws FaweException.FaweChunkLoadException; + int getCachedCombinedId4Data(int x, int y, int z) throws FaweException.FaweChunkLoadException; - public int getAdjacentLight(int x, int y, int z) { + default int getAdjacentLight(int x, int y, int z) { int light = 0; if ((light = Math.max(light, getSkyLight(x - 1, y, z))) == 15) { return light; @@ -446,22 +421,22 @@ public int getAdjacentLight(int x, int y, int z) { return Math.max(light, getSkyLight(x, y, z + 1)); } - public abstract boolean hasSky(); + boolean hasSky(); - public abstract int getSkyLight(int x, int y, int z); + int getSkyLight(int x, int y, int z); - public int getLight(int x, int y, int z) { + default int getLight(int x, int y, int z) { if (!hasSky()) { return getEmmittedLight(x, y, z); } return Math.max(getSkyLight(x, y, z), getEmmittedLight(x, y, z)); } - public abstract int getEmmittedLight(int x, int y, int z); + int getEmmittedLight(int x, int y, int z); - public abstract CompoundTag getTileEntity(int x, int y, int z) throws FaweException.FaweChunkLoadException; + CompoundTag getTileEntity(int x, int y, int z) throws FaweException.FaweChunkLoadException; - public int getCombinedId4Data(int x, int y, int z, int def) { + default int getCombinedId4Data(int x, int y, int z, int def) { try { return getCombinedId4Data(x, y, z); } catch (FaweException ignore) { @@ -469,7 +444,7 @@ public int getCombinedId4Data(int x, int y, int z, int def) { } } - public int getCachedCombinedId4Data(int x, int y, int z, int def) { + default int getCachedCombinedId4Data(int x, int y, int z, int def) { try { return getCachedCombinedId4Data(x, y, z); } catch (FaweException ignore) { @@ -477,7 +452,7 @@ public int getCachedCombinedId4Data(int x, int y, int z, int def) { } } - public int getCombinedId4DataDebug(int x, int y, int z, int def, EditSession session) { + default int getCombinedId4DataDebug(int x, int y, int z, int def, EditSession session) { try { return getCombinedId4Data(x, y, z); } catch (FaweException ignore) { @@ -488,7 +463,7 @@ public int getCombinedId4DataDebug(int x, int y, int z, int def, EditSession ses } } - public int getBrightness(int x, int y, int z) { + default int getBrightness(int x, int y, int z) { int combined = getCombinedId4Data(x, y, z); if (combined == 0) { return 0; @@ -500,11 +475,11 @@ public int getBrightness(int x, int y, int z) { return block.getLightValue(); } - public int getOpacityBrightnessPair(int x, int y, int z) { + default int getOpacityBrightnessPair(int x, int y, int z) { return MathMan.pair16(Math.min(15, getOpacity(x, y, z)), getBrightness(x, y, z)); } - public int getOpacity(int x, int y, int z) { + default int getOpacity(int x, int y, int z) { int combined = getCombinedId4Data(x, y, z); if (combined == 0) { return 0; @@ -516,31 +491,23 @@ public int getOpacity(int x, int y, int z) { return block.getLightOpacity(); } - public abstract int size(); + int size(); - public boolean isEmpty() { + default boolean isEmpty() { return size() == 0; } /** * Lock the thread until the queue is empty */ - public void flush() { + default void flush() { flush(10000); } - public SetQueue.QueueStage getStage() { - return stage; - } - - public void setStage(SetQueue.QueueStage stage) { - this.stage = stage; - } - /** * Lock the thread until the queue is empty */ - public void flush(int time) { + default void flush(int time) { if (size() > 0) { if (Fawe.isMainThread()) { SetQueue.IMP.flush(this); @@ -560,45 +527,11 @@ public void flush(int time) { } } - public ConcurrentLinkedDeque tasks = new ConcurrentLinkedDeque<>(); - - public void addNotifyTask(Runnable runnable) { - this.tasks.add(runnable); - } - - - public void runTasks() { - synchronized (this) { - this.notifyAll(); - } - if (getProgressTask() != null) { - try { - getProgressTask().run(ProgressType.DONE, 1); - } catch (Throwable e) { - e.printStackTrace(); - } - } - while (!tasks.isEmpty()) { - Runnable task = tasks.poll(); - if (task != null) { - try { - task.run(); - } catch (Throwable e) { - MainUtil.handleError(e); - } - } - } - } - - public void addTask(Runnable whenFree) { - tasks.add(whenFree); - } - - public boolean enqueue() { + default boolean enqueue() { return SetQueue.IMP.enqueue(this); } - public void dequeue() { + default void dequeue() { SetQueue.IMP.dequeue(this); } } diff --git a/core/src/main/java/com/boydti/fawe/object/MaskedFaweQueue.java b/core/src/main/java/com/boydti/fawe/object/MaskedFaweQueue.java index 651a783953..3df29ba23d 100644 --- a/core/src/main/java/com/boydti/fawe/object/MaskedFaweQueue.java +++ b/core/src/main/java/com/boydti/fawe/object/MaskedFaweQueue.java @@ -4,7 +4,7 @@ import com.boydti.fawe.object.extent.HeightBoundExtent; import com.boydti.fawe.object.extent.MultiRegionExtent; import com.boydti.fawe.object.extent.SingleRegionExtent; -import com.boydti.fawe.util.DelegateFaweQueue; +import com.boydti.fawe.object.queue.DelegateFaweQueue; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.Vector2D; diff --git a/core/src/main/java/com/boydti/fawe/object/Metadatable.java b/core/src/main/java/com/boydti/fawe/object/Metadatable.java index 80547f950a..83a9f55e92 100644 --- a/core/src/main/java/com/boydti/fawe/object/Metadatable.java +++ b/core/src/main/java/com/boydti/fawe/object/Metadatable.java @@ -21,6 +21,10 @@ public T getAndSetMeta(String key, T value) { return (T) this.meta.put(key, value); } + public boolean hasMeta() { + return !meta.isEmpty(); + } + /** * Get the metadata for a key. * diff --git a/core/src/main/java/com/boydti/fawe/object/brush/HeightBrush.java b/core/src/main/java/com/boydti/fawe/object/brush/HeightBrush.java index efc2c3a030..9d7d6228da 100644 --- a/core/src/main/java/com/boydti/fawe/object/brush/HeightBrush.java +++ b/core/src/main/java/com/boydti/fawe/object/brush/HeightBrush.java @@ -1,14 +1,18 @@ package com.boydti.fawe.object.brush; import com.boydti.fawe.config.BBC; +import com.boydti.fawe.jnbt.anvil.HeightMapMCAGenerator; +import com.boydti.fawe.object.FaweQueue; import com.boydti.fawe.object.PseudoRandom; 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.exception.FaweException; +import com.boydti.fawe.util.MathMan; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.Vector2D; import com.sk89q.worldedit.command.tool.brush.Brush; import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.function.mask.Mask; @@ -66,12 +70,85 @@ public void setRandomRotate(boolean randomRotate) { @Override public void build(EditSession editSession, Vector position, Pattern pattern, double sizeDouble) throws MaxChangedBlocksException { int size = (int) sizeDouble; + HeightMap map = getHeightMap(); + map.setSize(size); + + FaweQueue queue = editSession.getQueue(); + // 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()]); + } + + Vector origin = hmmg.getOrigin(); + + int bx = position.getBlockX(); + int bz = position.getBlockZ(); + + int minIndex = -(size * 2) - 1; + int width = hmmg.getWidth(); + + int minX = Math.max(-size, origin.getBlockX() - bx); + int minZ = Math.max(-size, origin.getBlockZ() - bz); + int maxX = Math.min(size, origin.getBlockX() + hmmg.getWidth() - 1 - bx); + int maxZ = Math.min(size, origin.getBlockZ() + 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 = maxX; 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) { + Vector2D min = new Vector2D(Math.max(0, bx - size), Math.max(0, bz - size)); + Vector2D max = new Vector2D(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 = new Vector2D(Math.max(0, bx - smoothSize), Math.max(0, bz - smoothSize)); + max = new Vector2D(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; } - HeightMap map = getHeightMap(); - map.setSize(size); map.perform(editSession, mask, position, size, rotation, yscale, smooth, false, layers); } } diff --git a/core/src/main/java/com/boydti/fawe/object/brush/heightmap/HeightMap.java b/core/src/main/java/com/boydti/fawe/object/brush/heightmap/HeightMap.java index fae7291ff9..562f76f5e7 100644 --- a/core/src/main/java/com/boydti/fawe/object/brush/heightmap/HeightMap.java +++ b/core/src/main/java/com/boydti/fawe/object/brush/heightmap/HeightMap.java @@ -55,7 +55,7 @@ default void applyHeightMapData(int[][] data, EditSession session, Mask mask, Ve } } - default int[][] generateHeightData(EditSession session, Mask mask, Vector pos, int size, int rotationMode, double yscale, boolean smooth, boolean towards, final boolean layers) { + default int[][] generateHeightData(EditSession session, Mask mask, Vector pos, int size, final int rotationMode, double yscale, boolean smooth, boolean towards, final boolean layers) { Vector top = session.getMaximumPoint(); int maxY = top.getBlockY(); int diameter = 2 * size + 1; @@ -72,7 +72,7 @@ default int[][] generateHeightData(EditSession session, Mask mask, Vector pos, i } Vector mutablePos = new Vector(0, 0, 0); if (towards) { - double sizePow = Math.pow(size, yscale); + double sizePowInv = 1d / Math.pow(size, yscale); int targetY = pos.getBlockY(); int tmpY = targetY; for (int x = -size; x <= size; x++) { @@ -110,7 +110,7 @@ default int[][] generateHeightData(EditSession session, Mask mask, Vector pos, i } double raisePow = Math.pow(raise, yscale); int diff = targetY - height; - double raiseScaled = diff * (raisePow / sizePow); + double raiseScaled = diff * (raisePow * sizePowInv); double raiseScaledAbs = Math.abs(raiseScaled); int random = PseudoRandom.random.random(256) < (int) ((Math.ceil(raiseScaledAbs) - Math.floor(raiseScaledAbs)) * 256) ? (diff > 0 ? 1 : -1) : 0; int raiseScaledInt = (int) raiseScaled + random; diff --git a/core/src/main/java/com/boydti/fawe/object/brush/heightmap/ScalableHeightMap.java b/core/src/main/java/com/boydti/fawe/object/brush/heightmap/ScalableHeightMap.java index 7577a534be..37b085e0c9 100644 --- a/core/src/main/java/com/boydti/fawe/object/brush/heightmap/ScalableHeightMap.java +++ b/core/src/main/java/com/boydti/fawe/object/brush/heightmap/ScalableHeightMap.java @@ -43,7 +43,7 @@ public double getHeight(int x, int z) { if (d2 > size2) { return 0; } - return size - MathMan.sqrtApprox(d2); + return Math.max(0, size - MathMan.sqrtApprox(d2)); } public static ScalableHeightMap fromShape(Shape shape) { diff --git a/core/src/main/java/com/boydti/fawe/object/change/CFIChange.java b/core/src/main/java/com/boydti/fawe/object/change/CFIChange.java new file mode 100644 index 0000000000..a967f090df --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/object/change/CFIChange.java @@ -0,0 +1,60 @@ +package com.boydti.fawe.object.change; + +import com.boydti.fawe.Fawe; +import com.boydti.fawe.jnbt.anvil.HeightMapMCAGenerator; +import com.boydti.fawe.object.FaweQueue; +import com.boydti.fawe.object.HasFaweQueue; +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 java.io.File; +import java.io.IOException; + + +import static com.google.common.base.Preconditions.checkNotNull; + +public class CFIChange implements Change { + private final File file; + + public CFIChange(File file) { + checkNotNull(file); + this.file = file; + } + + private HeightMapMCAGenerator getQueue(UndoContext context) { + ExtentTraverser found = new ExtentTraverser(context.getExtent()).find(HasFaweQueue.class); + if (found != null) { + FaweQueue queue = ((HasFaweQueue) found.get()).getQueue(); + if (queue instanceof HeightMapMCAGenerator) return (HeightMapMCAGenerator) queue; + } + Fawe.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(); + } + } +} diff --git a/core/src/main/java/com/boydti/fawe/object/change/StreamChange.java b/core/src/main/java/com/boydti/fawe/object/change/StreamChange.java new file mode 100644 index 0000000000..9a760cac08 --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/object/change/StreamChange.java @@ -0,0 +1,51 @@ +package com.boydti.fawe.object.change; + +import com.boydti.fawe.object.FaweInputStream; +import com.boydti.fawe.object.FaweOutputStream; +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import net.jpountz.lz4.LZ4BlockInputStream; +import net.jpountz.lz4.LZ4BlockOutputStream; + +public interface StreamChange { + public void flushChanges(FaweOutputStream out) throws IOException; + + public void undoChanges(FaweInputStream in) throws IOException; + + public void redoChanges(FaweInputStream in) throws IOException; + + default void flushChanges(File file) throws IOException { + try (BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file))) { + try (LZ4BlockOutputStream compressed = new LZ4BlockOutputStream(out)) { +// compressed.setLevel(Deflater.BEST_SPEED); + try (FaweOutputStream fos = new FaweOutputStream(compressed)) { + flushChanges(fos); + } + } + } + } + + default void undoChanges(File file) throws IOException { + try (BufferedInputStream in = new BufferedInputStream(new FileInputStream(file))) { + try (LZ4BlockInputStream compressed = new LZ4BlockInputStream(in)) { + try (FaweInputStream fis = new FaweInputStream(compressed)) { + undoChanges(fis); + } + } + } + } + + default void redoChanges(File file) throws IOException { + try (BufferedInputStream in = new BufferedInputStream(new FileInputStream(file))) { + try (LZ4BlockInputStream compressed = new LZ4BlockInputStream(in)) { + try (FaweInputStream fis = new FaweInputStream(compressed)) { + redoChanges(fis); + } + } + } + } +} diff --git a/core/src/main/java/com/boydti/fawe/object/changeset/CFIChangeSet.java b/core/src/main/java/com/boydti/fawe/object/changeset/CFIChangeSet.java new file mode 100644 index 0000000000..a6793f3466 --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/object/changeset/CFIChangeSet.java @@ -0,0 +1,81 @@ +package com.boydti.fawe.object.changeset; + +import com.boydti.fawe.Fawe; +import com.boydti.fawe.config.Settings; +import com.boydti.fawe.jnbt.anvil.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.BaseBiome; +import java.io.File; +import java.io.IOException; +import java.util.Collections; +import java.util.Iterator; +import java.util.UUID; + +public class CFIChangeSet extends FaweChangeSet { + + private final File file; + + public CFIChangeSet(HeightMapMCAGenerator hmmg, UUID uuid) throws IOException { + super(hmmg); + File folder = MainUtil.getFile(Fawe.imp().getDirectory(), Settings.IMP.PATHS.HISTORY + File.separator + uuid + File.separator + hmmg.getWorldName()); + int max = MainUtil.getMaxFileId(folder); + this.file = new File(folder, Integer.toString(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 boolean close() { + return true; + } + + @Override + public boolean closeAsync() { + return true; + } + + @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 z, BaseBiome from, BaseBiome to) { + throw new UnsupportedOperationException("Only CFI operations are supported"); + } + + @Override + public Iterator getIterator(boolean redo) { + return Collections.singleton(new CFIChange(file)).iterator(); + } + + @Override + public int size() { + return 1; + } +} diff --git a/core/src/main/java/com/boydti/fawe/object/changeset/FaweChangeSet.java b/core/src/main/java/com/boydti/fawe/object/changeset/FaweChangeSet.java index 66502d6e41..3527fb02dc 100644 --- a/core/src/main/java/com/boydti/fawe/object/changeset/FaweChangeSet.java +++ b/core/src/main/java/com/boydti/fawe/object/changeset/FaweChangeSet.java @@ -159,7 +159,6 @@ public EditSession toEditSession(FawePlayer player, RegionWrapper[] regions) { builder.allowedRegionsEverywhere(); } EditSession editSession = builder.build(); - editSession.setSize(1); return editSession; } diff --git a/core/src/main/java/com/boydti/fawe/object/clipboard/MultiClipboardHolder.java b/core/src/main/java/com/boydti/fawe/object/clipboard/MultiClipboardHolder.java index 3dee5b3e3e..857c4022b7 100644 --- a/core/src/main/java/com/boydti/fawe/object/clipboard/MultiClipboardHolder.java +++ b/core/src/main/java/com/boydti/fawe/object/clipboard/MultiClipboardHolder.java @@ -107,7 +107,7 @@ public List getClipboards() { @Override public List getHolders() { ArrayList holders = new ArrayList<>(); - for (ClipboardHolder holder : holders) { + for (ClipboardHolder holder : this.holders) { holders.addAll(holder.getHolders()); } return holders; diff --git a/core/src/main/java/com/boydti/fawe/object/collection/DifferentialArray.java b/core/src/main/java/com/boydti/fawe/object/collection/DifferentialArray.java new file mode 100644 index 0000000000..890417d714 --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/object/collection/DifferentialArray.java @@ -0,0 +1,237 @@ +package com.boydti.fawe.object.collection; + +import com.boydti.fawe.object.FaweInputStream; +import com.boydti.fawe.object.FaweOutputStream; +import com.boydti.fawe.object.io.serialize.Serialize; +import com.boydti.fawe.util.MainUtil; +import java.io.IOException; +import java.lang.reflect.Array; +import java.util.Arrays; + + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Records changes made through the {@link #setByte(int, byte)} or {@link #setChar(int, char)} method
+ * If you are editing the raw data, use {@link #record(Runnable)} + * @param + */ +public final class DifferentialArray implements DifferentialCollection { + private final byte[] dataBytes; + private byte[] changesBytes; + + private final char[] dataChars; + private char[] changesChars; + + @Serialize private final T data; + + private T changes; + + private boolean changed; + private int length; + + public DifferentialArray(T array) { + checkNotNull(array); + Class clazz = array.getClass(); + checkArgument(clazz.isArray(), "Data must be an array"); + checkArgument(clazz.getComponentType().isPrimitive(), "Data must be a primitive array"); + this.data = array; + + if (array instanceof byte[]) { + dataBytes = (byte[]) array; + length = dataBytes.length; + } else { + dataBytes = null; + } + if (array instanceof char[]) { + dataChars = (char[]) array; + length = dataChars.length; + } else { + dataChars = null; + } + } + + public void record(Runnable task) { + if (changes == null) { + if (data instanceof byte[]) { + changes = (T) (changesBytes = new byte[length]); + } else if (data instanceof char[]) { + changes = (T) (changesChars = new char[length]); + } + } + boolean changed = false; + T tmp; + if (changed) { + tmp = (T) MainUtil.copyNd(data); + } else { + tmp = changes; + System.arraycopy(data, 0, tmp, 0, Array.getLength(data)); + } + Throwable caught = null; + try { + task.run(); + } catch (Throwable e) { + caught = e; + task.run(); + } + if (tmp instanceof char[]) { + char[] tmpChars = (char[]) tmp; + for (int i = 0; i < tmpChars.length; i++) { + char tmpChar = tmpChars[i]; + char dataChar = dataChars[i]; + if (tmpChar != dataChar) { + this.changed = true; + tmpChars[i] -= dataChar; + } + } + if (tmp != changes) { + for (int i = 0; i < tmpChars.length; i++) { + changesChars[i] += tmpChars[i]; + } + } + } else if (tmp instanceof byte[]) { + byte[] tmpBytes = (byte[]) tmp; + for (int i = 0; i < tmpBytes.length; i++) { + byte tmpByte = tmpBytes[i]; + byte dataByte = dataBytes[i]; + if (tmpByte != dataByte) { + this.changed = true; + tmpBytes[i] -= dataByte; + } + } + if (tmp != changes) { + for (int i = 0; i < tmpBytes.length; i++) { + changesBytes[i] += tmpBytes[i]; + } + } + } + if (caught != null) { + if (caught instanceof RuntimeException) throw (RuntimeException) caught; + else throw new RuntimeException(caught); + } + } + + @Override + public void flushChanges(FaweOutputStream out) throws IOException { + boolean modified = isModified(); + out.writeBoolean(modified); + if (modified) { + if (dataBytes != null) { + out.write(changesBytes); + } else if (dataChars != null) { + for (char c : changesChars) { + out.writeChar(c); + } + } + } + clearChanges(); + } + + @Override + public void undoChanges(FaweInputStream in) throws IOException { + boolean modified = in.readBoolean(); + if (modified) { + if (dataBytes != null) { + if (changesBytes != null) { + for (int i = 0; i < dataBytes.length; i++) { + dataBytes[i] += changesBytes[i]; + } + } + for (int i = 0; i < dataBytes.length; i++) { + int read = in.read(); + dataBytes[i] += read; + } + } else if (dataChars != null) { + if (changesChars != null) { + for (int i = 0; i < dataChars.length; i++) { + dataChars[i] += changesChars[i]; + } + } + for (int i = 0; i < dataChars.length; i++) { + dataChars[i] += in.readChar(); + } + } + } + clearChanges(); + } + + @Override + public void redoChanges(FaweInputStream in) throws IOException { + boolean modified = in.readBoolean(); + if (modified) { + if (dataBytes != null) { + for (int i = 0; i < dataBytes.length; i++) { + int read = in.read(); + dataBytes[i] -= read; + } + } else if (dataChars != null) { + for (int i = 0; i < dataChars.length; i++) { + dataChars[i] -= in.readChar(); + } + } + } + clearChanges(); + } + + public void clearChanges() { + if (changed) { + changed = false; + if (changes != null) { + if (changesBytes != null) { + Arrays.fill(changesBytes, (byte) 0); + } + if (changesChars != null) { + Arrays.fill(changesChars, (char) 0); + } + } + } + } + + public byte[] getByteArray() { + return dataBytes; + } + + public char[] getCharArray() { + return dataChars; + } + + public boolean isModified() { + return changed; + } + + @Override + public T get() { + return data; + } + + public byte getByte(int index) { + return dataBytes[index]; + } + + public char getChar(int index) { + return dataChars[index]; + } + + public void setByte(int index, byte value) { + changed = true; + try { + changesBytes[index] += (dataBytes[index] - value); + } catch (NullPointerException ignore) { + changes = (T) (changesBytes = new byte[dataBytes.length]); + changesBytes[index] += (dataBytes[index] - value); + } + dataBytes[index] = value; + } + + public void setChar(int index, char value) { + changed = true; + try { + changesChars[index] += dataChars[index] - value; + } catch (NullPointerException ignore) { + changes = (T) (changesChars = new char[dataChars.length]); + changesChars[index] += dataChars[index] - value; + } + dataChars[index] = value; + } +} diff --git a/core/src/main/java/com/boydti/fawe/object/collection/DifferentialBlockBuffer.java b/core/src/main/java/com/boydti/fawe/object/collection/DifferentialBlockBuffer.java new file mode 100644 index 0000000000..01973964ce --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/object/collection/DifferentialBlockBuffer.java @@ -0,0 +1,212 @@ +package com.boydti.fawe.object.collection; + +import com.boydti.fawe.object.FaweInputStream; +import com.boydti.fawe.object.FaweOutputStream; +import java.io.IOException; +import java.lang.reflect.Array; + +/** + * Records changes made through the {@link #set(int, int, int, char)} method
+ * Changes are not recorded if you edit the raw data + */ +public final class DifferentialBlockBuffer implements DifferentialCollection { + + private final int width, length; + private final int t1, t2; + private char[][][][][] data; + private char[][][][][] changes; + + public DifferentialBlockBuffer(int width, int length) { + this.width = width; + this.length = length; + this.t1 = (length + 15) >> 4; + this.t2 = (width + 15) >> 4; + } + + @Override + public char[][][][][] get() { + return data; + } + + @Override + public void flushChanges(FaweOutputStream out) throws IOException { + boolean modified = isModified(); + out.writeBoolean(modified); + + if (modified) { + writeArray(changes, 0, 0, out); + } + clearChanges(); + } + + private void writeArray(Object arr, int level, int index, FaweOutputStream out) throws IOException { + if (level == 4) { + if (arr != null) { + char[] level4 = (char[]) arr; + out.writeVarInt(level4.length); + for (char c : level4) { + out.writeChar(c); + } + } else { + out.writeVarInt(0); + } + } else { + int len = arr == null ? 0 : Array.getLength(arr); + out.writeVarInt(len); + for (int i = 0; i < len; i++) { + Object elem = Array.get(arr, i); + writeArray(elem, level + 1, i, out); + } + } + } + + @Override + public void undoChanges(FaweInputStream in) throws IOException { + if (changes != null && changes.length != 0) throw new IllegalStateException("There are uncommitted changes, please flush first"); + boolean modified = in.readBoolean(); + if (modified) { + int len = in.readVarInt(); + if (len == 0) { + data = null; + } else { + for (int i = 0; i < len; i++) { + readArray(data, i, 1, in); + } + } + } + + clearChanges(); + } + + @Override + public void redoChanges(FaweInputStream in) throws IOException { + clearChanges(); + throw new UnsupportedOperationException("Not implemented"); + } + + private void readArray(Object dataElem, int index, int level, FaweInputStream in) throws IOException { + int len = in.readVarInt(); + if (level == 4) { + char[][] castedElem = (char[][]) dataElem; + if (len == 0) { + castedElem[index] = null; + } else { + char[] current = castedElem[index]; + for (int i = 0; i < len; i++) { + current[i] = in.readChar(); + } + } + } else { + if (len == 0) { + Array.set(dataElem, index, null); + } else { + Object nextElem = Array.get(dataElem, index); + for (int i = 0; i < len; i++) { + readArray(nextElem, i, level + 1, in); + } + } + } + } + + public boolean isModified() { + return changes != null; + } + + public void clearChanges() { + changes = null; + } + + public void set(int x, int y, int z, char combined) { + if (combined == 0) combined = 1; + int localX = x & 15; + int localZ = z & 15; + int chunkX = x >> 4; + int chunkZ = z >> 4; + if (data == null) { + data = new char[t1][][][][]; + changes = new char[0][][][][]; + } + + char[][][][] arr = data[chunkZ]; + if (arr == null) { + arr = data[chunkZ] = new char[t2][][][]; + } + char[][][] arr2 = arr[chunkX]; + if (arr2 == null) { + arr2 = arr[chunkX] = new char[256][][]; + } + + char[][] yMap = arr2[y]; + if (yMap == null) { + arr2[y] = yMap = new char[16][]; + } + boolean newSection; + char current; + char[] zMap = yMap[localZ]; + if (zMap == null) { + yMap[localZ] = zMap = new char[16]; + + if (changes == null) { + changes = new char[t1][][][][]; + } else if (changes != null && changes.length != 0) { + initialChange(changes, chunkX, chunkZ, localX, localZ, y, (char) -combined); + } + + } else { + if (changes == null || changes.length == 0) changes = new char[t1][][][][]; + appendChange(changes, chunkX, chunkZ, localX, localZ, y, (char) (zMap[localX] - combined)); + } + + zMap[localX] = combined; + } + + private void initialChange(char[][][][][] src, int chunkX, int chunkZ, int localX, int localZ, int y, char combined) { + char[][][][] arr = src[chunkZ]; + if (arr == null) { + src[chunkZ] = new char[0][][][]; + return; + } else if (arr.length == 0) return; + + char[][][] arr2 = arr[chunkX]; + if (arr2 == null) { + arr[chunkX] = new char[0][][]; + return; + } else if (arr2.length == 0) return; + + char[][] yMap = arr2[y]; + if (yMap == null) { + arr2[y] = new char[0][]; + return; + } else if (yMap.length == 0) return; + + char[] zMap = yMap[localZ]; + if (zMap == null) { + yMap[localZ] = new char[0]; + return; + } else if (zMap.length == 0) return; + + char current = zMap[localX]; + zMap[localX] = combined; + } + + private void appendChange(char[][][][][] src, int chunkX, int chunkZ, int localX, int localZ, int y, char combined) { + char[][][][] arr = src[chunkZ]; + if (arr == null || arr.length == 0) { + arr = src[chunkZ] = new char[t2][][][]; + } + char[][][] arr2 = arr[chunkX]; + if (arr2 == null || arr2.length == 0) { + arr2 = arr[chunkX] = new char[256][][]; + } + + char[][] yMap = arr2[y]; + if (yMap == null || yMap.length == 0) { + arr2[y] = yMap = new char[16][]; + } + char[] zMap = yMap[localZ]; + if (zMap == null || zMap.length == 0) { + yMap[localZ] = zMap = new char[16]; + } + zMap[localX] = combined; + } +} diff --git a/core/src/main/java/com/boydti/fawe/object/collection/DifferentialCollection.java b/core/src/main/java/com/boydti/fawe/object/collection/DifferentialCollection.java new file mode 100644 index 0000000000..21f9c80f6c --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/object/collection/DifferentialCollection.java @@ -0,0 +1,7 @@ +package com.boydti.fawe.object.collection; + +import com.boydti.fawe.object.change.StreamChange; + +public interface DifferentialCollection extends StreamChange { + public T get(); +} diff --git a/core/src/main/java/com/boydti/fawe/object/collection/IterableThreadLocal.java b/core/src/main/java/com/boydti/fawe/object/collection/IterableThreadLocal.java index ae2b52bb92..4eb8caf93a 100644 --- a/core/src/main/java/com/boydti/fawe/object/collection/IterableThreadLocal.java +++ b/core/src/main/java/com/boydti/fawe/object/collection/IterableThreadLocal.java @@ -62,7 +62,11 @@ public static void clean(ThreadLocal instance) { methodRemove = tlm.getClass().getDeclaredMethod("remove", ThreadLocal.class); methodRemove.setAccessible(true); } - methodRemove.invoke(tlm, instance); + if (methodRemove != null) { + try { + methodRemove.invoke(tlm, instance); + } catch (Throwable ignore) {} + } } } } diff --git a/core/src/main/java/com/boydti/fawe/object/extent/FastWorldEditExtent.java b/core/src/main/java/com/boydti/fawe/object/extent/FastWorldEditExtent.java index f13e71e7e6..9526a38200 100644 --- a/core/src/main/java/com/boydti/fawe/object/extent/FastWorldEditExtent.java +++ b/core/src/main/java/com/boydti/fawe/object/extent/FastWorldEditExtent.java @@ -39,6 +39,11 @@ public FaweQueue getQueue() { return queue; } + @Override + public int getMaxY() { + return queue.getMaxY(); + } + @Override public int getLight(int x, int y, int z) { return queue.getLight(x, y, z); diff --git a/core/src/main/java/com/boydti/fawe/object/io/serialize/Serialize.java b/core/src/main/java/com/boydti/fawe/object/io/serialize/Serialize.java new file mode 100644 index 0000000000..bc91fbc037 --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/object/io/serialize/Serialize.java @@ -0,0 +1,12 @@ +package com.boydti.fawe.object.io.serialize; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD}) +public @interface Serialize { + +} diff --git a/core/src/main/java/com/boydti/fawe/object/io/serialize/Serializer.java b/core/src/main/java/com/boydti/fawe/object/io/serialize/Serializer.java new file mode 100644 index 0000000000..735505bae4 --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/object/io/serialize/Serializer.java @@ -0,0 +1,50 @@ +package com.boydti.fawe.object.io.serialize; + +import com.boydti.fawe.util.ReflectionUtils; +import java.io.IOException; +import java.io.Serializable; +import java.lang.reflect.Field; + +/** + * Call serialize(stream) to serialize any field with @Serialize
+ * Call deserialize(stream) to deserialize any field with @Serialize
+ */ +public interface Serializer extends Serializable { + default void serialize(java.io.ObjectOutputStream stream) throws IOException { + try { + for (Field field : getClass().getDeclaredFields()) { + field.setAccessible(true); + if (field.getDeclaredAnnotation(Serialize.class) != null) { + Class type = field.getType(); + boolean primitive = type.isPrimitive(); + Object value = field.get(this); + if (primitive) { + stream.writeObject(value); + } else if (value == null){ + stream.writeByte(0); + } else { + stream.writeByte(1); + stream.writeObject(value); + } + } + } + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + + default void deserialize(java.io.ObjectInputStream stream) throws IOException, ClassNotFoundException { + for (Field field : getClass().getDeclaredFields()) { + if (field.getDeclaredAnnotation(Serialize.class) != null) { + Class type = field.getType(); + boolean primitive = type.isPrimitive(); + + if (primitive) { + ReflectionUtils.setField(field, this, stream.readObject()); + } else if (stream.readByte() == 1) { + ReflectionUtils.setField(field, this, stream.readObject()); + } + } + } + } +} diff --git a/core/src/main/java/com/boydti/fawe/object/mask/MaskedTargetBlock.java b/core/src/main/java/com/boydti/fawe/object/mask/MaskedTargetBlock.java index 380bf4b784..f02dc6a508 100644 --- a/core/src/main/java/com/boydti/fawe/object/mask/MaskedTargetBlock.java +++ b/core/src/main/java/com/boydti/fawe/object/mask/MaskedTargetBlock.java @@ -20,13 +20,14 @@ public BlockWorldVector getMaskedTargetBlock(boolean useLastBlock) { boolean searchForLastBlock = true; BlockWorldVector lastBlock = null; while (getNextBlock() != null) { - if (!mask.test(getCurrentBlock())) { + BlockWorldVector current = getCurrentBlock(); + if (!mask.test(current)) { if (searchForLastBlock) { - lastBlock = getCurrentBlock(); + lastBlock = current; if (lastBlock.getBlockY() <= 0 || lastBlock.getBlockY() >= world.getMaxY()) { searchForLastBlock = false; } - } + } else if (current.getBlockY() <= 0) break; } else { break; } @@ -34,4 +35,4 @@ public BlockWorldVector getMaskedTargetBlock(boolean useLastBlock) { BlockWorldVector currentBlock = getCurrentBlock(); return (currentBlock != null || !useLastBlock ? currentBlock : lastBlock); } -} +} \ No newline at end of file diff --git a/core/src/main/java/com/boydti/fawe/object/queue/DelegateFaweQueue.java b/core/src/main/java/com/boydti/fawe/object/queue/DelegateFaweQueue.java new file mode 100644 index 0000000000..fa3414ae10 --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/object/queue/DelegateFaweQueue.java @@ -0,0 +1,25 @@ +package com.boydti.fawe.object.queue; + +import com.boydti.fawe.object.FaweQueue; + +public class DelegateFaweQueue implements IDelegateFaweQueue { + private FaweQueue parent; + + public DelegateFaweQueue(FaweQueue parent) { + this.parent = parent; + } + + public FaweQueue getParent() { + return parent; + } + + public void setParent(FaweQueue parent) { + this.parent = parent; + setWorld(getQueue().getWorldName()); + } + + @Override + public FaweQueue getQueue() { + return parent; + } +} diff --git a/core/src/main/java/com/boydti/fawe/object/queue/FaweQueueDelegateExtent.java b/core/src/main/java/com/boydti/fawe/object/queue/FaweQueueDelegateExtent.java index e982be7fc7..6899b7858a 100644 --- a/core/src/main/java/com/boydti/fawe/object/queue/FaweQueueDelegateExtent.java +++ b/core/src/main/java/com/boydti/fawe/object/queue/FaweQueueDelegateExtent.java @@ -3,7 +3,6 @@ import com.boydti.fawe.FaweCache; import com.boydti.fawe.object.FaweQueue; import com.boydti.fawe.object.exception.FaweException; -import com.boydti.fawe.util.DelegateFaweQueue; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.MutableBlockVector2D; import com.sk89q.worldedit.Vector; diff --git a/core/src/main/java/com/boydti/fawe/object/queue/IDelegateFaweQueue.java b/core/src/main/java/com/boydti/fawe/object/queue/IDelegateFaweQueue.java new file mode 100644 index 0000000000..d8d5acf672 --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/object/queue/IDelegateFaweQueue.java @@ -0,0 +1,515 @@ +package com.boydti.fawe.object.queue; + +import com.boydti.fawe.config.Settings; +import com.boydti.fawe.example.Relighter; +import com.boydti.fawe.jnbt.anvil.generator.GenBase; +import com.boydti.fawe.jnbt.anvil.generator.Resource; +import com.boydti.fawe.object.FaweChunk; +import com.boydti.fawe.object.FawePlayer; +import com.boydti.fawe.object.FaweQueue; +import com.boydti.fawe.object.RegionWrapper; +import com.boydti.fawe.object.RunnableVal2; +import com.boydti.fawe.object.exception.FaweException; +import com.boydti.fawe.util.SetQueue; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.Vector2D; +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.entity.BaseEntity; +import com.sk89q.worldedit.entity.Entity; +import com.sk89q.worldedit.function.mask.Mask; +import com.sk89q.worldedit.function.operation.Operation; +import com.sk89q.worldedit.function.pattern.Pattern; +import com.sk89q.worldedit.regions.CuboidRegion; +import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.session.ClipboardHolder; +import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.world.World; +import com.sk89q.worldedit.world.biome.BaseBiome; +import com.sk89q.worldedit.world.registry.WorldData; +import java.io.File; +import java.util.Collection; +import java.util.List; +import java.util.UUID; +import javax.annotation.Nullable; + +public interface IDelegateFaweQueue extends FaweQueue { + + FaweQueue getQueue(); + + @Override + default void dequeue() { + getQueue().dequeue(); + } + + @Override + default Relighter getRelighter() { + return getQueue().getRelighter(); + } + + @Override + default Vector getMinimumPoint() { + return getQueue().getMinimumPoint(); + } + + @Override + default Vector getMaximumPoint() { + return getQueue().getMaximumPoint(); + } + + @Override + default BaseBlock getLazyBlock(int x, int y, int z) { + return getQueue().getLazyBlock(x, y, z); + } + + @Override + default boolean setBlock(int x, int y, int z, BaseBlock block) throws WorldEditException { + return getQueue().setBlock(x, y, z, block); + } + + @Override + default BaseBlock getBlock(Vector position) { + return getQueue().getBlock(position); + } + + @Override + default BaseBiome getBiome(Vector2D position) { + return getQueue().getBiome(position); + } + + @Override + default boolean setBlock(Vector position, BaseBlock block) throws WorldEditException { + return getQueue().setBlock(position, block); + } + + @Override + default boolean setBiome(Vector2D position, BaseBiome biome) { + return getQueue().setBiome(position, biome); + } + + @Override + default void addEditSession(EditSession session) { + getQueue().addEditSession(session); + } + + @Override + default void setProgressTracker(RunnableVal2 progressTask) { + getQueue().setProgressTracker(progressTask); + } + + @Override + default Collection getEditSessions() { + return getQueue().getEditSessions(); + } + + @Override + default boolean supports(Capability capability) { + return getQueue().supports(capability); + } + + @Override + default void optimize() { + getQueue().optimize(); + } + + @Override + default int setBlocks(CuboidRegion cuboid, int id, int data) { + return getQueue().setBlocks(cuboid, id, data); + } + + @Override + default boolean setBlock(int x, int y, int z, int id, int data) { + return getQueue().setBlock(x, y, z, id, data); + } + + @Override + default boolean setBlock(int x, int y, int z, int id) { + return getQueue().setBlock(x, y, z, id); + } + + @Override + default boolean setBlock(int x, int y, int z, int id, int data, CompoundTag nbt) { + return getQueue().setBlock(x, y, z, id, data, nbt); + } + + @Override + default void setTile(int x, int y, int z, CompoundTag tag) { + getQueue().setTile(x, y, z, tag); + } + + @Override + default void setEntity(int x, int y, int z, CompoundTag tag) { + getQueue().setEntity(x, y, z, tag); + } + + @Override + default void removeEntity(int x, int y, int z, UUID uuid) { + getQueue().removeEntity(x, y, z, uuid); + } + + @Override + default boolean setBiome(int x, int z, BaseBiome biome) { + return getQueue().setBiome(x, z, biome); + } + + @Override + default FaweChunk getFaweChunk(int x, int z) { + return getQueue().getFaweChunk(x, z); + } + + @Override + default Collection getFaweChunks() { + return getQueue().getFaweChunks(); + } + + @Override + default boolean setMCA(int mcaX, int mcaZ, RegionWrapper region, Runnable whileLocked, boolean save, boolean load) { + return getQueue().setMCA(mcaX, mcaZ, region, whileLocked, save, load); + } + + @Override + default void setChunk(FaweChunk chunk) { + getQueue().setChunk(chunk); + } + + @Override + default File getSaveFolder() { + return getQueue().getSaveFolder(); + } + + @Override + default int getMaxY() { + return getQueue().getMaxY(); + } + + @Override + default Settings getSettings() { + return getQueue().getSettings(); + } + + @Override + default void setSettings(Settings settings) { + getQueue().setSettings(settings); + } + + @Override + default void setWorld(String world) { + getQueue().setWorld(world); + } + + @Override + default World getWEWorld() { + return getQueue().getWEWorld(); + } + + @Override + default String getWorldName() { + return getQueue().getWorldName(); + } + + @Override + default long getModified() { + return getQueue().getModified(); + } + + @Override + default void setModified(long modified) { + getQueue().setModified(modified); + } + + @Override + default RunnableVal2 getProgressTask() { + return getQueue().getProgressTask(); + } + + @Override + default void setProgressTask(RunnableVal2 progressTask) { + getQueue().setProgressTask(progressTask); + } + + @Override + default void setChangeTask(RunnableVal2 changeTask) { + getQueue().setChangeTask(changeTask); + } + + @Override + default RunnableVal2 getChangeTask() { + return getQueue().getChangeTask(); + } + + @Override + default SetQueue.QueueStage getStage() { + return getQueue().getStage(); + } + + @Override + default void setStage(SetQueue.QueueStage stage) { + getQueue().setStage(stage); + } + + @Override + default void addNotifyTask(Runnable runnable) { + getQueue().addNotifyTask(runnable); + } + + @Override + default void runTasks() { + getQueue().runTasks(); + } + + @Override + default void addTask(Runnable whenFree) { + getQueue().addTask(whenFree); + } + + @Override + default void forEachBlockInChunk(int cx, int cz, RunnableVal2 onEach) { + getQueue().forEachBlockInChunk(cx, cz, onEach); + } + + @Override + default void forEachTileInChunk(int cx, int cz, RunnableVal2 onEach) { + getQueue().forEachTileInChunk(cx, cz, onEach); + } + + @Override + @Deprecated + default boolean regenerateChunk(int x, int z) { + return getQueue().regenerateChunk(x, z); + } + + @Override + default boolean regenerateChunk(int x, int z, @Nullable BaseBiome biome, @Nullable Long seed) { + return getQueue().regenerateChunk(x, z, biome, seed); + } + + @Override + default void startSet(boolean parallel) { + getQueue().startSet(parallel); + } + + @Override + default void endSet(boolean parallel) { + getQueue().endSet(parallel); + } + + @Override + default int cancel() { + return getQueue().cancel(); + } + + @Override + default void sendBlockUpdate(FaweChunk chunk, FawePlayer... players) { + getQueue().sendBlockUpdate(chunk, players); + } + + @Override + @Deprecated + default boolean next() { + return getQueue().next(); + } + + @Override + default boolean next(int amount, long time) { + return getQueue().next(amount, time); + } + + @Override + default void saveMemory() { + getQueue().saveMemory(); + } + + @Override + default void sendChunk(FaweChunk chunk) { + getQueue().sendChunk(chunk); + } + + @Override + default void sendChunk(int x, int z, int bitMask) { + getQueue().sendChunk(x, z, bitMask); + } + + @Override + default void clear() { + getQueue().clear(); + } + + @Override + default void addNotifyTask(int x, int z, Runnable runnable) { + getQueue().addNotifyTask(x, z, runnable); + } + + @Override + default boolean hasBlock(int x, int y, int z) throws FaweException.FaweChunkLoadException { + return getQueue().hasBlock(x, y, z); + } + + @Override + default int getBiomeId(int x, int z) throws FaweException.FaweChunkLoadException { + return getQueue().getBiomeId(x, z); + } + + @Override + default int getCombinedId4Data(int x, int y, int z) throws FaweException.FaweChunkLoadException { + return getQueue().getCombinedId4Data(x, y, z); + } + + @Override + default int getCachedCombinedId4Data(int x, int y, int z) throws FaweException.FaweChunkLoadException { + return getQueue().getCachedCombinedId4Data(x, y, z); + } + + @Override + default int getAdjacentLight(int x, int y, int z) { + return getQueue().getAdjacentLight(x, y, z); + } + + @Override + default boolean hasSky() { + return getQueue().hasSky(); + } + + @Override + default int getSkyLight(int x, int y, int z) { + return getQueue().getSkyLight(x, y, z); + } + + @Override + default int getLight(int x, int y, int z) { + return getQueue().getLight(x, y, z); + } + + @Override + default int getEmmittedLight(int x, int y, int z) { + return getQueue().getEmmittedLight(x, y, z); + } + + @Override + default CompoundTag getTileEntity(int x, int y, int z) throws FaweException.FaweChunkLoadException { + return getQueue().getTileEntity(x, y, z); + } + + @Override + default int getCombinedId4Data(int x, int y, int z, int def) { + return getQueue().getCombinedId4Data(x, y, z, def); + } + + @Override + default int getCachedCombinedId4Data(int x, int y, int z, int def) { + return getQueue().getCachedCombinedId4Data(x, y, z, def); + } + + @Override + default int getCombinedId4DataDebug(int x, int y, int z, int def, EditSession session) { + return getQueue().getCombinedId4DataDebug(x, y, z, def, session); + } + + @Override + default int getBrightness(int x, int y, int z) { + return getQueue().getBrightness(x, y, z); + } + + @Override + default int getOpacityBrightnessPair(int x, int y, int z) { + return getQueue().getOpacityBrightnessPair(x, y, z); + } + + @Override + default int getOpacity(int x, int y, int z) { + return getQueue().getOpacity(x, y, z); + } + + @Override + default int size() { + return getQueue().size(); + } + + @Override + default boolean isEmpty() { + return getQueue().isEmpty(); + } + + @Override + default void flush() { + getQueue().flush(); + } + + @Override + default void flush(int time) { + getQueue().flush(time); + } + + @Override + default boolean enqueue() { + return getQueue().enqueue(); + } + + @Override + default List getEntities(Region region) { + return getQueue().getEntities(region); + } + + @Override + default List getEntities() { + return getQueue().getEntities(); + } + + @Override + @Nullable + default Entity createEntity(Location location, BaseEntity entity) { + return getQueue().createEntity(location, entity); + } + + @Override + default BaseBlock getLazyBlock(Vector position) { + return getQueue().getLazyBlock(position); + } + + @Nullable + @Override + default Operation commit() { + return getQueue().commit(); + } + + @Override + default int getNearestSurfaceLayer(int x, int z, int y, int minY, int maxY) { + return getQueue().getNearestSurfaceLayer(x, z, y, minY, maxY); + } + + @Override + default int getNearestSurfaceTerrainBlock(int x, int z, int y, int minY, int maxY) { + return getQueue().getNearestSurfaceTerrainBlock(x, z, y, minY, maxY); + } + + @Override + default void addCaves(Region region) throws WorldEditException { + getQueue().addCaves(region); + } + + @Override + default void generate(Region region, GenBase gen) throws WorldEditException { + getQueue().generate(region, gen); + } + + @Override + default void addSchems(Region region, Mask mask, WorldData worldData, List clipboards, int rarity, boolean rotate) throws WorldEditException { + getQueue().addSchems(region, mask, worldData, clipboards, rarity, rotate); + } + + @Override + default void spawnResource(Region region, Resource gen, int rarity, int frequency) throws WorldEditException { + getQueue().spawnResource(region, gen, rarity, frequency); + } + + @Override + default void addOre(Region region, Mask mask, Pattern material, int size, int frequency, int rarity, int minY, int maxY) throws WorldEditException { + getQueue().addOre(region, mask, material, size, frequency, rarity, minY, maxY); + } + + @Override + default void addOres(Region region, Mask mask) throws WorldEditException { + getQueue().addOres(region, mask); + } +} diff --git a/core/src/main/java/com/boydti/fawe/object/queue/LazyFaweChunk.java b/core/src/main/java/com/boydti/fawe/object/queue/LazyFaweChunk.java new file mode 100644 index 0000000000..447d0db8a9 --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/object/queue/LazyFaweChunk.java @@ -0,0 +1,219 @@ +package com.boydti.fawe.object.queue; + +import com.boydti.fawe.object.FaweChunk; +import com.boydti.fawe.object.FaweQueue; +import com.boydti.fawe.object.visitor.FaweChunkVisitor; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.world.biome.BaseBiome; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import javax.annotation.Nullable; + +public abstract class LazyFaweChunk extends FaweChunk { + + private T parent; + + public LazyFaweChunk(FaweQueue queue, int chunkX, int chunkZ) { + super(queue, chunkX, chunkZ); + } + + private T internalGetOrCacheChunk() { + T tmp = parent; + if (tmp == null) parent = tmp = getChunk(); + return tmp; + } + + protected T getCachedChunk() { + return parent; + } + + public abstract T getChunk(); + + @Override + public FaweQueue getParent() { + return internalGetOrCacheChunk().getParent(); + } + + @Override + public long longHash() { + return internalGetOrCacheChunk().longHash(); + } + + @Override + public int hashCode() { + return internalGetOrCacheChunk().hashCode(); + } + + @Override + public void addToQueue() { + internalGetOrCacheChunk().addToQueue(); + } + + @Override + public int getBitMask() { + return internalGetOrCacheChunk().getBitMask(); + } + + @Override + public int getBlockCombinedId(int x, int y, int z) { + return internalGetOrCacheChunk().getBlockCombinedId(x, y, z); + } + + @Override + public void setBlock(int x, int y, int z, BaseBlock block) { + internalGetOrCacheChunk().setBlock(x, y, z, block); + } + + @Override + public BaseBlock getBlock(int x, int y, int z) { + return internalGetOrCacheChunk().getBlock(x, y, z); + } + + @Override + @Nullable + public char[] getIdArray(int layer) { + return internalGetOrCacheChunk().getIdArray(layer); + } + + @Override + public byte[][] getBlockLightArray() { + return internalGetOrCacheChunk().getBlockLightArray(); + } + + @Override + public byte[][] getSkyLightArray() { + return internalGetOrCacheChunk().getSkyLightArray(); + } + + @Override + public byte[] getBiomeArray() { + return internalGetOrCacheChunk().getBiomeArray(); + } + + @Override + public void forEachQueuedBlock(FaweChunkVisitor onEach) { + internalGetOrCacheChunk().forEachQueuedBlock(onEach); + } + + @Override + public char[][] getCombinedIdArrays() { + return internalGetOrCacheChunk().getCombinedIdArrays(); + } + + @Override + public void fill(int id, byte data) { + internalGetOrCacheChunk().fill(id, data); + } + + @Override + public void fillCuboid(int x1, int x2, int y1, int y2, int z1, int z2, int id, byte data) { + internalGetOrCacheChunk().fillCuboid(x1, x2, y1, y2, z1, z2, id, data); + } + + @Override + public void addNotifyTask(Runnable run) { + internalGetOrCacheChunk().addNotifyTask(run); + } + + @Override + public boolean hasNotifyTasks() { + return internalGetOrCacheChunk().hasNotifyTasks(); + } + + @Override + public void executeNotifyTasks() { + internalGetOrCacheChunk().executeNotifyTasks(); + } + + @Override + public void setTile(int x, int y, int z, CompoundTag tile) { + internalGetOrCacheChunk().setTile(x, y, z, tile); + } + + @Override + public void setEntity(CompoundTag entity) { + internalGetOrCacheChunk().setEntity(entity); + } + + @Override + public void removeEntity(UUID uuid) { + internalGetOrCacheChunk().removeEntity(uuid); + } + + @Override + public void setBlock(int x, int y, int z, int id) { + internalGetOrCacheChunk().setBlock(x, y, z, id); + } + + @Override + public void setBlock(int x, int y, int z, int id, int data) { + internalGetOrCacheChunk().setBlock(x, y, z, id, data); + } + + @Override + public Set getEntities() { + return internalGetOrCacheChunk().getEntities(); + } + + @Override + public Set getEntityRemoves() { + return internalGetOrCacheChunk().getEntityRemoves(); + } + + @Override + public Map getTiles() { + return internalGetOrCacheChunk().getTiles(); + } + + @Override + public CompoundTag getTile(int x, int y, int z) { + return internalGetOrCacheChunk().getTile(x, y, z); + } + + @Override + public void setBiome(int x, int z, BaseBiome biome) { + internalGetOrCacheChunk().setBiome(x, z, biome); + } + + @Override + public void setBiome(int x, int z, byte biome) { + internalGetOrCacheChunk().setBiome(x, z, biome); + } + + @Override + public void setBiome(byte biome) { + internalGetOrCacheChunk().setBiome(biome); + } + + @Override + public void optimize() { + internalGetOrCacheChunk().optimize(); + } + + @Override + public boolean equals(Object obj) { + return internalGetOrCacheChunk().equals(obj); + } + + @Override + public FaweChunk copy(boolean shallow) { + return internalGetOrCacheChunk().copy(shallow); + } + + @Override + public void start() { + internalGetOrCacheChunk().start(); + } + + @Override + public void end() { + internalGetOrCacheChunk().end(); + } + + @Override + public FaweChunk call() { + return internalGetOrCacheChunk().call(); + } +} diff --git a/core/src/main/java/com/boydti/fawe/util/CleanTextureUtil.java b/core/src/main/java/com/boydti/fawe/util/CleanTextureUtil.java index 653a27c66b..f441990ec0 100644 --- a/core/src/main/java/com/boydti/fawe/util/CleanTextureUtil.java +++ b/core/src/main/java/com/boydti/fawe/util/CleanTextureUtil.java @@ -17,6 +17,8 @@ public CleanTextureUtil(TextureUtil parent, int minPercent, int maxPercent) thro for (; minIndex > 0 && parent.distances[minIndex - 1] == min; minIndex--) ; for (; maxIndex < parent.distances.length - 1 && parent.distances[maxIndex + 1] == max; maxIndex++) ; int num = maxIndex - minIndex + 1; + this.validMixBiomeColors = parent.validMixBiomeColors; + this.validMixBiomeIds = parent.validMixBiomeIds; this.validBiomes = parent.validBiomes; this.blockColors = parent.blockColors; this.blockDistance = parent.blockDistance; @@ -42,4 +44,4 @@ public int getMin() { public int getMax() { return max; } -} +} \ No newline at end of file diff --git a/core/src/main/java/com/boydti/fawe/util/DelegateFaweQueue.java b/core/src/main/java/com/boydti/fawe/util/DelegateFaweQueue.java deleted file mode 100644 index c5a38c0bea..0000000000 --- a/core/src/main/java/com/boydti/fawe/util/DelegateFaweQueue.java +++ /dev/null @@ -1,465 +0,0 @@ -package com.boydti.fawe.util; - -import com.boydti.fawe.config.Settings; -import com.boydti.fawe.example.Relighter; -import com.boydti.fawe.object.FaweChunk; -import com.boydti.fawe.object.FawePlayer; -import com.boydti.fawe.object.FaweQueue; -import com.boydti.fawe.object.RegionWrapper; -import com.boydti.fawe.object.RunnableVal2; -import com.boydti.fawe.object.exception.FaweException; -import com.sk89q.jnbt.CompoundTag; -import com.sk89q.worldedit.EditSession; -import com.sk89q.worldedit.Vector; -import com.sk89q.worldedit.Vector2D; -import com.sk89q.worldedit.WorldEditException; -import com.sk89q.worldedit.blocks.BaseBlock; -import com.sk89q.worldedit.regions.CuboidRegion; -import com.sk89q.worldedit.world.World; -import com.sk89q.worldedit.world.biome.BaseBiome; -import java.io.File; -import java.util.Collection; -import java.util.Set; -import java.util.UUID; -import java.util.concurrent.ConcurrentLinkedDeque; -import javax.annotation.Nullable; - -public class DelegateFaweQueue extends FaweQueue { - private FaweQueue parent; - - public DelegateFaweQueue(FaweQueue parent) { - super(parent == null ? null : parent.getWEWorld()); - this.parent = parent; - } - - public FaweQueue getParent() { - return parent; - } - - public void setParent(FaweQueue parent) { - this.parent = parent; - setWorld(parent.getWorldName()); - } - - @Override - public void sendChunk(int x, int z, int bitMask) { - parent.sendChunk(x, z, bitMask); - } - - @Override - public boolean setMCA(int mcaX, int mcaZ, RegionWrapper region, Runnable whileLocked, boolean save, boolean load) { - return parent.setMCA(mcaX, mcaZ, region, whileLocked, save, load); - } - - @Override - public boolean supportsChangeTask() { - return parent.supportsChangeTask(); - } - - @Override - public String getWorldName() { - return parent.getWorldName(); - } - - @Override - public void addEditSession(EditSession session) { - parent.addEditSession(session); - } - - @Override - public World getWEWorld() { - return parent.getWEWorld(); - } - - @Override - public void setProgressTracker(RunnableVal2 progressTask) { - parent.setProgressTracker(progressTask); - } - - @Override - public Set getEditSessions() { - return parent.getEditSessions(); - } - - @Override - public ConcurrentLinkedDeque getSessions() { - return parent.getSessions(); - } - - @Override - public void setSessions(ConcurrentLinkedDeque sessions) { - parent.setSessions(sessions); - } - - @Override - public long getModified() { - return parent.getModified(); - } - - @Override - public void setModified(long modified) { - parent.setModified(modified); - } - - @Override - public RunnableVal2 getProgressTask() { - return parent.getProgressTask(); - } - - @Override - public void setProgressTask(RunnableVal2 progressTask) { - parent.setProgressTask(progressTask); - } - - @Override - public int getBiomeId(int x, int z) throws FaweException.FaweChunkLoadException { - return parent.getBiomeId(x, z); - } - - @Override - public void setChangeTask(RunnableVal2 changeTask) { - parent.setChangeTask(changeTask); - } - - @Override - public RunnableVal2 getChangeTask() { - return parent.getChangeTask(); - } - - @Override - public void optimize() { - parent.optimize(); - } - - @Override - public int setBlocks(CuboidRegion cuboid, int id, int data) { - return parent.setBlocks(cuboid, id, data); - } - - @Override - public boolean setBlock(int x, int y, int z, int id, int data) { - return parent.setBlock(x, y, z, id, data); - } - - @Override - public boolean setBlock(int x, int y, int z, int id) { - return parent.setBlock(x, y, z, id); - } - - @Override - public boolean setBlock(int x, int y, int z, int id, int data, CompoundTag nbt) { - return parent.setBlock(x, y, z, id, data, nbt); - } - - @Override - public void setTile(int x, int y, int z, CompoundTag tag) { - parent.setTile(x, y, z, tag); - } - - @Override - public void setEntity(int x, int y, int z, CompoundTag tag) { - parent.setEntity(x, y, z, tag); - } - - @Override - public void removeEntity(int x, int y, int z, UUID uuid) { - parent.removeEntity(x, y, z, uuid); - } - - @Override - public boolean setBiome(int x, int z, BaseBiome biome) { - return parent.setBiome(x, z, biome); - } - - @Override - public FaweChunk getFaweChunk(int x, int z) { - return parent.getFaweChunk(x, z); - } - - @Override - public Collection getFaweChunks() { - return parent.getFaweChunks(); - } - - @Override - public void setChunk(FaweChunk chunk) { - parent.setChunk(chunk); - } - - @Override - public File getSaveFolder() { - return parent.getSaveFolder(); - } - - @Override - public int getMaxY() { - return parent.getMaxY(); - } - - @Override - public void forEachBlockInChunk(int cx, int cz, RunnableVal2 onEach) { - parent.forEachBlockInChunk(cx, cz, onEach); - } - - @Override - public void forEachTileInChunk(int cx, int cz, RunnableVal2 onEach) { - parent.forEachTileInChunk(cx, cz, onEach); - } - - @Override - @Deprecated - public boolean regenerateChunk(int x, int z) { - return parent.regenerateChunk(x, z); - } - - @Override - public boolean regenerateChunk(int x, int z, @Nullable BaseBiome biome, Long seed) { - return parent.regenerateChunk(x, z, biome, seed); - } - - @Override - public void startSet(boolean parallel) { - parent.startSet(parallel); - } - - @Override - public void endSet(boolean parallel) { - parent.endSet(parallel); - } - - @Override - public int cancel() { - return parent.cancel(); - } - - @Override - public void sendBlockUpdate(FaweChunk chunk, FawePlayer... players) { - parent.sendBlockUpdate(chunk, players); - } - - @Deprecated - @Override - public boolean next() { - return parent.next(); - } - - @Override - public boolean next(int amount, long time) { - return parent.next(amount, time); - } - - @Override - public void saveMemory() { - parent.saveMemory(); - } - - @Override - public void sendChunk(FaweChunk chunk) { - parent.sendChunk(chunk); - } - - @Override - public void clear() { - parent.clear(); - } - - @Override - public void addNotifyTask(int x, int z, Runnable runnable) { - parent.addNotifyTask(x, z, runnable); - } - - @Override - public boolean hasBlock(int x, int y, int z) throws FaweException.FaweChunkLoadException { - return parent.hasBlock(x, y, z); - } - - @Override - public void addNotifyTask(Runnable runnable) { - parent.addNotifyTask(runnable); - } - - @Override - public int getCombinedId4Data(int x, int y, int z) throws FaweException.FaweChunkLoadException { - return parent.getCombinedId4Data(x, y, z); - } - - @Override - public int getCachedCombinedId4Data(int x, int y, int z) throws FaweException.FaweChunkLoadException { - return parent.getCachedCombinedId4Data(x, y, z); - } - - @Override - public int getAdjacentLight(int x, int y, int z) { - return parent.getAdjacentLight(x, y, z); - } - - @Override - public boolean hasSky() { - return parent.hasSky(); - } - - @Override - public int getSkyLight(int x, int y, int z) { - return parent.getSkyLight(x, y, z); - } - - @Override - public int getLight(int x, int y, int z) { - return parent.getLight(x, y, z); - } - - @Override - public int getEmmittedLight(int x, int y, int z) { - return parent.getEmmittedLight(x, y, z); - } - - @Override - public CompoundTag getTileEntity(int x, int y, int z) throws FaweException.FaweChunkLoadException { - return parent.getTileEntity(x, y, z); - } - - @Override - public int getCombinedId4Data(int x, int y, int z, int def) { - return parent.getCombinedId4Data(x, y, z, def); - } - - @Override - public int getCachedCombinedId4Data(int x, int y, int z, int def) { - return parent.getCachedCombinedId4Data(x, y, z, def); - } - - @Override - public int getCombinedId4DataDebug(int x, int y, int z, int def, EditSession session) { - return parent.getCombinedId4DataDebug(x, y, z, def, session); - } - - @Override - public int getBrightness(int x, int y, int z) { - return parent.getBrightness(x, y, z); - } - - @Override - public int getOpacityBrightnessPair(int x, int y, int z) { - return parent.getOpacityBrightnessPair(x, y, z); - } - - @Override - public int getOpacity(int x, int y, int z) { - return parent.getOpacity(x, y, z); - } - - @Override - public int size() { - return parent.size(); - } - - @Override - public boolean isEmpty() { - return parent.isEmpty(); - } - - @Override - public void flush() { - parent.flush(); - } - - @Override - public SetQueue.QueueStage getStage() { - return parent.getStage(); - } - - @Override - public void setStage(SetQueue.QueueStage stage) { - parent.setStage(stage); - } - - @Override - public void flush(int time) { - parent.flush(time); - } - - @Override - public void runTasks() { - parent.runTasks(); - } - - @Override - public void addTask(Runnable whenFree) { - parent.addTask(whenFree); - } - - @Override - public boolean enqueue() { - return parent.enqueue(); - } - - @Override - public void dequeue() { - parent.dequeue(); - } - - @Override - public Relighter getRelighter() { - return parent.getRelighter(); - } - - @Override - public Vector getMinimumPoint() { - return parent.getMinimumPoint(); - } - - @Override - public Vector getMaximumPoint() { - return parent.getMaximumPoint(); - } - - @Override - public BaseBlock getLazyBlock(int x, int y, int z) { - return parent.getLazyBlock(x, y, z); - } - - @Override - public boolean setBlock(int x, int y, int z, BaseBlock block) throws WorldEditException { - return parent.setBlock(x, y, z, block); - } - - @Override - public BaseBlock getBlock(Vector position) { - return parent.getBlock(position); - } - - @Override - public BaseBiome getBiome(Vector2D position) { - return parent.getBiome(position); - } - - @Override - public boolean setBlock(Vector position, BaseBlock block) throws WorldEditException { - return parent.setBlock(position, block); - } - - @Override - public boolean setBiome(Vector2D position, BaseBiome biome) { - return parent.setBiome(position, biome); - } - - @Override - public FaweQueue getQueue() { - return parent.getQueue(); - } - - @Override - public Settings getSettings() { - return parent.getSettings(); - } - - @Override - public void setSettings(Settings settings) { - parent.setSettings(settings); - } - - @Override - public void setWorld(String world) { - parent.setWorld(world); - } -} diff --git a/core/src/main/java/com/boydti/fawe/util/EditSessionBuilder.java b/core/src/main/java/com/boydti/fawe/util/EditSessionBuilder.java index 6645f1e15c..ddeb1226db 100644 --- a/core/src/main/java/com/boydti/fawe/util/EditSessionBuilder.java +++ b/core/src/main/java/com/boydti/fawe/util/EditSessionBuilder.java @@ -189,6 +189,6 @@ public EditSessionBuilder event(@Nullable EditSessionEvent event) { } public EditSession build() { - return new EditSession(worldName, world, queue, player, limit, changeSet, allowedRegions, autoQueue, fastmode, checkMemory, combineStages, blockBag, eventBus, event); + return new EditSession(worldName, world, queue, player, limit, changeSet, allowedRegions, autoQueue, fastmode, checkMemory, combineStages, blockBag, eventBus, event); } } diff --git a/core/src/main/java/com/boydti/fawe/util/MainUtil.java b/core/src/main/java/com/boydti/fawe/util/MainUtil.java index 38b78cb9fb..ed99fa9b86 100644 --- a/core/src/main/java/com/boydti/fawe/util/MainUtil.java +++ b/core/src/main/java/com/boydti/fawe/util/MainUtil.java @@ -946,10 +946,14 @@ public static Object copyNd(Object arr) { int innerArrayLength = Array.getLength(arr); Class component = arr.getClass().getComponentType(); Object newInnerArray = Array.newInstance(component, innerArrayLength); - //copy each elem of the array - for (int i = 0; i < innerArrayLength; i++) { - Object elem = copyNd(Array.get(arr, i)); - Array.set(newInnerArray, i, elem); + if (component.isPrimitive()) { + System.arraycopy(arr, 0, newInnerArray, 0, innerArrayLength); + } else { + //copy each elem of the array + for (int i = 0; i < innerArrayLength; i++) { + Object elem = copyNd(Array.get(arr, i)); + Array.set(newInnerArray, i, elem); + } } return newInnerArray; } else { diff --git a/core/src/main/java/com/boydti/fawe/util/MathMan.java b/core/src/main/java/com/boydti/fawe/util/MathMan.java index f619e63ceb..353c5154b2 100644 --- a/core/src/main/java/com/boydti/fawe/util/MathMan.java +++ b/core/src/main/java/com/boydti/fawe/util/MathMan.java @@ -530,6 +530,10 @@ public static final double getSD(int[] array, double av) { return Math.sqrt(sd / array.length); } + public static final int absByte(int value) { + return (value ^ (value >> 8)) - (value >> 8); + } + public static final int mod(int x, int y) { if (isPowerOfTwo(y)) { return x & (y - 1); diff --git a/core/src/main/java/com/boydti/fawe/util/ReflectionUtils.java b/core/src/main/java/com/boydti/fawe/util/ReflectionUtils.java index 701b92ee53..7e9fb760ae 100644 --- a/core/src/main/java/com/boydti/fawe/util/ReflectionUtils.java +++ b/core/src/main/java/com/boydti/fawe/util/ReflectionUtils.java @@ -345,11 +345,16 @@ public static Method findMethod(final Class clazz, int index, int hasMods, in return null; } - private static Method[] sortMethods(Method[] methods) { + public static Method[] sortMethods(Method[] methods) { Arrays.sort(methods, (o1, o2) -> o1.getName().compareTo(o2.getName())); return methods; } + public static Field[] sortFields(Field[] fields) { + Arrays.sort(fields, (o1, o2) -> o1.getName().compareTo(o2.getName())); + return fields; + } + public static Method findMethod(final Class clazz, int index, final Class returnType, Class... params) { return findMethod(clazz, index, 0, 0, returnType, params); } diff --git a/core/src/main/java/com/boydti/fawe/util/SetQueue.java b/core/src/main/java/com/boydti/fawe/util/SetQueue.java index e419fa8a2f..1bed49bdc3 100644 --- a/core/src/main/java/com/boydti/fawe/util/SetQueue.java +++ b/core/src/main/java/com/boydti/fawe/util/SetQueue.java @@ -3,6 +3,7 @@ import com.boydti.fawe.Fawe; import com.boydti.fawe.config.Settings; import com.boydti.fawe.object.FaweQueue; +import com.boydti.fawe.wrappers.WorldWrapper; import com.sk89q.worldedit.world.World; import java.util.ArrayList; import java.util.Collection; @@ -242,6 +243,8 @@ public Collection getInactiveQueues() { } public FaweQueue getNewQueue(World world, boolean fast, boolean autoqueue) { + world = WorldWrapper.unwrap(world); + if (world instanceof FaweQueue) return (FaweQueue) world; FaweQueue queue = Fawe.imp().getNewQueue(world, fast); if (autoqueue) { queue.setStage(QueueStage.INACTIVE); diff --git a/core/src/main/java/com/boydti/fawe/util/image/Drawable.java b/core/src/main/java/com/boydti/fawe/util/image/Drawable.java new file mode 100644 index 0000000000..2d811558a2 --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/util/image/Drawable.java @@ -0,0 +1,7 @@ +package com.boydti.fawe.util.image; + +import java.awt.image.BufferedImage; + +public interface Drawable { + public BufferedImage draw(); +} diff --git a/core/src/main/java/com/boydti/fawe/util/image/ImageViewer.java b/core/src/main/java/com/boydti/fawe/util/image/ImageViewer.java index 9ffffb5d4b..ba3771651f 100644 --- a/core/src/main/java/com/boydti/fawe/util/image/ImageViewer.java +++ b/core/src/main/java/com/boydti/fawe/util/image/ImageViewer.java @@ -1,8 +1,7 @@ package com.boydti.fawe.util.image; -import java.awt.image.BufferedImage; import java.io.Closeable; public interface ImageViewer extends Closeable{ - public void view(BufferedImage image); + public void view(Drawable drawable); } diff --git a/core/src/main/java/com/boydti/fawe/wrappers/PlayerWrapper.java b/core/src/main/java/com/boydti/fawe/wrappers/PlayerWrapper.java index 4c0300ab2d..f3acdb4534 100644 --- a/core/src/main/java/com/boydti/fawe/wrappers/PlayerWrapper.java +++ b/core/src/main/java/com/boydti/fawe/wrappers/PlayerWrapper.java @@ -3,15 +3,14 @@ import com.boydti.fawe.Fawe; import com.boydti.fawe.object.FawePlayer; import com.boydti.fawe.object.RunnableVal; +import com.boydti.fawe.util.EditSessionBuilder; import com.boydti.fawe.util.TaskManager; import com.sk89q.worldedit.BlockWorldVector; import com.sk89q.worldedit.EditSession; -import com.sk89q.worldedit.EditSessionFactory; import com.sk89q.worldedit.LocalSession; import com.sk89q.worldedit.LocalWorld; import com.sk89q.worldedit.PlayerDirection; import com.sk89q.worldedit.Vector; -import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.WorldVector; import com.sk89q.worldedit.WorldVectorFace; @@ -200,10 +199,9 @@ public boolean ascendUpwards(int distance, boolean alwaysGlass) { @Override public void floatAt(final int x, final int y, final int z, final boolean alwaysGlass) { - EditSessionFactory factory = WorldEdit.getInstance().getEditSessionFactory(); RuntimeException caught = null; try { - final EditSession edit = factory.getEditSession(parent.getWorld(), -1, null, this); + EditSession edit = new EditSessionBuilder(parent.getWorld()).player(FawePlayer.wrap(this)).build(); edit.setBlockFast(new Vector(x, y - 1, z), new BaseBlock(BlockType.GLASS.getID())); edit.flushQueue(); LocalSession session = Fawe.get().getWorldEdit().getSession(this); diff --git a/core/src/main/java/com/sk89q/worldedit/EditSession.java b/core/src/main/java/com/sk89q/worldedit/EditSession.java index 441c2bbedb..d829f1b311 100644 --- a/core/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/core/src/main/java/com/sk89q/worldedit/EditSession.java @@ -25,6 +25,7 @@ import com.boydti.fawe.config.BBC; import com.boydti.fawe.config.Settings; import com.boydti.fawe.example.MappedFaweQueue; +import com.boydti.fawe.jnbt.anvil.HeightMapMCAGenerator; import com.boydti.fawe.jnbt.anvil.MCAQueue; import com.boydti.fawe.jnbt.anvil.MCAWorld; import com.boydti.fawe.logging.LoggingChangeSet; @@ -48,7 +49,6 @@ import com.boydti.fawe.object.extent.FastWorldEditExtent; import com.boydti.fawe.object.extent.FaweRegionExtent; import com.boydti.fawe.object.extent.HeightBoundExtent; -import com.boydti.fawe.object.extent.LightingExtent; import com.boydti.fawe.object.extent.MultiRegionExtent; import com.boydti.fawe.object.extent.NullExtent; import com.boydti.fawe.object.extent.ProcessedWEExtent; @@ -102,7 +102,6 @@ import com.sk89q.worldedit.function.mask.RegionMask; import com.sk89q.worldedit.function.operation.ChangeSetExecutor; import com.sk89q.worldedit.function.operation.ForwardExtentCopy; -import com.sk89q.worldedit.function.operation.Operation; import com.sk89q.worldedit.function.operation.Operations; import com.sk89q.worldedit.function.pattern.BlockPattern; import com.sk89q.worldedit.function.util.RegionOffset; @@ -137,7 +136,7 @@ import com.sk89q.worldedit.util.Countable; import com.sk89q.worldedit.util.TreeGenerator; import com.sk89q.worldedit.util.eventbus.EventBus; -import com.sk89q.worldedit.world.AbstractWorld; +import com.sk89q.worldedit.world.SimpleWorld; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.biome.BaseBiome; import com.sk89q.worldedit.world.registry.WorldData; @@ -168,7 +167,7 @@ * {@link Extent}s that are chained together. For example, history is logged * using the {@link ChangeSetExtent}.

*/ -public class EditSession extends AbstractWorld implements HasFaweQueue, LightingExtent { +public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, SimpleWorld { /** * Used by {@link #setBlock(Vector, BaseBlock, Stage)} to * determine which {@link Extent}s should be bypassed. @@ -181,6 +180,7 @@ public enum Stage { private String worldName; private FaweQueue queue; private boolean wrapped; + private boolean fastMode; private AbstractDelegateExtent extent; private HistoryExtent history; private AbstractDelegateExtent bypassHistory; @@ -214,9 +214,10 @@ public EditSession(@Nonnull World world, @Nullable FaweQueue queue, @Nullable Fa } public EditSession(@Nullable String worldName, @Nullable World world, @Nullable FaweQueue queue, @Nullable FawePlayer player, @Nullable FaweLimit limit, @Nullable FaweChangeSet changeSet, @Nullable RegionWrapper[] allowedRegions, @Nullable Boolean autoQueue, @Nullable Boolean fastmode, @Nullable Boolean checkMemory, @Nullable Boolean combineStages, @Nullable BlockBag blockBag, @Nullable EventBus bus, @Nullable EditSessionEvent event) { + super(world); this.worldName = worldName == null ? world == null ? queue == null ? "" : queue.getWorldName() : Fawe.imp().getWorldName(world) : worldName; if (world == null && this.worldName != null) world = FaweAPI.getWorld(this.worldName); - this.world = world = WorldWrapper.wrap((AbstractWorld) world); + this.world = world = WorldWrapper.wrap(world); if (bus == null) { bus = WorldEdit.getInstance().getEventBus(); } @@ -235,11 +236,6 @@ public EditSession(@Nullable String worldName, @Nullable World world, @Nullable limit = player.getLimit(); } } - if (allowedRegions == null) { - if (player != null && !player.hasWorldEditBypass()) { - allowedRegions = player.getCurrentRegions(); - } - } if (autoQueue == null) { autoQueue = true; } @@ -250,8 +246,9 @@ public EditSession(@Nullable String worldName, @Nullable World world, @Nullable fastmode = player.getSession().hasFastMode(); } } + this.fastMode = fastmode; if (checkMemory == null) { - checkMemory = player != null && !fastmode; + checkMemory = player != null && !this.fastMode; } if (checkMemory) { if (MemUtil.isMemoryLimitedSlow()) { @@ -264,21 +261,30 @@ public EditSession(@Nullable String worldName, @Nullable World world, @Nullable this.originalLimit = limit; this.blockBag = limit.INVENTORY_MODE != 0 ? blockBag : null; this.limit = limit.copy(); + if (queue == null) { - if (world instanceof MCAWorld) { - queue = ((MCAWorld) world).getQueue(); + boolean placeChunks = this.fastMode || this.limit.FAST_PLACEMENT; + World unwrapped = WorldWrapper.unwrap(world); + if (unwrapped instanceof FaweQueue) { + queue = (FaweQueue) unwrapped; + } else if (unwrapped instanceof MCAWorld) { + queue = ((MCAWorld) unwrapped).getQueue(); + } else if (player != null && world.equals(player.getWorld())) { + queue = player.getFaweQueue(placeChunks, autoQueue); } else { - queue = SetQueue.IMP.getNewQueue(this, fastmode || this.limit.FAST_PLACEMENT, autoQueue); + queue = SetQueue.IMP.getNewQueue(world, placeChunks, autoQueue); } } if (combineStages == null) { - combineStages = Settings.IMP.HISTORY.COMBINE_STAGES && !(queue instanceof MCAQueue); - } - if (!this.limit.FAST_PLACEMENT || !queue.supportsChangeTask()) { - combineStages = false; - } - if (this.blockBag != null) { - combineStages = false; + combineStages = + // If it's enabled in the settings + Settings.IMP.HISTORY.COMBINE_STAGES + // If fast placement is disabled, it's slower to perform a copy on each chunk + && this.limit.FAST_PLACEMENT + // If the specific queue doesn't support it + && queue.supports(FaweQueue.Capability.CHANGE_TASKS) + // If the edit uses items from the inventory we can't use a delayed task + && this.blockBag == null; } if (Settings.IMP.EXPERIMENTAL.ANVIL_QUEUE_MODE && !(queue instanceof MCAQueue)) { queue = new MCAQueue(queue); @@ -298,7 +304,7 @@ public EditSession(@Nullable String worldName, @Nullable World world, @Nullable } this.bypassAll = wrapExtent(new FastWorldEditExtent(world, queue), bus, event, Stage.BEFORE_CHANGE); this.bypassHistory = (this.extent = wrapExtent(bypassAll, bus, event, Stage.BEFORE_REORDER)); - if (!fastmode || changeSet != null) { + if (!this.fastMode || changeSet != null) { if (changeSet == null) { if (Settings.IMP.HISTORY.USE_DISK) { UUID uuid = player == null ? CONSOLE : player.getUUID(); @@ -337,6 +343,11 @@ public EditSession(@Nullable String worldName, @Nullable World world, @Nullable } } } + if (allowedRegions == null) { + if (player != null && !player.hasWorldEditBypass() && !(queue instanceof HeightMapMCAGenerator)) { + allowedRegions = player.getCurrentRegions(); + } + } this.maxY = getWorld() == null ? 255 : world.getMaxY(); if (allowedRegions != null) { if (allowedRegions.length == 0) { @@ -354,6 +365,7 @@ public EditSession(@Nullable String worldName, @Nullable World world, @Nullable this.extent = new HeightBoundExtent(this.extent, this.limit, 0, maxY); } this.extent = wrapExtent(this.extent, bus, event, Stage.BEFORE_HISTORY); + setExtent(this.extent); } /** @@ -603,13 +615,23 @@ public WorldData getWorldData() { * @return the change set */ public ChangeSet getChangeSet() { - return history != null ? history.getChangeSet() : changeTask; + return changeTask != null ? changeTask : history != null ? history.getChangeSet() : null; } public FaweChangeSet getChangeTask() { return changeTask; } + /** + * Set the ChangeSet without hooking into any recording mechanism or triggering any actions.
+ * Used internally to set the ChangeSet during completion to record custom changes which aren't normally recorded + * @param set + */ + public void setRawChangeSet(@Nullable FaweChangeSet set) { + changeTask = set; + changes++; + } + /** * Change the ChangeSet being used for this EditSession * - If history is disabled, no changeset can be set @@ -819,6 +841,7 @@ public SurvivalModeExtent getSurvivalExtent() { * @param enabled true to enable */ public void setFastMode(final boolean enabled) { + this.fastMode = enabled; disableHistory(enabled); } @@ -1150,6 +1173,17 @@ public boolean smartSetBlock(final Vector position, final BaseBlock block) { } } + @Override + public boolean setBlock(Vector position, BaseBlock block) throws MaxChangedBlocksException { + try { + return setBlock(position, block, Stage.BEFORE_HISTORY); + } catch (MaxChangedBlocksException e) { + throw e; + } catch (WorldEditException e) { + throw new RuntimeException("Unexpected exception", e); + } + } + public boolean setBlock(int x, int y, int z, BaseBlock block) { this.changes++; try { @@ -1367,13 +1401,6 @@ public void flushQueue() { } } - @Override - public - @Nullable - Operation commit() { - return null; - } - /** * Count the number of blocks of a given list of types in a region. * diff --git a/core/src/main/java/com/sk89q/worldedit/LocalSession.java b/core/src/main/java/com/sk89q/worldedit/LocalSession.java index a543038a65..e9150a5158 100644 --- a/core/src/main/java/com/sk89q/worldedit/LocalSession.java +++ b/core/src/main/java/com/sk89q/worldedit/LocalSession.java @@ -190,6 +190,7 @@ public boolean loadSessionHistoryFromDisk(UUID uuid, World world) { if (Settings.IMP.HISTORY.USE_DISK) { MAX_HISTORY_SIZE = Integer.MAX_VALUE; } + world = WorldWrapper.unwrap(world); if (!world.equals(currentWorld)) { this.uuid = uuid; // Save history @@ -354,6 +355,7 @@ public void clearHistory() { history.clear(); historyNegativeIndex = 0; historySize = 0; + currentWorld = null; } /** @@ -440,7 +442,7 @@ public synchronized void remember(final EditSession editSession, final boolean a return; } // Don't store anything if no changes were made - if (editSession.size() == 0 || editSession.hasFastMode()) { + if (editSession.size() == 0) { return; } FaweChangeSet changeSet = (FaweChangeSet) editSession.getChangeSet(); @@ -510,10 +512,10 @@ public EditSession undo(@Nullable BlockBag newBlockBag, LocalPlayer player) { */ public EditSession undo(@Nullable BlockBag newBlockBag, Player player) { checkNotNull(player); - loadSessionHistoryFromDisk(player.getUniqueId(), player.getWorld()); + FawePlayer fp = FawePlayer.wrap(player); + loadSessionHistoryFromDisk(player.getUniqueId(), fp.getWorldForEditing()); if (getHistoryNegativeIndex() < history.size()) { FaweChangeSet changeSet = getChangeSet(history.get(getHistoryIndex())); - final FawePlayer fp = FawePlayer.wrap(player); EditSession newEditSession = new EditSessionBuilder(changeSet.getWorld()) .allowedRegionsEverywhere() .checkMemory(false) @@ -557,12 +559,12 @@ public EditSession redo(@Nullable BlockBag newBlockBag, LocalPlayer player) { */ public EditSession redo(@Nullable BlockBag newBlockBag, Player player) { checkNotNull(player); - loadSessionHistoryFromDisk(player.getUniqueId(), player.getWorld()); + FawePlayer fp = FawePlayer.wrap(player); + loadSessionHistoryFromDisk(player.getUniqueId(), fp.getWorldForEditing()); if (getHistoryNegativeIndex() > 0) { setDirty(); historyNegativeIndex--; FaweChangeSet changeSet = getChangeSet(history.get(getHistoryIndex())); - final FawePlayer fp = FawePlayer.wrap(player); EditSession newEditSession = new EditSessionBuilder(changeSet.getWorld()) .allowedRegionsEverywhere() .checkMemory(false) @@ -1288,11 +1290,15 @@ public EditSession createEditSession(Player player) { BlockBag blockBag = getBlockBag(player); - // Create an edit session - EditSession editSession = WorldEdit.getInstance().getEditSessionFactory() - .getEditSession(player.isPlayer() ? player.getWorld() : null, - getBlockChangeLimit(), blockBag, player); - editSession.setFastMode(fastMode); + World world = player.getWorld(); + boolean isPlayer = player.isPlayer(); + EditSessionBuilder builder = new EditSessionBuilder(world); + if (player.isPlayer()) builder.player(FawePlayer.wrap(player)); + builder.blockBag(blockBag); + builder.fastmode(fastMode); + + EditSession editSession = builder.build(); + Request.request().setEditSession(editSession); if (mask != null) { editSession.setMask(mask); diff --git a/core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java b/core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java index b4012c93cc..a15c55d71f 100644 --- a/core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java +++ b/core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java @@ -474,10 +474,17 @@ public BrushSettings scatterSchemBrush(Player player, EditSession editSession, L try { MultiClipboardHolder clipboards = ClipboardFormat.SCHEMATIC.loadAllFromInput(player, player.getWorld().getWorldData(), clipboard, true); if (clipboards == null) { + BBC.SCHEMATIC_NOT_FOUND.send(player, clipboard); return null; } + List holders = clipboards.getHolders(); + if (holders == null) { + BBC.SCHEMATIC_NOT_FOUND.send(player, clipboard); + return null; + } + return get(context) - .setBrush(new PopulateSchem(mask, clipboards.getHolders(), (int) density, rotate)) + .setBrush(new PopulateSchem(mask, holders, (int) density, rotate)) .setSize(radius); } catch (IOException e) { throw new RuntimeException(e); diff --git a/core/src/main/java/com/sk89q/worldedit/command/BrushProcessor.java b/core/src/main/java/com/sk89q/worldedit/command/BrushProcessor.java index 7e8791fa84..aaca703aa2 100644 --- a/core/src/main/java/com/sk89q/worldedit/command/BrushProcessor.java +++ b/core/src/main/java/com/sk89q/worldedit/command/BrushProcessor.java @@ -25,9 +25,11 @@ public BrushSettings process(CommandLocals locals, BrushSettings settings) throw LocalSession session = worldEdit.getSessionManager().get(actor); session.setTool(null, (Player) actor); BrushTool tool = session.getBrushTool((Player) actor); - tool.setPrimary(settings); - tool.setSecondary(settings); - BBC.BRUSH_EQUIPPED.send(actor, ((String) locals.get("arguments")).split(" ")[1]); + if (tool != null) { + tool.setPrimary(settings); + tool.setSecondary(settings); + BBC.BRUSH_EQUIPPED.send(actor, ((String) locals.get("arguments")).split(" ")[1]); + } return null; } } \ No newline at end of file diff --git a/core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java b/core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java index d1b97d66ab..9a61938439 100644 --- a/core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java +++ b/core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java @@ -437,10 +437,7 @@ public void place(Player player, LocalSession session, final EditSession editSes @Switch('a') final boolean ignoreAirBlocks, @Switch('o') boolean atOrigin, @Switch('s') boolean selectPasted) throws WorldEditException { ClipboardHolder holder = session.getClipboard(); - System.out.println(holder); final Clipboard clipboard = holder.getClipboard(); - System.out.println(clipboard.getDimensions()); - System.out.println(clipboard.getClass()); final Vector origin = clipboard.getOrigin(); final Vector to = atOrigin ? origin : session.getPlacementPosition(player); diff --git a/core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java b/core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java index 89968e7471..32364b9dd0 100644 --- a/core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java +++ b/core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java @@ -30,7 +30,6 @@ import com.boydti.fawe.object.exception.FaweException; import com.boydti.fawe.object.visitor.Fast2DIterator; import com.boydti.fawe.util.MathMan; -import com.boydti.fawe.util.SetQueue; import com.sk89q.jnbt.CompoundTag; import com.sk89q.minecraft.util.commands.Command; import com.sk89q.minecraft.util.commands.CommandContext; @@ -138,7 +137,7 @@ public void fixlighting(Player player) throws WorldEditException { public void getlighting(Player player) throws WorldEditException { FawePlayer fp = FawePlayer.wrap(player); final FaweLocation loc = fp.getLocation(); - FaweQueue queue = SetQueue.IMP.getNewQueue(loc.world, true, false); + FaweQueue queue = fp.getFaweQueue(false); fp.sendMessage("Light: " + queue.getEmmittedLight(loc.x, loc.y, loc.z) + " | " + queue.getSkyLight(loc.x, loc.y, loc.z)); } @@ -193,7 +192,7 @@ public void setlighting(Player player, @Selection Region region, @Range(min = 0, final FaweLocation loc = fp.getLocation(); final int cx = loc.x >> 4; final int cz = loc.z >> 4; - final NMSMappedFaweQueue queue = (NMSMappedFaweQueue) SetQueue.IMP.getNewQueue(fp.getWorld(), true, false); + final NMSMappedFaweQueue queue = (NMSMappedFaweQueue) fp.getFaweQueue(false); for (Vector pt : region) { queue.setBlockLight((int) pt.getX(), (int) pt.getY(), (int) pt.getZ(), value); } @@ -217,7 +216,7 @@ public void setskylighting(Player player, @Selection Region region, @Range(min = final FaweLocation loc = fp.getLocation(); final int cx = loc.x >> 4; final int cz = loc.z >> 4; - final NMSMappedFaweQueue queue = (NMSMappedFaweQueue) SetQueue.IMP.getNewQueue(fp.getWorld(), true, false); + final NMSMappedFaweQueue queue = (NMSMappedFaweQueue) fp.getFaweQueue(false); for (Vector pt : region) { queue.setSkyLight((int) pt.getX(), (int) pt.getY(), (int) pt.getZ(), value); } diff --git a/core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java b/core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java index 4a587b5c5a..62e71e0111 100644 --- a/core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java +++ b/core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java @@ -53,7 +53,6 @@ import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.util.eventbus.Subscribe; import com.sk89q.worldedit.world.World; -import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.EnumMap; @@ -277,24 +276,8 @@ public T createProxyActor(T base) { if (base instanceof Player) { Player player = (Player) base; - - Player permActor = queryCapability(Capability.PERMISSIONS).matchPlayer(player); - if (permActor == null) { - permActor = player; - } - - Player cuiActor = queryCapability(Capability.WORLDEDIT_CUI).matchPlayer(player); - if (cuiActor == null) { - cuiActor = player; - } - try { - Class clazz = Class.forName("com.sk89q.worldedit.extension.platform.PlayerProxy"); - Constructor constructor = clazz.getDeclaredConstructor(Player.class, Actor.class, Actor.class, World.class); - constructor.setAccessible(true); - return (T) constructor.newInstance(player, permActor, cuiActor, getWorldForEditing(player.getWorld())); - } catch (Throwable e) { - throw new RuntimeException(e); - } + FawePlayer fp = FawePlayer.wrap(player); + return (T) fp.createProxy(); } else { return base; } diff --git a/core/src/main/java/com/sk89q/worldedit/extension/platform/PlayerProxy.java b/core/src/main/java/com/sk89q/worldedit/extension/platform/PlayerProxy.java index 4202e428b8..d9e9e9dea1 100644 --- a/core/src/main/java/com/sk89q/worldedit/extension/platform/PlayerProxy.java +++ b/core/src/main/java/com/sk89q/worldedit/extension/platform/PlayerProxy.java @@ -8,6 +8,7 @@ import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extent.inventory.BlockBag; +import com.sk89q.worldedit.internal.LocalWorldAdapter; import com.sk89q.worldedit.internal.cui.CUIEvent; import com.sk89q.worldedit.session.SessionKey; import com.sk89q.worldedit.util.Location; @@ -20,6 +21,7 @@ public class PlayerProxy extends AbstractPlayerActor { private final Actor permActor; private final Actor cuiActor; private final World world; + private Vector offset = Vector.ZERO; public PlayerProxy(Player basePlayer, Actor permActor, Actor cuiActor, World world) { Preconditions.checkNotNull(basePlayer); @@ -32,6 +34,10 @@ public PlayerProxy(Player basePlayer, Actor permActor, Actor cuiActor, World wor this.world = world; } + public void setOffset(Vector position) { + this.offset = position; + } + public UUID getUniqueId() { return this.basePlayer.getUniqueId(); } @@ -62,11 +68,13 @@ public BaseEntity getState() { } public Location getLocation() { - return this.basePlayer.getLocation(); + Location loc = this.basePlayer.getLocation(); + return new Location(loc.getExtent(), loc.toVector().add(offset), loc.getDirection()); } public WorldVector getPosition() { - return this.basePlayer.getPosition(); + WorldVector wv = this.basePlayer.getPosition(); + return new WorldVector(LocalWorldAdapter.wrap(world), wv.add(offset)); } public double getPitch() { diff --git a/core/src/main/java/com/sk89q/worldedit/extent/AbstractDelegateExtent.java b/core/src/main/java/com/sk89q/worldedit/extent/AbstractDelegateExtent.java index 4ef9e8e478..86dc834485 100644 --- a/core/src/main/java/com/sk89q/worldedit/extent/AbstractDelegateExtent.java +++ b/core/src/main/java/com/sk89q/worldedit/extent/AbstractDelegateExtent.java @@ -46,6 +46,7 @@ public class AbstractDelegateExtent implements LightingExtent { private transient final Extent extent; + private MutableBlockVector mutable = new MutableBlockVector(0, 0, 0); /** * Create a new instance. @@ -64,6 +65,11 @@ public int getSkyLight(int x, int y, int z) { return 0; } + @Override + public int getMaxY() { + return extent.getMaxY(); + } + public int getBlockLight(int x, int y, int z) { if (extent instanceof LightingExtent) { return ((LightingExtent) extent).getBlockLight(x, y, z); @@ -115,8 +121,6 @@ public BaseBlock getBlock(Vector position) { return extent.getLazyBlock(position); } - private MutableBlockVector mutable = new MutableBlockVector(0, 0, 0); - @Override public BaseBlock getLazyBlock(int x, int y, int z) { mutable.mutX(x); @@ -188,6 +192,16 @@ public String toString() { return super.toString() + ":" + extent.toString(); } + @Override + public int getNearestSurfaceLayer(int x, int z, int y, int minY, int maxY) { + return extent.getNearestSurfaceLayer(x, z, y, minY, maxY); + } + + @Override + public int getNearestSurfaceTerrainBlock(int x, int z, int y, int minY, int maxY) { + return extent.getNearestSurfaceTerrainBlock(x, z, y, minY, maxY); + } + @Override public final @Nullable diff --git a/core/src/main/java/com/sk89q/worldedit/extent/Extent.java b/core/src/main/java/com/sk89q/worldedit/extent/Extent.java index 64287e0379..8e217e7d05 100644 --- a/core/src/main/java/com/sk89q/worldedit/extent/Extent.java +++ b/core/src/main/java/com/sk89q/worldedit/extent/Extent.java @@ -66,6 +66,10 @@ default BaseBlock getLazyBlock(int x, int y, int z) { return getLazyBlock(MutableBlockVector.get(x, y, z)); } + default int getMaxY() { + return 255; + } + default public int getNearestSurfaceLayer(int x, int z, int y, int minY, int maxY) { int clearanceAbove = maxY - y; int clearanceBelow = y - minY; @@ -95,7 +99,12 @@ default public int getNearestSurfaceLayer(int x, int z, int y, int minY, int max for (int layer = y - clearance - 1; layer >= minY; layer--) { block = getLazyBlock(x, layer, z); if (FaweCache.isLiquidOrGas(block.getId()) != state) { - return ((layer + offset) << 3) - (7 - (state ? block.getData() : data1)); + +// int blockHeight = (newHeight) >> 3; +// int layerHeight = (newHeight) & 0x7; + + int data = (state ? block.getData() : data1); + return ((layer + offset) << 3) + 0; } data1 = block.getData(); } @@ -171,6 +180,12 @@ default public void spawnResource(Region region, Resource gen, int rarity, int f } } + default boolean contain(Vector pt) { + Vector min = getMinimumPoint(); + Vector max = getMaximumPoint(); + return (pt.containedWithin(min, max)); + } + default public void addOre(Region region, Mask mask, Pattern material, int size, int frequency, int rarity, int minY, int maxY) throws WorldEditException { spawnResource(region, new OreGen(this, mask, material, size, minY, maxY), rarity, frequency); } diff --git a/core/src/main/java/com/sk89q/worldedit/internal/command/WorldEditBinding.java b/core/src/main/java/com/sk89q/worldedit/internal/command/WorldEditBinding.java index 41f3258859..c990651124 100644 --- a/core/src/main/java/com/sk89q/worldedit/internal/command/WorldEditBinding.java +++ b/core/src/main/java/com/sk89q/worldedit/internal/command/WorldEditBinding.java @@ -19,6 +19,7 @@ package com.sk89q.worldedit.internal.command; +import com.boydti.fawe.object.FawePlayer; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.IncompleteRegionException; import com.sk89q.worldedit.LocalSession; @@ -85,7 +86,7 @@ public WorldEditBinding(WorldEdit worldEdit) { public Object getSelection(ArgumentStack context, Selection selection) throws IncompleteRegionException, ParameterException { Player sender = getPlayer(context); LocalSession session = worldEdit.getSessionManager().get(sender); - return session.getSelection(sender.getWorld()); + return session.getSelection(FawePlayer.wrap(sender).getWorldForEditing()); } /** @@ -338,4 +339,4 @@ public static Class inject() { return WorldEditBinding.class; } -} +} \ No newline at end of file diff --git a/core/src/main/java/com/sk89q/worldedit/math/convolution/HeightMap.java b/core/src/main/java/com/sk89q/worldedit/math/convolution/HeightMap.java index 23f7e83b8f..843f1bec93 100644 --- a/core/src/main/java/com/sk89q/worldedit/math/convolution/HeightMap.java +++ b/core/src/main/java/com/sk89q/worldedit/math/convolution/HeightMap.java @@ -84,9 +84,19 @@ public HeightMap(EditSession session, Region region, boolean naturalOnly, boolea } else { // Store current heightmap data data = new int[width * height]; - for (int z = 0; z < height; ++z) { - for (int x = 0; x < width; ++x) { - data[z * width + x] = session.getHighestTerrainBlock(x + minX, z + minZ, minY, maxY, naturalOnly); + int index = 0; + if (naturalOnly) { + for (int z = 0; z < height; ++z) { + for (int x = 0; x < width; ++x, index++) { + data[index] = session.getHighestTerrainBlock(x + minX, z + minZ, minY, maxY, naturalOnly); + } + } + } else { + int yTmp = 255; + for (int z = 0; z < height; ++z) { + for (int x = 0; x < width; ++x, index++) { + data[index] = yTmp = session.getNearestSurfaceTerrainBlock(x + minX, z + minZ, yTmp, minY, maxY); + } } } } @@ -125,18 +135,7 @@ public int applyFilter(HeightMapFilter filter, int iterations) throws WorldEditE return layers ? applyLayers(newData) : apply(newData); } -// TODO -// public int averageFilter(int iterations) throws WorldEditException { -// Vector min = region.getMinimumPoint(); -// Vector max = region.getMaximumPoint(); -// int shift = layers ? 3 : 0; -// AverageHeightMapFilter filter = new AverageHeightMapFilter(data, width, height, min.getBlockY() << shift, max.getBlockY() << shift); -// int[] newData = filter.filter(iterations); -// return layers ? applyLayers(newData) : apply(newData); -// } - public int applyLayers(int[] data) throws WorldEditException { - System.out.println("Layers"); checkNotNull(data); Vector minY = region.getMinimumPoint(); @@ -154,6 +153,28 @@ public int applyLayers(int[] data) throws WorldEditException { // Apply heightmap int maxY4 = maxY << 4; int index = 0; + + if (!session.hasExtraExtents()) { + // TODO fast change height +// int chunkZLen = (height + 15) >> 4; +// int chunkXLen = (width + 15) >> 4; +// FaweQueue queue = session.getQueue(); +// if (queue instanceof MappedFaweQueue) { +// MappedFaweQueue mfq = (MappedFaweQueue) queue; +// for (int cz = 0; cz < chunkZLen; cz++) { +// for (int cx = 0; cx < chunkXLen; cx++) { +// mfq.queueChunkLoad(cx, cz, new RunnableVal() { +// @Override +// public void run(Object chunk) { +// todo +// } +// }); +// } +// } +// } + } + + for (int z = 0; z < height; ++z) { int zr = z + originZ; for (int x = 0; x < width; ++x) { @@ -209,7 +230,6 @@ public int applyLayers(int[] data) throws WorldEditException { } } } - return blocksChanged; } diff --git a/core/src/main/java/com/sk89q/worldedit/util/TargetBlock.java b/core/src/main/java/com/sk89q/worldedit/util/TargetBlock.java new file mode 100644 index 0000000000..02230de782 --- /dev/null +++ b/core/src/main/java/com/sk89q/worldedit/util/TargetBlock.java @@ -0,0 +1,214 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.util; + +import com.boydti.fawe.wrappers.WorldWrapper; +import com.sk89q.worldedit.*; +import com.sk89q.worldedit.blocks.BlockID; +import com.sk89q.worldedit.blocks.BlockType; +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.internal.LocalWorldAdapter; + +/** + * This class uses an inefficient method to figure out what block a player + * is looking towards. + * + *

Originally written by toi. It was ported to WorldEdit and trimmed down by + * sk89q. Thanks to Raphfrk for optimization of toi's original class.

+ */ +public class TargetBlock { + + private LocalWorld world; + private int maxDistance; + private double checkDistance, curDistance; + private Vector targetPos = new Vector(); + private Vector targetPosDouble = new Vector(); + private Vector prevPos = new Vector(); + private Vector offset = new Vector(); + + /** + * Constructor requiring a player, uses default values + * + * @param player player to work with + */ + public TargetBlock(LocalPlayer player) { + this.world = LocalWorldAdapter.adapt(player.getWorld()); + this.setValues(player.getPosition(), player.getYaw(), player.getPitch(), + 300, 1.65, 0.2); + } + + /** + * Constructor requiring a player, max distance and a checking distance + * + * @param player LocalPlayer to work with + * @param maxDistance how far it checks for blocks + * @param checkDistance how often to check for blocks, the smaller the more precise + */ + public TargetBlock(LocalPlayer player, int maxDistance, double checkDistance) { + this((Player) player, maxDistance, checkDistance); + } + + /** + * Constructor requiring a player, max distance and a checking distance + * + * @param player LocalPlayer to work with + * @param maxDistance how far it checks for blocks + * @param checkDistance how often to check for blocks, the smaller the more precise + */ + public TargetBlock(Player player, int maxDistance, double checkDistance) { + this.world = LocalWorldAdapter.adapt(player.getWorld()); + this.setValues(player.getPosition(), player.getYaw(), player.getPitch(), maxDistance, 1.65, checkDistance); + } + + /** + * Set the values, all constructors uses this function + * + * @param loc location of the view + * @param xRotation the X rotation + * @param yRotation the Y rotation + * @param maxDistance how far it checks for blocks + * @param viewHeight where the view is positioned in y-axis + * @param checkDistance how often to check for blocks, the smaller the more precise + */ + private void setValues(Vector loc, double xRotation, double yRotation, + int maxDistance, double viewHeight, double checkDistance) { + this.maxDistance = maxDistance; + this.checkDistance = checkDistance; + this.curDistance = 0; + xRotation = (xRotation + 90) % 360; + yRotation = yRotation * -1; + + double h = (checkDistance * Math.cos(Math.toRadians(yRotation))); + + offset = new Vector((h * Math.cos(Math.toRadians(xRotation))), + (checkDistance * Math.sin(Math.toRadians(yRotation))), + (h * Math.sin(Math.toRadians(xRotation)))); + + targetPosDouble = loc.add(0, viewHeight, 0); + targetPos = targetPosDouble.toBlockPoint(); + prevPos = targetPos; + } + + /** + * Returns any block at the sight. Returns null if out of range or if no + * viable target was found. Will try to return the last valid air block it finds. + * + * @return Block + */ + public BlockWorldVector getAnyTargetBlock() { + boolean searchForLastBlock = true; + BlockWorldVector lastBlock = null; + while (getNextBlock() != null) { + if (world.getBlockType(getCurrentBlock()) == BlockID.AIR) { + if (searchForLastBlock) { + lastBlock = getCurrentBlock(); + if (lastBlock.getBlockY() <= 0 || lastBlock.getBlockY() >= world.getMaxY()) { + searchForLastBlock = false; + } + } + } else { + break; + } + } + BlockWorldVector currentBlock = getCurrentBlock(); + return (currentBlock != null ? currentBlock : lastBlock); + } + + /** + * Returns the block at the sight. Returns null if out of range or if no + * viable target was found + * + * @return Block + */ + public BlockWorldVector getTargetBlock() { + while (getNextBlock() != null && world.getBlockType(getCurrentBlock()) == 0) ; + return getCurrentBlock(); + } + + /** + * Returns the block at the sight. Returns null if out of range or if no + * viable target was found + * + * @return Block + */ + public BlockWorldVector getSolidTargetBlock() { + while (getNextBlock() != null && BlockType.canPassThrough(world.getBlock(getCurrentBlock()))) ; + return getCurrentBlock(); + } + + /** + * Get next block + * + * @return next block position + */ + public BlockWorldVector getNextBlock() { + prevPos = targetPos; + do { + curDistance += checkDistance; + + targetPosDouble = offset.add(targetPosDouble.getX(), + targetPosDouble.getY(), + targetPosDouble.getZ()); + targetPos = targetPosDouble.toBlockPoint(); + } while (curDistance <= maxDistance + && targetPos.getBlockX() == prevPos.getBlockX() + && targetPos.getBlockY() == prevPos.getBlockY() + && targetPos.getBlockZ() == prevPos.getBlockZ()); + + if (curDistance > maxDistance) { + return null; + } + + return new BlockWorldVector(world, targetPos); + } + + /** + * Returns the current block along the line of vision + * + * @return block position + */ + public BlockWorldVector getCurrentBlock() { + if (curDistance > maxDistance) { + return null; + } else { + return new BlockWorldVector(world, targetPos); + } + } + + /** + * Returns the previous block in the aimed path + * + * @return block position + */ + public BlockWorldVector getPreviousBlock() { + return new BlockWorldVector(world, prevPos); + } + + public WorldVectorFace getAnyTargetBlockFace() { + getAnyTargetBlock(); + return WorldVectorFace.getWorldVectorFace(world, getCurrentBlock(), getPreviousBlock()); + } + + public WorldVectorFace getTargetBlockFace() { + getAnyTargetBlock(); + return WorldVectorFace.getWorldVectorFace(world, getCurrentBlock(), getPreviousBlock()); + } + +} diff --git a/core/src/main/java/com/sk89q/worldedit/world/SimpleWorld.java b/core/src/main/java/com/sk89q/worldedit/world/SimpleWorld.java new file mode 100644 index 0000000000..2ae715f560 --- /dev/null +++ b/core/src/main/java/com/sk89q/worldedit/world/SimpleWorld.java @@ -0,0 +1,186 @@ +package com.sk89q.worldedit.world; + +import com.boydti.fawe.util.SetQueue; +import com.sk89q.worldedit.BlockVector2D; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.MaxChangedBlocksException; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.blocks.BaseItem; +import com.sk89q.worldedit.blocks.BaseItemStack; +import com.sk89q.worldedit.blocks.BlockID; +import com.sk89q.worldedit.blocks.BlockType; +import com.sk89q.worldedit.extension.platform.Platform; +import com.sk89q.worldedit.function.mask.BlockMask; +import com.sk89q.worldedit.function.mask.Mask; +import com.sk89q.worldedit.function.operation.Operation; +import com.sk89q.worldedit.util.Direction; +import com.sk89q.worldedit.util.TreeGenerator; +import javax.annotation.Nullable; + +public interface SimpleWorld extends World { + @Override + default boolean useItem(Vector position, BaseItem item, Direction face) { + return false; + } + + @Override + default boolean setBlockType(Vector position, int type) { + try { + return setBlock(position, new BaseBlock(type)); + } catch (WorldEditException ignored) { + return false; + } + } + + @Override + default void setBlockData(Vector position, int data) { + try { + setBlock(position, new BaseBlock(getLazyBlock(position).getType(), data)); + } catch (WorldEditException ignored) { + } + } + + @Override + default boolean setTypeIdAndData(Vector position, int type, int data) { + try { + return setBlock(position, new BaseBlock(type, data)); + } catch (WorldEditException ignored) { + return false; + } + } + + @Override + default boolean setBlock(Vector pt, BaseBlock block) throws WorldEditException { + return setBlock(pt, block, true); + } + + @Override + default int getMaxY() { + return getMaximumPoint().getBlockY(); + } + + @Override + default boolean isValidBlockType(int type) { + return BlockType.fromID(type) != null; + } + + @Override + default boolean usesBlockData(int type) { + // We future proof here by assuming all unknown blocks use data + return BlockType.usesData(type) || BlockType.fromID(type) == null; + } + + @Override + default Mask createLiquidMask() { + return new BlockMask(this, + new BaseBlock(BlockID.STATIONARY_LAVA, -1), + new BaseBlock(BlockID.LAVA, -1), + new BaseBlock(BlockID.STATIONARY_WATER, -1), + new BaseBlock(BlockID.WATER, -1)); + } + + @Override + default int getBlockType(Vector pt) { + return getLazyBlock(pt).getType(); + } + + @Override + default int getBlockData(Vector pt) { + return getLazyBlock(pt).getData(); + } + + @Override + default void dropItem(Vector pt, BaseItemStack item, int times) { + for (int i = 0; i < times; ++i) { + dropItem(pt, item); + } + } + + @Override + default void simulateBlockMine(Vector pt) { + BaseBlock block = getLazyBlock(pt); + BaseItemStack stack = BlockType.getBlockDrop(block.getId(), (short) block.getData()); + + if (stack != null) { + final int amount = stack.getAmount(); + if (amount > 1) { + dropItem(pt, new BaseItemStack(stack.getType(), 1, stack.getData()), amount); + } else { + dropItem(pt, stack, amount); + } + } + + try { + setBlock(pt, new BaseBlock(BlockID.AIR)); + } catch (WorldEditException e) { + throw new RuntimeException(e); + } + } + + @Override + default boolean generateTree(EditSession editSession, Vector pt) throws MaxChangedBlocksException { + return generateTree(TreeGenerator.TreeType.TREE, editSession, pt); + } + + @Override + default boolean generateBigTree(EditSession editSession, Vector pt) throws MaxChangedBlocksException { + return generateTree(TreeGenerator.TreeType.BIG_TREE, editSession, pt); + } + + @Override + default boolean generateBirchTree(EditSession editSession, Vector pt) throws MaxChangedBlocksException { + return generateTree(TreeGenerator.TreeType.BIRCH, editSession, pt); + } + + @Override + default boolean generateRedwoodTree(EditSession editSession, Vector pt) throws MaxChangedBlocksException { + return generateTree(TreeGenerator.TreeType.REDWOOD, editSession, pt); + } + + @Override + default boolean generateTallRedwoodTree(EditSession editSession, Vector pt) throws MaxChangedBlocksException { + return generateTree(TreeGenerator.TreeType.TALL_REDWOOD, editSession, pt); + } + + @Override + default void checkLoadedChunk(Vector pt) { + } + + @Override + default void fixAfterFastMode(Iterable chunks) { + } + + @Override + default void fixLighting(Iterable chunks) { + } + + @Override + default boolean playEffect(Vector position, int type, int data) { + return false; + } + + @SuppressWarnings("deprecation") + @Override + default boolean queueBlockBreakEffect(Platform server, Vector position, int blockId, double priority) { + SetQueue.IMP.addTask(() -> playEffect(position, 2001, blockId)); + return true; + } + + @Override + default Vector getMinimumPoint() { + return new Vector(-30000000, 0, -30000000); + } + + @Override + default Vector getMaximumPoint() { + return new Vector(30000000, 255, 30000000); + } + + @Override + default @Nullable + Operation commit() { + return null; + } +} diff --git a/favs/src/main/java/com/thevoxelbox/voxelsniper/Sniper.java b/favs/src/main/java/com/thevoxelbox/voxelsniper/Sniper.java index 13668def28..642060247a 100644 --- a/favs/src/main/java/com/thevoxelbox/voxelsniper/Sniper.java +++ b/favs/src/main/java/com/thevoxelbox/voxelsniper/Sniper.java @@ -1,7 +1,6 @@ package com.thevoxelbox.voxelsniper; import com.boydti.fawe.Fawe; -import com.boydti.fawe.FaweAPI; import com.boydti.fawe.bukkit.wrapper.AsyncWorld; import com.boydti.fawe.config.BBC; import com.boydti.fawe.config.Settings; @@ -167,7 +166,7 @@ public synchronized boolean snipeOnCurrentThread(FawePlayer fp, final Action act Player bukkitPlayer = getPlayer(); World bukkitWorld = bukkitPlayer.getWorld(); - FaweQueue baseQueue = FaweAPI.createQueue(fp.getLocation().world, false); + FaweQueue baseQueue = fp.getFaweQueue(false); RegionWrapper[] mask = WEManager.IMP.getMask(fp); if (mask.length == 0) { BBC.NO_REGION.send(fp);