diff --git a/src/main/java/net/craftcitizen/imagemaps/ImageMap.java b/src/main/java/net/craftcitizen/imagemaps/ImageMap.java index dff5266..1f3a60d 100644 --- a/src/main/java/net/craftcitizen/imagemaps/ImageMap.java +++ b/src/main/java/net/craftcitizen/imagemaps/ImageMap.java @@ -1,33 +1,33 @@ package net.craftcitizen.imagemaps; -import java.util.HashMap; -import java.util.Map; - import org.bukkit.configuration.serialization.ConfigurationSerializable; import org.bukkit.configuration.serialization.SerializableAs; +import java.util.HashMap; +import java.util.Map; + @SerializableAs("ImageMaps.Map") public class ImageMap implements ConfigurationSerializable { - + private String filename; private int x; private int y; private double scale; - + public ImageMap(String filename, int x, int y, double scale) { this.filename = filename; this.x = x; this.y = y; this.scale = scale; } - + public ImageMap(Map map) { this.filename = map.get("image").toString(); this.x = (Integer) map.get("x"); this.y = (Integer) map.get("y"); this.scale = (Double) map.get("scale"); } - + @Override public Map serialize() { Map map = new HashMap<>(); @@ -35,26 +35,26 @@ public class ImageMap implements ConfigurationSerializable { map.put("x", x); map.put("y", y); map.put("scale", scale); - + return map; } - + public String getFilename() { return filename; } - + public int getX() { return x; } - + public int getY() { return y; } - + public double getScale() { return scale; } - + @Override public int hashCode() { final int prime = 31; @@ -67,7 +67,7 @@ public class ImageMap implements ConfigurationSerializable { result = prime * result + y; return result; } - + @Override public boolean equals(Object obj) { if (this == obj) @@ -78,8 +78,7 @@ public class ImageMap implements ConfigurationSerializable { if (filename == null) { if (other.filename != null) return false; - } - else if (!filename.equals(other.filename)) + } else if (!filename.equals(other.filename)) return false; if (Double.doubleToLongBits(scale) != Double.doubleToLongBits(other.scale)) return false; diff --git a/src/main/java/net/craftcitizen/imagemaps/ImageMapHelpCommand.java b/src/main/java/net/craftcitizen/imagemaps/ImageMapHelpCommand.java index 2490297..a2a46fe 100644 --- a/src/main/java/net/craftcitizen/imagemaps/ImageMapHelpCommand.java +++ b/src/main/java/net/craftcitizen/imagemaps/ImageMapHelpCommand.java @@ -1,10 +1,5 @@ package net.craftcitizen.imagemaps; -import java.util.Map; - -import org.bukkit.command.CommandSender; -import org.bukkit.plugin.Plugin; - import de.craftlancer.core.command.HelpCommand; import de.craftlancer.core.command.SubCommand; import de.craftlancer.core.util.MessageLevel; @@ -12,13 +7,17 @@ import de.craftlancer.core.util.MessageUtil; import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.chat.BaseComponent; import net.md_5.bungee.api.chat.TextComponent; +import org.bukkit.command.CommandSender; +import org.bukkit.plugin.Plugin; + +import java.util.Map; public class ImageMapHelpCommand extends HelpCommand { - + public ImageMapHelpCommand(Plugin plugin, Map map) { super("imagemaps.help", plugin, map); } - + @Override public void help(CommandSender sender) { if (((ImageMaps) getPlugin()).isGlowingSupported()) { @@ -42,18 +41,18 @@ public class ImageMapHelpCommand extends HelpCommand { MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, buildMessage("/imagemap list [page]", " - lists all files in the images folder")); MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, buildMessage("/imagemap help [command]", " - shows help")); } - + private static BaseComponent buildMessage(String str1, String str2) { BaseComponent combined = new TextComponent(); - + BaseComponent comp1 = new TextComponent(str1); comp1.setColor(ChatColor.WHITE); BaseComponent comp2 = new TextComponent(str2); comp2.setColor(ChatColor.GRAY); - + combined.addExtra(comp1); combined.addExtra(comp2); - + return combined; } } diff --git a/src/main/java/net/craftcitizen/imagemaps/ImageMapInfoCommand.java b/src/main/java/net/craftcitizen/imagemaps/ImageMapInfoCommand.java index 22d8432..fb7df8e 100644 --- a/src/main/java/net/craftcitizen/imagemaps/ImageMapInfoCommand.java +++ b/src/main/java/net/craftcitizen/imagemaps/ImageMapInfoCommand.java @@ -1,13 +1,5 @@ package net.craftcitizen.imagemaps; -import java.awt.image.BufferedImage; -import java.io.File; -import java.util.Collections; -import java.util.List; - -import org.bukkit.command.Command; -import org.bukkit.command.CommandSender; - import de.craftlancer.core.Utils; import de.craftlancer.core.util.MessageLevel; import de.craftlancer.core.util.MessageUtil; @@ -16,33 +8,40 @@ import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.chat.BaseComponent; import net.md_5.bungee.api.chat.ClickEvent; import net.md_5.bungee.api.chat.TextComponent; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; + +import java.awt.image.BufferedImage; +import java.io.File; +import java.util.Collections; +import java.util.List; public class ImageMapInfoCommand extends ImageMapSubCommand { - + public ImageMapInfoCommand(ImageMaps plugin) { super("imagemaps.info", plugin, true); } - + @Override protected String execute(CommandSender sender, Command cmd, String label, String[] args) { if (!checkSender(sender)) { MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.WARNING, "You can't run this command."); return null; } - + if (args.length < 2) { MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.WARNING, "You must specify a file name."); return null; } - + String filename = args[1]; BufferedImage image = getPlugin().getImage(filename); - + if (image == null) { MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.WARNING, "No image with this name exists."); return null; } - + Tuple size = getPlugin().getImageSize(filename, null); BaseComponent reloadAction = new TextComponent("[Reload]"); reloadAction.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, String.format("/imagemap reload \"%s\"", filename))); @@ -53,14 +52,14 @@ public class ImageMapInfoCommand extends ImageMapSubCommand { BaseComponent deleteAction = new TextComponent("[Delete]"); deleteAction.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, String.format("/imagemap delete \"%s\"", filename))); deleteAction.setColor(ChatColor.RED); - + BaseComponent actions = new TextComponent("Action: "); actions.addExtra(reloadAction); actions.addExtra(" "); actions.addExtra(placeAction); actions.addExtra(" "); actions.addExtra(deleteAction); - + MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.INFO, "Image Information: "); MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, String.format("File Name: %s", filename)); MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, String.format("Resolution: %dx%d", image.getWidth(), image.getHeight())); @@ -68,18 +67,18 @@ public class ImageMapInfoCommand extends ImageMapSubCommand { MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, actions); return null; } - + @Override public void help(CommandSender sender) { MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, "Displays information about an image."); MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.INFO, "Usage: /imagemap info "); } - + @Override protected List onTabComplete(CommandSender sender, String[] args) { if (args.length == 2) return Utils.getMatches(args[1], new File(plugin.getDataFolder(), "images").list()); - + return Collections.emptyList(); } } diff --git a/src/main/java/net/craftcitizen/imagemaps/ImageMapListCommand.java b/src/main/java/net/craftcitizen/imagemaps/ImageMapListCommand.java index eae95f3..0160c7e 100644 --- a/src/main/java/net/craftcitizen/imagemaps/ImageMapListCommand.java +++ b/src/main/java/net/craftcitizen/imagemaps/ImageMapListCommand.java @@ -1,24 +1,24 @@ package net.craftcitizen.imagemaps; -import java.io.File; -import org.bukkit.command.Command; -import org.bukkit.command.CommandSender; - import de.craftlancer.core.Utils; import de.craftlancer.core.util.MessageLevel; import de.craftlancer.core.util.MessageUtil; import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.chat.BaseComponent; import net.md_5.bungee.api.chat.ClickEvent; -import net.md_5.bungee.api.chat.TextComponent; import net.md_5.bungee.api.chat.ClickEvent.Action; +import net.md_5.bungee.api.chat.TextComponent; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; + +import java.io.File; public class ImageMapListCommand extends ImageMapSubCommand { - + public ImageMapListCommand(ImageMaps plugin) { super("imagemaps.list", plugin, true); } - + @Override protected String execute(CommandSender sender, Command cmd, String label, String[] args) { if (!checkSender(sender)) { @@ -29,12 +29,12 @@ public class ImageMapListCommand extends ImageMapSubCommand { String[] fileList = new File(plugin.getDataFolder(), "images").list(); long page = args.length >= 2 ? Utils.parseIntegerOrDefault(args[1], 0) - 1 : 0; int numPages = (int) Math.ceil((double) fileList.length / Utils.ELEMENTS_PER_PAGE); - - + + MessageUtil.sendMessage(plugin, sender, MessageLevel.INFO, String.format("## Image List Page %d of %d ##", page + 1, numPages)); - + boolean even = false; - for(String filename : Utils.paginate(fileList, page)) { + for (String filename : Utils.paginate(fileList, page)) { BaseComponent infoAction = new TextComponent("[Info]"); infoAction.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, String.format("/imagemap info \"%s\"", filename))); infoAction.setColor(ChatColor.GOLD); @@ -47,7 +47,7 @@ public class ImageMapListCommand extends ImageMapSubCommand { BaseComponent deleteAction = new TextComponent("[Delete]"); deleteAction.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, String.format("/imagemap delete \"%s\"", filename))); deleteAction.setColor(ChatColor.RED); - + BaseComponent message = new TextComponent(filename); message.setColor(even ? ChatColor.GRAY : ChatColor.WHITE); message.addExtra(" "); @@ -58,24 +58,24 @@ public class ImageMapListCommand extends ImageMapSubCommand { message.addExtra(placeAction); message.addExtra(" "); message.addExtra(deleteAction); - + MessageUtil.sendMessage(plugin, sender, MessageLevel.NORMAL, message); even = !even; } - + BaseComponent navigation = new TextComponent(); BaseComponent prevPage = new TextComponent(String.format("<< Page %d", Math.max(page, 1))); BaseComponent nextPage = new TextComponent(String.format("Page %d >>", Math.min(page + 1, numPages))); prevPage.setClickEvent(new ClickEvent(Action.RUN_COMMAND, "/imagemap list " + Math.max(page, 1))); nextPage.setClickEvent(new ClickEvent(Action.RUN_COMMAND, "/imagemap list " + Math.min(page + 2, numPages))); - + navigation.addExtra(prevPage); navigation.addExtra(" | "); navigation.addExtra(nextPage); MessageUtil.sendMessage(plugin, sender, MessageLevel.INFO, navigation); return null; } - + @Override public void help(CommandSender sender) { MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, "Lists all files in the images folder."); diff --git a/src/main/java/net/craftcitizen/imagemaps/ImageMapPlaceCommand.java b/src/main/java/net/craftcitizen/imagemaps/ImageMapPlaceCommand.java index 947e79f..23096f9 100644 --- a/src/main/java/net/craftcitizen/imagemaps/ImageMapPlaceCommand.java +++ b/src/main/java/net/craftcitizen/imagemaps/ImageMapPlaceCommand.java @@ -1,44 +1,43 @@ package net.craftcitizen.imagemaps; +import de.craftlancer.core.Utils; +import de.craftlancer.core.util.MessageLevel; +import de.craftlancer.core.util.MessageUtil; +import de.craftlancer.core.util.Tuple; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.metadata.FixedMetadataValue; + import java.io.File; import java.util.Arrays; import java.util.Collections; import java.util.List; -import org.bukkit.command.Command; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; -import org.bukkit.metadata.FixedMetadataValue; - -import de.craftlancer.core.Utils; -import de.craftlancer.core.util.MessageLevel; -import de.craftlancer.core.util.MessageUtil; -import de.craftlancer.core.util.Tuple; - public class ImageMapPlaceCommand extends ImageMapSubCommand { - + public ImageMapPlaceCommand(ImageMaps plugin) { super("imagemaps.place", plugin, false); } - + @Override protected String execute(CommandSender sender, Command cmd, String label, String[] args) { if (!checkSender(sender)) { MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.WARNING, "You can't run this command."); return null; } - + if (args.length < 2) { MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.WARNING, "You must specify a file name."); return null; } - + String filename = args[1]; boolean isInvisible = false; boolean isFixed = false; boolean isGlowing = false; Tuple scale; - + if (getPlugin().isInvisibilitySupported()) { isInvisible = args.length >= 3 && Boolean.parseBoolean(args[2]); isFixed = args.length >= 4 && Boolean.parseBoolean(args[3]); @@ -56,28 +55,28 @@ public class ImageMapPlaceCommand extends ImageMapSubCommand { MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.WARNING, "Filename contains illegal character."); return null; } - + if (!getPlugin().hasImage(filename)) { MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.WARNING, "No image with this name exists."); return null; } - + Player player = (Player) sender; player.setMetadata(ImageMaps.PLACEMENT_METADATA, new FixedMetadataValue(getPlugin(), new PlacementData(filename, isInvisible, isFixed, isGlowing, scale))); - + Tuple size = getPlugin().getImageSize(filename, scale); MessageUtil.sendMessage(getPlugin(), - sender, - MessageLevel.NORMAL, - String.format("Started placing of %s. It needs a %d by %d area.", args[1], size.getKey(), size.getValue())); + sender, + MessageLevel.NORMAL, + String.format("Started placing of %s. It needs a %d by %d area.", args[1], size.getKey(), size.getValue())); MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, "Right click on the block, that should be the upper left corner."); return null; } - + @Override public void help(CommandSender sender) { MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, "Starts placing an image."); - + if (getPlugin().isGlowingSupported()) { MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.INFO, "Usage: /imagemap place [frameInvisible] [frameFixed] [frameGlowing] [size]"); } else if (getPlugin().isInvisibilitySupported()) { @@ -88,24 +87,24 @@ public class ImageMapPlaceCommand extends ImageMapSubCommand { MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, "Size format: XxY -> 5x2, use -1 for default"); MessageUtil.sendMessage(getPlugin(), - sender, - MessageLevel.NORMAL, - "The plugin will scale the map to not be larger than the given size while maintaining the aspect ratio."); + sender, + MessageLevel.NORMAL, + "The plugin will scale the map to not be larger than the given size while maintaining the aspect ratio."); MessageUtil.sendMessage(getPlugin(), - sender, - MessageLevel.NORMAL, - "It's recommended to avoid the size function in favor of using properly sized source images."); + sender, + MessageLevel.NORMAL, + "It's recommended to avoid the size function in favor of using properly sized source images."); } - + private static Tuple parseScale(String string) { String[] tmp = string.split("x"); - + if (tmp.length < 2) return new Tuple<>(-1, -1); - + return new Tuple<>(Utils.parseIntegerOrDefault(tmp[0], -1), Utils.parseIntegerOrDefault(tmp[1], -1)); } - + @Override protected List onTabComplete(CommandSender sender, String[] args) { if (args.length > 2 && !getPlugin().isInvisibilitySupported() diff --git a/src/main/java/net/craftcitizen/imagemaps/ImageMapReloadCommand.java b/src/main/java/net/craftcitizen/imagemaps/ImageMapReloadCommand.java index 6b6de91..1a79386 100644 --- a/src/main/java/net/craftcitizen/imagemaps/ImageMapReloadCommand.java +++ b/src/main/java/net/craftcitizen/imagemaps/ImageMapReloadCommand.java @@ -1,62 +1,61 @@ package net.craftcitizen.imagemaps; +import de.craftlancer.core.Utils; +import de.craftlancer.core.util.MessageLevel; +import de.craftlancer.core.util.MessageUtil; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; + import java.io.File; import java.util.Collections; import java.util.List; -import org.bukkit.command.Command; -import org.bukkit.command.CommandSender; - -import de.craftlancer.core.Utils; -import de.craftlancer.core.util.MessageLevel; -import de.craftlancer.core.util.MessageUtil; - public class ImageMapReloadCommand extends ImageMapSubCommand { - + public ImageMapReloadCommand(ImageMaps plugin) { super("imagemap.reload", plugin, true); } - + @Override protected String execute(CommandSender sender, Command cmd, String label, String[] args) { if (!checkSender(sender)) { MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.WARNING, "You can't run this command."); return null; } - + if (args.length < 2) { MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.WARNING, "You must specify a file name."); return null; } - + String filename = args[1]; - + if (filename.contains("/") || filename.contains("\\") || filename.contains(":")) { MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.WARNING, "Filename contains illegal character."); return null; } - + if (getPlugin().reloadImage(filename)) MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, "Image reloaded."); else MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, "Image couldn't be reloaded (does it exist?)."); - + return null; } - + @Override public void help(CommandSender sender) { MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, "Reloads an image from disk, to be used when the file changed."); MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, "Avoid resolution changes, since they won't be scaled."); MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.INFO, "Usage: /imagemap reload "); } - + @Override protected List onTabComplete(CommandSender sender, String[] args) { if (args.length == 2) return Utils.getMatches(args[1], new File(plugin.getDataFolder(), "images").list()); - + return Collections.emptyList(); } - + } diff --git a/src/main/java/net/craftcitizen/imagemaps/ImageMapRenderer.java b/src/main/java/net/craftcitizen/imagemaps/ImageMapRenderer.java index 17fb6d1..bd15fa8 100644 --- a/src/main/java/net/craftcitizen/imagemaps/ImageMapRenderer.java +++ b/src/main/java/net/craftcitizen/imagemaps/ImageMapRenderer.java @@ -1,27 +1,26 @@ package net.craftcitizen.imagemaps; -import java.awt.geom.AffineTransform; -import java.awt.image.AffineTransformOp; -import java.awt.image.BufferedImage; - +import de.craftlancer.core.LambdaRunnable; import org.bukkit.entity.Player; import org.bukkit.map.MapCanvas; import org.bukkit.map.MapPalette; import org.bukkit.map.MapRenderer; import org.bukkit.map.MapView; -import de.craftlancer.core.LambdaRunnable; +import java.awt.geom.AffineTransform; +import java.awt.image.AffineTransformOp; +import java.awt.image.BufferedImage; public class ImageMapRenderer extends MapRenderer { private ImageMaps plugin; - + private BufferedImage image = null; private boolean first = true; - + private final int x; private final int y; private final double scale; - + public ImageMapRenderer(ImageMaps plugin, BufferedImage image, int x, int y, double scale) { this.plugin = plugin; this.x = x; @@ -29,22 +28,22 @@ public class ImageMapRenderer extends MapRenderer { this.scale = scale; recalculateInput(image); } - + public void recalculateInput(BufferedImage input) { if (x * ImageMaps.MAP_WIDTH > Math.round(input.getWidth() * scale) || y * ImageMaps.MAP_HEIGHT > Math.round(input.getHeight() * scale)) return; - + int x1 = (int) Math.floor(x * ImageMaps.MAP_WIDTH / scale); int y1 = (int) Math.floor(y * ImageMaps.MAP_HEIGHT / scale); - + int x2 = (int) Math.ceil(Math.min(input.getWidth(), ((x + 1) * ImageMaps.MAP_WIDTH / scale))); int y2 = (int) Math.ceil(Math.min(input.getHeight(), ((y + 1) * ImageMaps.MAP_HEIGHT / scale))); - + if (x2 - x1 <= 0 || y2 - y1 <= 0) return; - + this.image = input.getSubimage(x1, y1, x2 - x1, y2 - y1); - + if (scale != 1D) { BufferedImage resized = new BufferedImage(ImageMaps.MAP_WIDTH, ImageMaps.MAP_HEIGHT, input.getType() == 0 ? image.getType() : input.getType()); AffineTransform at = new AffineTransform(); @@ -52,17 +51,17 @@ public class ImageMapRenderer extends MapRenderer { AffineTransformOp scaleOp = new AffineTransformOp(at, AffineTransformOp.TYPE_BILINEAR); this.image = scaleOp.filter(this.image, resized); } - + first = true; } - + @Override public void render(MapView view, MapCanvas canvas, Player player) { if (image != null && first) { new LambdaRunnable(() -> { @SuppressWarnings("deprecation") byte[] imageData = MapPalette.imageToBytes(image); - + new LambdaRunnable(() -> { for (int x2 = 0; x2 < image.getWidth(null); ++x2) { for (int y2 = 0; y2 < image.getHeight(null); ++y2) { @@ -71,10 +70,10 @@ public class ImageMapRenderer extends MapRenderer { } }).runTaskLater(plugin, System.nanoTime() % 20); // spread out pseudo randomly in a very naive way - + }).runTaskAsynchronously(plugin); first = false; } } - + } diff --git a/src/main/java/net/craftcitizen/imagemaps/ImageMapSubCommand.java b/src/main/java/net/craftcitizen/imagemaps/ImageMapSubCommand.java index d9c975b..2e81216 100644 --- a/src/main/java/net/craftcitizen/imagemaps/ImageMapSubCommand.java +++ b/src/main/java/net/craftcitizen/imagemaps/ImageMapSubCommand.java @@ -3,7 +3,7 @@ package net.craftcitizen.imagemaps; import de.craftlancer.core.command.SubCommand; public abstract class ImageMapSubCommand extends SubCommand { - + public ImageMapSubCommand(String permission, ImageMaps plugin, boolean console) { super(permission, plugin, console); } diff --git a/src/main/java/net/craftcitizen/imagemaps/ImageMaps.java b/src/main/java/net/craftcitizen/imagemaps/ImageMaps.java index fbf8437..e091716 100644 --- a/src/main/java/net/craftcitizen/imagemaps/ImageMaps.java +++ b/src/main/java/net/craftcitizen/imagemaps/ImageMaps.java @@ -1,17 +1,16 @@ package net.craftcitizen.imagemaps; -import java.awt.image.BufferedImage; -import java.io.File; -import java.io.IOException; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; -import java.util.Map.Entry; -import java.util.logging.Level; -import java.util.stream.Collectors; - -import javax.imageio.ImageIO; - +import com.google.common.io.Files; +import de.craftlancer.core.LambdaRunnable; +import de.craftlancer.core.SemanticVersion; +import de.craftlancer.core.Utils; +import de.craftlancer.core.util.MessageLevel; +import de.craftlancer.core.util.MessageUtil; +import de.craftlancer.core.util.Tuple; +import net.md_5.bungee.api.ChatColor; +import net.md_5.bungee.api.chat.BaseComponent; +import net.md_5.bungee.api.chat.ComponentBuilder; +import net.md_5.bungee.api.chat.TextComponent; import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.Rotation; @@ -22,11 +21,7 @@ import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.configuration.serialization.ConfigurationSerialization; -import org.bukkit.entity.EntityType; -import org.bukkit.entity.GlowItemFrame; -import org.bukkit.entity.Hanging; -import org.bukkit.entity.ItemFrame; -import org.bukkit.entity.Player; +import org.bukkit.entity.*; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.block.Action; @@ -38,18 +33,16 @@ import org.bukkit.map.MapView; import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.scheduler.BukkitRunnable; -import com.google.common.io.Files; - -import de.craftlancer.core.LambdaRunnable; -import de.craftlancer.core.SemanticVersion; -import de.craftlancer.core.Utils; -import de.craftlancer.core.util.MessageLevel; -import de.craftlancer.core.util.MessageUtil; -import de.craftlancer.core.util.Tuple; -import net.md_5.bungee.api.ChatColor; -import net.md_5.bungee.api.chat.BaseComponent; -import net.md_5.bungee.api.chat.ComponentBuilder; -import net.md_5.bungee.api.chat.TextComponent; +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.logging.Level; +import java.util.stream.Collectors; // TODO permissions per image or folder // TODO per-user maps @@ -58,80 +51,79 @@ public class ImageMaps extends JavaPlugin implements Listener { private static final String CONFIG_VERSION_KEY = "storageVersion"; private static final int CONFIG_VERSION = 1; private static final long AUTOSAVE_PERIOD = 18000L; // 15 minutes - + public static final String PLACEMENT_METADATA = "imagemaps.place"; - + public static final int MAP_WIDTH = 128; public static final int MAP_HEIGHT = 128; private static final String IMAGES_DIR = "images"; - + private Map imageCache = new HashMap<>(); private Map maps = new HashMap<>(); - + private Material toggleItem; - + static { ConfigurationSerialization.registerClass(ImageMap.class); } - + @Override public void onEnable() { BaseComponent prefix = new TextComponent( new ComponentBuilder("[").color(ChatColor.GRAY).append("ImageMaps").color(ChatColor.AQUA).append("]").color(ChatColor.GRAY).create()); MessageUtil.registerPlugin(this, prefix, ChatColor.GRAY, ChatColor.YELLOW, ChatColor.RED, ChatColor.DARK_RED, ChatColor.DARK_AQUA); - + if (!new File(getDataFolder(), IMAGES_DIR).exists()) new File(getDataFolder(), IMAGES_DIR).mkdirs(); - + saveDefaultConfig(); - + toggleItem = Material.matchMaterial(getConfig().getString("toggleItem", Material.WOODEN_HOE.name())); if (toggleItem == null) { toggleItem = Material.WOODEN_HOE; getLogger().warning("Given toggleItem is invalid, defaulting to WOODEN_HOE"); } - + getCommand("imagemap").setExecutor(new ImageMapCommandHandler(this)); getServer().getPluginManager().registerEvents(this, this); - + loadMaps(); - + new LambdaRunnable(this::saveMaps).runTaskTimer(this, AUTOSAVE_PERIOD, AUTOSAVE_PERIOD); } - + @Override public void onDisable() { saveMaps(); } - + @EventHandler(ignoreCancelled = true) public void onToggleFrameProperty(PlayerInteractEntityEvent event) { if (!isInvisibilitySupported()) return; - + if (event.getRightClicked().getType() != EntityType.ITEM_FRAME) return; - + ItemFrame frame = (ItemFrame) event.getRightClicked(); Player p = event.getPlayer(); - + if (p.getInventory().getItemInMainHand().getType() != toggleItem) return; - + if (p.isSneaking()) { if (p.hasPermission("imagemaps.toggleFixed")) { event.setCancelled(true); frame.setFixed(!frame.isFixed()); MessageUtil.sendMessage(this, p, MessageLevel.INFO, String.format("Frame set to %s.", frame.isFixed() ? "fixed" : "unfixed")); } - } - else if (p.hasPermission("imagemaps.toggleVisible")) { + } else if (p.hasPermission("imagemaps.toggleVisible")) { event.setCancelled(true); frame.setVisible(!frame.isVisible()); MessageUtil.sendMessage(this, p, MessageLevel.INFO, String.format("Frame set to %s.", frame.isVisible() ? "visible" : "invisible")); } } - + public boolean isInvisibilitySupported() { SemanticVersion version = Utils.getMCVersion(); return version.getMajor() >= 1 && version.getMinor() >= 16; @@ -144,52 +136,51 @@ public class ImageMaps extends JavaPlugin implements Listener { public boolean isUpDownFaceSupported() { SemanticVersion version = Utils.getMCVersion(); - + if (version.getMajor() < 1) return false; if (version.getMajor() == 1 && version.getMinor() == 14 && version.getRevision() >= 4) return true; return version.getMinor() > 14; } - + public boolean isSetTrackingSupported() { SemanticVersion version = Utils.getMCVersion(); - + return version.getMajor() >= 1 && version.getMinor() >= 14; } - + private void saveMaps() { FileConfiguration config = new YamlConfiguration(); config.set(CONFIG_VERSION_KEY, CONFIG_VERSION); config.set("maps", maps.entrySet().stream().collect(Collectors.toMap(Entry::getValue, Entry::getKey))); - + BukkitRunnable saveTask = new LambdaRunnable(() -> { try { config.save(new File(getDataFolder(), MAPS_YML)); - } - catch (IOException e) { + } catch (IOException e) { e.printStackTrace(); } }); - + if (isEnabled()) saveTask.runTaskAsynchronously(this); else saveTask.run(); } - + private void loadMaps() { File configFile = new File(getDataFolder(), MAPS_YML); - - if(!configFile.exists()) + + if (!configFile.exists()) return; - + Configuration config = YamlConfiguration.loadConfiguration(configFile); int version = config.getInt(CONFIG_VERSION_KEY, -1); - + if (version == -1) config = convertLegacyMaps(config); - + ConfigurationSection section = config.getConfigurationSection("maps"); if (section != null) section.getValues(false).forEach((a, b) -> { @@ -198,7 +189,7 @@ public class ImageMaps extends JavaPlugin implements Listener { @SuppressWarnings("deprecation") MapView map = Bukkit.getMap(id); BufferedImage image = getImage(imageMap.getFilename()); - + if (image == null) { getLogger().warning(() -> "Image file " + imageMap.getFilename() + " not found. Removing map!"); return; @@ -208,27 +199,26 @@ public class ImageMaps extends JavaPlugin implements Listener { return; } - if(isSetTrackingSupported()) + if (isSetTrackingSupported()) map.setTrackingPosition(false); map.getRenderers().forEach(map::removeRenderer); map.addRenderer(new ImageMapRenderer(this, image, imageMap.getX(), imageMap.getY(), imageMap.getScale())); maps.put(imageMap, id); }); } - + private Configuration convertLegacyMaps(Configuration config) { getLogger().info("Converting maps from Version <1.0"); - + try { Files.copy(new File(getDataFolder(), MAPS_YML), new File(getDataFolder(), MAPS_YML + ".backup")); - } - catch (IOException e) { + } catch (IOException e) { getLogger().severe("Failed to backup maps.yml!"); e.printStackTrace(); } - + Map map = new HashMap<>(); - + for (String key : config.getKeys(false)) { int id = Integer.parseInt(key); String image = config.getString(key + ".image"); @@ -237,67 +227,66 @@ public class ImageMaps extends JavaPlugin implements Listener { double scale = config.getDouble(key + ".scale", 1.0); map.put(id, new ImageMap(image, x, y, scale)); } - + config = new YamlConfiguration(); config.set(CONFIG_VERSION_KEY, CONFIG_VERSION); config.createSection("maps", map); return config; } - + public boolean hasImage(String filename) { if (imageCache.containsKey(filename.toLowerCase())) return true; - + File file = new File(getDataFolder(), IMAGES_DIR + File.separatorChar + filename); - + return file.exists() && getImage(filename) != null; } - + public BufferedImage getImage(String filename) { if (filename.contains("/") || filename.contains("\\") || filename.contains(":")) { getLogger().warning("Someone tried to get image with illegal characters in file name."); return null; } - + if (imageCache.containsKey(filename.toLowerCase())) return imageCache.get(filename.toLowerCase()); - + File file = new File(getDataFolder(), IMAGES_DIR + File.separatorChar + filename); BufferedImage image = null; - + if (!file.exists()) return null; - + try { image = ImageIO.read(file); imageCache.put(filename.toLowerCase(), image); - } - catch (IOException e) { + } catch (IOException e) { getLogger().log(Level.SEVERE, String.format("Error while trying to read image %s.", file.getName()), e); } - + return image; } - + @EventHandler public void onInteract(PlayerInteractEvent event) { Player player = event.getPlayer(); - + if (!player.hasMetadata(PLACEMENT_METADATA)) return; - + if (event.getAction() == Action.RIGHT_CLICK_AIR) { player.removeMetadata(PLACEMENT_METADATA, this); MessageUtil.sendMessage(this, player, MessageLevel.NORMAL, "Image placement cancelled."); return; } - + if (event.getAction() != Action.RIGHT_CLICK_BLOCK) return; - + PlacementData data = (PlacementData) player.getMetadata(PLACEMENT_METADATA).get(0).value(); PlacementResult result = placeImage(player, event.getClickedBlock(), event.getBlockFace(), data); - + switch (result) { case INVALID_FACING: MessageUtil.sendMessage(this, player, MessageLevel.WARNING, "You can't place an image on this block face."); @@ -320,34 +309,34 @@ public class ImageMaps extends JavaPlugin implements Listener { case SUCCESS: break; } - + player.removeMetadata(PLACEMENT_METADATA, this); event.setCancelled(true); } - + private PlacementResult placeImage(Player player, Block block, BlockFace face, PlacementData data) { if (!isAxisAligned(face)) { getLogger().severe("Someone tried to create an image with an invalid block facing"); return PlacementResult.INVALID_FACING; } - + if (face.getModY() != 0 && !isUpDownFaceSupported()) return PlacementResult.INVALID_FACING; - + Block b = block.getRelative(face); BufferedImage image = getImage(data.getFilename()); Tuple size = getImageSize(data.getFilename(), data.getSize()); BlockFace widthDirection = calculateWidthDirection(player, face); BlockFace heightDirection = calculateHeightDirection(player, face); - + if (widthDirection == null || heightDirection == null) return PlacementResult.INVALID_DIRECTION; - + // check for space for (int x = 0; x < size.getKey(); x++) for (int y = 0; y < size.getValue(); y++) { Block frameBlock = b.getRelative(widthDirection, x).getRelative(heightDirection, y); - + if (!block.getRelative(widthDirection, x).getRelative(heightDirection, y).getType().isSolid()) return PlacementResult.INSUFFICIENT_WALL; if (frameBlock.getType().isSolid()) @@ -355,12 +344,12 @@ public class ImageMaps extends JavaPlugin implements Listener { if (!b.getWorld().getNearbyEntities(frameBlock.getLocation().add(0.5, 0.5, 0.5), 0.5, 0.5, 0.5, Hanging.class::isInstance).isEmpty()) return PlacementResult.OVERLAPPING_ENTITY; } - + ImagePlaceEvent event = new ImagePlaceEvent(player, block, widthDirection, heightDirection, size.getKey(), size.getValue(), data); Bukkit.getPluginManager().callEvent(event); if (event.isCancelled()) return PlacementResult.EVENT_CANCELLED; - + // spawn item frame for (int x = 0; x < size.getKey(); x++) for (int y = 0; y < size.getValue(); y++) { @@ -369,13 +358,13 @@ public class ImageMaps extends JavaPlugin implements Listener { frame.setFacingDirection(face); frame.setItem(getMapItem(image, x, y, data)); frame.setRotation(facingToRotation(heightDirection, widthDirection)); - + if (isInvisibilitySupported()) { frame.setFixed(data.isFixed()); frame.setVisible(!data.isInvisible()); } } - + return PlacementResult.SUCCESS; } @@ -415,24 +404,24 @@ public class ImageMaps extends JavaPlugin implements Listener { public boolean reloadImage(String filename) { if (!imageCache.containsKey(filename.toLowerCase())) return false; - + imageCache.remove(filename.toLowerCase()); BufferedImage image = getImage(filename); - + if (image == null) { getLogger().warning(() -> "Failed to reload image: " + filename); return false; } - + maps.entrySet().stream().filter(a -> a.getKey().getFilename().equalsIgnoreCase(filename)).map(a -> Bukkit.getMap(a.getValue())) - .flatMap(a -> a.getRenderers().stream()).filter(ImageMapRenderer.class::isInstance).forEach(a -> ((ImageMapRenderer) a).recalculateInput(image)); + .flatMap(a -> a.getRenderers().stream()).filter(ImageMapRenderer.class::isInstance).forEach(a -> ((ImageMapRenderer) a).recalculateInput(image)); return true; } - + @SuppressWarnings("deprecation") private ItemStack getMapItem(BufferedImage image, int x, int y, PlacementData data) { ItemStack item = new ItemStack(Material.FILLED_MAP); - + ImageMap imageMap = new ImageMap(data.getFilename(), x, y, getScale(image, data.getSize())); if (maps.containsKey(imageMap)) { MapMeta meta = (MapMeta) item.getItemMeta(); @@ -440,62 +429,62 @@ public class ImageMaps extends JavaPlugin implements Listener { item.setItemMeta(meta); return item; } - + MapView map = getServer().createMap(getServer().getWorlds().get(0)); map.getRenderers().forEach(map::removeRenderer); map.addRenderer(new ImageMapRenderer(this, image, x, y, getScale(image, data.getSize()))); - if(isSetTrackingSupported()) + if (isSetTrackingSupported()) map.setTrackingPosition(false); - + MapMeta meta = ((MapMeta) item.getItemMeta()); meta.setMapView(map); item.setItemMeta(meta); maps.put(imageMap, map.getId()); - + return item; } - + public Tuple getImageSize(String filename, Tuple size) { BufferedImage image = getImage(filename); - + if (image == null) return new Tuple<>(0, 0); - + double finalScale = getScale(image, size); int finalX = (int) ((MAP_WIDTH - 1 + Math.ceil(image.getWidth() * finalScale)) / MAP_WIDTH); int finalY = (int) ((MAP_HEIGHT - 1 + Math.ceil(image.getHeight() * finalScale)) / MAP_HEIGHT); - + return new Tuple<>(finalX, finalY); } - + public double getScale(String filename, Tuple size) { return getScale(getImage(filename), size); } - + public double getScale(BufferedImage image, Tuple size) { if (image == null) return 1.0; - + int baseX = image.getWidth(); int baseY = image.getHeight(); - + double finalScale = 1D; - + if (size != null) { int targetX = size.getKey() * MAP_WIDTH; int targetY = size.getValue() * MAP_HEIGHT; - + double scaleX = size.getKey() > 0 ? (double) targetX / baseX : Double.MAX_VALUE; double scaleY = size.getValue() > 0 ? (double) targetY / baseY : Double.MAX_VALUE; - + finalScale = Math.min(scaleX, scaleY); if (finalScale >= Double.MAX_VALUE) finalScale = 1D; } - + return finalScale; } - + private static Rotation facingToRotation(BlockFace heightDirection, BlockFace widthDirection) { switch (heightDirection) { case WEST: @@ -510,7 +499,7 @@ public class ImageMaps extends JavaPlugin implements Listener { return Rotation.NONE; } } - + private static BlockFace calculateWidthDirection(Player player, BlockFace face) { float yaw = (360.0f + player.getLocation().getYaw()) % 360.0f; switch (face) { @@ -536,7 +525,7 @@ public class ImageMaps extends JavaPlugin implements Listener { return null; } } - + private static BlockFace calculateHeightDirection(Player player, BlockFace face) { float yaw = (360.0f + player.getLocation().getYaw()) % 360.0f; switch (face) { @@ -567,7 +556,7 @@ public class ImageMaps extends JavaPlugin implements Listener { return null; } } - + private static boolean isAxisAligned(BlockFace face) { switch (face) { case DOWN: diff --git a/src/main/java/net/craftcitizen/imagemaps/ImagePlaceEvent.java b/src/main/java/net/craftcitizen/imagemaps/ImagePlaceEvent.java index 93a824a..6f981b6 100644 --- a/src/main/java/net/craftcitizen/imagemaps/ImagePlaceEvent.java +++ b/src/main/java/net/craftcitizen/imagemaps/ImagePlaceEvent.java @@ -12,7 +12,7 @@ import org.bukkit.event.HandlerList; */ public class ImagePlaceEvent extends Event implements Cancellable { private static final HandlerList handlers = new HandlerList(); - + private final Player player; private final Block block; private final BlockFace widthDirection; @@ -20,9 +20,9 @@ public class ImagePlaceEvent extends Event implements Cancellable { private final int width; private final int height; private final PlacementData cache; - + private boolean cancelled; - + public ImagePlaceEvent(Player player, Block block, BlockFace widthDirection, BlockFace heightDirection, int width, int height, PlacementData cache) { this.player = player; this.block = block; @@ -32,18 +32,19 @@ public class ImagePlaceEvent extends Event implements Cancellable { this.height = height; this.cache = cache; } - + /** - * The player attempting to place the image + * The player attempting to place the image + * * @return the player attempting to place the image */ public Player getPlayer() { return player; } - + /** * The initial block the image is placed against. - * + * * @return the initial block the image is placed against */ public Block getBlock() { @@ -52,7 +53,7 @@ public class ImagePlaceEvent extends Event implements Cancellable { /** * The direction in which maps are placed in the height direction of the image. - * + * * @return the height direction of the map placement */ public BlockFace getHeightDirection() { @@ -61,16 +62,16 @@ public class ImagePlaceEvent extends Event implements Cancellable { /** * The direction in which maps are placed in the width direction of the image. - * + * * @return the width direction of the map placement */ public BlockFace getWidthDirection() { return widthDirection; } - + /** * The width of the image in maps - * + * * @return the width of the image in maps */ public int getWidth() { @@ -79,37 +80,37 @@ public class ImagePlaceEvent extends Event implements Cancellable { /** * The height of the image in maps - * + * * @return the height of the image in maps */ public int getHeight() { return height; } - + /** * The placement data used to place the image - * + * * @return the placement data */ public PlacementData getCacheEntry() { return cache; } - + @Override public boolean isCancelled() { return cancelled; } - + @Override public void setCancelled(boolean cancel) { this.cancelled = cancel; } - + @Override public HandlerList getHandlers() { return getHandlerList(); } - + public static HandlerList getHandlerList() { return handlers; } diff --git a/src/main/java/net/craftcitizen/imagemaps/PlacementData.java b/src/main/java/net/craftcitizen/imagemaps/PlacementData.java index 5197baf..5049067 100644 --- a/src/main/java/net/craftcitizen/imagemaps/PlacementData.java +++ b/src/main/java/net/craftcitizen/imagemaps/PlacementData.java @@ -6,13 +6,13 @@ import de.craftlancer.core.util.Tuple; * Data associated with placing an image. */ public class PlacementData { - + private final String filename; private final boolean isInvisible; private final boolean isFixed; private final boolean isGlowing; private final Tuple scale; - + public PlacementData(String filename, boolean isInvisible, boolean isFixed, boolean isGlowing, Tuple scale) { this.filename = filename; this.isInvisible = isInvisible; @@ -20,34 +20,34 @@ public class PlacementData { this.isGlowing = isGlowing; this.scale = scale; } - + /** * The file name of the image to be placed - * + * * @return the file name of the image */ public String getFilename() { return filename; } - + /** * Whether the placed item frame will have the "fixed" property set. * A fixed frame can't be destroyed or modified by survival players. - * + *

* Only supported in 1.16 or higher! - * + * * @return whether the placed frames will be fixed or not */ public boolean isFixed() { return isFixed; } - + /** * Whether the placed item frame will have the "invisible" property set. * An invisible frame won't be rendered, leaving only the item/map visible. - * + *

* Only supported in 1.16 or higher! - * + * * @return whether the placed frames will be invisible or not */ public boolean isInvisible() { @@ -57,7 +57,7 @@ public class PlacementData { /** * Whether the placed item frame will be a glowing one. - * + *

* Only supported in 1.17 or higher! * * @return whether the placed frames will be a glowing one @@ -69,9 +69,9 @@ public class PlacementData { /** * The requested size of the image. The actual size might be smaller * since the plugin won't modify aspect ratios. - * + *

* Values of -1 stand for the default value of an unscaled map. - * + * * @return the requested size of the image */ public Tuple getSize() {