From b70a163c3c63378b3fd1dbd9b2cbeb5f032f09e4 Mon Sep 17 00:00:00 2001 From: SydMontague Date: Mon, 1 May 2023 11:05:42 +0200 Subject: [PATCH] Update 1.0.11 - potentially fix color cache error - added /imagemap cleanup command - added /imagemap debuginfo command --- .github/FUNDING.yml | 24 +- .github/workflows/maven.yml | 66 +- LICENSE | 42 +- README.MD | 196 +-- pom.xml | 228 ++-- .../net/craftcitizen/imagemaps/ImageMap.java | 183 +-- .../imagemaps/ImageMapCleanupCommand.java | 37 + .../imagemaps/ImageMapCommandHandler.java | 34 +- .../imagemaps/ImageMapDebugInfoCommand.java | 39 + .../imagemaps/ImageMapDeleteCommand.java | 129 +- .../imagemaps/ImageMapDownloadCommand.java | 236 ++-- .../imagemaps/ImageMapHelpCommand.java | 125 +- .../imagemaps/ImageMapInfoCommand.java | 173 +-- .../imagemaps/ImageMapListCommand.java | 172 +-- .../imagemaps/ImageMapPlaceCommand.java | 261 ++-- .../imagemaps/ImageMapReloadCommand.java | 125 +- .../imagemaps/ImageMapRenderer.java | 148 +- .../imagemaps/ImageMapSubCommand.java | 30 +- .../net/craftcitizen/imagemaps/ImageMaps.java | 1187 +++++++++-------- .../imagemaps/ImagePlaceEvent.java | 235 ++-- .../craftcitizen/imagemaps/PlacementData.java | 160 +-- .../imagemaps/PlacementResult.java | 22 +- src/main/resources/plugin.yml | 100 +- 23 files changed, 2050 insertions(+), 1902 deletions(-) create mode 100644 src/main/java/net/craftcitizen/imagemaps/ImageMapCleanupCommand.java create mode 100644 src/main/java/net/craftcitizen/imagemaps/ImageMapDebugInfoCommand.java diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 231da55..96e6f64 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,13 +1,13 @@ -# These are supported funding model platforms - -custom: ["paypal.me/sydmontague"] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] - -# github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] -# patreon: # Replace with a single Patreon username -# open_collective: # Replace with a single Open Collective username -# ko_fi: # Replace with a single Ko-fi username -# tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel -# community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry -# liberapay: # Replace with a single Liberapay username -# issuehunt: # Replace with a single IssueHunt username +# These are supported funding model platforms + +custom: ["paypal.me/sydmontague"] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] + +# github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] +# patreon: # Replace with a single Patreon username +# open_collective: # Replace with a single Open Collective username +# ko_fi: # Replace with a single Ko-fi username +# tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +# community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +# liberapay: # Replace with a single Liberapay username +# issuehunt: # Replace with a single IssueHunt username # otechie: # Replace with a single Otechie username \ No newline at end of file diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 0071b00..14d7dc9 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -1,33 +1,33 @@ -# This workflow will build a Java project with Maven -# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven - -name: Java CI with Maven - -on: - push: - branches: [ master ] - -jobs: - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - name: Set up JDK 1.8 - uses: actions/setup-java@v1 - with: - java-version: 1.8 - - name: Build with Maven - run: | - mkdir -p ~/.m2 - echo "github${{ github.actor }}${{ github.token }}" > ~/.m2/settings.xml - mvn -B package --file pom.xml - - uses: actions/upload-artifact@v1 - with: - name: Package - path: target/ImageMaps.jar - - name: Publish to GitHub Packages Apache Maven - run: mvn deploy - env: - GITHUB_TOKEN: ${{ github.token }} # GITHUB_TOKEN is the default env for the password +# This workflow will build a Java project with Maven +# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven + +name: Java CI with Maven + +on: + push: + branches: [ master ] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up JDK 1.8 + uses: actions/setup-java@v1 + with: + java-version: 1.8 + - name: Build with Maven + run: | + mkdir -p ~/.m2 + echo "github${{ github.actor }}${{ github.token }}" > ~/.m2/settings.xml + mvn -B package --file pom.xml + - uses: actions/upload-artifact@v1 + with: + name: Package + path: target/ImageMaps.jar + - name: Publish to GitHub Packages Apache Maven + run: mvn deploy + env: + GITHUB_TOKEN: ${{ github.token }} # GITHUB_TOKEN is the default env for the password diff --git a/LICENSE b/LICENSE index 0c82f04..ab8d960 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,21 @@ -MIT License - -Copyright (c) 2020 SydMontague - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +MIT License + +Copyright (c) 2020 SydMontague + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.MD b/README.MD index 1afb215..f552fa2 100644 --- a/README.MD +++ b/README.MD @@ -1,99 +1,99 @@ -# ImageMaps - -ImageMaps is a plugin for Bukkit Minecraft servers that enables you to place custom images in your world -by rendering them onto maps and placing them in item frames. - -This can be used to create info graphics, custom paintings and even the appearance of custom textures. - -![Example Screenshot](./screenshot.png) - -## Installation -You will need a Server that implements Bukkit 1.13 or newer and the Spigot-API. Some features are only available in newer -releases. Make sure to use the latest release of your server software before reporting issues. - -Put the plugin into your servers `plugins` folder and start it up. A folder for the plugin will be created -automatically. You can place your images in the `images` folder inside it. - -### Upgrading from versions < 1.0.0 -The plugin should convert your maps.yml automatically. It will attempt to create a backup before doing so, -but it is recommended to do one manually as well. - -## Usage -In order for you use place images they must be present in the `images` folder. You can either put them -there via FTP or just copying it, or by using the plugin's download command, allowing you to access -images from the Internet. - -To place images just run the `/imagemap place` command and right click the block that should be the -upper left corner of the image (based on your perspective). - -Images will by default split into 128x128 pixel sub-images to fit on a map. The image may be scaled via -command parameters, however **it is recommended to prepare correctly scaled images**. - -The color palette of Minecraft maps is very limited, requiring the plugin/Bukkit to convert the input image. -This will result in your images looking different ingame than the source. The available color table can be -found here: https://minecraft.gamepedia.com/Map_item_format#Color_table - -![Example of color conversion](./colorConversion.png) - -The invisible and fixed (-> can't be modified) item frame properties are only available in 1.16+. You can modify them for any item frame -by using a configurable (default a wooden hoe) items. Right click to toggle visibility and Shift+Right click to -toggle the fixed state. - -### Commands -* /imagemap place <filename> [frameInvisible] [frameFixed] [frameGlowing] [size] - * Starts the placement process - * frameInvisible and frameFixed are only available on 1.16+ - * frameGlowing is only available on 1.17+ -* /imagemap download <filename> <sourceURL> - * downloads an image from the given URL into the `images` folder -* /imagemap delete <filename> - * deletes an image from the `images` folder -* /imagemap info <filename> - * prints info, such as resolution and expected size -* /imagemap reload <filename> - * reloads an image, to be used when the image file changed -* /imagemap list [page] - * lists all images in the `images` folder -* /imagemap help [command] - * prints help for commands - -### Permissions -`imagemaps.*` - grants access to all permissions -`imagemaps.place` - grants access to /imagemap place command -`imagemaps.download` - grants access to /imagemap download command -`imagemaps.delete` - grants access to /imagemap delete command -`imagemaps.info` - grants access to /imagemap info command -`imagemaps.list` - grants access to /imagemap list command -`imagemaps.reload` - grants access to /imagemap reload command -`imagemaps.help` - grants access to /imagemap help command -`imagemaps.toggleFixed` - allows to toggle the "fixed" state of frames with a wooden hoe -`imagemaps.toggleVisible` - allows to toggle the "visible" state of frames with a wooden hoe - -All permissions are default true for OPs. - - -## Maven -You will need to [configure your environment to access GitHub repositories](https://docs.github.com/en/packages/using-github-packages-with-your-projects-ecosystem/configuring-apache-maven-for-use-with-github-packages). - -``` - - github - ImageMaps repository - https://maven.pkg.github.com/SydMontague/ImageMaps - -``` -``` - - net.craftcitizen - imagemaps - 1.0.1 - -``` - -## Contact -* Discord: SydMontague#8056 -* directly on GitHub -* E-Mail: sydmontague@web.de -* Spigot: https://www.spigotmc.org/members/sydmontague.37699/ - +# ImageMaps + +ImageMaps is a plugin for Bukkit Minecraft servers that enables you to place custom images in your world +by rendering them onto maps and placing them in item frames. + +This can be used to create info graphics, custom paintings and even the appearance of custom textures. + +![Example Screenshot](./screenshot.png) + +## Installation +You will need a Server that implements Bukkit 1.13 or newer and the Spigot-API. Some features are only available in newer +releases. Make sure to use the latest release of your server software before reporting issues. + +Put the plugin into your servers `plugins` folder and start it up. A folder for the plugin will be created +automatically. You can place your images in the `images` folder inside it. + +### Upgrading from versions < 1.0.0 +The plugin should convert your maps.yml automatically. It will attempt to create a backup before doing so, +but it is recommended to do one manually as well. + +## Usage +In order for you use place images they must be present in the `images` folder. You can either put them +there via FTP or just copying it, or by using the plugin's download command, allowing you to access +images from the Internet. + +To place images just run the `/imagemap place` command and right click the block that should be the +upper left corner of the image (based on your perspective). + +Images will by default split into 128x128 pixel sub-images to fit on a map. The image may be scaled via +command parameters, however **it is recommended to prepare correctly scaled images**. + +The color palette of Minecraft maps is very limited, requiring the plugin/Bukkit to convert the input image. +This will result in your images looking different ingame than the source. The available color table can be +found here: https://minecraft.gamepedia.com/Map_item_format#Color_table + +![Example of color conversion](./colorConversion.png) + +The invisible and fixed (-> can't be modified) item frame properties are only available in 1.16+. You can modify them for any item frame +by using a configurable (default a wooden hoe) items. Right click to toggle visibility and Shift+Right click to +toggle the fixed state. + +### Commands +* /imagemap place <filename> [frameInvisible] [frameFixed] [frameGlowing] [size] + * Starts the placement process + * frameInvisible and frameFixed are only available on 1.16+ + * frameGlowing is only available on 1.17+ +* /imagemap download <filename> <sourceURL> + * downloads an image from the given URL into the `images` folder +* /imagemap delete <filename> + * deletes an image from the `images` folder +* /imagemap info <filename> + * prints info, such as resolution and expected size +* /imagemap reload <filename> + * reloads an image, to be used when the image file changed +* /imagemap list [page] + * lists all images in the `images` folder +* /imagemap help [command] + * prints help for commands + +### Permissions +`imagemaps.*` - grants access to all permissions +`imagemaps.place` - grants access to /imagemap place command +`imagemaps.download` - grants access to /imagemap download command +`imagemaps.delete` - grants access to /imagemap delete command +`imagemaps.info` - grants access to /imagemap info command +`imagemaps.list` - grants access to /imagemap list command +`imagemaps.reload` - grants access to /imagemap reload command +`imagemaps.help` - grants access to /imagemap help command +`imagemaps.toggleFixed` - allows to toggle the "fixed" state of frames with a wooden hoe +`imagemaps.toggleVisible` - allows to toggle the "visible" state of frames with a wooden hoe + +All permissions are default true for OPs. + + +## Maven +You will need to [configure your environment to access GitHub repositories](https://docs.github.com/en/packages/using-github-packages-with-your-projects-ecosystem/configuring-apache-maven-for-use-with-github-packages). + +``` + + github + ImageMaps repository + https://maven.pkg.github.com/SydMontague/ImageMaps + +``` +``` + + net.craftcitizen + imagemaps + 1.0.1 + +``` + +## Contact +* Discord: SydMontague#8056 +* directly on GitHub +* E-Mail: sydmontague@web.de +* Spigot: https://www.spigotmc.org/members/sydmontague.37699/ + This plugin has been developed for the [CraftCitizen](https://craftcitizen.net) survival Minecraft server. \ No newline at end of file diff --git a/pom.xml b/pom.xml index f128bf8..918897d 100644 --- a/pom.xml +++ b/pom.xml @@ -1,115 +1,115 @@ - - 4.0.0 - net.craftcitizen - imagemaps - 1.0.10 - ImageMaps - Render Images onto maps! - - - - github - GitHub ImageMaps Packages - https://maven.pkg.github.com/SydMontague/ImageMaps - - - - - UTF-8 - - - - - spigot-repo - https://hub.spigotmc.org/nexus/content/groups/public/ - - - github - GitHub CLCore Packages - https://maven.pkg.github.com/SydMontague/CLCore - - - - - - org.spigotmc - spigot-api - 1.17.1-R0.1-SNAPSHOT - - - - de.craftlancer - clcore - 0.4.0-SNAPSHOT - - - * - * - - - - - - - - - ${basedir}/src/main/resources - true - - - ${project.name} - - - org.apache.maven.plugins - maven-shade-plugin - 3.2.4 - - - - de.craftlancer:clcore - - de/craftlancer/core/command/* - de/craftlancer/core/util/* - de/craftlancer/core/LambdaRunnable* - de/craftlancer/core/Utils* - de/craftlancer/core/SemanticVersion* - - - - - - de.craftlancer:clcore - - - - - de.craftlancer.core - net.craftcitizen.imagemaps.clcore - - - - - - package - - shade - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.8.1 - - 1.8 - 1.8 - - - - - + + 4.0.0 + net.craftcitizen + imagemaps + 1.0.11 + ImageMaps + Render Images onto maps! + + + + github + GitHub ImageMaps Packages + https://maven.pkg.github.com/SydMontague/ImageMaps + + + + + UTF-8 + + + + + spigot-repo + https://hub.spigotmc.org/nexus/content/groups/public/ + + + github + GitHub CLCore Packages + https://maven.pkg.github.com/SydMontague/CLCore + + + + + + org.spigotmc + spigot-api + 1.17.1-R0.1-SNAPSHOT + + + + de.craftlancer + clcore + 0.4.0-SNAPSHOT + + + * + * + + + + + + + + + ${basedir}/src/main/resources + true + + + ${project.name} + + + org.apache.maven.plugins + maven-shade-plugin + 3.2.4 + + + + de.craftlancer:clcore + + de/craftlancer/core/command/* + de/craftlancer/core/util/* + de/craftlancer/core/LambdaRunnable* + de/craftlancer/core/Utils* + de/craftlancer/core/SemanticVersion* + + + + + + de.craftlancer:clcore + + + + + de.craftlancer.core + net.craftcitizen.imagemaps.clcore + + + + + + package + + shade + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + 1.8 + 1.8 + + + + + \ No newline at end of file diff --git a/src/main/java/net/craftcitizen/imagemaps/ImageMap.java b/src/main/java/net/craftcitizen/imagemaps/ImageMap.java index 1f3a60d..98c9d28 100644 --- a/src/main/java/net/craftcitizen/imagemaps/ImageMap.java +++ b/src/main/java/net/craftcitizen/imagemaps/ImageMap.java @@ -1,91 +1,92 @@ -package net.craftcitizen.imagemaps; - -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<>(); - map.put("image", filename); - 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; - int result = 1; - result = prime * result + ((filename == null) ? 0 : filename.hashCode()); - long temp; - temp = Double.doubleToLongBits(scale); - result = prime * result + (int) (temp ^ (temp >>> 32)); - result = prime * result + x; - result = prime * result + y; - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (!(obj instanceof ImageMap)) - return false; - ImageMap other = (ImageMap) obj; - if (filename == null) { - if (other.filename != null) - return false; - } else if (!filename.equals(other.filename)) - return false; - if (Double.doubleToLongBits(scale) != Double.doubleToLongBits(other.scale)) - return false; - if (x != other.x) - return false; - if (y != other.y) - return false; - return true; - } -} +package net.craftcitizen.imagemaps; + +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<>(); + map.put("image", filename); + 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; + int result = 1; + result = prime * result + ((filename == null) ? 0 : filename.hashCode()); + long temp; + temp = Double.doubleToLongBits(scale); + result = prime * result + (int) (temp ^ (temp >>> 32)); + result = prime * result + x; + result = prime * result + y; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!(obj instanceof ImageMap)) + return false; + ImageMap other = (ImageMap) obj; + if (filename == null) { + if (other.filename != null) + return false; + } + else if (!filename.equals(other.filename)) + return false; + if (Double.doubleToLongBits(scale) != Double.doubleToLongBits(other.scale)) + return false; + if (x != other.x) + return false; + if (y != other.y) + return false; + return true; + } +} diff --git a/src/main/java/net/craftcitizen/imagemaps/ImageMapCleanupCommand.java b/src/main/java/net/craftcitizen/imagemaps/ImageMapCleanupCommand.java new file mode 100644 index 0000000..a938936 --- /dev/null +++ b/src/main/java/net/craftcitizen/imagemaps/ImageMapCleanupCommand.java @@ -0,0 +1,37 @@ +package net.craftcitizen.imagemaps; + +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; + +import de.craftlancer.core.util.MessageLevel; +import de.craftlancer.core.util.MessageUtil; + +public class ImageMapCleanupCommand extends ImageMapSubCommand { + + public ImageMapCleanupCommand(ImageMaps plugin) { + super("imagemaps.admin", 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; + } + + int removedMaps = getPlugin().cleanupMaps(); + MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, + "Removed " + removedMaps + " invalid images/maps."); + return null; + } + + @Override + public void help(CommandSender sender) { + MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, + "Removes maps with invalid IDs or missing image files."); + MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.WARNING, + "This action is not reverseable. It is recommended to create a backup of your maps.yml first!"); + MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.INFO, "Usage: /imagemap cleanup"); + } + +} diff --git a/src/main/java/net/craftcitizen/imagemaps/ImageMapCommandHandler.java b/src/main/java/net/craftcitizen/imagemaps/ImageMapCommandHandler.java index f27d473..83f7c2a 100644 --- a/src/main/java/net/craftcitizen/imagemaps/ImageMapCommandHandler.java +++ b/src/main/java/net/craftcitizen/imagemaps/ImageMapCommandHandler.java @@ -1,16 +1,18 @@ -package net.craftcitizen.imagemaps; - -import de.craftlancer.core.command.CommandHandler; - -public class ImageMapCommandHandler extends CommandHandler { - public ImageMapCommandHandler(ImageMaps plugin) { - super(plugin); - registerSubCommand("download", new ImageMapDownloadCommand(plugin)); - registerSubCommand("delete", new ImageMapDeleteCommand(plugin)); - registerSubCommand("place", new ImageMapPlaceCommand(plugin)); - registerSubCommand("info", new ImageMapInfoCommand(plugin)); - registerSubCommand("list", new ImageMapListCommand(plugin)); - registerSubCommand("reload", new ImageMapReloadCommand(plugin)); - registerSubCommand("help", new ImageMapHelpCommand(plugin, getCommands()), "?"); - } -} +package net.craftcitizen.imagemaps; + +import de.craftlancer.core.command.CommandHandler; + +public class ImageMapCommandHandler extends CommandHandler { + public ImageMapCommandHandler(ImageMaps plugin) { + super(plugin); + registerSubCommand("download", new ImageMapDownloadCommand(plugin)); + registerSubCommand("delete", new ImageMapDeleteCommand(plugin)); + registerSubCommand("place", new ImageMapPlaceCommand(plugin)); + registerSubCommand("info", new ImageMapInfoCommand(plugin)); + registerSubCommand("list", new ImageMapListCommand(plugin)); + registerSubCommand("reload", new ImageMapReloadCommand(plugin)); + registerSubCommand("cleanup", new ImageMapCleanupCommand(plugin)); + registerSubCommand("debuginfo", new ImageMapDebugInfoCommand(plugin)); + registerSubCommand("help", new ImageMapHelpCommand(plugin, getCommands()), "?"); + } +} diff --git a/src/main/java/net/craftcitizen/imagemaps/ImageMapDebugInfoCommand.java b/src/main/java/net/craftcitizen/imagemaps/ImageMapDebugInfoCommand.java new file mode 100644 index 0000000..d5edd2f --- /dev/null +++ b/src/main/java/net/craftcitizen/imagemaps/ImageMapDebugInfoCommand.java @@ -0,0 +1,39 @@ +package net.craftcitizen.imagemaps; + +import javax.imageio.ImageIO; + +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; + +import de.craftlancer.core.util.MessageLevel; +import de.craftlancer.core.util.MessageUtil; + +public class ImageMapDebugInfoCommand extends ImageMapSubCommand { + + public ImageMapDebugInfoCommand(ImageMaps plugin) { + super("imagemaps.admin", plugin, true); + } + + @Override + protected String execute(CommandSender sender, Command cmd, String label, String[] args) { + MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, + "ImageMaps Version " + getPlugin().getDescription().getVersion()); + MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, "OS: " + System.getProperty("os.name")); + MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, "ImageIO Params:"); + MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, + "Formats: " + String.join(", ", ImageIO.getReaderFormatNames())); + MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, + "Suffixes: " + String.join(", ", ImageIO.getReaderFileSuffixes())); + MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, + "MIME: " + String.join(", ", ImageIO.getReaderMIMETypes())); + MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, + "Uses Cache: " + Boolean.toString(ImageIO.getUseCache())); + return null; + } + + @Override + public void help(CommandSender sender) { + MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, "Prints some debug output."); + } + +} diff --git a/src/main/java/net/craftcitizen/imagemaps/ImageMapDeleteCommand.java b/src/main/java/net/craftcitizen/imagemaps/ImageMapDeleteCommand.java index d119244..c33fcb2 100644 --- a/src/main/java/net/craftcitizen/imagemaps/ImageMapDeleteCommand.java +++ b/src/main/java/net/craftcitizen/imagemaps/ImageMapDeleteCommand.java @@ -1,64 +1,65 @@ -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; - -public class ImageMapDeleteCommand extends ImageMapSubCommand { - - public ImageMapDeleteCommand(ImageMaps plugin) { - super("imagemaps.delete", 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().hasImage(filename)) { - MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.WARNING, "No image with this name exists."); - return null; - } - - if (getPlugin().deleteImage(filename)) { - MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, "File deleted."); - } else { - MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.WARNING, "Failed to delete file."); - } - return null; - } - - @Override - public void help(CommandSender sender) { - MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, "Deletes an image."); - MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.INFO, "Usage: /imagemap delete "); - } - - @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(); - } -} +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; + +public class ImageMapDeleteCommand extends ImageMapSubCommand { + + public ImageMapDeleteCommand(ImageMaps plugin) { + super("imagemaps.delete", 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().hasImage(filename)) { + MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.WARNING, "No image with this name exists."); + return null; + } + + if (getPlugin().deleteImage(filename)) { + MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, "File deleted."); + } + else { + MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.WARNING, "Failed to delete file."); + } + return null; + } + + @Override + public void help(CommandSender sender) { + MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, "Deletes an image."); + MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.INFO, "Usage: /imagemap delete "); + } + + @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/ImageMapDownloadCommand.java b/src/main/java/net/craftcitizen/imagemaps/ImageMapDownloadCommand.java index d40c0bf..4cad8db 100644 --- a/src/main/java/net/craftcitizen/imagemaps/ImageMapDownloadCommand.java +++ b/src/main/java/net/craftcitizen/imagemaps/ImageMapDownloadCommand.java @@ -1,114 +1,122 @@ -package net.craftcitizen.imagemaps; - -import de.craftlancer.core.LambdaRunnable; -import de.craftlancer.core.util.MessageLevel; -import de.craftlancer.core.util.MessageUtil; -import org.bukkit.command.Command; -import org.bukkit.command.CommandSender; - -import javax.imageio.ImageIO; -import java.awt.image.BufferedImage; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.net.HttpURLConnection; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLConnection; - -public class ImageMapDownloadCommand extends ImageMapSubCommand { - - public ImageMapDownloadCommand(ImageMaps plugin) { - super("imagemaps.download", 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 < 3) { - MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.WARNING, "You must specify a file name and a download link."); - return null; - } - - String filename = args[1]; - String url = args[2]; - - if (filename.contains("/") || filename.contains("\\") || filename.contains(":")) { - MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.WARNING, "Filename contains illegal character."); - return null; - } - - new LambdaRunnable(() -> download(sender, url, filename)).runTaskAsynchronously(plugin); - return null; - } - - private void download(CommandSender sender, String input, String filename) { - try { - URL srcURL = new URL(input); - - if (!srcURL.getProtocol().startsWith("http")) { - MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.WARNING, "Download URL is not valid."); - return; - } - - URLConnection connection = srcURL.openConnection(); - - if (!(connection instanceof HttpURLConnection)) { - MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.WARNING, "Download URL is not valid."); - return; - } - - connection.setRequestProperty("User-Agent", "ImageMaps/0"); - - if (((HttpURLConnection) connection).getResponseCode() != 200) { - MessageUtil.sendMessage(getPlugin(), - sender, - MessageLevel.WARNING, - String.format("Download failed, HTTP Error code %d.", ((HttpURLConnection) connection).getResponseCode())); - return; - } - - String mimeType = connection.getHeaderField("Content-type"); - if (!(mimeType.startsWith("image/"))) { - MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.WARNING, String.format("Download is a %s file, not image.", mimeType)); - return; - } - - try (InputStream str = connection.getInputStream()) { - BufferedImage image = ImageIO.read(str); - if (image == null) { - MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.WARNING, "Downloaded file is not an image!"); - return; - } - - File outFile = new File(plugin.getDataFolder(), "images" + File.separatorChar + filename); - boolean fileExisted = outFile.exists(); - ImageIO.write(image, "PNG", outFile); - - if (fileExisted) { - MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.WARNING, "File already exists, overwriting!"); - getPlugin().reloadImage(filename); - } - } catch (IllegalArgumentException ex) { - MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.WARNING, "Received no data"); - return; - } - MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, "Download complete."); - } catch (MalformedURLException ex) { - MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.WARNING, "Malformatted URL"); - } catch (IOException ex) { - MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.ERROR, "An IO Exception happened, see server log"); - ex.printStackTrace(); - } - } - - @Override - public void help(CommandSender sender) { - MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, "Downloads an image from an URL."); - MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.INFO, "Usage: /imagemap download "); - } -} +package net.craftcitizen.imagemaps; + +import de.craftlancer.core.LambdaRunnable; +import de.craftlancer.core.util.MessageLevel; +import de.craftlancer.core.util.MessageUtil; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; + +public class ImageMapDownloadCommand extends ImageMapSubCommand { + + public ImageMapDownloadCommand(ImageMaps plugin) { + super("imagemaps.download", 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 < 3) { + MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.WARNING, + "You must specify a file name and a download link."); + return null; + } + + String filename = args[1]; + String url = args[2]; + + if (filename.contains("/") || filename.contains("\\") || filename.contains(":")) { + MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.WARNING, "Filename contains illegal character."); + return null; + } + + new LambdaRunnable(() -> download(sender, url, filename)).runTaskAsynchronously(plugin); + return null; + } + + private void download(CommandSender sender, String input, String filename) { + try { + URL srcURL = new URL(input); + + if (!srcURL.getProtocol().startsWith("http")) { + MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.WARNING, "Download URL is not valid."); + return; + } + + URLConnection connection = srcURL.openConnection(); + + if (!(connection instanceof HttpURLConnection)) { + MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.WARNING, "Download URL is not valid."); + return; + } + + connection.setRequestProperty("User-Agent", "ImageMaps/0"); + + if (((HttpURLConnection) connection).getResponseCode() != 200) { + MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.WARNING, + String.format("Download failed, HTTP Error code %d.", + ((HttpURLConnection) connection).getResponseCode())); + return; + } + + String mimeType = connection.getHeaderField("Content-type"); + if (!(mimeType.startsWith("image/"))) { + MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.WARNING, + String.format("Download is a %s file, not image.", mimeType)); + return; + } + + try (InputStream str = connection.getInputStream()) { + BufferedImage image = ImageIO.read(str); + if (image == null) { + MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.WARNING, + "Downloaded file is not an image!"); + return; + } + + File outFile = new File(plugin.getDataFolder(), "images" + File.separatorChar + filename); + boolean fileExisted = outFile.exists(); + ImageIO.write(image, "PNG", outFile); + + if (fileExisted) { + MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.WARNING, + "File already exists, overwriting!"); + getPlugin().reloadImage(filename); + } + } + catch (IllegalArgumentException ex) { + MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.WARNING, "Received no data"); + return; + } + MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, "Download complete."); + } + catch (MalformedURLException ex) { + MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.WARNING, "Malformatted URL"); + } + catch (IOException ex) { + MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.ERROR, + "An IO Exception happened, see server log"); + ex.printStackTrace(); + } + } + + @Override + public void help(CommandSender sender) { + MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, "Downloads an image from an URL."); + MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.INFO, + "Usage: /imagemap download "); + } +} diff --git a/src/main/java/net/craftcitizen/imagemaps/ImageMapHelpCommand.java b/src/main/java/net/craftcitizen/imagemaps/ImageMapHelpCommand.java index a2a46fe..5867d26 100644 --- a/src/main/java/net/craftcitizen/imagemaps/ImageMapHelpCommand.java +++ b/src/main/java/net/craftcitizen/imagemaps/ImageMapHelpCommand.java @@ -1,58 +1,67 @@ -package net.craftcitizen.imagemaps; - -import de.craftlancer.core.command.HelpCommand; -import de.craftlancer.core.command.SubCommand; -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.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()) { - MessageUtil.sendMessage(getPlugin(), - sender, - MessageLevel.NORMAL, - buildMessage("/imagemap place [frameInvisible] [frameFixed] [frameGlowing] [size]", " - starts image placement")); - } else if (((ImageMaps) getPlugin()).isInvisibilitySupported()) { - MessageUtil.sendMessage(getPlugin(), - sender, - MessageLevel.NORMAL, - buildMessage("/imagemap place [frameInvisible] [frameFixed] [size]", " - starts image placement")); - } else { - MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, buildMessage("/imagemap place [size]", " - starts image placement")); - } - - MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, buildMessage("/imagemap download ", " - downloads an image")); - MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, buildMessage("/imagemap delete ", " - deletes an image")); - MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, buildMessage("/imagemap info ", " - displays image info")); - MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, buildMessage("/imagemap reload ", " - reloads an image from disk")); - 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; - } -} +package net.craftcitizen.imagemaps; + +import de.craftlancer.core.command.HelpCommand; +import de.craftlancer.core.command.SubCommand; +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.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()) { + MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, + buildMessage("/imagemap place [frameInvisible] [frameFixed] [frameGlowing] [size]", + " - starts image placement")); + } + else if (((ImageMaps) getPlugin()).isInvisibilitySupported()) { + MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, + buildMessage("/imagemap place [frameInvisible] [frameFixed] [size]", + " - starts image placement")); + } + else { + MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, + buildMessage("/imagemap place [size]", " - starts image placement")); + } + + MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, + buildMessage("/imagemap download ", " - downloads an image")); + MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, + buildMessage("/imagemap delete ", " - deletes an image")); + MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, + buildMessage("/imagemap info ", " - displays image info")); + MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, + buildMessage("/imagemap reload ", " - reloads an image from disk")); + MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, + buildMessage("/imagemap cleanup", " - removes invalid maps from plugin")); + 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 fb7df8e..cc6df82 100644 --- a/src/main/java/net/craftcitizen/imagemaps/ImageMapInfoCommand.java +++ b/src/main/java/net/craftcitizen/imagemaps/ImageMapInfoCommand.java @@ -1,84 +1,89 @@ -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 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))); - reloadAction.setColor(ChatColor.GOLD); - BaseComponent placeAction = new TextComponent("[Place]"); - placeAction.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, String.format("/imagemap place \"%s\"", filename))); - placeAction.setColor(ChatColor.GOLD); - 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())); - MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, String.format("Ingame Size: %dx%d", size.getKey(), size.getValue())); - 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(); - } -} +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 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))); + reloadAction.setColor(ChatColor.GOLD); + BaseComponent placeAction = new TextComponent("[Place]"); + placeAction.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, + String.format("/imagemap place \"%s\"", filename))); + placeAction.setColor(ChatColor.GOLD); + 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())); + MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, + String.format("Ingame Size: %dx%d", size.getKey(), size.getValue())); + 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 0160c7e..5ab6f6e 100644 --- a/src/main/java/net/craftcitizen/imagemaps/ImageMapListCommand.java +++ b/src/main/java/net/craftcitizen/imagemaps/ImageMapListCommand.java @@ -1,84 +1,88 @@ -package net.craftcitizen.imagemaps; - -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.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)) { - MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.WARNING, "You can't run this command."); - return null; - } - - 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)) { - BaseComponent infoAction = new TextComponent("[Info]"); - infoAction.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, String.format("/imagemap info \"%s\"", filename))); - infoAction.setColor(ChatColor.GOLD); - BaseComponent reloadAction = new TextComponent("[Reload]"); - reloadAction.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, String.format("/imagemap reload \"%s\"", filename))); - reloadAction.setColor(ChatColor.GOLD); - BaseComponent placeAction = new TextComponent("[Place]"); - placeAction.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, String.format("/imagemap place \"%s\"", filename))); - placeAction.setColor(ChatColor.GOLD); - 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(" "); - message.addExtra(infoAction); - message.addExtra(" "); - message.addExtra(reloadAction); - message.addExtra(" "); - 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."); - MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.INFO, "Usage: /imagemap list [page]"); - } -} +package net.craftcitizen.imagemaps; + +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.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)) { + MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.WARNING, "You can't run this command."); + return null; + } + + 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)) { + BaseComponent infoAction = new TextComponent("[Info]"); + infoAction.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, + String.format("/imagemap info \"%s\"", filename))); + infoAction.setColor(ChatColor.GOLD); + BaseComponent reloadAction = new TextComponent("[Reload]"); + reloadAction.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, + String.format("/imagemap reload \"%s\"", filename))); + reloadAction.setColor(ChatColor.GOLD); + BaseComponent placeAction = new TextComponent("[Place]"); + placeAction.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, + String.format("/imagemap place \"%s\"", filename))); + placeAction.setColor(ChatColor.GOLD); + 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(" "); + message.addExtra(infoAction); + message.addExtra(" "); + message.addExtra(reloadAction); + message.addExtra(" "); + 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 + 2, 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."); + MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.INFO, "Usage: /imagemap list [page]"); + } +} diff --git a/src/main/java/net/craftcitizen/imagemaps/ImageMapPlaceCommand.java b/src/main/java/net/craftcitizen/imagemaps/ImageMapPlaceCommand.java index 23096f9..a0fc3b3 100644 --- a/src/main/java/net/craftcitizen/imagemaps/ImageMapPlaceCommand.java +++ b/src/main/java/net/craftcitizen/imagemaps/ImageMapPlaceCommand.java @@ -1,128 +1,133 @@ -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; - -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]); - if (getPlugin().isGlowingSupported()) { - isGlowing = args.length >= 5 && Boolean.parseBoolean(args[4]); - scale = args.length >= 6 ? parseScale(args[5]) : new Tuple<>(-1, -1); - } else { - scale = args.length >= 5 ? parseScale(args[4]) : new Tuple<>(-1, -1); - } - } else { - scale = args.length >= 3 ? parseScale(args[2]) : new Tuple<>(-1, -1); - } - - if (filename.contains("/") || filename.contains("\\") || filename.contains(":")) { - 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())); - 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()) { - MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.INFO, "Usage: /imagemap place [frameInvisible] [frameFixed] [size]"); - } else { - MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.INFO, "Usage: /imagemap place [size]"); - } - - 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."); - MessageUtil.sendMessage(getPlugin(), - 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() - || args.length > 4 && !getPlugin().isGlowingSupported()) { - return Collections.emptyList(); - } - - switch (args.length) { - case 2: - return Utils.getMatches(args[1], new File(plugin.getDataFolder(), "images").list()); - case 3: - return Utils.getMatches(args[2], Arrays.asList("true", "false")); - case 4: - return Utils.getMatches(args[3], Arrays.asList("true", "false")); - case 5: - return Utils.getMatches(args[4], Arrays.asList("true", "false")); - default: - return Collections.emptyList(); - } - } -} +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; + +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]); + if (getPlugin().isGlowingSupported()) { + isGlowing = args.length >= 5 && Boolean.parseBoolean(args[4]); + scale = args.length >= 6 ? parseScale(args[5]) : new Tuple<>(-1, -1); + } + else { + scale = args.length >= 5 ? parseScale(args[4]) : new Tuple<>(-1, -1); + } + } + else { + scale = args.length >= 3 ? parseScale(args[2]) : new Tuple<>(-1, -1); + } + + if (filename.contains("/") || filename.contains("\\") || filename.contains(":")) { + 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())); + 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()) { + MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.INFO, + "Usage: /imagemap place [frameInvisible] [frameFixed] [size]"); + } + else { + MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.INFO, "Usage: /imagemap place [size]"); + } + + 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."); + MessageUtil.sendMessage(getPlugin(), 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() + || args.length > 4 && !getPlugin().isGlowingSupported()) { + return Collections.emptyList(); + } + + switch (args.length) { + case 2: + return Utils.getMatches(args[1], new File(plugin.getDataFolder(), "images").list()); + case 3: + return Utils.getMatches(args[2], Arrays.asList("true", "false")); + case 4: + return Utils.getMatches(args[3], Arrays.asList("true", "false")); + case 5: + return Utils.getMatches(args[4], Arrays.asList("true", "false")); + default: + return Collections.emptyList(); + } + } +} diff --git a/src/main/java/net/craftcitizen/imagemaps/ImageMapReloadCommand.java b/src/main/java/net/craftcitizen/imagemaps/ImageMapReloadCommand.java index 1a79386..4a3561e 100644 --- a/src/main/java/net/craftcitizen/imagemaps/ImageMapReloadCommand.java +++ b/src/main/java/net/craftcitizen/imagemaps/ImageMapReloadCommand.java @@ -1,61 +1,64 @@ -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; - -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(); - } - -} +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; + +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 bd15fa8..e9e79c5 100644 --- a/src/main/java/net/craftcitizen/imagemaps/ImageMapRenderer.java +++ b/src/main/java/net/craftcitizen/imagemaps/ImageMapRenderer.java @@ -1,79 +1,69 @@ -package net.craftcitizen.imagemaps; - -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 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; - this.y = y; - 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(); - at.scale(scale, scale); - 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) { - canvas.setPixel(x2, y2, imageData[y2 * image.getWidth(null) + x2]); - } - } - }).runTaskLater(plugin, System.nanoTime() % 20); - // spread out pseudo randomly in a very naive way - - }).runTaskAsynchronously(plugin); - first = false; - } - } - -} +package net.craftcitizen.imagemaps; + +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 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; + this.y = y; + 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(); + at.scale(scale, scale); + 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(() -> canvas.drawImage(0, 0, image)).runTaskLater(plugin, System.nanoTime() % 60); + // spread out pseudo randomly in a very naive way + first = false; + } + } + +} diff --git a/src/main/java/net/craftcitizen/imagemaps/ImageMapSubCommand.java b/src/main/java/net/craftcitizen/imagemaps/ImageMapSubCommand.java index 2e81216..e602dc4 100644 --- a/src/main/java/net/craftcitizen/imagemaps/ImageMapSubCommand.java +++ b/src/main/java/net/craftcitizen/imagemaps/ImageMapSubCommand.java @@ -1,15 +1,15 @@ -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); - } - - @Override - public ImageMaps getPlugin() { - return (ImageMaps) super.getPlugin(); - } -} +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); + } + + @Override + public ImageMaps getPlugin() { + return (ImageMaps) super.getPlugin(); + } +} diff --git a/src/main/java/net/craftcitizen/imagemaps/ImageMaps.java b/src/main/java/net/craftcitizen/imagemaps/ImageMaps.java index 41c22b9..14bb246 100644 --- a/src/main/java/net/craftcitizen/imagemaps/ImageMaps.java +++ b/src/main/java/net/craftcitizen/imagemaps/ImageMaps.java @@ -1,574 +1,613 @@ -package net.craftcitizen.imagemaps; - -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; -import org.bukkit.block.Block; -import org.bukkit.block.BlockFace; -import org.bukkit.configuration.Configuration; -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.*; -import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; -import org.bukkit.event.block.Action; -import org.bukkit.event.player.PlayerInteractEntityEvent; -import org.bukkit.event.player.PlayerInteractEvent; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.MapMeta; -import org.bukkit.map.MapView; -import org.bukkit.plugin.java.JavaPlugin; -import org.bukkit.scheduler.BukkitRunnable; - -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 -public class ImageMaps extends JavaPlugin implements Listener { - private static final String MAPS_YML = "maps.yml"; - 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 && - (!isGlowingSupported() || event.getRightClicked().getType() != EntityType.GLOW_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")) { - 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; - } - - public boolean isGlowingSupported() { - SemanticVersion version = Utils.getMCVersion(); - return version.getMajor() >= 1 && version.getMinor() >= 17; - } - - 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) { - e.printStackTrace(); - } - }); - - if (isEnabled()) - saveTask.runTaskAsynchronously(this); - else - saveTask.run(); - } - - private void loadMaps() { - File configFile = new File(getDataFolder(), MAPS_YML); - - 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) -> { - int id = Integer.parseInt(a); - ImageMap imageMap = (ImageMap) b; - @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; - } - if (map == null) { - getLogger().warning(() -> "Map " + id + " referenced but does not exist. Removing map!"); - return; - } - - 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) { - 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"); - int x = config.getInt(key + ".x") / MAP_WIDTH; - int y = config.getInt(key + ".y") / MAP_HEIGHT; - 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) { - 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."); - break; - case INVALID_DIRECTION: - MessageUtil.sendMessage(this, player, MessageLevel.WARNING, "Couldn't calculate how to place the map."); - break; - case EVENT_CANCELLED: - MessageUtil.sendMessage(this, player, MessageLevel.NORMAL, "Image placement cancelled by another plugin."); - break; - case INSUFFICIENT_SPACE: - MessageUtil.sendMessage(this, player, MessageLevel.NORMAL, "Map couldn't be placed, the space is blocked."); - break; - case INSUFFICIENT_WALL: - MessageUtil.sendMessage(this, player, MessageLevel.NORMAL, "Map couldn't be placed, the supporting wall is too small."); - break; - case OVERLAPPING_ENTITY: - MessageUtil.sendMessage(this, player, MessageLevel.NORMAL, "Map couldn't be placed, there is another entity in the way."); - break; - 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()) - return PlacementResult.INSUFFICIENT_SPACE; - 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++) { - Class itemFrameClass = data.isGlowing() ? GlowItemFrame.class : ItemFrame.class; - ItemFrame frame = block.getWorld().spawn(b.getRelative(widthDirection, x).getRelative(heightDirection, y).getLocation(), itemFrameClass); - 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; - } - - public boolean deleteImage(String filename) { - File file = new File(getDataFolder(), IMAGES_DIR + File.separatorChar + filename); - - boolean fileDeleted = false; - if (file.exists()) { - fileDeleted = file.delete(); - } - - imageCache.remove(filename.toLowerCase()); - - Iterator> it = maps.entrySet().iterator(); - while (it.hasNext()) { - Entry entry = it.next(); - ImageMap imageMap = entry.getKey(); - if (!imageMap.getFilename().equalsIgnoreCase(filename)) { - continue; - } - - @SuppressWarnings("deprecation") - MapView map = Bukkit.getMap(entry.getValue()); - - if (map == null) { - continue; - } - map.getRenderers().forEach(map::removeRenderer); - it.remove(); - } - - saveMaps(); - return fileDeleted; - } - - @SuppressWarnings("deprecation") - 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)); - 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(); - meta.setMapId(maps.get(imageMap)); - 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()) - 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: - return Rotation.CLOCKWISE_45; - case NORTH: - return widthDirection == BlockFace.WEST ? Rotation.CLOCKWISE : Rotation.NONE; - case EAST: - return Rotation.CLOCKWISE_135; - case SOUTH: - return widthDirection == BlockFace.WEST ? Rotation.CLOCKWISE : Rotation.NONE; - default: - return Rotation.NONE; - } - } - - private static BlockFace calculateWidthDirection(Player player, BlockFace face) { - float yaw = (360.0f + player.getLocation().getYaw()) % 360.0f; - switch (face) { - case NORTH: - return BlockFace.WEST; - case SOUTH: - return BlockFace.EAST; - case EAST: - return BlockFace.NORTH; - case WEST: - return BlockFace.SOUTH; - case UP: - case DOWN: - if (Utils.isBetween(yaw, 45.0, 135.0)) - return BlockFace.NORTH; - else if (Utils.isBetween(yaw, 135.0, 225.0)) - return BlockFace.EAST; - else if (Utils.isBetween(yaw, 225.0, 315.0)) - return BlockFace.SOUTH; - else - return BlockFace.WEST; - default: - return null; - } - } - - private static BlockFace calculateHeightDirection(Player player, BlockFace face) { - float yaw = (360.0f + player.getLocation().getYaw()) % 360.0f; - switch (face) { - case NORTH: - case SOUTH: - case EAST: - case WEST: - return BlockFace.DOWN; - case UP: - if (Utils.isBetween(yaw, 45.0, 135.0)) - return BlockFace.EAST; - else if (Utils.isBetween(yaw, 135.0, 225.0)) - return BlockFace.SOUTH; - else if (Utils.isBetween(yaw, 225.0, 315.0)) - return BlockFace.WEST; - else - return BlockFace.NORTH; - case DOWN: - if (Utils.isBetween(yaw, 45.0, 135.0)) - return BlockFace.WEST; - else if (Utils.isBetween(yaw, 135.0, 225.0)) - return BlockFace.NORTH; - else if (Utils.isBetween(yaw, 225.0, 315.0)) - return BlockFace.EAST; - else - return BlockFace.SOUTH; - default: - return null; - } - } - - private static boolean isAxisAligned(BlockFace face) { - switch (face) { - case DOWN: - case UP: - case WEST: - case EAST: - case SOUTH: - case NORTH: - return true; - default: - return false; - } - } -} +package net.craftcitizen.imagemaps; + +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; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.configuration.Configuration; +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.*; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.block.Action; +import org.bukkit.event.player.PlayerInteractEntityEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.MapMeta; +import org.bukkit.map.MapView; +import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.scheduler.BukkitRunnable; + +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 +public class ImageMaps extends JavaPlugin implements Listener { + private static final String MAPS_YML = "maps.yml"; + 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 + && (!isGlowingSupported() || event.getRightClicked().getType() != EntityType.GLOW_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")) { + 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; + } + + public boolean isGlowingSupported() { + SemanticVersion version = Utils.getMCVersion(); + return version.getMajor() >= 1 && version.getMinor() >= 17; + } + + 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) { + e.printStackTrace(); + } + }); + + if (isEnabled()) + saveTask.runTaskAsynchronously(this); + else + saveTask.run(); + } + + private void loadMaps() { + File configFile = new File(getDataFolder(), MAPS_YML); + + 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) -> { + int id = Integer.parseInt(a); + ImageMap imageMap = (ImageMap) b; + @SuppressWarnings("deprecation") + MapView map = Bukkit.getMap(id); + BufferedImage image = getImage(imageMap.getFilename()); + maps.put(imageMap, id); + + if (image == null) { + getLogger().warning(() -> "Image file " + imageMap.getFilename() + " not found!"); + return; + } + if (map == null) { + getLogger().warning(() -> "Map " + id + " referenced but does not exist!"); + return; + } + + if (isSetTrackingSupported()) + map.setTrackingPosition(false); + map.getRenderers().forEach(map::removeRenderer); + map.addRenderer(new ImageMapRenderer(this, image, imageMap.getX(), imageMap.getY(), + imageMap.getScale())); + }); + } + + public int cleanupMaps() { + int start = maps.size(); + + maps.entrySet().removeIf(a -> { + @SuppressWarnings("deprecation") + MapView map = Bukkit.getMap(a.getValue().intValue()); + BufferedImage image = getImage(a.getKey().getFilename()); + + return map == null || image == null; + }); + + return start - maps.size(); + } + + 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) { + 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"); + int x = config.getInt(key + ".x") / MAP_WIDTH; + int y = config.getInt(key + ".y") / MAP_HEIGHT; + 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; + } + + // TODO stop returning null, begin throwing exception + 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) { + getLogger().log(Level.SEVERE, String.format("Error while trying to read image %s.", file.getName()), e); + } + + if (image == null) + getLogger().log(Level.WARNING, () -> String.format("Failed to read file as image %s.", file.getName())); + + 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."); + break; + case INVALID_DIRECTION: + MessageUtil.sendMessage(this, player, MessageLevel.WARNING, "Couldn't calculate how to place the map."); + break; + case EVENT_CANCELLED: + MessageUtil.sendMessage(this, player, MessageLevel.NORMAL, + "Image placement cancelled by another plugin."); + break; + case INSUFFICIENT_SPACE: + MessageUtil.sendMessage(this, player, MessageLevel.NORMAL, + "Map couldn't be placed, the space is blocked."); + break; + case INSUFFICIENT_WALL: + MessageUtil.sendMessage(this, player, MessageLevel.NORMAL, + "Map couldn't be placed, the supporting wall is too small."); + break; + case OVERLAPPING_ENTITY: + MessageUtil.sendMessage(this, player, MessageLevel.NORMAL, + "Map couldn't be placed, there is another entity in the way."); + break; + 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()) + return PlacementResult.INSUFFICIENT_SPACE; + 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++) { + Class itemFrameClass = data.isGlowing() ? GlowItemFrame.class : ItemFrame.class; + ItemFrame frame = block.getWorld().spawn( + b.getRelative(widthDirection, x) + .getRelative(heightDirection, y).getLocation(), + itemFrameClass); + 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; + } + + public boolean deleteImage(String filename) { + File file = new File(getDataFolder(), IMAGES_DIR + File.separatorChar + filename); + + boolean fileDeleted = false; + if (file.exists()) { + fileDeleted = file.delete(); + } + + imageCache.remove(filename.toLowerCase()); + + Iterator> it = maps.entrySet().iterator(); + while (it.hasNext()) { + Entry entry = it.next(); + ImageMap imageMap = entry.getKey(); + if (!imageMap.getFilename().equalsIgnoreCase(filename)) { + continue; + } + + @SuppressWarnings("deprecation") + MapView map = Bukkit.getMap(entry.getValue()); + + if (map == null) { + continue; + } + map.getRenderers().forEach(map::removeRenderer); + it.remove(); + } + + saveMaps(); + return fileDeleted; + } + + @SuppressWarnings("deprecation") + 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)); + 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(); + meta.setMapId(maps.get(imageMap)); + 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()) + 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: + return Rotation.CLOCKWISE_45; + case NORTH: + return widthDirection == BlockFace.WEST ? Rotation.CLOCKWISE : Rotation.NONE; + case EAST: + return Rotation.CLOCKWISE_135; + case SOUTH: + return widthDirection == BlockFace.WEST ? Rotation.CLOCKWISE : Rotation.NONE; + default: + return Rotation.NONE; + } + } + + private static BlockFace calculateWidthDirection(Player player, BlockFace face) { + float yaw = (360.0f + player.getLocation().getYaw()) % 360.0f; + switch (face) { + case NORTH: + return BlockFace.WEST; + case SOUTH: + return BlockFace.EAST; + case EAST: + return BlockFace.NORTH; + case WEST: + return BlockFace.SOUTH; + case UP: + case DOWN: + if (Utils.isBetween(yaw, 45.0, 135.0)) + return BlockFace.NORTH; + else if (Utils.isBetween(yaw, 135.0, 225.0)) + return BlockFace.EAST; + else if (Utils.isBetween(yaw, 225.0, 315.0)) + return BlockFace.SOUTH; + else + return BlockFace.WEST; + default: + return null; + } + } + + private static BlockFace calculateHeightDirection(Player player, BlockFace face) { + float yaw = (360.0f + player.getLocation().getYaw()) % 360.0f; + switch (face) { + case NORTH: + case SOUTH: + case EAST: + case WEST: + return BlockFace.DOWN; + case UP: + if (Utils.isBetween(yaw, 45.0, 135.0)) + return BlockFace.EAST; + else if (Utils.isBetween(yaw, 135.0, 225.0)) + return BlockFace.SOUTH; + else if (Utils.isBetween(yaw, 225.0, 315.0)) + return BlockFace.WEST; + else + return BlockFace.NORTH; + case DOWN: + if (Utils.isBetween(yaw, 45.0, 135.0)) + return BlockFace.WEST; + else if (Utils.isBetween(yaw, 135.0, 225.0)) + return BlockFace.NORTH; + else if (Utils.isBetween(yaw, 225.0, 315.0)) + return BlockFace.EAST; + else + return BlockFace.SOUTH; + default: + return null; + } + } + + private static boolean isAxisAligned(BlockFace face) { + switch (face) { + case DOWN: + case UP: + case WEST: + case EAST: + case SOUTH: + case NORTH: + return true; + default: + return false; + } + } +} diff --git a/src/main/java/net/craftcitizen/imagemaps/ImagePlaceEvent.java b/src/main/java/net/craftcitizen/imagemaps/ImagePlaceEvent.java index 6f981b6..58c7639 100644 --- a/src/main/java/net/craftcitizen/imagemaps/ImagePlaceEvent.java +++ b/src/main/java/net/craftcitizen/imagemaps/ImagePlaceEvent.java @@ -1,117 +1,118 @@ -package net.craftcitizen.imagemaps; - -import org.bukkit.block.Block; -import org.bukkit.block.BlockFace; -import org.bukkit.entity.Player; -import org.bukkit.event.Cancellable; -import org.bukkit.event.Event; -import org.bukkit.event.HandlerList; - -/** - * Called when an image is attempted to be placed. - */ -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; - private final BlockFace heightDirection; - 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; - this.widthDirection = widthDirection; - this.heightDirection = heightDirection; - this.width = width; - this.height = height; - this.cache = cache; - } - - /** - * 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() { - return block; - } - - /** - * 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() { - return heightDirection; - } - - /** - * 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() { - return width; - } - - /** - * 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; - } -} +package net.craftcitizen.imagemaps; + +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; + +/** + * Called when an image is attempted to be placed. + */ +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; + private final BlockFace heightDirection; + 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; + this.widthDirection = widthDirection; + this.heightDirection = heightDirection; + this.width = width; + this.height = height; + this.cache = cache; + } + + /** + * 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() { + return block; + } + + /** + * 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() { + return heightDirection; + } + + /** + * 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() { + return width; + } + + /** + * 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 5049067..41d4dd1 100644 --- a/src/main/java/net/craftcitizen/imagemaps/PlacementData.java +++ b/src/main/java/net/craftcitizen/imagemaps/PlacementData.java @@ -1,80 +1,80 @@ -package net.craftcitizen.imagemaps; - -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; - this.isFixed = isFixed; - 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() { - return isInvisible; - } - - - /** - * 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 - */ - public boolean isGlowing() { - return isGlowing; - } - - /** - * 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() { - return scale; - } -} +package net.craftcitizen.imagemaps; + +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; + this.isFixed = isFixed; + 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() { + return isInvisible; + } + + /** + * 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 + */ + public boolean isGlowing() { + return isGlowing; + } + + /** + * 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() { + return scale; + } +} diff --git a/src/main/java/net/craftcitizen/imagemaps/PlacementResult.java b/src/main/java/net/craftcitizen/imagemaps/PlacementResult.java index 8261ed3..1bffc4e 100644 --- a/src/main/java/net/craftcitizen/imagemaps/PlacementResult.java +++ b/src/main/java/net/craftcitizen/imagemaps/PlacementResult.java @@ -1,11 +1,11 @@ -package net.craftcitizen.imagemaps; - -public enum PlacementResult { - INVALID_FACING, - EVENT_CANCELLED, - INVALID_DIRECTION, - INSUFFICIENT_WALL, - INSUFFICIENT_SPACE, - SUCCESS, - OVERLAPPING_ENTITY; -} +package net.craftcitizen.imagemaps; + +public enum PlacementResult { + INVALID_FACING, + EVENT_CANCELLED, + INVALID_DIRECTION, + INSUFFICIENT_WALL, + INSUFFICIENT_SPACE, + SUCCESS, + OVERLAPPING_ENTITY; +} diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index fb1992c..e05205b 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,48 +1,52 @@ -main: net.craftcitizen.imagemaps.ImageMaps -author: SydMontague -version: ${project.version} -api-version: 1.13 -name: ImageMaps -commands: - imagemap: - description: ImageMaps plugin commands, place images on walls - permission-message: You do not have permission to use this command. - usage: | - /imagemap place [frameVisible] [frameFixed] [frameGlowing] [size] - starts image placement - /imagemap download - downloads an image - /imagemap delete - deletes an image - /imagemap info - displays image info - /imagemap reload - reloads an image from disk - /imagemap list [page] - lists all files in the images folder - /imagemap help [command] - shows help -permissions: - imagemaps.*: - default: op - children: - imagemaps.place: true - imagemaps.download: true - imagemaps.delete: true - imagemaps.info: true - imagemaps.list: true - imagemaps.reload: true - imagemaps.help: true - imagemaps.toggleFixed: true - imagemaps.toggleVisible: true - imagemaps.place: - default: op - imagemaps.download: - default: op - imagemaps.delete: - default: op - imagemaps.info: - default: op - imagemaps.list: - default: op - imagemaps.reload: - default: op - imagemaps.help: - default: op - imagemaps.toggleFixed: - default: op - imagemaps.toggleVisible: - default: op \ No newline at end of file +main: net.craftcitizen.imagemaps.ImageMaps +author: SydMontague +version: ${project.version} +api-version: 1.13 +name: ImageMaps +commands: + imagemap: + description: ImageMaps plugin commands, place images on walls + permission-message: You do not have permission to use this command. + usage: | + /imagemap place [frameVisible] [frameFixed] [frameGlowing] [size] - starts image placement + /imagemap download - downloads an image + /imagemap delete - deletes an image + /imagemap info - displays image info + /imagemap reload - reloads an image from disk + /imagemap list [page] - lists all files in the images folder + /imagemap help [command] - shows help +permissions: + imagemaps.*: + default: op + children: + imagemaps.place: true + imagemaps.download: true + imagemaps.delete: true + imagemaps.info: true + imagemaps.list: true + imagemaps.reload: true + imagemaps.help: true + imagemaps.toggleFixed: true + imagemaps.toggleVisible: true + imagemaps.place: + default: op + imagemaps.download: + default: op + imagemaps.delete: + default: op + imagemaps.info: + default: op + imagemaps.list: + default: op + imagemaps.reload: + default: op + imagemaps.help: + default: op + imagemaps.toggleFixed: + default: op + imagemaps.toggleVisible: + default: op + imagemaps.admin: + default: op + children: + imagemaps.*: true \ No newline at end of file