From 8ac2bf6da35c852108567ae658f1464d11e7508e Mon Sep 17 00:00:00 2001 From: Jesse Boyd Date: Tue, 12 Sep 2017 03:02:44 +1000 Subject: [PATCH] Interactive CFI --- build.gradle | 1 + .../com/boydti/fawe/bukkit/FaweBukkit.java | 37 + .../bukkit/block/BrushBoundBaseBlock.java | 2 +- .../fawe/bukkit/util/cui/CUIListener.java | 31 + .../fawe/bukkit/util/cui/StructureCUI.java | 51 ++ .../util/image/BukkitImageListener.java | 259 ++++++ .../bukkit/util/image/BukkitImageViewer.java | 156 ++++ .../fawe/bukkit/wrapper/AsyncBlockState.java | 8 + .../fawe/bukkit/wrapper/state/AsyncSign.java | 43 + core/build.gradle | 1 + core/src/main/java/com/boydti/fawe/Fawe.java | 21 + .../main/java/com/boydti/fawe/FaweCache.java | 2 + core/src/main/java/com/boydti/fawe/IFawe.java | 6 +- .../com/boydti/fawe/command/CFICommand.java | 83 ++ .../com/boydti/fawe/command/CFICommands.java | 843 ++++++++++++++++++ .../fawe/command/FawePrimitiveBinding.java | 9 + .../java/com/boydti/fawe/config/Settings.java | 5 + .../fawe/jnbt/anvil/HeightMapMCADrawer.java | 142 +++ .../jnbt/anvil/HeightMapMCAGenerator.java | 484 +++++----- .../com/boydti/fawe/jnbt/anvil/MCAWriter.java | 12 +- .../com/boydti/fawe/object/HistoryExtent.java | 11 +- .../regions/general/plot/CreateFromImage.java | 13 +- .../boydti/fawe/util/CleanTextureUtil.java | 12 + .../com/boydti/fawe/util/ExtentTraverser.java | 13 +- .../boydti/fawe/util/FilteredTextureUtil.java | 7 + .../main/java/com/boydti/fawe/util/Jars.java | 28 +- .../com/boydti/fawe/util/TextureUtil.java | 15 +- .../com/boydti/fawe/util/chat/Message.java | 13 + .../java/com/boydti/fawe/util/cui/CUI.java | 18 + .../com/boydti/fawe/util/image/ImageUtil.java | 94 ++ .../boydti/fawe/util/image/ImageViewer.java | 8 + .../com/sk89q/worldedit/LocalSession.java | 19 +- .../extension/platform/CommandManager.java | 42 +- .../worldedit/math/convolution/HeightMap.java | 9 +- .../worldedit/session/ClipboardHolder.java | 4 + .../session/DelegateClipboardHolder.java | 45 + .../command/parametric/ParametricBuilder.java | 7 + 37 files changed, 2276 insertions(+), 278 deletions(-) create mode 100644 bukkit/src/main/java/com/boydti/fawe/bukkit/util/cui/CUIListener.java create mode 100644 bukkit/src/main/java/com/boydti/fawe/bukkit/util/cui/StructureCUI.java create mode 100644 bukkit/src/main/java/com/boydti/fawe/bukkit/util/image/BukkitImageListener.java create mode 100644 bukkit/src/main/java/com/boydti/fawe/bukkit/util/image/BukkitImageViewer.java create mode 100644 bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/state/AsyncSign.java create mode 100644 core/src/main/java/com/boydti/fawe/command/CFICommand.java create mode 100644 core/src/main/java/com/boydti/fawe/command/CFICommands.java create mode 100644 core/src/main/java/com/boydti/fawe/jnbt/anvil/HeightMapMCADrawer.java create mode 100644 core/src/main/java/com/boydti/fawe/util/cui/CUI.java create mode 100644 core/src/main/java/com/boydti/fawe/util/image/ImageUtil.java create mode 100644 core/src/main/java/com/boydti/fawe/util/image/ImageViewer.java create mode 100644 core/src/main/java/com/sk89q/worldedit/session/DelegateClipboardHolder.java diff --git a/build.gradle b/build.gradle index 1498ace2..f2f75036 100644 --- a/build.gradle +++ b/build.gradle @@ -104,5 +104,6 @@ subprojects { maven {url "http://ci.frostcast.net/plugin/repository/everything"} maven {url "http://maven.sk89q.com/artifactory/repo"} maven {url "http://repo.spongepowered.org/maven"} + maven {url "https://repo.inventivetalent.org/content/groups/public/"} } } 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 451d3c67..6a185f33 100644 --- a/bukkit/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java +++ b/bukkit/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java @@ -17,6 +17,8 @@ import com.boydti.fawe.bukkit.regions.Worldguard; import com.boydti.fawe.bukkit.util.BukkitTaskMan; import com.boydti.fawe.bukkit.util.ItemUtil; import com.boydti.fawe.bukkit.util.VaultUtil; +import com.boydti.fawe.bukkit.util.image.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; import com.boydti.fawe.bukkit.v0.ChunkListener; @@ -33,9 +35,11 @@ import com.boydti.fawe.object.FaweCommand; import com.boydti.fawe.object.FawePlayer; import com.boydti.fawe.object.FaweQueue; import com.boydti.fawe.regions.FaweMaskManager; +import com.boydti.fawe.util.Jars; import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.ReflectionUtils; import com.boydti.fawe.util.TaskManager; +import com.boydti.fawe.util.image.ImageViewer; import com.boydti.fawe.util.metrics.BStats; import com.sk89q.bukkit.util.FallbackRegistrationListener; import com.sk89q.worldedit.bukkit.BukkitPlayerBlockBag; @@ -44,6 +48,7 @@ import com.sk89q.worldedit.bukkit.EditSessionBlockChangeDelegate; import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.world.World; import java.io.File; +import java.io.FileOutputStream; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -59,6 +64,7 @@ import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.RegisteredServiceProvider; import org.primesoft.blockshub.BlocksHubBukkit; @@ -68,6 +74,8 @@ public class FaweBukkit implements IFawe, Listener { private VaultUtil vault; private WorldEditPlugin worldedit; private ItemUtil itemUtil; + private boolean listening; + private BukkitImageListener listener; public VaultUtil getVault() { return this.vault; @@ -130,6 +138,35 @@ public class FaweBukkit implements IFawe, Listener { }); } + @Override + public synchronized ImageViewer getImageViewer(FawePlayer fp) { + if (listening && listener == null) return null; + try { + listening = true; + PluginManager manager = Bukkit.getPluginManager(); + if (manager.getPlugin("PacketListenerApi") == null) { + File output = new File(plugin.getDataFolder().getParentFile(), "PacketListenerAPI_v3.6.0-SNAPSHOT.jar"); + byte[] jarData = Jars.PL_v3_6_0.download(); + try (FileOutputStream fos = new FileOutputStream(output)) { + fos.write(jarData); + } + } + if (manager.getPlugin("MapManager") == null) { + File output = new File(plugin.getDataFolder().getParentFile(), "MapManager_v1.4.0-SNAPSHOT.jar"); + byte[] jarData = Jars.MM_v1_4_0.download(); + try (FileOutputStream fos = new FileOutputStream(output)) { + fos.write(jarData); + } + } + BukkitImageViewer viewer = new BukkitImageViewer((Player) fp.parent); + if (listener == null) { + this.listener = new BukkitImageListener(plugin); + } + return viewer; + } catch (Throwable ignore) {} + return null; + } + @Override public int getPlayerCount() { return plugin.getServer().getOnlinePlayers().size(); diff --git a/bukkit/src/main/java/com/boydti/fawe/bukkit/block/BrushBoundBaseBlock.java b/bukkit/src/main/java/com/boydti/fawe/bukkit/block/BrushBoundBaseBlock.java index e262c2bb..f7988e18 100644 --- a/bukkit/src/main/java/com/boydti/fawe/bukkit/block/BrushBoundBaseBlock.java +++ b/bukkit/src/main/java/com/boydti/fawe/bukkit/block/BrushBoundBaseBlock.java @@ -42,7 +42,7 @@ public class BrushBoundBaseBlock extends BaseBlock implements BrushHolder { } public BrushBoundBaseBlock(Player player, LocalSession session, ItemStack item) { - super(item.getTypeId(), item.getType().getMaxDurability() != 0 ? 0 : Math.max(0, item.getDurability()), getNBT(item)); + super(item.getTypeId(), item.getType().getMaxDurability() != 0 || item.getDurability() > 15 ? 0 : Math.max(0, item.getDurability()), getNBT(item)); this.item = item; this.tool = brushCache.get(getKey(item)); this.player = player; diff --git a/bukkit/src/main/java/com/boydti/fawe/bukkit/util/cui/CUIListener.java b/bukkit/src/main/java/com/boydti/fawe/bukkit/util/cui/CUIListener.java new file mode 100644 index 00000000..2e93a4b1 --- /dev/null +++ b/bukkit/src/main/java/com/boydti/fawe/bukkit/util/cui/CUIListener.java @@ -0,0 +1,31 @@ +package com.boydti.fawe.bukkit.util.cui; + +import com.boydti.fawe.object.FawePlayer; +import com.boydti.fawe.util.cui.CUI; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerMoveEvent; +import org.bukkit.plugin.Plugin; + +public class CUIListener implements Listener { + + public CUIListener(Plugin plugin) { + Bukkit.getPluginManager().registerEvents(this, plugin); + } + + @EventHandler + public void onPlayerMove(PlayerMoveEvent event) { + Location from = event.getFrom(); + Location to = event.getTo(); + if ((int) from.getX() != (int) to.getX() || (int) from.getZ() != (int) to.getZ()) { + FawePlayer player = FawePlayer.wrap(event.getPlayer()); + CUI cui = player.getMeta("CUI"); + if (cui instanceof StructureCUI) { + StructureCUI sCui = (StructureCUI) cui; + } + } + } + +} diff --git a/bukkit/src/main/java/com/boydti/fawe/bukkit/util/cui/StructureCUI.java b/bukkit/src/main/java/com/boydti/fawe/bukkit/util/cui/StructureCUI.java new file mode 100644 index 00000000..a4e783fc --- /dev/null +++ b/bukkit/src/main/java/com/boydti/fawe/bukkit/util/cui/StructureCUI.java @@ -0,0 +1,51 @@ +package com.boydti.fawe.bukkit.util.cui; + +import com.boydti.fawe.object.FawePlayer; +import com.boydti.fawe.util.cui.CUI; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.Vector2D; +import com.sk89q.worldedit.internal.cui.CUIEvent; +import com.sk89q.worldedit.internal.cui.SelectionPointEvent; +import com.sk89q.worldedit.internal.cui.SelectionShapeEvent; +import java.util.HashMap; +import java.util.Map; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.entity.Player; + +public class StructureCUI extends CUI { + private boolean cuboid; + private Map chunkMap = new HashMap<>(); + + public StructureCUI(FawePlayer player) { + super(player); + } + + @Override + public void dispatchCUIEvent(CUIEvent event) { + if (event instanceof SelectionShapeEvent) { + clear(); + this.cuboid = event.getParameters()[0].equalsIgnoreCase("cuboid"); + } else if (cuboid && event instanceof SelectionPointEvent) { + + } + } + + public void draw(Vector pos1, Vector pos2) { + Player player = this.getPlayer().parent; + Location position = player.getLocation(); + + int view; + if (Bukkit.getVersion().contains("paper")) { + view = player.getViewDistance(); + } else { + view = Bukkit.getViewDistance(); + } + + } + + public void clear() { + + } +} diff --git a/bukkit/src/main/java/com/boydti/fawe/bukkit/util/image/BukkitImageListener.java b/bukkit/src/main/java/com/boydti/fawe/bukkit/util/image/BukkitImageListener.java new file mode 100644 index 00000000..6050ed91 --- /dev/null +++ b/bukkit/src/main/java/com/boydti/fawe/bukkit/util/image/BukkitImageListener.java @@ -0,0 +1,259 @@ +package com.boydti.fawe.bukkit.util.image; + +import com.boydti.fawe.command.CFICommands; +import com.boydti.fawe.jnbt.anvil.HeightMapMCAGenerator; +import com.boydti.fawe.object.FawePlayer; +import com.boydti.fawe.object.brush.BrushSettings; +import com.boydti.fawe.object.extent.FastWorldEditExtent; +import com.boydti.fawe.util.EditSessionBuilder; +import com.boydti.fawe.util.ExtentTraverser; +import com.boydti.fawe.util.TaskManager; +import com.boydti.fawe.util.image.ImageViewer; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.LocalSession; +import com.sk89q.worldedit.MaxChangedBlocksException; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.command.tool.BrushTool; +import com.sk89q.worldedit.command.tool.InvalidToolBindException; +import com.sk89q.worldedit.command.tool.brush.Brush; +import java.util.Collection; +import java.util.List; +import java.util.Set; +import java.util.UUID; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.Rotation; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.entity.Entity; +import org.bukkit.entity.ItemFrame; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.Event; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.block.Action; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.hanging.HangingBreakByEntityEvent; +import org.bukkit.event.player.PlayerEvent; +import org.bukkit.event.player.PlayerInteractEntityEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.plugin.Plugin; + +public class BukkitImageListener implements Listener { + private Location mutable = new Location(Bukkit.getWorlds().get(0), 0, 0, 0); + + public BukkitImageListener(Plugin plugin) { + Bukkit.getPluginManager().registerEvents(this, plugin); + } + + @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) + public void onHangingBreakByEntity(HangingBreakByEntityEvent event) { + if(!(event.getRemover() instanceof Player)) return; + handleInteract(event, (Player) event.getRemover(), event.getEntity(), false); + } + + @EventHandler(priority = EventPriority.LOWEST) + public void onEntityDamageByEntity(EntityDamageByEntityEvent event) { + if(!(event.getDamager() instanceof Player)) return; + handleInteract(event, (Player) event.getDamager(), event.getEntity(), false); + } + + @EventHandler(priority = EventPriority.LOWEST) + public void onPlayerInteract(PlayerInteractEvent event) { + if (event.useItemInHand() == Event.Result.DENY) return; + + Player player = event.getPlayer(); + FawePlayer fp = FawePlayer.wrap(player); + if (fp.getMeta("CFISettings") == null) return; + try { + if (event.getHand() == EquipmentSlot.OFF_HAND) return; + } catch (NoSuchFieldError | NoSuchMethodError ignored) {} + + List target = player.getLastTwoTargetBlocks((Set) null, 100); + if (target.isEmpty()) return; + + Block targetBlock = target.get(0); + World world = player.getWorld(); + mutable.setWorld(world); + mutable.setX(targetBlock.getX() + 0.5); + mutable.setY(targetBlock.getY() + 0.5); + mutable.setZ(targetBlock.getZ() + 0.5); + Collection entities = world.getNearbyEntities(mutable, 0.46875, 0, 0.46875); + + if (!entities.isEmpty()) { + Action action = event.getAction(); + boolean primary = action == Action.RIGHT_CLICK_AIR || action == Action.RIGHT_CLICK_BLOCK; + + double minDist = Integer.MAX_VALUE; + ItemFrame minItemFrame = null; + + for (Entity entity : entities) { + if (entity instanceof ItemFrame) { + ItemFrame itemFrame = (ItemFrame) entity; + Location loc = itemFrame.getLocation(); + double dx = loc.getX() - mutable.getX(); + double dy = loc.getY() - mutable.getY(); + double dz = loc.getZ() - mutable.getZ(); + double dist = dx * dx + dy * dy + dz * dz; + if (dist < minDist) { + minItemFrame = itemFrame; + minDist = dist; + } + } + } + if (minItemFrame != null) { + handleInteract(event, minItemFrame, primary); + if (event.isCancelled()) return; + } + } + } + + @EventHandler(priority = EventPriority.LOWEST) + public void onPlayerInteractEntity(PlayerInteractEntityEvent event) { + handleInteract(event, event.getRightClicked(), true); + } + + private BukkitImageViewer get(HeightMapMCAGenerator generator) { + if (generator == null) return null; + + ImageViewer viewer = generator.getImageViewer(); + if (viewer == null || !(viewer instanceof BukkitImageViewer)) return null; + + BukkitImageViewer biv = (BukkitImageViewer) viewer; + return biv; + } + + private void handleInteract(PlayerEvent event, Entity entity, boolean primary) { + handleInteract(event, event.getPlayer(), entity, primary); + } + + private void handleInteract(Event event, Player player, Entity entity, boolean primary) { + if (!(entity instanceof ItemFrame)) return; + ItemFrame itemFrame = (ItemFrame) entity; + + FawePlayer fp = FawePlayer.wrap(player); + CFICommands.CFISettings settings = fp.getMeta("CFISettings"); + HeightMapMCAGenerator generator = settings == null ? null : settings.getGenerator(); + BukkitImageViewer viewer = get(generator); + if (viewer == null) return; + + if (itemFrame.getRotation() != Rotation.NONE) { + itemFrame.setRotation(Rotation.NONE); + } + + LocalSession session = fp.getSession(); + BrushTool tool; + try { + tool = session.getBrushTool(fp.getPlayer(), false); + } catch (InvalidToolBindException e) { return; } + if (tool == null) return; + BrushSettings context = primary ? tool.getPrimary() : tool.getSecondary(); + Brush brush = context.getBrush(); + + ItemFrame[][] frames = viewer.getItemFrames(); + if (frames == null || brush == null) { + viewer.selectFrame(itemFrame); + TaskManager.IMP.laterAsync(new Runnable() { + @Override + public void run() { + viewer.view(generator.draw()); + } + }, 1); + return; + } + + if (brush == null) return; + tool.setContext(context); + + if (event instanceof Cancellable) { + ((Cancellable) event).setCancelled(true); + } + + Location target = itemFrame.getLocation(); + Location source = player.getLocation(); + + double yawRad = Math.toRadians(source.getYaw() + 90d); + double pitchRad = Math.toRadians(-source.getPitch()); + + double a = Math.cos(pitchRad); + double xRat = Math.cos(yawRad) * a; + double zRat = Math.sin(yawRad) * a; + + BlockFace facing = itemFrame.getFacing(); + double thickness = 1/32d + 1/128d; + double modX = facing.getModX(); + double modZ = facing.getModZ(); + double dx = source.getX() - target.getX() - modX * thickness; + double dy = source.getY() + player.getEyeHeight() - target.getY(); + double dz = source.getZ() - target.getZ() - modZ * thickness; + + double offset; + double localX; + if (modX != 0) { + offset = dx / xRat; + localX = (-modX) * (dz - offset * zRat); + } else { + offset = dz / zRat; + localX = (modZ) * (dx - offset * xRat); + } + double localY = dy - offset * Math.sin(pitchRad); + int localPixelX = (int)((localX + 0.5) * 128); + int localPixelY = (int)((localY + 0.5) * 128); + + UUID uuid = itemFrame.getUniqueId(); + for (int blockX = 0; blockX < frames.length; blockX++) { + for (int blockY = 0; blockY < frames[0].length; blockY++) { + if (uuid.equals(frames[blockX][blockY].getUniqueId())) { + int pixelX = localPixelX + blockX * 128; + int pixelY = (128 * frames[0].length) - (localPixelY + blockY * 128 + 1); + + int width = generator.getWidth(); + int length = generator.getLength(); + int worldX = (int) (pixelX * width / (frames.length * 128d)); + int worldZ = (int) (pixelY * length / (frames[0].length * 128d)); + + if (worldX < 0 || worldX > width || worldZ < 0 || worldZ > length) return; + + Vector wPos = new Vector(worldX, 0, worldZ); + + fp.runAction(new Runnable() { + @Override + public void run() { + viewer.refresh(); + int topY = generator.getNearestSurfaceTerrainBlock(wPos.getBlockX(), wPos.getBlockZ(), 255, 0, 255); + wPos.mutY(topY); + + EditSession es = new EditSessionBuilder(fp.getWorld()).player(fp).combineStages(false).autoQueue(false).blockBag(null).limitUnlimited().build(); + ExtentTraverser last = new ExtentTraverser(es.getExtent()).last(); + if (last.get() instanceof FastWorldEditExtent) last = last.previous(); + last.setNext(generator); + try { + brush.build(es, wPos, context.getMaterial(), context.getSize()); + } catch (MaxChangedBlocksException e) { + e.printStackTrace(); + } + es.flushQueue(); + viewer.view(generator.draw()); + } + }, true, true); + + + + + return; + } + } + } + + + + + + + } +} \ No newline at end of file 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 new file mode 100644 index 00000000..c42e0ecc --- /dev/null +++ b/bukkit/src/main/java/com/boydti/fawe/bukkit/util/image/BukkitImageViewer.java @@ -0,0 +1,156 @@ +package com.boydti.fawe.bukkit.util.image; + +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 org.bukkit.Bukkit; +import org.bukkit.Location; +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.inventivetalent.mapmanager.MapManagerPlugin; +import org.inventivetalent.mapmanager.controller.MapController; +import org.inventivetalent.mapmanager.controller.MultiMapController; +import org.inventivetalent.mapmanager.manager.MapManager; +import org.inventivetalent.mapmanager.wrapper.MapWrapper; + +public class BukkitImageViewer implements ImageViewer { + private final MapManager mapManager; + private final Player player; + private BufferedImage last; + private ItemFrame[][] frames; + private boolean reverse; + + + public BukkitImageViewer(Player player) { + mapManager = ((MapManagerPlugin) Bukkit.getPluginManager().getPlugin("MapManager")).getMapManager(); + this.player = player; + } + + public void selectFrame(ItemFrame start) { + Location pos1 = start.getLocation().clone(); + Location pos2 = start.getLocation().clone(); + + BlockFace facing = start.getFacing(); + int planeX = facing.getModX() == 0 ? 1 : 0; + int planeY = facing.getModY() == 0 ? 1 : 0; + int planeZ = facing.getModZ() == 0 ? 1 : 0; + + ItemFrame[][] res = find(pos1, pos2, facing); + Location tmp; + while (true) { + if (res != null) { + frames = res; + } + tmp = pos1.clone().subtract(planeX, planeY, planeZ); + if ((res = find(tmp, pos2, facing)) != null) { + pos1 = tmp; + continue; + } + tmp = pos2.clone().add(planeX, planeY, planeZ); + if ((res = find(pos1, tmp, facing)) != null) { + pos2 = tmp; + continue; + } + tmp = pos1.clone().subtract(planeX, 0, planeZ); + if ((res = find(tmp, pos2, facing)) != null) { + pos1 = tmp; + continue; + } + tmp = pos2.clone().add(planeX, 0, planeZ); + if ((res = find(pos1, tmp, facing)) != null) { + pos2 = tmp; + continue; + } + tmp = pos1.clone().subtract(0, 1, 0); + if ((res = find(tmp, pos2, facing)) != null) { + pos1 = tmp; + continue; + } + tmp = pos2.clone().add(0, 1, 0); + if ((res = find(pos1, tmp, facing)) != null) { + pos2 = tmp; + continue; + } + break; + } + } + + public ItemFrame[][] getItemFrames() { + return frames; + } + + private ItemFrame[][] find(Location pos1, Location pos2, BlockFace facing) { + try { + Location distance = pos2.clone().subtract(pos1).add(1, 1, 1); + int width = Math.max(distance.getBlockX(), distance.getBlockZ()); + ItemFrame[][] frames = new ItemFrame[width][distance.getBlockY()]; + + World world = pos1.getWorld(); + + this.reverse = (facing == BlockFace.NORTH || facing == BlockFace.EAST); + int v = 0; + for (double y = pos1.getY(); y <= pos2.getY(); y++, v++) { + int h = 0; + for (double z = pos1.getZ(); z <= pos2.getZ(); z++) { + for (double x = pos1.getX(); x <= pos2.getX(); x++, h++) { + Location pos = new Location(world, x, y, z); + Collection entities = world.getNearbyEntities(pos, 0.1, 0.1, 0.1); + boolean contains = false; + for (Entity ent : entities) { + if (ent instanceof ItemFrame && ((ItemFrame) ent).getFacing() == facing) { + ItemFrame itemFrame = (ItemFrame) ent; + itemFrame.setRotation(Rotation.NONE); + contains = true; + frames[reverse ? width - 1 - h : h][v] = (ItemFrame) ent; + break; + } + } + if (!contains) return null; + } + } + } + return frames; + } catch (Throwable e) { + e.printStackTrace(); + } + return null; + } + + @Override + public void view(BufferedImage image) { + last = image; + if (this.frames != null) { + int width = frames.length; + int height = frames[0].length; + BufferedImage scaled = ImageUtil.getScaledInstance(image, 128 * width, 128 * height, RenderingHints.VALUE_INTERPOLATION_BILINEAR, false); + MapWrapper mapWrapper = mapManager.wrapMultiImage(scaled, width, height); + MultiMapController controller = (MultiMapController) mapWrapper.getController(); + controller.addViewer(player); + controller.sendContent(player); + controller.showInFrames(player, frames, true); + } else { + BufferedImage scaled = ImageUtil.getScaledInstance(image, 128, 128, RenderingHints.VALUE_INTERPOLATION_BILINEAR, false); + MapWrapper mapWrapper = mapManager.wrapImage(scaled); + MapController controller = mapWrapper.getController(); + controller.addViewer(player); + controller.sendContent(player); + controller.showInHand(player, true); + } + } + + public void refresh() { + if (last != null) view(last); + } + + @Override + public void close() throws IOException { + last = null; + } +} \ No newline at end of file diff --git a/bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncBlockState.java b/bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncBlockState.java index a147da51..08c53a5b 100644 --- a/bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncBlockState.java +++ b/bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncBlockState.java @@ -131,6 +131,14 @@ public class AsyncBlockState implements BlockState { return result; } + public CompoundTag getNbtData() { + return nbt; + } + + public void setNbtData(CompoundTag nbt) { + this.nbt = nbt; + } + @Override public byte getRawData() { return data; diff --git a/bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/state/AsyncSign.java b/bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/state/AsyncSign.java new file mode 100644 index 00000000..cda636a8 --- /dev/null +++ b/bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/state/AsyncSign.java @@ -0,0 +1,43 @@ +package com.boydti.fawe.bukkit.wrapper.state; + +import com.boydti.fawe.bukkit.wrapper.AsyncBlock; +import com.boydti.fawe.bukkit.wrapper.AsyncBlockState; +import com.boydti.fawe.util.ReflectionUtils; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.jnbt.StringTag; +import com.sk89q.jnbt.Tag; +import java.util.Map; +import org.bukkit.block.Sign; + +public class AsyncSign extends AsyncBlockState implements Sign { + public AsyncSign(AsyncBlock block) { + super(block); + } + + @Override + public String[] getLines() { + CompoundTag nbt = getNbtData(); + String[] data = new String[4]; + if (nbt != null) { + for (int i = 1; i <= 4; i++) { + data[i - 1] = nbt.getString("Text" + i); + } + } + return data; + } + + @Override + public String getLine(int index) throws IndexOutOfBoundsException { + CompoundTag nbt = getNbtData(); + return nbt == null ? null : nbt.getString("Text" + (index + 1)); + } + + @Override + public void setLine(int index, String line) throws IndexOutOfBoundsException { + CompoundTag nbt = getNbtData(); + if (nbt != null) { + Map map = ReflectionUtils.getMap(nbt.getValue()); + map.put("Text" + (index + 1), new StringTag(line)); + } + } +} diff --git a/core/build.gradle b/core/build.gradle index 50f6e5f2..f5a4d704 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -15,6 +15,7 @@ dependencies { compile(group: 'com.sk89q.worldedit', name: 'worldedit-core', version:'6.1.3-SNAPSHOT') { exclude(module: 'bukkit-classloader-check') } + compile 'org.inventivetalent:mapmanager:1.4.0-SNAPSHOT' } processResources { diff --git a/core/src/main/java/com/boydti/fawe/Fawe.java b/core/src/main/java/com/boydti/fawe/Fawe.java index d6f71189..05a78e5b 100644 --- a/core/src/main/java/com/boydti/fawe/Fawe.java +++ b/core/src/main/java/com/boydti/fawe/Fawe.java @@ -19,6 +19,7 @@ import com.boydti.fawe.util.Updater; import com.boydti.fawe.util.WEManager; import com.boydti.fawe.util.chat.ChatManager; import com.boydti.fawe.util.chat.PlainChatManager; +import com.boydti.fawe.util.cui.CUI; import com.boydti.fawe.util.metrics.BStats; import com.sk89q.jnbt.NBTInputStream; import com.sk89q.jnbt.NBTOutputStream; @@ -68,6 +69,7 @@ import com.sk89q.worldedit.extension.factory.DefaultMaskParser; import com.sk89q.worldedit.extension.factory.DefaultTransformParser; import com.sk89q.worldedit.extension.factory.HashTagPatternParser; import com.sk89q.worldedit.extension.platform.AbstractPlayerActor; +import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extension.platform.CommandManager; import com.sk89q.worldedit.extension.platform.PlatformManager; import com.sk89q.worldedit.extension.platform.PlayerProxy; @@ -355,6 +357,25 @@ public class Fawe { return false; } + public CUI getCUI(Actor actor) { + FawePlayer fp = FawePlayer.wrap(actor); + CUI cui = fp.getMeta("CUI"); + if (cui == null) { + cui = Fawe.imp().getCUI(fp); + if (cui != null) { + synchronized (fp) { + CUI tmp = fp.getMeta("CUI"); + if (tmp == null) { + fp.setMeta("CUI", cui); + } else { + cui = tmp; + } + } + } + } + return cui; + } + public ChatManager getChatManager() { return chatManager; } diff --git a/core/src/main/java/com/boydti/fawe/FaweCache.java b/core/src/main/java/com/boydti/fawe/FaweCache.java index 26f959d5..ad81788a 100644 --- a/core/src/main/java/com/boydti/fawe/FaweCache.java +++ b/core/src/main/java/com/boydti/fawe/FaweCache.java @@ -812,6 +812,8 @@ public class FaweCache { return asTag((int[]) value); } else if (value instanceof Tag) { return (Tag) value; + } else if (value instanceof Boolean) { + return asTag((byte) ((boolean) value ? 1 : 0)); } else if (value == null) { System.out.println("Invalid nbt: " + value); return null; diff --git a/core/src/main/java/com/boydti/fawe/IFawe.java b/core/src/main/java/com/boydti/fawe/IFawe.java index f3eff835..a2e7600c 100644 --- a/core/src/main/java/com/boydti/fawe/IFawe.java +++ b/core/src/main/java/com/boydti/fawe/IFawe.java @@ -5,6 +5,8 @@ import com.boydti.fawe.object.FawePlayer; import com.boydti.fawe.object.FaweQueue; import com.boydti.fawe.regions.FaweMaskManager; import com.boydti.fawe.util.TaskManager; +import com.boydti.fawe.util.cui.CUI; +import com.boydti.fawe.util.image.ImageViewer; import com.sk89q.worldedit.world.World; import java.io.File; import java.util.Collection; @@ -33,6 +35,9 @@ public interface IFawe { public void startMetrics(); + default CUI getCUI(FawePlayer player) { return null; } + + default ImageViewer getImageViewer(FawePlayer player) { return null; } default int getPlayerCount() { return Fawe.get().getCachedPlayers().size(); @@ -42,7 +47,6 @@ public interface IFawe { public boolean isOnlineMode(); - public String getPlatform(); public UUID getUUID(String name); diff --git a/core/src/main/java/com/boydti/fawe/command/CFICommand.java b/core/src/main/java/com/boydti/fawe/command/CFICommand.java new file mode 100644 index 00000000..de54b5f1 --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/command/CFICommand.java @@ -0,0 +1,83 @@ +package com.boydti.fawe.command; + +import com.boydti.fawe.config.Commands; +import com.boydti.fawe.object.FawePlayer; +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.WorldEdit; +import com.sk89q.worldedit.command.MethodCommands; +import com.sk89q.worldedit.util.command.SimpleDispatcher; +import com.sk89q.worldedit.util.command.parametric.ParametricBuilder; + +public class CFICommand extends MethodCommands { + + private final CFICommands child; + private final SimpleDispatcher dispatcher; + + public CFICommand(WorldEdit worldEdit, ParametricBuilder builder) { + super(worldEdit); + this.child = new CFICommands(worldEdit); + this.dispatcher = new SimpleDispatcher(); + builder.registerMethodsAsCommands(dispatcher, child); + } + + @Command( + aliases = {"cfi", "createfromimage"}, + usage = "", + min = 0, + max = -1, + anyFlags = true, + desc = "Start CreateFromImage" + ) + @CommandPermissions("worldedit.anvil.cfi") + public void cfi(FawePlayer fp, CommandContext context) throws CommandException { + CFICommands.CFISettings settings = child.getSettings(fp); + if (!settings.hasGenerator()) { + switch (context.argsLength()) { + case 0: { + String hmCmd = child.alias() + " "; + if (settings.image == null) { + hmCmd += "image"; + } else { + hmCmd = Commands.getAlias(CFICommands.class, "heightmap") + " " + settings.imageArg; + } + child.msg("What do you want to use as the base?").newline() + .text("&7[&aHeightMap&7]").cmdTip(hmCmd).text(" - A heightmap like ").text("&7[&athis&7]").linkTip("http://i.imgur.com/qCd30MR.jpg") + .newline() + .text("&7[&aEmpty&7]").cmdTip(child.alias() + " empty").text("- An empty map of a specific size") + .send(fp); + break; + } + default: { + String remaining = context.getJoinedStrings(0); + if (!dispatcher.contains(context.getString(0))) { + switch (context.argsLength()) { + case 1: { + String cmd = Commands.getAlias(CFICommands.class, "heightmap") + " " + context.getJoinedStrings(0); + dispatcher.call(cmd, context.getLocals(), new String[0]); + return; + } + case 2: { + String cmd = Commands.getAlias(CFICommands.class, "empty") + " " + context.getJoinedStrings(0); + dispatcher.call(cmd, context.getLocals(), new String[0]); + return; + } + } + } + dispatcher.call(remaining, context.getLocals(), new String[0]); + } + } + } else { + switch (context.argsLength()) { + case 0: + child.mainMenu(fp); + break; + default: + dispatcher.call(context.getJoinedStrings(0), context.getLocals(), new String[0]); + break; + } + } + } +} diff --git a/core/src/main/java/com/boydti/fawe/command/CFICommands.java b/core/src/main/java/com/boydti/fawe/command/CFICommands.java new file mode 100644 index 00000000..4ad99478 --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/command/CFICommands.java @@ -0,0 +1,843 @@ +package com.boydti.fawe.command; + +import com.boydti.fawe.Fawe; +import com.boydti.fawe.FaweCache; +import com.boydti.fawe.config.BBC; +import com.boydti.fawe.config.Commands; +import com.boydti.fawe.jnbt.anvil.HeightMapMCAGenerator; +import com.boydti.fawe.object.FawePlayer; +import com.boydti.fawe.object.RunnableVal; +import com.boydti.fawe.util.CleanTextureUtil; +import com.boydti.fawe.util.FilteredTextureUtil; +import com.boydti.fawe.util.ImgurUtility; +import com.boydti.fawe.util.StringMan; +import com.boydti.fawe.util.TaskManager; +import com.boydti.fawe.util.TextureUtil; +import com.boydti.fawe.util.chat.Message; +import com.boydti.fawe.util.image.ImageUtil; +import com.intellectualcrafters.plot.PS; +import com.intellectualcrafters.plot.commands.Auto; +import com.intellectualcrafters.plot.config.C; +import com.intellectualcrafters.plot.config.Settings; +import com.intellectualcrafters.plot.object.Plot; +import com.intellectualcrafters.plot.object.PlotId; +import com.intellectualcrafters.plot.object.PlotPlayer; +import com.intellectualcrafters.plot.object.worlds.PlotAreaManager; +import com.intellectualcrafters.plot.object.worlds.SinglePlotArea; +import com.intellectualcrafters.plot.object.worlds.SinglePlotAreaManager; +import com.intellectualcrafters.plot.util.MathMan; +import com.sk89q.minecraft.util.commands.Command; +import com.sk89q.minecraft.util.commands.CommandContext; +import com.sk89q.minecraft.util.commands.CommandPermissions; +import com.sk89q.worldedit.EmptyClipboardException; +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.extension.input.InputParseException; +import com.sk89q.worldedit.extension.input.ParserContext; +import com.sk89q.worldedit.extent.clipboard.Clipboard; +import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat; +import com.sk89q.worldedit.function.mask.Mask; +import com.sk89q.worldedit.function.pattern.Pattern; +import com.sk89q.worldedit.session.ClipboardHolder; +import com.sk89q.worldedit.session.request.Request; +import com.sk89q.worldedit.util.command.binding.Switch; +import com.sk89q.worldedit.util.command.parametric.Optional; +import com.sk89q.worldedit.util.command.parametric.ParameterException; +import com.sk89q.worldedit.world.World; +import com.sk89q.worldedit.world.biome.BaseBiome; +import com.sk89q.worldedit.world.registry.BundledBlockData; +import com.sk89q.worldedit.world.registry.WorldData; +import java.awt.image.BufferedImage; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.net.URL; +import java.util.HashSet; +import java.util.Set; +import javax.imageio.ImageIO; + +@Command(aliases = {"/cfi"}, desc = "Create a world from images: [More Info](https://git.io/v5iDy)") +public class CFICommands extends MethodCommands { + + /** + * Create a new instance. + * + * @param worldEdit reference to WorldEdit + */ + public CFICommands(WorldEdit worldEdit) { + super(worldEdit); + } + + @Command( + aliases = {"heightmap"}, + usage = "", + desc = "Start CFI with a height map as a base" + ) + @CommandPermissions("worldedit.anvil.cfi") + public void heightmap(FawePlayer fp, BufferedImage image) { + HeightMapMCAGenerator generator = new HeightMapMCAGenerator(image, null); + setup(generator, fp); + } + + @Command( + aliases = {"empty"}, + usage = " ", + desc = "Start CFI with an empty map as a base" + ) + @CommandPermissions("worldedit.anvil.cfi") + public void heightmap(FawePlayer fp, int width, int length) { + HeightMapMCAGenerator generator = new HeightMapMCAGenerator(width, length, null); + setup(generator, fp); + } + + private void setup(HeightMapMCAGenerator generator, FawePlayer fp) { + CFISettings settings = getSettings(fp); + settings.remove().setGenerator(generator).bind(); + generator.setImageViewer(Fawe.imp().getImageViewer(fp)); + mainMenu(fp); + } + + @Command( + aliases = {"brush"}, + usage = "", + desc = "Info about using brushes with CFI" + ) + @CommandPermissions("worldedit.anvil.cfi") + public void brush(FawePlayer fp) throws ParameterException{ + CFISettings settings = assertSettings(fp); + Message msg; + if (settings.getGenerator().getImageViewer() != null) { + msg = msg("CFI supports using brushes during creation").newline() + .text(" - Place the map on a wall of item frames").newline() + .text(" - Use any WorldEdit brush on the item frames").newline() + .text(" - Example: ").text("Video").linkTip("https://goo.gl/PK4DMG").newline(); + } else { + msg = msg("This is not supported with your platform/version").newline(); + } + msg.text("&8> &7[&aNext&7]").cmdTip(alias()).send(fp); + } + + @Command( + aliases = {"cancel", "exit"}, + usage = "", + desc = "Cancel creation" + ) + @CommandPermissions("worldedit.anvil.cfi") + public void cancel(FawePlayer fp) throws ParameterException, IOException { + getSettings(fp).remove(); + fp.sendMessage(BBC.getPrefix() + "Cancelled!"); + } + + @Command( + aliases = {"done", "create"}, + usage = "", + desc = "Create the world" + ) + @CommandPermissions("worldedit.anvil.cfi") + public void done(FawePlayer fp) throws ParameterException, IOException { + CFISettings settings = assertSettings(fp); + + PlotAreaManager manager = PS.get().getPlotAreaManager(); + if (manager instanceof SinglePlotAreaManager) { + SinglePlotAreaManager sManager = (SinglePlotAreaManager) manager; + SinglePlotArea area = sManager.getArea(); + PlotPlayer player = PlotPlayer.wrap(fp.parent); + + fp.sendMessage(BBC.getPrefix() + "Claiming world"); + Plot plot = TaskManager.IMP.sync(new RunnableVal() { + @Override + public void run(Plot o) { + int currentPlots = Settings.Limit.GLOBAL ? player.getPlotCount() : player.getPlotCount(area.worldname); + int diff = player.getAllowedPlots() - currentPlots; + if (diff < 1) { + C.CANT_CLAIM_MORE_PLOTS_NUM.send(player, -diff); + return; + } + if (area.getMeta("lastPlot") == null) { + area.setMeta("lastPlot", new PlotId(0, 0)); + } + PlotId lastId = (PlotId) area.getMeta("lastPlot"); + while (true) { + lastId = Auto.getNextPlotId(lastId, 1); + if (area.canClaim(player, lastId, lastId)) { + break; + } + } + area.setMeta("lastPlot", lastId); + this.value = area.getPlot(lastId); + this.value.setOwner(player.getUUID()); + } + }); + if (plot == null) return; + + File folder = new File(PS.imp().getWorldContainer(), plot.getWorldName() + File.separator + "region"); + HeightMapMCAGenerator generator = settings.getGenerator(); + generator.setFolder(folder); + + fp.sendMessage(BBC.getPrefix() + "Generating"); + generator.generate(); + settings.remove(); + fp.sendMessage(BBC.getPrefix() + "Done!"); + TaskManager.IMP.sync(new RunnableVal() { + @Override + public void run(Object value) { + plot.teleportPlayer(player); + } + }); + } else { + fp.sendMessage(BBC.getPrefix() + "Must have the `worlds` component enabled in the PlotSquared config.yml"); + } + } + + @Command( + aliases = {"column", "setcolumn"}, + usage = " [url|mask]", + desc = "Set the floor and main block" + ) + @CommandPermissions("worldedit.anvil.cfi") + public void column(FawePlayer fp, Pattern pattern, @Optional BufferedImage image, @Optional Mask mask, @Switch('w') boolean disableWhiteOnly) throws ParameterException{ + HeightMapMCAGenerator gen = assertSettings(fp).getGenerator(); + if (image != null) gen.setColumn(image, pattern, !disableWhiteOnly); + else if (mask != null) gen.setColumn(mask, pattern); + else gen.setColumn(pattern); + fp.sendMessage("Set column!"); + } + + @Command( + aliases = {"floor", "setfloor"}, + usage = " [url|mask]", + desc = "Set the floor (default: grass)" + ) + @CommandPermissions("worldedit.anvil.cfi") + public void floor(FawePlayer fp, Pattern pattern, @Optional BufferedImage image, @Optional Mask mask, @Switch('w') boolean disableWhiteOnly) throws ParameterException{ + HeightMapMCAGenerator gen = assertSettings(fp).getGenerator(); + if (image != null) gen.setFloor(image, pattern, !disableWhiteOnly); + else if (mask != null) gen.setFloor(mask, pattern); + else gen.setFloor(pattern); + fp.sendMessage("Set floor!"); + } + + @Command( + aliases = {"main", "setmain"}, + usage = " [url|mask]", + desc = "Set the main block (default: stone)" + ) + @CommandPermissions("worldedit.anvil.cfi") + public void main(FawePlayer fp, Pattern pattern, @Optional BufferedImage image, @Optional Mask mask, @Switch('w') boolean disableWhiteOnly) throws ParameterException{ + HeightMapMCAGenerator gen = assertSettings(fp).getGenerator(); + if (image != null) gen.setMain(image, pattern, !disableWhiteOnly); + else if (mask != null) gen.setMain(mask, pattern); + else gen.setMain(pattern); + fp.sendMessage("Set main!"); + } + + @Command( + aliases = {"overlay", "setoverlay"}, + usage = " [url|mask]", + desc = "Set the overlay block", + help = "Change the block directly above the floor (default: air)\n" + + "e.g. Tallgrass" + ) + @CommandPermissions("worldedit.anvil.cfi") + public void overlay(FawePlayer fp, Pattern pattern, @Optional BufferedImage image, @Optional Mask mask, @Switch('w') boolean disableWhiteOnly) throws ParameterException{ + HeightMapMCAGenerator gen = assertSettings(fp).getGenerator(); + if (image != null) gen.setOverlay(image, pattern, !disableWhiteOnly); + else if (mask != null) gen.setOverlay(mask, pattern); + else gen.setOverlay(pattern); + fp.sendMessage("Set overlay!"); + } + + @Command( + aliases = {"smooth"}, + usage = " [image|mask]", + desc = "Smooth the terrain", + help = "Smooth terrain within an image-mask, or worldedit mask\n" + + " - You can use !0 as the mask to smooth everything\n" + + " - This supports smoothing snow layers (set the floor to 78:7)\n" + + " - A good value for radius and iterations would be 1 8." + ) + @CommandPermissions("worldedit.anvil.cfi") + public void smooth(FawePlayer fp, int radius, int iterations, @Optional BufferedImage image, @Optional Mask mask, @Switch('w') boolean disableWhiteOnly) throws ParameterException{ + HeightMapMCAGenerator gen = assertSettings(fp).getGenerator(); + if (image != null) gen.smooth(image, !disableWhiteOnly, radius, iterations); + else gen.smooth(mask, radius, iterations); + fp.sendMessage("Performed smooth!"); + } + + @Command( + aliases = {"snow"}, + usage = "[image|mask]", + desc = "Create some snow" + ) + @CommandPermissions("worldedit.anvil.cfi") + public void snow(FawePlayer fp, @Optional BufferedImage image, @Optional Mask mask, @Switch('w') boolean disableWhiteOnly) throws ParameterException{ + HeightMapMCAGenerator gen = assertSettings(fp).getGenerator(); + floor(fp, FaweCache.getBlock(78, 7), image, mask, disableWhiteOnly); + smooth(fp, 1, 8, image, mask, disableWhiteOnly); + msg("Added snow!").newline().text("&8> &7[&aNext&7]").cmdTip(alias()).send(fp); + } + + @Command( + aliases = {"biomepriority", "palettebiomepriority", "setpalettebiomepriority"}, + usage = "[percent=50]", + desc = "Set the biome priority", + help = "Increase or decrease biome priority when using blockBiomeColor.\n" + + "A value of 50 is the default\n" + + "Above 50 will prefer to color with biomes\n" + + "Below 50 will prefer to color with blocks" + ) + @CommandPermissions("worldedit.anvil.cfi") + public void biomepriority(FawePlayer fp, int value) throws ParameterException{ + assertSettings(fp).getGenerator().setBiomePriority(value); + coloring(fp); + } + + @Command( + aliases = {"paletteblocks", "colorpaletterblocks", "setcolorpaletteblocks"}, + usage = "", + desc = "Set the blocks used for coloring", + help = "Allow only specific blocks to be used for coloring\n" + + "`blocks` is a list of blocks e.g. stone,bedrock,wool\n" + + "`#clipboard` will only use the blocks present in your clipboard." + ) + @CommandPermissions("worldedit.anvil.cfi") + public void paletteblocks(FawePlayer fp, @Optional String arg) throws ParameterException, EmptyClipboardException, InputParseException, FileNotFoundException { + if (arg == null) { + msg("What blocks do you want to color with?").newline() + .text("&7[&aAll&7]").cmdTip(alias() + " PaletteBlocks *").text(" - All available blocks") + .newline() + .text("&7[&aClipboard&7]").cmdTip(alias() + " PaletteBlocks #clipboard").text(" - The blocks in your clipboard") + .newline() + .text("&7[&aList&7]").suggestTip(alias() + " PaletteBlocks stone,gravel").text(" - A comma separated list of blocks") + .newline() + .text("&7[&aComplexity&7]").cmdTip(alias() + " Complexity").text(" - Block textures within a complexity range") + .newline() + .text("&8< &7[&aBack&7]").cmdTip(alias() + " " + Commands.getAlias(CFICommands.class, "coloring")) + .send(fp); + return; + } + HeightMapMCAGenerator generator = assertSettings(fp).getGenerator(); + ParserContext context = new ParserContext(); + context.setActor(fp.getPlayer()); + context.setWorld(fp.getWorld()); + context.setSession(fp.getSession()); + context.setExtent(generator); + Request.request().setExtent(generator); + + Set blocks; + switch (arg.toLowerCase()) { + case "*": { + generator.setTextureUtil(Fawe.get().getTextureUtil()); + return; + } + case "#clipboard": { + ClipboardHolder holder = fp.getSession().getClipboard(); + Clipboard clipboard = holder.getClipboard(); + boolean[] ids = new boolean[Character.MAX_VALUE + 1]; + for (Vector pt : clipboard.getRegion()) { + ids[clipboard.getBlock(pt).getCombined()] = true; + } + blocks = new HashSet<>(); + for (int combined = 0; combined < ids.length; combined++) { + if (ids[combined]) blocks.add(FaweCache.CACHE_BLOCK[combined]); + } + break; + } + default: { + blocks = worldEdit.getBlockFactory().parseFromListInput(arg, context); + break; + } + } + generator.setTextureUtil(new FilteredTextureUtil(Fawe.get().getTextureUtil(), blocks)); + coloring(fp); + } + + @Command( + aliases = {"randomization", "paletterandomization"}, + usage = "", + desc = "Set whether randomization is enabled", + help = "This is enabled by default, randomization will add some random variation in the blocks used to closer match the provided image.\n" + + "If disabled, the closest block to the color will always be used.\n" + + "Randomization will allow mixing biomes when coloring with biomes" + ) + @CommandPermissions("worldedit.anvil.cfi") + public void randomization(FawePlayer fp, boolean enabled) throws ParameterException { + assertSettings(fp).getGenerator().setTextureRandomVariation(enabled); + coloring(fp); + } + + @Command( + aliases = {"complexity", "palettecomplexity"}, + usage = " ", + desc = "Set the complexity for coloring", + help = "Set the complexity for coloring\n" + + "Filter out blocks to use based on their complexity, which is a measurement of how much color variation there is in the texture for that block.\n" + + "Glazed terracotta is complex, and not very pleasant for terrain, whereas stone and wool are simpler textures.\n" + + "Using 0 73 for the min/max would use the simplest 73% of blocks for coloring, and is a reasonable value." + ) + @CommandPermissions("worldedit.anvil.cfi") + public void complexity(FawePlayer fp, int min, int max) throws ParameterException, FileNotFoundException { + HeightMapMCAGenerator gen = assertSettings(fp).getGenerator(); + if (min == 0 && max == 100) gen.setTextureUtil(Fawe.get().getTextureUtil()); + else gen.setTextureUtil(new CleanTextureUtil(Fawe.get().getTextureUtil(), min, max)); + coloring(fp); + } + + @Command( + aliases = {"schem", "schematic", "schems", "schematics", "addschems"}, + usage = "[url] ", + desc = "Populate schematics", + help = "Populate a schematic on the terrain\n" + + " - Change the mask (e.g. angle mask) to only place the schematic in specific locations.\n" + + " - The rarity is a value between 0 and 100.\n" + + " - The distance is the spacing between each schematic" + ) + @CommandPermissions("worldedit.anvil.cfi") + public void schem(FawePlayer fp, @Optional BufferedImage imageMask, Mask mask, String schematic, int rarity, int distance, boolean rotate) throws ParameterException, IOException, WorldEditException { + HeightMapMCAGenerator gen = assertSettings(fp).getGenerator(); + + World world = fp.getWorld(); + WorldData wd = world.getWorldData(); + ClipboardHolder[] clipboards = ClipboardFormat.SCHEMATIC.loadAllFromInput(fp.getPlayer(), wd, schematic, true); + if (clipboards == null) { + return; + } + if (imageMask == null) { + gen.addSchems(mask, wd, clipboards, rarity, distance, rotate); + } else { + gen.addSchems(imageMask, mask, wd, clipboards, rarity, distance, rotate); + } + msg("Added schematics!").newline().text("&8> &7[&aNext&7]").cmdTip(alias()).send(fp); + } + + @Command( + aliases = {"biome", "setbiome"}, + usage = " [image|mask]", + desc = "Set the biome", + help = "Set the biome in specific parts of the map.\n" + + " - If an image is used, the biome will have a chance to be set based on how white the pixel is (white #FFF = 100% chance)" + + " - The whiteOnly parameter determines if only white values on the image are set" + + " - If a mask is used, the biome will be set anywhere the mask applies" + ) + @CommandPermissions("worldedit.anvil.cfi") + public void biome(FawePlayer fp, BaseBiome biome, @Optional BufferedImage image, @Optional Mask mask, @Switch('w') boolean disableWhiteOnly) throws ParameterException{ + HeightMapMCAGenerator gen = assertSettings(fp).getGenerator(); + if (image != null) gen.setBiome(image, (byte) biome.getId(), !disableWhiteOnly); + else if (mask != null) gen.setBiome(mask, (byte) biome.getId()); + else gen.setBiome((byte) biome.getId()); + msg("Set biome!").newline().text("&8> &7[&aNext&7]").cmdTip(alias()).send(fp); + } + + @Command( + aliases = {"caves", "addcaves"}, + desc = "Generate vanilla caves" + ) + @CommandPermissions("worldedit.anvil.cfi") + public void caves(FawePlayer fp) throws ParameterException, WorldEditException { + assertSettings(fp).getGenerator().addCaves(); + msg("Added caves!").newline().text("&8> &7[&aNext&7]").cmdTip(alias()).send(fp); + } + + @Command( + aliases = {"ore", "addore"}, + usage = " ", + desc = "Add an ore", + help = "Use a specific pattern and settings to generate ore" + ) + @CommandPermissions("worldedit.anvil.cfi") + public void ore(FawePlayer fp, Mask mask, Pattern pattern, int size, int frequency, int rariry, int minY, int maxY) throws ParameterException, WorldEditException { + assertSettings(fp).getGenerator().addOre(mask, pattern, size, frequency, rariry, minY, maxY); + msg("Added ore!").newline().text("&8> &7[&aNext&7]").cmdTip(alias()).send(fp); + } + + @Command( + aliases = {"ores", "addores"}, + usage = "", + desc = "Generate the vanilla ores" + ) + @CommandPermissions("worldedit.anvil.cfi") + public void ores(FawePlayer fp, Mask mask) throws ParameterException, WorldEditException { + assertSettings(fp).getGenerator().addDefaultOres(mask); + msg("Added ores!").newline().text("&8> &7[&aNext&7]").cmdTip(alias()).send(fp); + } + + @Command( + aliases = {"height", "setheight"}, + usage = "", + desc = "Set the height", + help = "Set the terrain height either based on an image heightmap, or a numeric value." + ) + @CommandPermissions("worldedit.anvil.cfi") + public void height(FawePlayer fp, String arg) throws ParameterException, WorldEditException { + HeightMapMCAGenerator gen = assertSettings(fp).getGenerator(); + if (!MathMan.isInteger(arg)) { + gen.setHeight(ImageUtil.getImage(arg)); + } else { + gen.setHeights(Integer.parseInt(arg)); + } + msg("Set height!").newline().text("&8> &7[&aNext&7]").cmdTip(alias()).send(fp); + } + + @Command( + aliases = {"water", "waterid"}, + usage = "", + desc = "Change the block used for water\n" + + "e.g. Lava" + ) + @CommandPermissions("worldedit.anvil.cfi") + public void waterId(FawePlayer fp, BaseBlock block) throws ParameterException, WorldEditException { + assertSettings(fp).getGenerator().setWaterId(block.getId()); + msg("Set water id!").newline().text("&8> &7[&aNext&7]").cmdTip(alias()).send(fp); + } + + @Command( + aliases = {"waterheight", "sealevel", "setwaterheight"}, + usage = "", + desc = "Set the level water is generated at\n" + + "Set the level water is generated at\n" + + " - By default water is disabled (with a value of 0)" + ) + @CommandPermissions("worldedit.anvil.cfi") + public void height(FawePlayer fp, int height) throws ParameterException, WorldEditException { + assertSettings(fp).getGenerator().setWaterHeight(height); + msg("Set height!").newline().text("&8> &7[&aNext&7]").cmdTip(alias()).send(fp); + } + + @Command( + aliases = {"glass", "glasscolor", "setglasscolor"}, + usage = "", + desc = "Color terrain using glass" + ) + @CommandPermissions("worldedit.anvil.cfi") + public void glass(FawePlayer fp, BufferedImage image, @Optional BufferedImage imageMask, @Optional Mask mask, @Switch('w') boolean disableWhiteOnly) throws ParameterException, WorldEditException { + assertSettings(fp).getGenerator().setColorWithGlass(image); + msg("Set color with glass!").newline().text("&8> &7[&aNext&7]").cmdTip(alias()).send(fp); + } + + @Command( + aliases = {"color", "setcolor", "blockcolor", "blocks"}, + usage = " [imageMask|mask]", + desc = "Set the color with blocks and biomes", + help = "Color the terrain using only blocks\n" + + "Provide an image, or worldedit mask for the 2nd argument to restrict what areas are colored\n" + + "The -w (disableWhiteOnly) will randomly apply depending on the pixel luminance" + ) + @CommandPermissions("worldedit.anvil.cfi") + public void color(FawePlayer fp, BufferedImage image, @Optional BufferedImage imageMask, @Optional Mask mask, @Switch('w') boolean disableWhiteOnly) throws ParameterException, WorldEditException { + HeightMapMCAGenerator gen = assertSettings(fp).getGenerator(); + if (imageMask != null) gen.setColor(image, imageMask, !disableWhiteOnly); + else if (mask != null) gen.setColor(image, mask); + else gen.setColor(image); + msg("Set color with blocks!").newline().text("&8> &7[&aNext&7]").cmdTip(alias()).send(fp); + } + + @Command( + aliases = {"blockbiomecolor", "setblockandbiomecolor", "blockandbiome"}, + usage = " [imageMask|mask]", + desc = "Set the color with blocks and biomes", + help = "Color the terrain using blocks and biomes.\n" + + "Provide an image, or worldedit mask to restrict what areas are colored\n" + + "The -w (disableWhiteOnly) will randomly apply depending on the pixel luminance" + ) + @CommandPermissions("worldedit.anvil.cfi") + public void blockbiome(FawePlayer fp, BufferedImage image, @Optional BufferedImage imageMask, @Optional Mask mask, @Switch('w') boolean disableWhiteOnly) throws ParameterException, WorldEditException { + assertSettings(fp).getGenerator().setBlockAndBiomeColor(image, mask, imageMask, !disableWhiteOnly); + msg("Set color with blocks and biomes!").newline().text("&8> &7[&aNext&7]").cmdTip(alias()).send(fp); + } + + @Command( + aliases = {"biomecolor", "setbiomecolor", "biomes"}, + usage = " [imageMask|mask]", + desc = "Color the terrain using biomes.\n" + + "Note: Biome coloring does not change blocks:\n" + + " - If you changed the block to something other than grass you will not see anything." + ) + @CommandPermissions("worldedit.anvil.cfi") + public void biomecolor(FawePlayer fp, BufferedImage image, @Optional BufferedImage imageMask, @Optional Mask mask, @Switch('w') boolean disableWhiteOnly) throws ParameterException, WorldEditException { + assertSettings(fp).getGenerator().setBiomeColor(image); + msg("Set color with biomes!").newline().text("&8> &7[&aNext&7]").cmdTip(alias()).send(fp); + } + + + @Command( + aliases = {"coloring", "palette"}, + usage = "", + desc = "Color the world using an image" + ) + @CommandPermissions("worldedit.anvil.cfi") + public void coloring(FawePlayer fp) throws ParameterException{ + CFISettings settings = assertSettings(fp); + HeightMapMCAGenerator gen = settings.getGenerator(); + boolean rand = gen.getTextureRandomVariation(); + String mask; + if (settings.imageMask != null) { + mask = "Mask Image"; + } else if (settings.mask != null) { + mask = "WorldEdit Mask"; + } else { + mask = "NONE"; + } + TextureUtil tu = gen.getRawTextureUtil(); + String blocks; + if (tu.getClass() == TextureUtil.class) { + blocks = "All"; + } else if (tu.getClass() == CleanTextureUtil.class) { + CleanTextureUtil clean = (CleanTextureUtil) tu; + blocks = "Complexity(" + clean.getMin() + "," + clean.getMax() + ")"; + } else if (tu.getClass() == FilteredTextureUtil.class) { + blocks = "Selected"; + } else { + blocks = "Undefined"; + } + + Set materials = new HashSet<>(); + char[] blockArray = tu.getValidBlockIds(); + for (char combined : blockArray) { + int id = FaweCache.getId(combined); + BundledBlockData.BlockEntry block = BundledBlockData.getInstance().findById(id); + if (block != null) { + if (block.id.contains(":")) + materials.add(block.id.contains(":") ? block.id.substring(block.id.indexOf(":") + 1) : block.id); + } else materials.add(Integer.toString(id)); + } + String blockList = materials.size() > 100 ? materials.size() + " blocks" : StringMan.join(materials, ','); + + int biomePriority = gen.getBiomePriority(); + + Message msg = msg("Current settings:").newline() + .text("Randomization ").text("&7[&a" + (Boolean.toString(rand).toUpperCase()) + "&7]").cmdTip(alias() + " randomization " + (!rand)) + .newline() + .text("Mask ").text("&7[&a" + mask + "&7]").cmdTip(alias() + " mask") + .newline() + .text("Blocks ").text("&7[&a" + blocks + "&7]").tooltip(blockList).command(alias() + " paletteBlocks") + .newline() + .text("BiomePriority ").text("&7[&a" + biomePriority + "&7]").cmdTip(alias() + " biomepriority") + .newline(); + + if (settings.image != null) { + StringBuilder colorArgs = new StringBuilder(); + colorArgs.append(" " + settings.imageArg); + if (settings.imageMask != null) colorArgs.append(" " + settings.imageMaskArg); + if (settings.mask != null) colorArgs.append(" " + settings.maskArg); + if (!settings.whiteOnly) colorArgs.append(" -w"); + + msg.text("Image: ") + .text("&7[&a" + settings.imageArg + "&7]").cmdTip(alias() + " " + Commands.getAlias(CFICommands.class, "image")) + .newline().newline() + .text("Let's Color: ") + .cmdOptions(alias() + " ", colorArgs.toString(), "Biomes", "Blocks", "BlockAndBiome", "Glass") + .newline(); + } else { + msg.newline().text("You can color a world using an image like ") + .text("&7[&aThis&7]").linkTip("http://i.imgur.com/vJYinIU.jpg").newline() + .text("Please provide an image: ") + .text("&7[&aNone&7]").cmdTip(alias() + " " + Commands.getAlias(Command.class, "image")).newline(); + } + msg.text("&8< &7[&aBack&7]").cmdTip(alias()).send(fp); + } + + @Command( + aliases = {"mask"}, + usage = "", + desc = "Select a mask" + ) + @CommandPermissions("worldedit.anvil.cfi") + public void mask(FawePlayer fp, @Optional BufferedImage imageMask, @Optional Mask mask, @Switch('w') boolean disableWhiteOnly, CommandContext context) throws ParameterException{ + CFISettings settings = assertSettings(fp); + String[] split = getArguments(context).split(" "); + int index = 2; + settings.imageMask = imageMask; + settings.imageMaskArg = imageMask != null ? split[index++] : null; + settings.mask = mask; + settings.maskArg = mask != null ? split[index++] : null; + settings.whiteOnly = disableWhiteOnly; + + StringBuilder cmd = new StringBuilder(alias() + " mask "); + if (!settings.whiteOnly) cmd.append("-w "); + + msg("Current settings:").newline() + .text("Image Mask ").text("&7[&a" + settings.imageMaskArg + "&7]").suggestTip(cmd + "http://") + .newline() + .text("WorldEdit Mask ").text("&7[&a" + settings.maskArg + "&7]").suggestTip(cmd + "") + .send(fp); + } + + @Command( + aliases = {"download"}, + desc = "Download the current image" + ) + @CommandPermissions("worldedit.anvil.cfi") + public void download(FawePlayer fp) throws ParameterException, IOException { + CFISettings settings = assertSettings(fp); + BufferedImage image = settings.getGenerator().draw(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ImageIO.write(image, "jpg", baos ); + byte[] data = baos.toByteArray(); + fp.sendMessage(BBC.getPrefix() + "Please wait..."); + URL url = ImgurUtility.uploadImage(data); + BBC.DOWNLOAD_LINK.send(fp, url); + } + + @Command( + aliases = {"image"}, + usage = "", + desc = "Select an image" + ) + @CommandPermissions("worldedit.anvil.cfi") + public void image(FawePlayer fp, @Optional BufferedImage image, CommandContext context) throws ParameterException{ + CFISettings settings = getSettings(fp).bind(); + String[] split = getArguments(context).split(" "); + int index = 2; + + settings.image = image; + settings.imageArg = image != null ? split[index++] : null; + String maskArg = settings.maskArg == null ? "Click Here" : settings.maskArg; + + StringBuilder cmd = new StringBuilder(alias() + " image "); + Message msg; + if (image == null) { + msg = msg("Please provide an image:").newline() + .text("From a URL: ").text("&7[&aClick Here&7]").suggestTip(cmd + "http://") + .newline() + .text("From a file: ").text("&7[&aClick Here&7]").suggestTip(cmd + "file://"); + } else { + msg = msg("Current image: ") + .text("&7[&a" + settings.imageArg + "&7]").suggestTip(cmd.toString()) + .newline(); + if (settings.hasGenerator()) { + msg.text("&8< &7[&aBack&7]").cmdTip(alias() + " " + Commands.getAlias(CFICommands.class, "coloring")); + } else { + msg.text("&8> &7[&aNext&7]").cmdTip(alias() + " " + Commands.getAlias(CFICommands.class, "heightmap " + settings.imageArg)); + } + } + msg.send(fp); + } + + @Command( + aliases = {"populate"}, + usage = "", + desc = "" + ) + @CommandPermissions("worldedit.anvil.cfi") + public void populate(FawePlayer fp) throws ParameterException{ + CFISettings settings = assertSettings(fp); + + msg("What would you like to populate?").newline() + .cmdOptions(alias() + " ", "", "Ores", "Ore", "Caves", "Schematics", "Snow") + .newline().text("&8< &7[&aBack&7]").cmdTip(alias()) + .send(fp); + } + + @Command( + aliases = {"component", "components"}, + usage = "", + desc = "Components menu" + ) + @CommandPermissions("worldedit.anvil.cfi") + public void component(FawePlayer fp) throws ParameterException{ + CFISettings settings = assertSettings(fp); + msg("What component would you like to change?").newline() + .cmdOptions(alias() + " ", "", "WaterId", "WaterHeight", "Floor", "Main", "Column", "Overlay", "Height", "Smooth") + .newline().text("&8< &7[&aBack&7]").cmdTip(alias()) + .send(fp); + } + + + + private CFISettings assertSettings(FawePlayer fp) throws ParameterException { + CFISettings settings = getSettings(fp); + if (!settings.hasGenerator()) throw new ParameterException("Please use /" + alias()); + return settings; + } + + + protected CFISettings getSettings(FawePlayer fp) { + CFISettings settings = fp.getMeta("CFISettings"); + return settings == null ? new CFISettings(fp) : settings; + } + + public static class CFISettings { + private final FawePlayer fp; + + private HeightMapMCAGenerator generator; + + protected BufferedImage image; + protected String imageArg; + protected Mask mask; + protected BufferedImage imageMask; + protected boolean whiteOnly = true; + protected String maskArg; + protected String imageMaskArg; + + public CFISettings(FawePlayer player) { + this.fp = player; + } + + public boolean hasGenerator() { + return generator != null; + } + + public HeightMapMCAGenerator getGenerator() { + return generator; + } + + public void setMask(Mask mask, String arg) { + this.mask = mask; + this.maskArg = arg; + } + + public void setImage(BufferedImage image, String arg) { + this.image = image; + } + + public void setImageMask(BufferedImage imageMask, String arg) { + this.imageMask = imageMask; + this.imageMaskArg = arg; + } + + public CFISettings setGenerator(HeightMapMCAGenerator generator) { + this.generator = generator; + return this; + } + + public CFISettings bind() { + fp.setMeta("CFISettings", this); + return this; + } + + public CFISettings remove() { + fp.deleteMeta("CFISettings"); + generator = null; + image = null; + imageArg = null; + mask = null; + imageMask = null; + whiteOnly = true; + maskArg = null; + imageMaskArg = null; + return this; + } + } + + protected String alias() { + return Commands.getAlias(CFICommand.class, "/cfi"); + } + + protected Message msg(String text) { + return new Message(BBC.getPrefix()) + .text(text); + } + + protected void mainMenu(FawePlayer fp) { + msg("What do you want to do now?").newline() + .cmdOptions(alias() + " ", "", "Coloring", "Populate", "Component", "Brush") + .newline().text("&3<> &7[&aView&7]").command(alias() + " " + Commands.getAlias(CFICommands.class, "download")).tooltip("View full resolution image") + .newline().text("&4>< &7[&aCancel&7]").cmdTip(alias() + " " + Commands.getAlias(CFICommands.class, "cancel")) + .newline().text("&2>> &7[&aDone&7]").cmdTip(alias() + " " + Commands.getAlias(CFICommands.class, "done")) + .send(fp); + } +} diff --git a/core/src/main/java/com/boydti/fawe/command/FawePrimitiveBinding.java b/core/src/main/java/com/boydti/fawe/command/FawePrimitiveBinding.java index f1388cb0..2f5f0a42 100644 --- a/core/src/main/java/com/boydti/fawe/command/FawePrimitiveBinding.java +++ b/core/src/main/java/com/boydti/fawe/command/FawePrimitiveBinding.java @@ -4,6 +4,7 @@ import com.boydti.fawe.Fawe; import com.boydti.fawe.object.FawePlayer; import com.boydti.fawe.object.extent.NullExtent; import com.boydti.fawe.object.extent.ResettableExtent; +import com.boydti.fawe.util.image.ImageUtil; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.LocalSession; import com.sk89q.worldedit.Vector; @@ -29,6 +30,7 @@ import com.sk89q.worldedit.util.command.parametric.BindingHelper; import com.sk89q.worldedit.util.command.parametric.BindingMatch; import com.sk89q.worldedit.util.command.parametric.ParameterException; import com.sk89q.worldedit.world.World; +import java.awt.image.BufferedImage; import java.lang.annotation.Annotation; import javax.annotation.Nullable; @@ -66,6 +68,13 @@ public class FawePrimitiveBinding extends BindingHelper { } } } + @BindingMatch( + type = {BufferedImage.class}, + behavior = BindingBehavior.CONSUMES + ) + public BufferedImage getImage(ArgumentStack context) throws ParameterException { + return ImageUtil.getImage(context.next()); + } @BindingMatch( type = {Extent.class}, 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 21263258..2114983d 100644 --- a/core/src/main/java/com/boydti/fawe/config/Settings.java +++ b/core/src/main/java/com/boydti/fawe/config/Settings.java @@ -310,6 +310,11 @@ public class Settings extends Config { "Allows brushes to be persistent", }) public boolean PERSISTENT_BRUSHES = false; + @Comment({ + "Allow CFI visualization", + }) + public boolean CFI_VISUALIZATION = false; + } public static class WEB { 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 new file mode 100644 index 00000000..7ddbb472 --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/jnbt/anvil/HeightMapMCADrawer.java @@ -0,0 +1,142 @@ +package com.boydti.fawe.jnbt.anvil; + +import com.boydti.fawe.Fawe; +import com.boydti.fawe.FaweCache; +import com.boydti.fawe.util.MathMan; +import com.boydti.fawe.util.TextureUtil; +import com.sk89q.worldedit.blocks.BlockID; +import java.awt.image.BufferedImage; +import java.awt.image.DataBufferInt; +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.TimeUnit; + +public final class HeightMapMCADrawer { + private final HeightMapMCAGenerator gen; + private final TextureUtil tu; + private final ForkJoinPool pool; + + public HeightMapMCADrawer(HeightMapMCAGenerator generator, TextureUtil textureUtil) { + this.gen = generator; + this.tu = textureUtil; + this.pool = new ForkJoinPool(); + } + + public HeightMapMCADrawer(HeightMapMCAGenerator generator) { + this(generator, Fawe.get().getCachedTextureUtil(false, 0, 100)); + } + + public BufferedImage draw() { + BufferedImage img = new BufferedImage(gen.getWidth(), gen.getLength(), BufferedImage.TYPE_INT_RGB); + final char[] overlay = gen.overlay == null ? gen.floor : gen.overlay; + final char[] floor = gen.floor; + final char[] main = gen.main; + final byte[] heights = gen.heights; + final int waterHeight = gen.waterHeight; + final int width = gen.getWidth(); + final int length = gen.getLength(); + + int[] raw = ((DataBufferInt) img.getRaster().getDataBuffer()).getData(); + + int parallelism = pool.getParallelism(); + int size = (heights.length + parallelism - 1) / parallelism; + for (int i = 0; i < parallelism; i++) { + int start = i * size; + int end = Math.min(heights.length, start + size); + pool.submit((Runnable) () -> { + for (int index = start; index < end; index ++) { + int height = (heights[index] & 0xFF); + char combined; + if ((combined = overlay[index]) == 0) { + height--; + combined = floor[index]; + if (combined == 0) { + height--; + combined = main[index]; + } + } + // draw combined + int color; + switch (combined >> 4) { + case 2: + color = getAverageBiomeColor(gen.biomes, width, index); + break; + case 78: + color = (0xDD << 16) + (0xDD << 8) + (0xDD << 0); + break; + default: + color = tu.getColor(FaweCache.CACHE_BLOCK[combined]); + break; + } + int slope = getSlope(heights, width, index, height); + if (slope != 0) { + slope = (slope << 3) + (slope << 2); + int r = MathMan.clamp(((color >> 16) & 0xFF) + slope, 0, 255); + int g = MathMan.clamp(((color >> 8) & 0xFF) + slope, 0, 255); + int b = MathMan.clamp(((color >> 0) & 0xFF) + slope, 0, 255); + color = (r << 16) + (g << 8) + (b << 0); + } + if (height + 1 < waterHeight) { + byte waterId = gen.waterId; + int waterColor = 0; + switch (waterId) { + case BlockID.WATER: + case BlockID.STATIONARY_WATER: + color = tu.averageColor((0x11 << 16) + (0x66 << 8) + (0xCC), color); + break; + case BlockID.LAVA: + case BlockID.STATIONARY_LAVA: + color = (0xCC << 16) + (0x33 << 8) + (0); + break; + default: + color = tu.getColor(FaweCache.getBlock(waterId, 0)); + break; + } + } + raw[index] = color; + } + }); + } + pool.awaitQuiescence(Long.MAX_VALUE, TimeUnit.MILLISECONDS); + pool.shutdownNow(); + return img; + } + + private final int getAverageBiomeColor(byte[] biomes, int width, int index) { + int c0 = tu.getBiome(biomes[index] & 0xFF).grassCombined; + int c2 = getBiome(biomes, index + 1 + width, index); + int c1 = getBiome(biomes, index - 1 - width, index); +// int c3 = getBiome(biomes, index + width, index); +// int c4 = getBiome(biomes, index - width, index); + int r = ((c0 >> 16) & 0xFF) + ((c1 >> 16) & 0xFF) + ((c2 >> 16) & 0xFF);// + ((c3 >> 16) & 0xFF) + ((c4 >> 16) & 0xFF); + int g = ((c0 >> 8) & 0xFF) + ((c1 >> 8) & 0xFF) + ((c2 >> 8) & 0xFF);// + ((c3 >> 8) & 0xFF) + ((c4 >> 8) & 0xFF); + int b = ((c0) & 0xFF) + ((c1) & 0xFF) + ((c2) & 0xFF);// + ((c3) & 0xFF) + ((c4) & 0xFF); + r = r * 85 >> 8; + g = g * 85 >> 8; + b = b * 85 >> 8; + return (r << 16) + (g << 8) + (b); + } + + private final int getBiome(byte[] biomes, int newIndex, int index) { + if (newIndex < 0 || newIndex >= biomes.length) newIndex = index; + int biome = biomes[newIndex] & 0xFF; + return tu.getBiome(biome).grassCombined; + } + + private int getSlope(byte[] heights, int width, int index, int height) { + return ( + + getHeight(heights, index + 1, height) +// + getHeight(heights, index + width, height) + + getHeight(heights, index + width + 1, height) + - getHeight(heights, index - 1, height) +// - getHeight(heights, index - width, height) + - getHeight(heights, index - width - 1, height) + ); + } + + private int getHeight(byte[] heights, int index, int height) { + if (index < 0 || index >= heights.length) return height; + return heights[index] & 0xFF; + } +} + + 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 e27a02df..e64d0bfb 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 @@ -11,6 +11,7 @@ import com.boydti.fawe.util.CachedTextureUtil; import com.boydti.fawe.util.MathMan; import com.boydti.fawe.util.RandomTextureUtil; import com.boydti.fawe.util.TextureUtil; +import com.boydti.fawe.util.image.ImageViewer; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.MutableBlockVector; import com.sk89q.worldedit.Vector; @@ -46,17 +47,18 @@ public class HeightMapMCAGenerator extends MCAWriter implements Extent { private final Int2ObjectOpenHashMap blocks = new Int2ObjectOpenHashMap<>(); - public final byte[] heights; - private final byte[] biomes; - private final char[] floor; - private final char[] main; - private char[] overlay; - private int waterHeight = 0; - private TextureUtil textureUtil; - private boolean randomVariation = true; - private int biomePriority = 0; - private byte waterId = BlockID.STATIONARY_WATER; - private 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; + protected TextureUtil textureUtil; + protected boolean randomVariation = true; + protected int biomePriority = 0; + protected byte waterId = BlockID.STATIONARY_WATER; + protected boolean modifiedMain = false; + private ImageViewer viewer; public HeightMapMCAGenerator(BufferedImage img, File regionFolder) { this(img.getWidth(), img.getHeight(), regionFolder); @@ -76,6 +78,28 @@ public class HeightMapMCAGenerator extends MCAWriter implements Extent { Arrays.fill(floor, grass); } + public void setImageViewer(ImageViewer viewer) { + this.viewer = viewer; + update(); + } + + public ImageViewer getImageViewer() { + return viewer; + } + + private void update() { + if (viewer != null) { + viewer.view(draw()); + } + } + + public TextureUtil getRawTextureUtil() { + if (textureUtil == null) { + textureUtil = Fawe.get().getTextureUtil(); + } + return this.textureUtil; + } + public TextureUtil getTextureUtil() { if (textureUtil == null) { textureUtil = Fawe.get().getTextureUtil(); @@ -96,16 +120,22 @@ public class HeightMapMCAGenerator extends MCAWriter implements Extent { public void setWaterHeight(int waterHeight) { this.waterHeight = waterHeight; + update(); } public void setWaterId(int waterId) { this.waterId = (byte) waterId; + update(); } public void setTextureRandomVariation(boolean randomVariation) { this.randomVariation = randomVariation; } + public boolean getTextureRandomVariation() { + return this.randomVariation; + } + public void setTextureUtil(TextureUtil textureUtil) { this.textureUtil = textureUtil; } @@ -172,8 +202,23 @@ public class HeightMapMCAGenerator extends MCAWriter implements Extent { } } } + } 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); + } + } + } } } + update(); } public void setHeight(BufferedImage img) { @@ -183,17 +228,20 @@ public class HeightMapMCAGenerator extends MCAWriter implements Extent { heights[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, ClipboardHolder[] 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, ClipboardHolder[] clipboards, int rarity, int distance, boolean randomRotate) throws WorldEditException { @@ -245,6 +293,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements Extent { } } } + update(); } public void addSchems(Mask mask, WorldData worldData, ClipboardHolder[] clipboards, int rarity, int distance, boolean randomRotate) throws WorldEditException { @@ -294,15 +343,18 @@ public class HeightMapMCAGenerator extends MCAWriter implements Extent { } } } + 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 @@ -323,7 +375,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements Extent { @Override public boolean setBiome(Vector2D position, BaseBiome biome) { int index = position.getBlockZ() * getWidth() + position.getBlockX(); - if (index < 0 || index >= heights.length) return false; + if (index < 0 || index >= heights.length) index = Math.floorMod(index, heights.length); biomes[index] = (byte) biome.getId(); return true; } @@ -331,46 +383,48 @@ public class HeightMapMCAGenerator extends MCAWriter implements Extent { @Override public boolean setBlock(int x, int y, int z, BaseBlock block) throws WorldEditException { int index = z * getWidth() + x; - if (index < 0 || index >= heights.length) return false; + if (index < 0 || index >= heights.length) index = Math.floorMod(index, heights.length); int height = heights[index] & 0xFF; - char combined = (char) FaweCache.getCombined(block); - if (y > height) { - if (y == height + 1) { - if (overlay == null) { - overlay = new char[getArea()]; - } - overlay[index] = combined; + char combined = (char) block.getCombined(); + switch (y - height) { + case 0: + floor[index] = combined; + return true; + case 1: + char mainId = main[index]; + char floorId = floor[index]; + floor[index] = combined; + heights[index]++; + 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; - } - } else if (y == height) { - floor[index] = combined; - return true; } - 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; } @Override public BaseBiome getBiome(Vector2D position) { int index = position.getBlockZ() * getWidth() + position.getBlockX(); - if (index < 0 || index >= heights.length) return EditSession.nullBiome; + if (index < 0 || index >= heights.length) index = Math.floorMod(index, heights.length); return FaweCache.CACHE_BIOME[biomes[index] & 0xFF]; } @@ -387,7 +441,8 @@ public class HeightMapMCAGenerator extends MCAWriter implements Extent { @Override public BaseBlock getLazyBlock(int x, int y, int z) { int index = z * getWidth() + x; - if (index < 0 || index >= heights.length) return EditSession.nullBlock; + if (y < 0) return EditSession.nullBlock; + if (index < 0 || index >= heights.length) index = Math.floorMod(index, heights.length); int height = heights[index] & 0xFF; if (y > height) { if (y == height + 1) { @@ -431,14 +486,14 @@ public class HeightMapMCAGenerator extends MCAWriter implements Extent { @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) return y; + if (index < 0 || index >= heights.length) index = Math.floorMod(index, heights.length); return ((heights[index] & 0xFF) << 3) + (floor[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) return y; + if (index < 0 || index >= heights.length) index = Math.floorMod(index, heights.length); return heights[index] & 0xFF; } @@ -454,12 +509,21 @@ public class HeightMapMCAGenerator extends MCAWriter implements Extent { } } } + update(); + } + + public BufferedImage draw() { + return new HeightMapMCADrawer(this).draw(); } public void setBiomePriority(int value) { this.biomePriority = ((value * 65536) / 100) - 32768; } + public int getBiomePriority() { + return ((biomePriority + 32768) * 100) / 65536; + } + public void setBlockAndBiomeColor(BufferedImage img, Mask mask, BufferedImage imgMask, boolean whiteOnly) { if (mask == null && imgMask == null) { setBlockAndBiomeColor(img); @@ -495,6 +559,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements Extent { biomes[index] = (byte) buffer[1]; } } + update(); } public void setBlockAndBiomeColor(BufferedImage img) { @@ -519,12 +584,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements Extent { biomes[index] = (byte) buffer[1]; } } - } - - private void setBiomeIfZero(int index, byte value) { - if (biomes[index] == 0) { - biomes[index] = value; - } + update(); } public void setBiomeColor(BufferedImage img) { @@ -542,6 +602,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements Extent { index++; } } + update(); } public void setColor(BufferedImage img, BufferedImage mask, boolean white) { @@ -566,9 +627,10 @@ public class HeightMapMCAGenerator extends MCAWriter implements Extent { } } } + update(); } - public void setColor(BufferedImage img, Mask mask, boolean white) { + 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; @@ -590,6 +652,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements Extent { } } } + update(); } public void setColor(BufferedImage img) { @@ -610,6 +673,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements Extent { index++; } } + update(); } public void setColorWithGlass(BufferedImage img) { @@ -628,6 +692,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements Extent { index++; } } + update(); } public void setBiome(Mask mask, byte biome) { @@ -643,255 +708,270 @@ public class HeightMapMCAGenerator extends MCAWriter implements Extent { } } } + update(); } public void setOverlay(BufferedImage img, Pattern pattern, boolean white) { if (pattern instanceof BaseBlock) { setOverlay(img, (char) ((BaseBlock) pattern).getCombined(), white); - return; - } - 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(); + } 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(); + } } } } + update(); } public void setMain(BufferedImage img, Pattern pattern, boolean white) { if (pattern instanceof BaseBlock) { setMain(img, (char) ((BaseBlock) pattern).getCombined(), white); - return; - } - 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(); + } 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(); + } } } } + update(); } public void setFloor(BufferedImage img, Pattern pattern, boolean white) { if (pattern instanceof BaseBlock) { setFloor(img, (char) ((BaseBlock) pattern).getCombined(), white); - return; - } - 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(); + } 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(); + } } } } + update(); } public void setColumn(BufferedImage img, Pattern pattern, boolean white) { if (pattern instanceof BaseBlock) { setColumn(img, (char) ((BaseBlock) pattern).getCombined(), white); - return; - } - 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; + } 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; + } } } } + update(); } public void setOverlay(Mask mask, Pattern pattern) { if (pattern instanceof BaseBlock) { setOverlay(mask, (char) ((BaseBlock) pattern).getCombined()); - return; - } - int index = 0; - if (overlay == null) overlay = 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; - mutable.mutX(x); - mutable.mutY(y); - if (mask.test(mutable)) { - overlay[index] = (char) pattern.apply(mutable).getCombined(); + } else { + int index = 0; + if (overlay == null) overlay = 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; + mutable.mutX(x); + mutable.mutY(y); + if (mask.test(mutable)) { + overlay[index] = (char) pattern.apply(mutable).getCombined(); + } } } } + update(); } public void setFloor(Mask mask, Pattern pattern) { if (pattern instanceof BaseBlock) { setFloor(mask, (char) ((BaseBlock) pattern).getCombined()); - return; - } - 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); - if (mask.test(mutable)) { - floor[index] = (char) pattern.apply(mutable).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); + if (mask.test(mutable)) { + floor[index] = (char) pattern.apply(mutable).getCombined(); + } } } } + update(); } public void setMain(Mask mask, Pattern pattern) { if (pattern instanceof BaseBlock) { setMain(mask, (char) ((BaseBlock) pattern).getCombined()); - return; - } - 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; - mutable.mutX(x); - mutable.mutY(y); - if (mask.test(mutable)) { - main[index] = (char) pattern.apply(mutable).getCombined(); + } else { + 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; + mutable.mutX(x); + mutable.mutY(y); + if (mask.test(mutable)) { + main[index] = (char) pattern.apply(mutable).getCombined(); + } } } } + update(); } public void setColumn(Mask mask, Pattern pattern) { if (pattern instanceof BaseBlock) { setColumn(mask, (char) ((BaseBlock) pattern).getCombined()); - return; - } - 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; - mutable.mutX(x); - mutable.mutY(y); - if (mask.test(mutable)) { - char combined = (char) pattern.apply(mutable).getCombined(); - floor[index] = combined; - main[index] = combined; + } else { + 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; + mutable.mutX(x); + mutable.mutY(y); + if (mask.test(mutable)) { + char combined = (char) pattern.apply(mutable).getCombined(); + floor[index] = combined; + main[index] = combined; + } } } } + update(); } public void setBiome(int biome) { Arrays.fill(biomes, (byte) biome); + update(); } public void setFloor(Pattern value) { if (value instanceof BaseBlock) { setFloor(((BaseBlock) value).getCombined()); - return; - } - 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(); + } 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(); + } } } + update(); } public void setColumn(Pattern value) { if (value instanceof BaseBlock) { setColumn(((BaseBlock) value).getCombined()); - return; - } - 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; + } 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; + } } } + update(); } public void setMain(Pattern value) { if (value instanceof BaseBlock) { setMain(((BaseBlock) value).getCombined()); - return; - } - 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(); + } 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(); + } } } + update(); } public void setOverlay(Pattern value) { if (overlay == null) overlay = new char[getArea()]; if (value instanceof BaseBlock) { setOverlay(((BaseBlock) value).getCombined()); - return; - } - 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(); + } 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(); + } } } + update(); } public void setHeights(int value) { Arrays.fill(heights, (byte) value); + update(); } @Override diff --git a/core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAWriter.java b/core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAWriter.java index d043055b..921a8de2 100644 --- a/core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAWriter.java +++ b/core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAWriter.java @@ -10,7 +10,7 @@ import java.util.concurrent.TimeUnit; import java.util.zip.Deflater; public abstract class MCAWriter { - private final File folder; + private File folder; private final int length; private final int width; private final int area; @@ -18,9 +18,6 @@ public abstract class MCAWriter { public MCAWriter(int width, int length, File regionFolder) { - if (!regionFolder.exists()) { - regionFolder.mkdirs(); - } this.folder = regionFolder; this.width = width; this.length = length; @@ -31,6 +28,10 @@ public abstract class MCAWriter { return folder; } + public void setFolder(File folder) { + this.folder = folder; + } + public final int getWidth() { return width; } @@ -69,6 +70,9 @@ public abstract class MCAWriter { public abstract MCAChunk write(MCAChunk input, int startX, int endX, int startZ, int endZ); public void generate() throws IOException { + if (!folder.exists()) { + folder.mkdirs(); + } final ForkJoinPool pool = new ForkJoinPool(); int bcx = 0; int bcz = 0; diff --git a/core/src/main/java/com/boydti/fawe/object/HistoryExtent.java b/core/src/main/java/com/boydti/fawe/object/HistoryExtent.java index f908cb55..ee4abd03 100644 --- a/core/src/main/java/com/boydti/fawe/object/HistoryExtent.java +++ b/core/src/main/java/com/boydti/fawe/object/HistoryExtent.java @@ -29,7 +29,6 @@ import static com.google.common.base.Preconditions.checkNotNull; */ public class HistoryExtent extends AbstractDelegateExtent { - private final AbstractDelegateExtent extent; private FaweChangeSet changeSet; private final FaweQueue queue; private final EditSession session; @@ -43,7 +42,6 @@ public class HistoryExtent extends AbstractDelegateExtent { public HistoryExtent(final EditSession session, final Extent extent, final FaweChangeSet changeSet, FaweQueue queue) { super(extent); checkNotNull(changeSet); - this.extent = (AbstractDelegateExtent) extent; this.queue = queue; this.changeSet = changeSet; this.session = session; @@ -92,12 +90,7 @@ public class HistoryExtent extends AbstractDelegateExtent { } catch (FaweException ignore) { return false; } - return extent.setBlock(x, y, z, block); - } - - @Override - public BaseBlock getLazyBlock(int x, int y, int z) { - return extent.getLazyBlock(x, y, z); + return getExtent().setBlock(x, y, z, block); } @Override @@ -143,7 +136,7 @@ public class HistoryExtent extends AbstractDelegateExtent { BaseBiome oldBiome = this.getBiome(position); if (oldBiome.getId() != newBiome.getId()) { this.changeSet.addBiomeChange(position.getBlockX(), position.getBlockZ(), oldBiome, newBiome); - return extent.setBiome(position, newBiome); + return getExtent().setBiome(position, newBiome); } else { return false; } diff --git a/core/src/main/java/com/boydti/fawe/regions/general/plot/CreateFromImage.java b/core/src/main/java/com/boydti/fawe/regions/general/plot/CreateFromImage.java index 417170fc..2f5d478b 100644 --- a/core/src/main/java/com/boydti/fawe/regions/general/plot/CreateFromImage.java +++ b/core/src/main/java/com/boydti/fawe/regions/general/plot/CreateFromImage.java @@ -79,8 +79,8 @@ public class CreateFromImage extends Command { @Override public void run() { FawePlayer fp = FawePlayer.wrap(player.getName()); - HeightMapMCAGenerator generator = player.getMeta("HMGenerator"); - Plot plot = player.getMeta("HMGeneratorPlot"); + HeightMapMCAGenerator generator = fp.getMeta("HMGenerator"); + Plot plot = fp.getMeta("HMGeneratorPlot"); if (generator == null) { final Vector2D dimensions; final BufferedImage image; @@ -164,7 +164,8 @@ public class CreateFromImage extends Command { } else { generator = new HeightMapMCAGenerator(dimensions.getBlockX(), dimensions.getBlockZ(), folder); } - player.setMeta("HMGenerator", generator); + generator.setImageViewer(Fawe.imp().getImageViewer(fp)); + fp.setMeta("HMGenerator", generator); player.setMeta("HMGeneratorPlot", plot); } }, true, false); @@ -310,7 +311,7 @@ public class CreateFromImage extends Command { } else { Mask mask = we.getMaskFactory().parseFromInput(argList.get(1), context); boolean whiteOnly = argList.size() < 4 || Boolean.parseBoolean(argList.get(3)); - generator.setColor(image, mask, whiteOnly); + generator.setColor(image, mask); } } else { generator.setColor(image); @@ -594,8 +595,8 @@ public class CreateFromImage extends Command { return; case "exit": case "cancel": - player.deleteMeta("HMGenerator"); - player.deleteMeta("HMGeneratorPlot"); + fp.deleteMeta("HMGenerator"); + fp.deleteMeta("HMGeneratorPlot"); player.sendMessage(BBC.getPrefix() + "Cancelled!"); return; default: 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 6c70e798..653a27c6 100644 --- a/core/src/main/java/com/boydti/fawe/util/CleanTextureUtil.java +++ b/core/src/main/java/com/boydti/fawe/util/CleanTextureUtil.java @@ -4,8 +4,12 @@ import java.io.FileNotFoundException; import java.util.Arrays; public class CleanTextureUtil extends TextureUtil { + private final int min, max; + public CleanTextureUtil(TextureUtil parent, int minPercent, int maxPercent) throws FileNotFoundException { super(parent.getFolder()); + this.min = minPercent; + this.max = maxPercent; int minIndex = ((parent.distances.length - 1) * minPercent) / 100; int maxIndex = ((parent.distances.length - 1) * maxPercent) / 100; long min = parent.distances[minIndex]; @@ -30,4 +34,12 @@ public class CleanTextureUtil extends TextureUtil { } this.calculateLayerArrays(); } + + public int getMin() { + return min; + } + + public int getMax() { + return max; + } } diff --git a/core/src/main/java/com/boydti/fawe/util/ExtentTraverser.java b/core/src/main/java/com/boydti/fawe/util/ExtentTraverser.java index 32fb3868..4b40f25e 100644 --- a/core/src/main/java/com/boydti/fawe/util/ExtentTraverser.java +++ b/core/src/main/java/com/boydti/fawe/util/ExtentTraverser.java @@ -28,8 +28,7 @@ public class ExtentTraverser { public boolean setNext(T next) { try { Field field = AbstractDelegateExtent.class.getDeclaredField("extent"); - field.setAccessible(true); - field.set(root, next); + ReflectionUtils.setFailsafeFieldValue(field, root, next); return true; } catch (Throwable e) { e.printStackTrace(); @@ -37,6 +36,16 @@ public class ExtentTraverser { } } + public ExtentTraverser last() { + ExtentTraverser last = this; + ExtentTraverser traverser = this; + while (traverser != null && traverser.get() instanceof AbstractDelegateExtent) { + last = traverser; + traverser = traverser.next(); + } + return last; + } + public boolean insert(T extent) { try { Field field = AbstractDelegateExtent.class.getDeclaredField("extent"); diff --git a/core/src/main/java/com/boydti/fawe/util/FilteredTextureUtil.java b/core/src/main/java/com/boydti/fawe/util/FilteredTextureUtil.java index 82ea6d4e..99dbd497 100644 --- a/core/src/main/java/com/boydti/fawe/util/FilteredTextureUtil.java +++ b/core/src/main/java/com/boydti/fawe/util/FilteredTextureUtil.java @@ -6,8 +6,11 @@ import java.io.FileNotFoundException; import java.util.Set; public class FilteredTextureUtil extends TextureUtil { + private final Set blocks; + public FilteredTextureUtil(TextureUtil parent, Set blocks) throws FileNotFoundException { super(parent.getFolder()); + this.blocks = blocks; this.validMixBiomeColors = parent.validMixBiomeColors; this.validMixBiomeIds = parent.validMixBiomeIds; this.validBiomes = parent.validBiomes; @@ -33,4 +36,8 @@ public class FilteredTextureUtil extends TextureUtil { } this.calculateLayerArrays(); } + + public Set getBlocks() { + return blocks; + } } \ No newline at end of file diff --git a/core/src/main/java/com/boydti/fawe/util/Jars.java b/core/src/main/java/com/boydti/fawe/util/Jars.java index ab316e1f..f0fbc0d5 100644 --- a/core/src/main/java/com/boydti/fawe/util/Jars.java +++ b/core/src/main/java/com/boydti/fawe/util/Jars.java @@ -3,18 +3,24 @@ package com.boydti.fawe.util; import com.boydti.fawe.Fawe; import java.io.DataInputStream; import java.io.IOException; -import java.math.BigInteger; import java.net.URL; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; -import java.util.Arrays; public enum Jars { WE_B_6_1_7_2("https://addons.cursecdn.com/files/2431/372/worldedit-bukkit-6.1.7.2.jar", "711be37301a327aba4e347131875d0564dbfdc2f41053a12db97f0234661778b", 1726340), VS_B_5_171_0("https://addons-origin.cursecdn.com/files/912/511/VoxelSniper-5.171.0-SNAPSHOT.jar", - "292c3b38238e0d8e5f036381d28bccfeb15df67cae53d28b52d066bc6238208f", 3632776); + "292c3b38238e0d8e5f036381d28bccfeb15df67cae53d28b52d066bc6238208f", 3632776), + + MM_v1_4_0("https://github.com/InventivetalentDev/MapManager/releases/download/1.4.0-SNAPSHOT/MapManager_v1.4.0-SNAPSHOT.jar", + "004A39B0A06E80DE3226B4BCC6080D2DB9B6411CCFB48D647F4FF55B5B91B600", 163279), + + PL_v3_6_0("https://github.com/InventivetalentDev/PacketListenerAPI/releases/download/3.6.0-SNAPSHOT/PacketListenerAPI_v3.6.0-SNAPSHOT.jar", + "3B26C4EF9BE253E9E7C07AD5AC46CB0C047003EFD36DD433D6B739EB6AAE9410", 166508), + + ; public final String url; public final int filesize; @@ -28,9 +34,9 @@ public enum Jars { * @param filesize * Size of this jar in bytes */ - private Jars(String url, String digest, int filesize) { + Jars(String url, String digest, int filesize) { this.url = url; - this.digest = digest; + this.digest = digest.toUpperCase(); this.filesize = filesize; } @@ -43,16 +49,14 @@ public enum Jars { if (dis.read() != -1) { // assert that we've read everything throw new IllegalStateException("downloaded jar is longer than expected"); } + MessageDigest md = MessageDigest.getInstance("SHA-256"); + byte[] jarDigestBytes = md.digest(jarBytes); + String jarDigest = javax.xml.bind.DatatypeConverter.printHexBinary(jarDigestBytes).toUpperCase(); - MessageDigest md; - md = MessageDigest.getInstance("SHA-256"); - byte[] thisDigest = md.digest(jarBytes); - byte[] realDigest = new BigInteger(this.digest.toUpperCase(), 16).toByteArray(); - - if (Arrays.equals(thisDigest, realDigest)) { + if (this.digest.equals(jarDigest)) { Fawe.debug("++++ HASH CHECK ++++"); Fawe.debug(this.url); - Fawe.debug(javax.xml.bind.DatatypeConverter.printHexBinary(thisDigest)); + Fawe.debug(this.digest); return jarBytes; } else { throw new IllegalStateException("downloaded jar does not match the hash"); diff --git a/core/src/main/java/com/boydti/fawe/util/TextureUtil.java b/core/src/main/java/com/boydti/fawe/util/TextureUtil.java index 05cc4f75..a1039997 100644 --- a/core/src/main/java/com/boydti/fawe/util/TextureUtil.java +++ b/core/src/main/java/com/boydti/fawe/util/TextureUtil.java @@ -685,6 +685,7 @@ public class TextureUtil { if (biome.grass != 0 && !biome.name.equalsIgnoreCase("Unknown Biome")) { valid.add(biome); } + biome.grassCombined = multiplyColor(grass, biome.grass); } this.validBiomes = valid.toArray(new BiomeColor[valid.size()]); @@ -779,10 +780,10 @@ public class TextureUtil { int red2 = (c2 >> 16) & 0xFF; int green2 = (c2 >> 8) & 0xFF; int blue2 = (c2 >> 0) & 0xFF; - int red = ((red1 + red2)) / 2; - int green = ((green1 + green2)) / 2; - int blue = ((blue1 + blue2)) / 2; - int alpha = ((alpha1 + alpha2)) / 2; + int red = ((red1 + red2)) >> 1; + int green = ((green1 + green2)) >> 1; + int blue = ((blue1 + blue2)) >> 1; + int alpha = ((alpha1 + alpha2)) >> 1; return (alpha << 24) + (red << 16) + (green << 8) + (blue << 0); } @@ -1049,6 +1050,7 @@ public class TextureUtil { public float temperature; public float rainfall; public int grass; + public int grassCombined; public int foliage; public BiomeColor(int id, String name, float temperature, float rainfall, int grass, int foliage) { @@ -1057,7 +1059,12 @@ public class TextureUtil { this.temperature = temperature; this.rainfall = rainfall; this.grass = grass; + this.grassCombined = grass; this.foliage = foliage; } } + + public char[] getValidBlockIds() { + return validBlockIds.clone(); + } } diff --git a/core/src/main/java/com/boydti/fawe/util/chat/Message.java b/core/src/main/java/com/boydti/fawe/util/chat/Message.java index b50db7e8..563d92b7 100644 --- a/core/src/main/java/com/boydti/fawe/util/chat/Message.java +++ b/core/src/main/java/com/boydti/fawe/util/chat/Message.java @@ -87,6 +87,19 @@ public class Message { return tooltip(commandAndTooltip).command(commandAndTooltip); } + public Message linkTip(String linkAndTooltip) { + return tooltip(linkAndTooltip).link(linkAndTooltip); + } + + public Message cmdOptions(String prefix, String suffix, String... options) { + for (int i = 0; i < options.length; i++) { + if (i != 0) text(" &8|&7 "); + text("&7[&a" + options[i] + "&7]") + .cmdTip(prefix + options[i] + suffix); + } + return this; + } + public Message suggestTip(String commandAndTooltip) { return tooltip(commandAndTooltip).suggest(commandAndTooltip); } diff --git a/core/src/main/java/com/boydti/fawe/util/cui/CUI.java b/core/src/main/java/com/boydti/fawe/util/cui/CUI.java new file mode 100644 index 00000000..540f6a45 --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/util/cui/CUI.java @@ -0,0 +1,18 @@ +package com.boydti.fawe.util.cui; + +import com.boydti.fawe.object.FawePlayer; +import com.sk89q.worldedit.internal.cui.CUIEvent; + +public abstract class CUI { + private final FawePlayer player; + + public CUI(FawePlayer player) { + this.player = player; + } + + public FawePlayer getPlayer() { + return player; + } + + public abstract void dispatchCUIEvent(CUIEvent event); +} diff --git a/core/src/main/java/com/boydti/fawe/util/image/ImageUtil.java b/core/src/main/java/com/boydti/fawe/util/image/ImageUtil.java new file mode 100644 index 00000000..e32c5da2 --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/util/image/ImageUtil.java @@ -0,0 +1,94 @@ +package com.boydti.fawe.util.image; + +import com.boydti.fawe.Fawe; +import com.boydti.fawe.util.MainUtil; +import com.sk89q.worldedit.util.command.parametric.ParameterException; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.Transparency; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.net.URL; + +public class ImageUtil { + public static BufferedImage getScaledInstance(BufferedImage img, + int targetWidth, + int targetHeight, + Object hint, + boolean higherQuality) + { + if (img.getHeight() == targetHeight && img.getWidth() == targetWidth) { + return img; + } + int type = (img.getTransparency() == Transparency.OPAQUE) ? + BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB; + BufferedImage ret = (BufferedImage)img; + int w, h; + if (higherQuality) { + // Use multi-step technique: start with original size, then + // scale down in multiple passes with drawImage() + // until the target size is reached + w = img.getWidth(); + h = img.getHeight(); + } else { + // Use one-step technique: scale directly from original + // size to target size with a single drawImage() call + w = targetWidth; + h = targetHeight; + } + + do { + if (higherQuality && w > targetWidth) { + w /= 2; + if (w < targetWidth) { + w = targetWidth; + } + } + + if (higherQuality && h > targetHeight) { + h /= 2; + if (h < targetHeight) { + h = targetHeight; + } + } + + BufferedImage tmp = new BufferedImage(w, h, type); + Graphics2D g2 = tmp.createGraphics(); + g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, hint); + g2.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED); + g2.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_SPEED); + g2.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED); + g2.drawImage(ret, 0, 0, w, h, null); + g2.dispose(); + + ret = tmp; + } while (w != targetWidth || h != targetHeight); + + return ret; + } + + public static BufferedImage getImage(String arg) throws ParameterException { + try { + if (arg.startsWith("http")) { + if (arg.contains("imgur.com") && !arg.contains("i.imgur.com")) { + arg = "https://i.imgur.com/" + arg.split("imgur.com/")[1] + ".png"; + } + URL url = new URL(arg); + BufferedImage img = MainUtil.readImage(url); + if (img == null) { + throw new IOException("Failed to read " + url + ", please try again later"); + } + return img; + } else if (arg.startsWith("file://")) { + arg = arg.substring(7); + File file = MainUtil.getFile(MainUtil.getFile(Fawe.imp().getDirectory(), com.boydti.fawe.config.Settings.IMP.PATHS.HEIGHTMAP), arg); + return MainUtil.readImage(file); + } else { + throw new ParameterException("Invalid image " + arg); + } + } catch (IOException e) { + throw new ParameterException(e); + } + } +} 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 new file mode 100644 index 00000000..9ffffb5d --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/util/image/ImageViewer.java @@ -0,0 +1,8 @@ +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); +} diff --git a/core/src/main/java/com/sk89q/worldedit/LocalSession.java b/core/src/main/java/com/sk89q/worldedit/LocalSession.java index 328354f3..f7f8c480 100644 --- a/core/src/main/java/com/sk89q/worldedit/LocalSession.java +++ b/core/src/main/java/com/sk89q/worldedit/LocalSession.java @@ -34,6 +34,7 @@ import com.boydti.fawe.object.extent.ResettableExtent; import com.boydti.fawe.util.EditSessionBuilder; import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.StringMan; +import com.boydti.fawe.util.cui.CUI; import com.boydti.fawe.wrappers.WorldWrapper; import com.sk89q.jchronic.Chronic; import com.sk89q.jchronic.Options; @@ -1130,6 +1131,9 @@ public class LocalSession { if (hasCUISupport) { actor.dispatchCUIEvent(event); + } else { + CUI cui = Fawe.get().getCUI(actor); + if (cui != null) cui.dispatchCUIEvent(event); } } @@ -1151,22 +1155,16 @@ public class LocalSession { */ public void dispatchCUISelection(Actor actor) { checkNotNull(actor); - - if (!hasCUISupport) { - return; - } - if (selector instanceof CUIRegion) { CUIRegion tempSel = (CUIRegion) selector; if (tempSel.getProtocolVersion() > cuiVersion) { - actor.dispatchCUIEvent(new SelectionShapeEvent(tempSel.getLegacyTypeID())); + dispatchCUIEvent(actor, new SelectionShapeEvent(tempSel.getLegacyTypeID())); tempSel.describeLegacyCUI(this, actor); } else { - actor.dispatchCUIEvent(new SelectionShapeEvent(tempSel.getTypeID())); + dispatchCUIEvent(actor, new SelectionShapeEvent(tempSel.getTypeID())); tempSel.describeCUI(this, actor); } - } } @@ -1177,11 +1175,6 @@ public class LocalSession { */ public void describeCUI(Actor actor) { checkNotNull(actor); - - if (!hasCUISupport) { - return; - } - if (selector instanceof CUIRegion) { CUIRegion tempSel = (CUIRegion) selector; diff --git a/core/src/main/java/com/sk89q/worldedit/extension/platform/CommandManager.java b/core/src/main/java/com/sk89q/worldedit/extension/platform/CommandManager.java index 02cca37c..fea9697b 100644 --- a/core/src/main/java/com/sk89q/worldedit/extension/platform/CommandManager.java +++ b/core/src/main/java/com/sk89q/worldedit/extension/platform/CommandManager.java @@ -21,6 +21,8 @@ package com.sk89q.worldedit.extension.platform; import com.boydti.fawe.Fawe; import com.boydti.fawe.command.AnvilCommands; +import com.boydti.fawe.command.CFICommand; +import com.boydti.fawe.command.CFICommands; import com.boydti.fawe.command.MaskBinding; import com.boydti.fawe.command.PatternBinding; import com.boydti.fawe.config.BBC; @@ -125,7 +127,7 @@ public final class CommandManager { private final DynamicStreamHandler dynamicHandler = new DynamicStreamHandler(); private final ExceptionConverter exceptionConverter; - + private ParametricBuilder builder; private Map methodMap; private static CommandManager INSTANCE; @@ -151,7 +153,27 @@ public final class CommandManager { commandLog.addHandler(dynamicHandler); dynamicHandler.setFormatter(new LogFormat()); + builder = new ParametricBuilder(); + builder.setAuthorizer(new ActorAuthorizer()); + builder.setDefaultCompleter(new UserCommandCompleter(platformManager)); + builder.addBinding(new WorldEditBinding(worldEdit)); + + builder.addBinding(new PatternBinding(worldEdit), com.sk89q.worldedit.function.pattern.Pattern.class); + builder.addBinding(new MaskBinding(worldEdit), com.sk89q.worldedit.function.mask.Mask.class); + + builder.addInvokeListener(new LegacyCommandsHandler()); + builder.addInvokeListener(new CommandLoggingHandler(worldEdit, commandLog)); + this.methodMap = new ConcurrentHashMap<>(); + + try { + Class.forName("com.intellectualcrafters.plot.PS"); + CFICommands cfiCmds = new CFICommands(worldEdit); + CFICommand cfi = new CFICommand(worldEdit, builder); + registerCommands(cfi); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } } /** @@ -173,14 +195,6 @@ public final class CommandManager { */ public void registerCommands(Object clazz, String... aliases) { if (platform != null) { - ParametricBuilder builder = new ParametricBuilder(); - builder.setAuthorizer(new ActorAuthorizer()); - builder.setDefaultCompleter(new UserCommandCompleter(platformManager)); - builder.addBinding(new WorldEditBinding(worldEdit)); - - builder.addBinding(new PatternBinding(worldEdit), com.sk89q.worldedit.function.pattern.Pattern.class); - builder.addBinding(new MaskBinding(worldEdit), com.sk89q.worldedit.function.mask.Mask.class); - DispatcherNode graph = new CommandGraph().builder(builder).commands(); if (aliases.length == 0) { graph = graph.registerMethods(clazz); @@ -198,16 +212,6 @@ public final class CommandManager { * Initialize the dispatcher */ public void setupDispatcher() { - ParametricBuilder builder = new ParametricBuilder(); - builder.setAuthorizer(new ActorAuthorizer()); - builder.setDefaultCompleter(new UserCommandCompleter(platformManager)); - builder.addBinding(new WorldEditBinding(worldEdit)); - - builder.addBinding(new PatternBinding(worldEdit), com.sk89q.worldedit.function.pattern.Pattern.class); - builder.addBinding(new MaskBinding(worldEdit), com.sk89q.worldedit.function.mask.Mask.class); - - builder.addInvokeListener(new LegacyCommandsHandler()); - builder.addInvokeListener(new CommandLoggingHandler(worldEdit, commandLog)); DispatcherNode graph = new CommandGraph().builder(builder).commands(); for (Map.Entry entry : methodMap.entrySet()) { 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 bbfb42f7..fd4a50e9 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 @@ -244,15 +244,14 @@ public class HeightMap { // Skip water/lava if (!FaweCache.isLiquidOrGas(existing.getId())) { - session.setBlock(xr, newHeight, zr, existing); - ++blocksChanged; - - // Grow -- start from 1 below top replacing airblocks - for (int y = newHeight - 1 - originY; y >= curHeight; --y) { + for (int y = curHeight; y <= newHeight - 1 - originY; y++) { int copyFrom = (int) (y * scale); session.setBlock(xr, originY + y, zr, session.getBlock(xr, originY + copyFrom, zr)); ++blocksChanged; } + + session.setBlock(xr, newHeight, zr, existing); + ++blocksChanged; } } else if (curHeight > newHeight) { // Set the top block of the column to be the same type diff --git a/core/src/main/java/com/sk89q/worldedit/session/ClipboardHolder.java b/core/src/main/java/com/sk89q/worldedit/session/ClipboardHolder.java index 6f9e45d2..f3bc99ff 100644 --- a/core/src/main/java/com/sk89q/worldedit/session/ClipboardHolder.java +++ b/core/src/main/java/com/sk89q/worldedit/session/ClipboardHolder.java @@ -51,6 +51,10 @@ public class ClipboardHolder { this.worldData = worldData; } + protected ClipboardHolder() { + worldData = null; + } + /** * Get the mapping used for blocks, entities, and so on. * diff --git a/core/src/main/java/com/sk89q/worldedit/session/DelegateClipboardHolder.java b/core/src/main/java/com/sk89q/worldedit/session/DelegateClipboardHolder.java new file mode 100644 index 00000000..6b910bfb --- /dev/null +++ b/core/src/main/java/com/sk89q/worldedit/session/DelegateClipboardHolder.java @@ -0,0 +1,45 @@ +package com.sk89q.worldedit.session; + +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.extent.clipboard.Clipboard; +import com.sk89q.worldedit.math.transform.Transform; +import com.sk89q.worldedit.world.registry.WorldData; + +public class DelegateClipboardHolder extends ClipboardHolder { + private final ClipboardHolder parent; + + public DelegateClipboardHolder(ClipboardHolder holder) { + super(holder.getClipboard(), holder.getWorldData()); + this.parent = holder; + } + + @Override + public WorldData getWorldData() { + return parent.getWorldData(); + } + + @Override + public Clipboard getClipboard() { + return parent.getClipboard(); + } + + @Override + public void setTransform(Transform transform) { + parent.setTransform(transform); + } + + @Override + public Transform getTransform() { + return parent.getTransform(); + } + + @Override + public PasteBuilder createPaste(Extent targetExtent, WorldData targetWorldData) { + return parent.createPaste(targetExtent, targetWorldData); + } + + @Override + public void close() { + parent.close(); + } +} diff --git a/core/src/main/java/com/sk89q/worldedit/util/command/parametric/ParametricBuilder.java b/core/src/main/java/com/sk89q/worldedit/util/command/parametric/ParametricBuilder.java index 79035358..70480942 100644 --- a/core/src/main/java/com/sk89q/worldedit/util/command/parametric/ParametricBuilder.java +++ b/core/src/main/java/com/sk89q/worldedit/util/command/parametric/ParametricBuilder.java @@ -20,13 +20,20 @@ package com.sk89q.worldedit.util.command.parametric; import com.boydti.fawe.command.FawePrimitiveBinding; +import com.boydti.fawe.command.MaskBinding; +import com.boydti.fawe.command.PatternBinding; import com.boydti.fawe.config.Commands; import com.google.common.collect.ImmutableBiMap.Builder; 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.WorldEdit; import com.sk89q.worldedit.command.MethodCommands; +import com.sk89q.worldedit.internal.command.ActorAuthorizer; +import com.sk89q.worldedit.internal.command.CommandLoggingHandler; +import com.sk89q.worldedit.internal.command.UserCommandCompleter; +import com.sk89q.worldedit.internal.command.WorldEditBinding; import com.sk89q.worldedit.util.auth.Authorizer; import com.sk89q.worldedit.util.auth.NullAuthorizer; import com.sk89q.worldedit.util.command.CallableProcessor;