diff --git a/pom.xml b/pom.xml index a9f47966..16f2bbd5 100644 --- a/pom.xml +++ b/pom.xml @@ -35,27 +35,43 @@ ~ knowledge of the CeCILL license and that you accept its terms. --> - + 4.0.0 fr.moribus ImageOnMap - 4.2.2 + 5.0.0 jar UTF-8 1.8 1.8 + 2.1.0 + + kotlin-maven-plugin + + + compile + process-sources + + compile + + + + org.jetbrains.kotlin + ${kotlin.version} + org.apache.maven.plugins maven-shade-plugin 3.1.0 - false + false fr.zcraft:quartzlib @@ -117,7 +133,10 @@ zdevelopers-quartzlib https://maven.zcraft.fr/QuartzLib - + + spring-repo + https://repo.spring.io/release + CodeMC @@ -127,20 +146,27 @@ - org.bukkit - bukkit - 1.13.2-R0.1-SNAPSHOT + org.jetbrains.kotlin + kotlin-stdlib + ${kotlin.version} - fr.zcraft - quartzlib - 0.0.6 + org.spigotmc + spigot-api + 1.20.4-R0.1-SNAPSHOT + provided + org.bstats bstats-bukkit 1.8 compile + + fr.zcraft + quartzlib + 0.0.7-SNAPSHOT + diff --git a/src/main/java/fr/moribus/imageonmap/Argument.java b/src/main/java/fr/moribus/imageonmap/Argument.java new file mode 100644 index 00000000..b678a960 --- /dev/null +++ b/src/main/java/fr/moribus/imageonmap/Argument.java @@ -0,0 +1,316 @@ +/* + * Copyright or © or Copr. Moribus (2013) + * Copyright or © or Copr. ProkopyL (2015) + * Copyright or © or Copr. Amaury Carrade (2016 – 2022) + * Copyright or © or Copr. Vlammar (2019 – 2024) + * + * This software is a computer program whose purpose is to allow insertion of + * custom images in a Minecraft world. + * + * This software is governed by the CeCILL license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL license and that you accept its terms. + */ + +package fr.moribus.imageonmap; + + +import fr.zcraft.quartzlib.tools.PluginLogger; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import org.bukkit.Bukkit; + +public class Argument { + // args) { + for (Argument a : args) { + if (a.getName().equalsIgnoreCase(argumentName)) { + return a; + } + } + PluginLogger.error("Can't find argument"); + return null; + } + //TODO add error message for the console and/or player + + public String getName() { + return name; + } + + public Type getType() { + return type; + } + + public Status getStatus() { + return status; + } + + public static T findContent(String argumentName, List args) { + Argument argument = Argument.findArgument(argumentName, args); + if (argument != null) { + return argument.getContent(); + } + + PluginLogger.error("Error: argument {0} was not found", argumentName); + for (Argument a : args) { + PluginLogger.info("argument disponible {0}", a.getName()); + } + + return argument.getDefault(); + } + + public T getDefault() { + if (defaultValue == null) { + return null; + } + switch (type) { + case BOOLEAN: + return (T) Boolean.valueOf((Boolean) defaultValue); + case INT: + return (T) Integer.getInteger((String) defaultValue); + case UUID: + return (T) UUID.fromString((String) defaultValue); + case DOUBLE: + return (T) Double.valueOf((String) defaultValue); + case STRING: + return (T) defaultValue; + case ONLINE_PLAYER: + return (T) Bukkit.getPlayer(toUUID((String) defaultValue)); + case OFFLINE_PLAYER: + return (T) Bukkit.getOfflinePlayer(toUUID((String) defaultValue)); + + default: + PluginLogger.info("To be implemented"); + return null; + } + } + + public T getContent() { + switch (type) { + case BOOLEAN: + return (T) Boolean.valueOf(content); + case INT: + return (T) Integer.getInteger(content); + case UUID: + return (T) UUID.fromString(content); + case DOUBLE: + return (T) Double.valueOf(content); + case STRING: + return (T) content; + case ONLINE_PLAYER: + return (T) Bukkit.getPlayer(toUUID(content)); + case OFFLINE_PLAYER: + return (T) Bukkit.getOfflinePlayer(toUUID(content)); + + default: + PluginLogger.info("To be implemented"); + return null; + } + } + + private void setEmpty() { + this.content = ""; + } + + private void setDefault(String defaultValue) { + this.defaultValue = defaultValue; + } + + private void setContent(String content) { + //check if added content is type compatible + String msg = "Content wasn't parsable to type '{0}', got the following string {1}"; + switch (type) { + case BOOLEAN: + case ONLINE_PLAYER: + case OFFLINE_PLAYER: + case STRING: + this.content = content; + break; + case DOUBLE: + try { + Double.valueOf(content); + } catch (NumberFormatException e) { + PluginLogger.warning(msg, + "double", content); + throw e; + } + this.content = content; + break; + case UUID: + try { + UUID.fromString(content); + } catch (IllegalArgumentException e) { + PluginLogger.warning(msg, + "UUID", content); + throw e; + } + this.content = content; + break; + case INT: + try { + Integer.parseInt(content); + } catch (NumberFormatException e) { + PluginLogger.warning(msg, + "integer", content); + throw e; + } + this.content = content; + break; + default: + } + + + } + + private UUID toUUID(String content) { + try { + return UUID.fromString(content); + } catch (IllegalArgumentException e) { + return Bukkit.getOfflinePlayer(content).getUniqueId(); + } + } + + public static boolean isAmbiguous(List prototype, boolean isPlayer) { + //TODO add check on the possible type + for (int i = 1; i < prototype.size(); i++) { + Argument arg = prototype.get(i); + Argument previous = prototype.get(i - 1); + if ((arg.getStatus() == Status.OPTIONAL && previous.getStatus() == Status.OPTIONAL) + || (isPlayer + && ((arg.getStatus() == Status.OPTIONAL_FOR_PLAYER_ONLY + && previous.getStatus() == Status.OPTIONAL) + || (arg.getStatus() == Status.OPTIONAL + && previous.getStatus() == Status.OPTIONAL_FOR_PLAYER_ONLY) + || (arg.getStatus() == Status.OPTIONAL_FOR_PLAYER_ONLY + && previous.getStatus() == Status.OPTIONAL_FOR_PLAYER_ONLY)))) { + return true; + } + } + return false; + } + + public static Map parseArguments(List prototype, ArrayList args, + boolean isPlayer) + throws Exception { + //check if the command is not ambiguous + if (isAmbiguous(prototype, isPlayer)) { + throw new Exception("Parsing error, ambiguous command prototype"); + } + // givemap Vlammar Vlammar:"carte 1" 10 + // string |string| string + Map map = new HashMap<>(); + List uncertain = new ArrayList<>(); + + for (int i = 0; i < args.size(); i++) { + boolean next = false; + String arg = args.get(i); + PluginLogger.info("Argument: {0}", arg); + for (int j = i; j < prototype.size(); j++) { + PluginLogger.info("j = {0}", j); + Argument a = prototype.get(j); + switch (a.status) { + case MANDATORY: + PluginLogger.info("OBLIGATORY"); + if (uncertain.isEmpty()) { + map.put(a.getName(), a); + a.setContent(arg); + PluginLogger.info("argument : \n{0}", a.toString()); + next = true; + } else { + for (Argument l : uncertain) { + //if size doesnt match or + try { + a.setContent(a.content); //todo erreur ? + } catch (Exception e) { + //shift to the right + } + } + map.put(a.getName(), a); + uncertain = new ArrayList<>(); + } + break; + case OPTIONAL: + PluginLogger.info(a.getName()); + a.setContent(arg); + uncertain.add(a); + PluginLogger.info("argument : \n{0}", a.toString()); + break; + case OPTIONAL_FOR_PLAYER_ONLY: + PluginLogger.info("OPTIONAL_FOR_PLAYER_ONLY"); + if (!isPlayer) { + PluginLogger.info(a.getName()); + map.put(a.getName(), a); + a.setContent(arg); + PluginLogger.info("argument : \n{0}", a.toString()); + next = true; + } else { + PluginLogger.info(a.getName()); + uncertain.add(a); + PluginLogger.info("argument : \n{0}", a.toString()); + break; + //Str Str Str Int + // [playerFrom] [playerFrom]: + } + break; + default: + throw new Exception("Status does not exist"); + } + if (next) { + break; + } + } + } + return map; + } + + @Override + public String toString() { + return "{ name: " + name + ";\ntype: " + type + ";\nstatus: " + status + ";\ncontent: " + content + " }"; + } +} + diff --git a/src/main/java/fr/moribus/imageonmap/ImageOnMap.java b/src/main/java/fr/moribus/imageonmap/ImageOnMap.java index 321c3de4..5ba86f1e 100644 --- a/src/main/java/fr/moribus/imageonmap/ImageOnMap.java +++ b/src/main/java/fr/moribus/imageonmap/ImageOnMap.java @@ -1,8 +1,8 @@ /* * Copyright or © or Copr. Moribus (2013) * Copyright or © or Copr. ProkopyL (2015) - * Copyright or © or Copr. Amaury Carrade (2016 – 2021) - * Copyright or © or Copr. Vlammar (2019 – 2021) + * Copyright or © or Copr. Amaury Carrade (2016 – 2022) + * Copyright or © or Copr. Vlammar (2019 – 2024) * * This software is a computer program whose purpose is to allow insertion of * custom images in a Minecraft world. @@ -43,14 +43,16 @@ import fr.moribus.imageonmap.commands.maptool.GetRemainingCommand; import fr.moribus.imageonmap.commands.maptool.GiveCommand; import fr.moribus.imageonmap.commands.maptool.ListCommand; +import fr.moribus.imageonmap.commands.maptool.MigrateCommand; import fr.moribus.imageonmap.commands.maptool.NewCommand; +import fr.moribus.imageonmap.commands.maptool.RemotePlacingCommand; import fr.moribus.imageonmap.commands.maptool.RenameCommand; import fr.moribus.imageonmap.commands.maptool.UpdateCommand; import fr.moribus.imageonmap.image.ImageIOExecutor; import fr.moribus.imageonmap.image.ImageRendererExecutor; -import fr.moribus.imageonmap.image.MapInitEvent; -import fr.moribus.imageonmap.map.MapManager; -import fr.moribus.imageonmap.ui.MapItemManager; +import fr.moribus.imageonmap.image.PosterInitEvent; +import fr.moribus.imageonmap.map.PosterManager; +import fr.moribus.imageonmap.ui.PosterItemManager; import fr.zcraft.quartzlib.components.commands.CommandWorkers; import fr.zcraft.quartzlib.components.commands.Commands; import fr.zcraft.quartzlib.components.gui.Gui; @@ -60,19 +62,48 @@ import fr.zcraft.quartzlib.tools.UpdateChecker; import java.io.File; import java.io.IOException; +import java.util.HashMap; +import java.util.Map; import org.bstats.bukkit.Metrics; +//TODO ERROR when loading plugin org.bukkit.plugin.InvalidPluginException: java.lang.NoClassDefFoundError: +// fr/zcraft/imageonmap/quartzlib/core/QuartzPlugin +//TODO list: bug sur supprimer map si coin manquant, passer en position au lieu de frame; +// repair fonction permet avec la carte en main si clic droit de réparer (permission); +// replace IG si clic droit alors remplacer le dessin si meme taille sinon mettre un message erreur; +// bug: pas de message d'erreur si pas assez de place; +// finir déploiement plus efficace des cartes; +// bug rotation des frames au sol et plafond (faire un fix mieux que juste mettre x rotation); +// continuer rework des commandes; +// GUI permettre de changer objets utilisés et ajouter ressource pack; +// rework des GUI (map part utile ???); +// Pouvoir déployer carte à distance; +// carte interractive (5.1?); +// glow effect a refaire (vraiment utile ???); +// Miniature des maps en 1x1 (migration?); +// ecrire directement sur les cartes pour ne pas tout perdre(migration); +// bug: suppression image chat (inconnue ?) laisse item frame invisible; +// passer invisibiliter et glow dans le GUI si permission +// avec un clic droit sur les frames et fenetre en verre pour la protection contre le grief des mobs et potion +// invisibilité pour rendre frame invisible (peut etre que l'item utilisé pour le craft); proposer craft RP des cartes +// soit avec un item map vide+ un item de peintre ? soit avec un atelier? +// soit avec certains objets simple dans inventaire public final class ImageOnMap extends QuartzPlugin { + private static final String IMAGES_DIRECTORY_NAME = "images"; - private static final String MAPS_DIRECTORY_NAME = "maps"; + private static final String RENDERS_DIRECTORY_NAME = "renders"; + private static final String POSTERS_DIRECTORY_NAME = "maps"; private static ImageOnMap plugin; - private final File mapsDirectory; private File imagesDirectory; + + private File rendersDirectory; + private File postersDirectory; private CommandWorkers commandWorker; public ImageOnMap() { imagesDirectory = new File(this.getDataFolder(), IMAGES_DIRECTORY_NAME); - mapsDirectory = new File(this.getDataFolder(), MAPS_DIRECTORY_NAME); + rendersDirectory = new File(this.getDataFolder(), RENDERS_DIRECTORY_NAME); + postersDirectory = new File(this.getDataFolder(), POSTERS_DIRECTORY_NAME); plugin = this; } @@ -84,33 +115,49 @@ public File getImagesDirectory() { return imagesDirectory; } - public File getMapsDirectory() { - return mapsDirectory; + public File getRendersDirectory() { + return rendersDirectory; + } + + public File getPostersDirectory() { + return postersDirectory; + } + + public File getImageFile(int posterID) { + return new File(imagesDirectory, "map" + posterID + ".png"); } - public File getImageFile(int mapID) { - return new File(imagesDirectory, "map" + mapID + ".png"); + public File getRenderFile(int posterID) { + return new File(rendersDirectory, "render" + posterID + ".png"); } public CommandWorkers getCommandWorker() { return commandWorker; } - @SuppressWarnings("unchecked") + private Map checkDirs() throws IOException { + Map dirs = new HashMap<>(); + dirs.put("mapsDirectory", checkPluginDirectory(postersDirectory)); + dirs.put("rendersDirectory", checkPluginDirectory(rendersDirectory)); + dirs.put("imagesDirectory", checkPluginDirectory(imagesDirectory)); + return dirs; + } + @Override public void onEnable() { // Creating the images and maps directories if necessary try { - //imagesDirectory = checkPluginDirectory(imagesDirectory, V3Migrator.getOldImagesDirectory(this)); - checkPluginDirectory(mapsDirectory); - checkPluginDirectory(imagesDirectory); + Map directories = checkDirs(); + postersDirectory = directories.get("mapsDirectory"); + rendersDirectory = directories.get("rendersDirectory"); + imagesDirectory = directories.get("imagesDirectory"); } catch (final IOException ex) { PluginLogger.error("FATAL: " + ex.getMessage()); + //disable the plugin this.setEnabled(false); return; } - saveDefaultConfig(); commandWorker = loadComponent(CommandWorkers.class); loadComponents(I18n.class, Gui.class, Commands.class, PluginConfiguration.class, ImageIOExecutor.class, @@ -119,13 +166,13 @@ public void onEnable() { //Init all the things ! I18n.setPrimaryLocale(PluginConfiguration.LANG.get()); - MapManager.init(); - MapInitEvent.init(); - MapItemManager.init(); - + PosterManager.init(); + PosterInitEvent.init(); + PosterItemManager.init(); + String commandGroupName = "maptool"; Commands.register( - "maptool", + commandGroupName, NewCommand.class, ListCommand.class, GetCommand.class, @@ -134,22 +181,24 @@ public void onEnable() { GiveCommand.class, GetRemainingCommand.class, ExploreCommand.class, - //MigrateCommand.class,//Removed for now doesn't work nor is useful, maybe useful later on - UpdateCommand.class + MigrateCommand.class, + UpdateCommand.class, + RemotePlacingCommand.class ); - Commands.registerShortcut("maptool", NewCommand.class, "tomap"); - Commands.registerShortcut("maptool", ExploreCommand.class, "maps"); - Commands.registerShortcut("maptool", GiveCommand.class, "givemap"); + Commands.registerShortcut(commandGroupName, NewCommand.class, "tomap"); + Commands.registerShortcut(commandGroupName, ExploreCommand.class, "maps"); + Commands.registerShortcut(commandGroupName, GiveCommand.class, "givemap"); + Commands.registerShortcut(commandGroupName, RemotePlacingCommand.class, "placemap"); if (PluginConfiguration.CHECK_FOR_UPDATES.get()) { UpdateChecker.boot("imageonmap.26585"); } if (PluginConfiguration.COLLECT_DATA.get()) { - final Metrics metrics = new Metrics(this,5920); - metrics.addCustomChart(new Metrics.SingleLineChart("rendered-images", MapManager::getImagesCount)); - metrics.addCustomChart(new Metrics.SingleLineChart("used-minecraft-maps", MapManager::getMapCount)); + final Metrics metrics = new Metrics(this, 5920); + metrics.addCustomChart(new Metrics.SingleLineChart("rendered-images", PosterManager::getImagesCount)); + metrics.addCustomChart(new Metrics.SingleLineChart("used-minecraft-maps", PosterManager::getPosterCount)); } else { PluginLogger.warning("Collect data disabled"); } @@ -157,8 +206,8 @@ public void onEnable() { @Override public void onDisable() { - MapManager.exit(); - MapItemManager.exit(); + PosterManager.exit(); + PosterItemManager.exit(); //MigratorExecutor.waitForMigration();//Removed for now doesn't work nor is useful, maybe useful later on super.onDisable(); diff --git a/src/main/java/fr/moribus/imageonmap/Permissions.java b/src/main/java/fr/moribus/imageonmap/Permissions.java index cf079c2d..469f121e 100644 --- a/src/main/java/fr/moribus/imageonmap/Permissions.java +++ b/src/main/java/fr/moribus/imageonmap/Permissions.java @@ -1,8 +1,8 @@ /* * Copyright or © or Copr. Moribus (2013) * Copyright or © or Copr. ProkopyL (2015) - * Copyright or © or Copr. Amaury Carrade (2016 – 2021) - * Copyright or © or Copr. Vlammar (2019 – 2021) + * Copyright or © or Copr. Amaury Carrade (2016 – 2022) + * Copyright or © or Copr. Vlammar (2019 – 2024) * * This software is a computer program whose purpose is to allow insertion of * custom images in a Minecraft world. @@ -50,9 +50,9 @@ public enum Permissions { GET("imageonmap.get"), GETOTHER("imageonmap.getother"), RENAME("imageonmap.rename"), - PLACE_SPLATTER_MAP("imageonmap.placesplattermap"), - PLACE_INVISIBLE_SPLATTER_MAP("imageonmap.placeinvisiblesplattermap"), - REMOVE_SPLATTER_MAP("imageonmap.removesplattermap"), + PLACE_SPLATTER_POSTER("imageonmap.placesplattermap"), + PLACE_INVISIBLE_SPLATTER_POSTER("imageonmap.placeinvisiblesplattermap"), + REMOVE_SPLATTER_POSTER("imageonmap.removesplattermap"), DELETE("imageonmap.delete"), DELETEOTHER("imageonmap.deleteother"), UPDATE("imageonmap.update"), @@ -60,9 +60,11 @@ public enum Permissions { ADMINISTRATIVE("imageonmap.administrative"), BYPASS_SIZE("imageonmap.bypasssize"), BYPASS_IMAGE_LIMIT("imageonmap.bypassimagelimit"), - BYPASS_MAP_LIMIT("imageonmap.bypassmaplimit"), + BYPASS_POSTER_LIMIT("imageonmap.bypassmaplimit"), GIVE("imageonmap.give"), - BYPASS_WHITELIST("imageonmap.bypasswhitelist"); + BYPASS_WHITELIST("imageonmap.bypasswhitelist"), + REMOTE_PLACING("imageonmap.remoteplacing"), + REPAIR_POSTER("imageonmap.repairmap"); private final String permission; private final String[] aliases; @@ -79,6 +81,12 @@ public enum Permissions { * @return {@code true} if this permission is granted to the permissible. */ public boolean grantedTo(Permissible permissible) { + //true only if not a player. If the console or a command block as send the command we can assume that it has + //enough privilege + if (permissible == null || permissible.isOp()) { + return true; + } + if (permissible.hasPermission(permission)) { return true; } @@ -103,17 +111,13 @@ public int getLimitPermission(Permissible permissible, LimitType type) { String prefix = String.format("imageonmap.%slimit.", type.name()); for (PermissionAttachmentInfo pai : perms) { String permString = pai.getPermission().toLowerCase(); - if (permString.startsWith(prefix)) { - if (pai.getValue()) { - try { - int limit = Integer.parseInt(permString.split(prefix)[1].trim()); - return limit; - } catch (Exception e) { - PluginLogger.warning( - I.t("The correct syntax for setting map limit node is: ImageOnMap.mapLimit.X " - + "where you can replace X with the limit of map a player is allowed to have")); - } - + if (permString.startsWith(prefix) && pai.getValue()) { + try { + return Integer.parseInt(permString.split(prefix)[1].trim()); + } catch (Exception e) { + PluginLogger.warning( + I.t("The correct syntax for setting map limit node is: ImageOnMap.mapLimit.X " + + "where you can replace X with the limit of map a player is allowed to have")); } } } @@ -121,7 +125,7 @@ public int getLimitPermission(Permissible permissible, LimitType type) { } public enum LimitType { - map, + poster, image } } \ No newline at end of file diff --git a/src/main/java/fr/moribus/imageonmap/PluginConfiguration.java b/src/main/java/fr/moribus/imageonmap/PluginConfiguration.java index c5f0bae5..93d80819 100644 --- a/src/main/java/fr/moribus/imageonmap/PluginConfiguration.java +++ b/src/main/java/fr/moribus/imageonmap/PluginConfiguration.java @@ -1,8 +1,8 @@ /* * Copyright or © or Copr. Moribus (2013) * Copyright or © or Copr. ProkopyL (2015) - * Copyright or © or Copr. Amaury Carrade (2016 – 2021) - * Copyright or © or Copr. Vlammar (2019 – 2021) + * Copyright or © or Copr. Amaury Carrade (2016 – 2022) + * Copyright or © or Copr. Vlammar (2019 – 2024) * * This software is a computer program whose purpose is to allow insertion of * custom images in a Minecraft world. @@ -46,22 +46,24 @@ public final class PluginConfiguration extends Configuration { - public static ConfigurationItem LANG = item("lang", Locale.class); + public static final ConfigurationItem LANG = item("lang", Locale.class); - public static ConfigurationItem COLLECT_DATA = item("collect-data", true); + public static final ConfigurationItem COLLECT_DATA = item("collect-data", true); - public static ConfigurationItem CHECK_FOR_UPDATES = item("check-for-updates", true); + public static final ConfigurationItem CHECK_FOR_UPDATES = item("check-for-updates", true); - public static ConfigurationItem MAP_GLOBAL_LIMIT = item("map-global-limit", 0, "Limit-map-by-server"); - public static ConfigurationItem MAP_PLAYER_LIMIT = item("map-player-limit", 0, "Limit-map-by-player"); + public static final ConfigurationItem POSTER_GLOBAL_LIMIT = + item("map-global-limit", 0, "Limit-map-by-server"); + public static final ConfigurationItem POSTER_PLAYER_LIMIT = + item("map-player-limit", 0, "Limit-map-by-player"); - public static ConfigurationItem SAVE_FULL_IMAGE = item("save-full-image", true); + public static final ConfigurationItem SAVE_FULL_IMAGE = item("save-full-image", true); - public static ConfigurationItem LIMIT_SIZE_X = item("limit-map-size-x", 0); - public static ConfigurationItem LIMIT_SIZE_Y = item("limit-map-size-y", 0); + public static final ConfigurationItem LIMIT_SIZE_X = item("limit-map-size-x", 0); + public static final ConfigurationItem LIMIT_SIZE_Y = item("limit-map-size-y", 0); - public static ConfigurationList IMAGES_HOSTNAMES_WHITELIST = + public static final ConfigurationList IMAGES_HOSTNAMES_WHITELIST = list("images-hostnames-whitelist", String.class); } diff --git a/src/main/java/fr/moribus/imageonmap/Status.java b/src/main/java/fr/moribus/imageonmap/Status.java new file mode 100644 index 00000000..7e6a2c8e --- /dev/null +++ b/src/main/java/fr/moribus/imageonmap/Status.java @@ -0,0 +1,41 @@ +/* + * Copyright or © or Copr. Moribus (2013) + * Copyright or © or Copr. ProkopyL (2015) + * Copyright or © or Copr. Amaury Carrade (2016 – 2022) + * Copyright or © or Copr. Vlammar (2019 – 2024) + * + * This software is a computer program whose purpose is to allow insertion of + * custom images in a Minecraft world. + * + * This software is governed by the CeCILL license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL license and that you accept its terms. + */ + +package fr.moribus.imageonmap; + +public enum Status { + MANDATORY, OPTIONAL, OPTIONAL_FOR_PLAYER_ONLY +} diff --git a/src/main/java/fr/moribus/imageonmap/Type.java b/src/main/java/fr/moribus/imageonmap/Type.java new file mode 100644 index 00000000..d458fe6b --- /dev/null +++ b/src/main/java/fr/moribus/imageonmap/Type.java @@ -0,0 +1,41 @@ +/* + * Copyright or © or Copr. Moribus (2013) + * Copyright or © or Copr. ProkopyL (2015) + * Copyright or © or Copr. Amaury Carrade (2016 – 2022) + * Copyright or © or Copr. Vlammar (2019 – 2024) + * + * This software is a computer program whose purpose is to allow insertion of + * custom images in a Minecraft world. + * + * This software is governed by the CeCILL license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL license and that you accept its terms. + */ + +package fr.moribus.imageonmap; + +public enum Type { + STRING, INT, DOUBLE, BOOLEAN, UUID, ONLINE_PLAYER,OFFLINE_PLAYER +} diff --git a/src/main/java/fr/moribus/imageonmap/commands/IoMCommand.java b/src/main/java/fr/moribus/imageonmap/commands/IoMCommand.java index cc06be23..5f48aa06 100644 --- a/src/main/java/fr/moribus/imageonmap/commands/IoMCommand.java +++ b/src/main/java/fr/moribus/imageonmap/commands/IoMCommand.java @@ -1,8 +1,8 @@ /* * Copyright or © or Copr. Moribus (2013) * Copyright or © or Copr. ProkopyL (2015) - * Copyright or © or Copr. Amaury Carrade (2016 – 2021) - * Copyright or © or Copr. Vlammar (2019 – 2021) + * Copyright or © or Copr. Amaury Carrade (2016 – 2022) + * Copyright or © or Copr. Vlammar (2019 – 2024) * * This software is a computer program whose purpose is to allow insertion of * custom images in a Minecraft world. @@ -37,8 +37,8 @@ package fr.moribus.imageonmap.commands; import fr.moribus.imageonmap.PluginConfiguration; -import fr.moribus.imageonmap.map.ImageMap; -import fr.moribus.imageonmap.map.MapManager; +import fr.moribus.imageonmap.map.ImagePoster; +import fr.moribus.imageonmap.map.PosterManager; import fr.zcraft.quartzlib.components.commands.Command; import fr.zcraft.quartzlib.components.commands.CommandException; import fr.zcraft.quartzlib.components.i18n.I; @@ -46,14 +46,36 @@ import java.util.ArrayList; import java.util.List; import java.util.UUID; -import java.util.function.Consumer; import java.util.stream.Collectors; import org.bukkit.Bukkit; -import org.bukkit.OfflinePlayer; import org.bukkit.entity.Player; public abstract class IoMCommand extends Command { + protected UUID getPlayerUUID(String playerName) { + return Bukkit.getOfflinePlayer(playerName).getUniqueId(); + //TODO replace with homemade solution using https://api.mojang.com/users/profiles/minecraft/ + } + + private boolean checkTooArguments(boolean bool, String msg) throws CommandException { + if (bool) { + throwInvalidArgument(msg); + } + return bool; + } + + protected boolean checkTooManyArguments(boolean bool) throws CommandException { + return checkTooArguments(bool, I.t("Too many parameters!")); + } + + protected boolean checkTooFewArguments(boolean bool) throws CommandException { + return checkTooArguments(bool, I.t("Too few parameters!")); + } + + protected boolean checkArguments(boolean bool1, boolean bool2) throws CommandException { + //TODO WTF is happening here + return !(checkTooManyArguments(bool1) || checkTooFewArguments(bool2)); + } protected boolean checkHostnameWhitelist(final URL url) { final List hostnames = PluginConfiguration.IMAGES_HOSTNAMES_WHITELIST.get() @@ -72,39 +94,6 @@ protected boolean checkHostnameWhitelist(final URL url) { .anyMatch(h -> h.equalsIgnoreCase(url.getHost())); } - protected void retrieveUUID(final String arg, final Consumer consumer) { - // If it is being removed we may have to use Mojang services - consumer.accept(Bukkit.getOfflinePlayer(arg).getUniqueId()); - } - - protected ImageMap getMapFromArgs() throws CommandException { - return getMapFromArgs(playerSender(), 0, true); - } - - protected ImageMap getMapFromArgs(final Player player, final int index, boolean expand) throws CommandException { - if (args.length <= index) { - throwInvalidArgument(I.t("You need to give a map name.")); - } - - ImageMap map; - String mapName = args[index]; - - if (expand) { - for (int i = index + 1, c = args.length; i < c; i++) { - mapName += " " + args[i]; - } - } - - mapName = mapName.trim(); - map = MapManager.getMap(player.getUniqueId(), mapName); - - if (map == null) { - error(I.t("This map does not exist.")); - } - - return map; - } - protected ArrayList getArgs() { ArrayList arguments = new ArrayList<>(); @@ -160,19 +149,20 @@ protected ArrayList getArgs() { } - protected List getMatchingMapNames(Player player, String prefix) { - return getMatchingMapNames(MapManager.getMapList(player.getUniqueId()), prefix); + protected List getMatchingPosterNames(Player player, String prefix) { + return getMatchingPosterNames(PosterManager.getPosterList(player.getUniqueId()), prefix); } - protected List getMatchingMapNames(Iterable maps, String prefix) { + protected List getMatchingPosterNames(Iterable posters, String prefix) { List matches = new ArrayList<>(); - for (ImageMap map : maps) { - if (map.getId().startsWith(prefix)) { - matches.add(map.getId()); + for (ImagePoster poster : posters) { + if (poster.getId().startsWith(prefix)) { + matches.add(poster.getId()); } } return matches; } + } diff --git a/src/main/java/fr/moribus/imageonmap/commands/maptool/DeleteCommand.java b/src/main/java/fr/moribus/imageonmap/commands/maptool/DeleteCommand.java index 1ea4e597..ef88b85a 100644 --- a/src/main/java/fr/moribus/imageonmap/commands/maptool/DeleteCommand.java +++ b/src/main/java/fr/moribus/imageonmap/commands/maptool/DeleteCommand.java @@ -1,8 +1,8 @@ /* * Copyright or © or Copr. Moribus (2013) * Copyright or © or Copr. ProkopyL (2015) - * Copyright or © or Copr. Amaury Carrade (2016 – 2021) - * Copyright or © or Copr. Vlammar (2019 – 2021) + * Copyright or © or Copr. Amaury Carrade (2016 – 2022) + * Copyright or © or Copr. Vlammar (2019 – 2024) * * This software is a computer program whose purpose is to allow insertion of * custom images in a Minecraft world. @@ -38,9 +38,9 @@ import fr.moribus.imageonmap.Permissions; import fr.moribus.imageonmap.commands.IoMCommand; -import fr.moribus.imageonmap.map.ImageMap; -import fr.moribus.imageonmap.map.MapManager; -import fr.moribus.imageonmap.map.MapManagerException; +import fr.moribus.imageonmap.map.ImagePoster; +import fr.moribus.imageonmap.map.PosterManager; +import fr.moribus.imageonmap.map.PosterManagerException; import fr.zcraft.quartzlib.components.commands.CommandException; import fr.zcraft.quartzlib.components.commands.CommandInfo; import fr.zcraft.quartzlib.components.commands.WithFlags; @@ -50,24 +50,25 @@ import fr.zcraft.quartzlib.tools.text.RawMessage; import java.util.ArrayList; import java.util.List; +import java.util.UUID; import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; -@CommandInfo(name = "delete", usageParameters = "[player name]: [--confirm]") +@CommandInfo(name = "delete", usageParameters = "[player name]: [--confirm]") @WithFlags({"confirm"}) public class DeleteCommand extends IoMCommand { - private static RawText deleteMsg(Class klass, String playerName, ImageMap map) { + private static RawText deleteMsg(Class klass, String playerName, ImagePoster poster) { return new RawText(I.t("You are going to delete") + " ") - .then(map.getId()) + .then(poster.getId()) .color(ChatColor.GOLD) .then(". " + I.t("Are you sure ? ")) .color(ChatColor.WHITE) .then(I.t("[Confirm]")) .color(ChatColor.GREEN) .hover(new RawText(I.t("{red}This map will be deleted {bold}forever{red}!"))) - .command(klass, playerName + ":" + "\"" + map.getId() + "\"", "--confirm") + .command(klass, playerName + ":" + "\"" + poster.getId() + "\"", "--confirm") .build(); } @@ -76,56 +77,76 @@ protected void run() throws CommandException { ArrayList arguments = getArgs(); final boolean confirm = hasFlag("confirm"); - if (arguments.size() > 3 || (arguments.size() > 2 && !confirm)) { - throwInvalidArgument(I.t("Too many parameters!")); - return; - } - if (arguments.size() < 1) { - throwInvalidArgument(I.t("Too few parameters!")); + boolean isTooMany = arguments.size() > 3 || (arguments.size() > 2 && !confirm); + boolean isTooFew = arguments.isEmpty(); + if (!checkArguments(isTooMany, isTooFew)) { return; } final String playerName; - final String mapName; - final Player sender = playerSender(); + final String posterName; + final Player sender; + Player playerSender; + try { + playerSender = playerSender(); + } catch (CommandException ignored) { + if (arguments.size() != 2) { + throwInvalidArgument(I.t("Player name is required from the console")); + } + playerSender = null; + } + + sender = playerSender; + boolean notPlayer = sender == null; if (arguments.size() == 2 || arguments.size() == 3) { if (!Permissions.DELETEOTHER.grantedTo(sender)) { throwNotAuthorized(); return; } - playerName = arguments.get(0); - mapName = arguments.get(1); + posterName = arguments.get(1); } else { playerName = sender.getName(); - mapName = arguments.get(0); + posterName = arguments.get(0); } + UUID uuid = getPlayerUUID(playerName); + ImagePoster poster = PosterManager.getPoster(uuid, posterName); + if (poster == null) { + final String msg = "This map does not exist."; + if (notPlayer) { + PluginLogger.warning("" + msg); + } else { + warning(sender, I.t(msg)); + } - retrieveUUID(playerName, uuid -> { - ImageMap map = MapManager.getMap(uuid, mapName); + return; + } - if (map == null) { - warning(sender, I.t("This map does not exist.")); - return; - } + if (!confirm && !notPlayer) { + RawText msg = deleteMsg(getClass(), playerName, poster); - if (!confirm) { - RawText msg = deleteMsg(getClass(), playerName, map); - RawMessage.send(sender, msg); + if (notPlayer) { + PluginLogger.info("" + msg.toFormattedText()); } else { - if (sender != null && sender.isOnline() && sender.getInventory() != null) { - MapManager.clear(sender.getInventory(), map); - } - - try { - MapManager.deleteMap(map); - success(sender, I.t("Map successfully deleted.")); - } catch (MapManagerException ex) { - PluginLogger.warning(I.t("A non-existent map was requested to be deleted", ex)); - warning(sender, I.t("This map does not exist.")); + RawMessage.send(sender, msg); + } + } else { + if (sender != null && sender.isOnline()) { + PosterManager.clear(sender.getInventory(), poster); + } + try { + PosterManager.deletePoster(poster); + String msg = I.t("Map successfully deleted."); + if (sender != null) { + success(sender, msg); + } else { + PluginLogger.info(msg); } + } catch (PosterManagerException ex) { + PluginLogger.warning(I.t("A non-existent map was requested to be deleted", ex)); + warning(sender, I.t("This map does not exist.")); } - }); + } } @@ -133,7 +154,7 @@ protected void run() throws CommandException { @Override protected List complete() throws CommandException { if (args.length == 1) { - return getMatchingMapNames(playerSender(), args[0]); + return getMatchingPosterNames(playerSender(), args[0]); } return null; diff --git a/src/main/java/fr/moribus/imageonmap/commands/maptool/ExploreCommand.java b/src/main/java/fr/moribus/imageonmap/commands/maptool/ExploreCommand.java index ae05a3d6..4551a76d 100644 --- a/src/main/java/fr/moribus/imageonmap/commands/maptool/ExploreCommand.java +++ b/src/main/java/fr/moribus/imageonmap/commands/maptool/ExploreCommand.java @@ -1,8 +1,8 @@ /* * Copyright or © or Copr. Moribus (2013) * Copyright or © or Copr. ProkopyL (2015) - * Copyright or © or Copr. Amaury Carrade (2016 – 2021) - * Copyright or © or Copr. Vlammar (2019 – 2021) + * Copyright or © or Copr. Amaury Carrade (2016 – 2022) + * Copyright or © or Copr. Vlammar (2019 – 2024) * * This software is a computer program whose purpose is to allow insertion of * custom images in a Minecraft world. @@ -39,12 +39,10 @@ import fr.moribus.imageonmap.Permissions; import fr.moribus.imageonmap.commands.IoMCommand; -import fr.moribus.imageonmap.gui.MapListGui; +import fr.moribus.imageonmap.gui.PosterListGui; import fr.zcraft.quartzlib.components.commands.CommandException; import fr.zcraft.quartzlib.components.commands.CommandInfo; import fr.zcraft.quartzlib.components.gui.Gui; -import fr.zcraft.quartzlib.components.i18n.I; -import fr.zcraft.quartzlib.tools.PluginLogger; import java.util.ArrayList; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; @@ -57,13 +55,14 @@ public class ExploreCommand extends IoMCommand { @Override protected void run() throws CommandException { ArrayList arguments = getArgs(); - if (arguments.size() > 1) { - throwInvalidArgument(I.t("Too many parameters!")); + boolean isTooMany = arguments.size() > 1; + boolean isTooFew = false; + if (!checkArguments(isTooMany, isTooFew)) { return; } final String playerName; - final Player sender = playerSender(); + if (arguments.size() == 1) { if (!Permissions.LISTOTHER.grantedTo(sender)) { throwNotAuthorized(); @@ -74,14 +73,12 @@ protected void run() throws CommandException { playerName = sender.getName(); } - retrieveUUID(playerName, uuid -> { - - OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(uuid); - if (sender.isOnline()) { - Gui.open(sender, new MapListGui(offlinePlayer, playerName)); - } - - }); + OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(playerName); + //TODO replace with homemade solution using https://api.mojang.com/users/profiles/minecraft/playername + // to get the UUId + if (sender.isOnline()) { + Gui.open(sender, new PosterListGui(offlinePlayer, playerName)); + } } diff --git a/src/main/java/fr/moribus/imageonmap/commands/maptool/GetCommand.java b/src/main/java/fr/moribus/imageonmap/commands/maptool/GetCommand.java index e527a31d..b9f3dee5 100644 --- a/src/main/java/fr/moribus/imageonmap/commands/maptool/GetCommand.java +++ b/src/main/java/fr/moribus/imageonmap/commands/maptool/GetCommand.java @@ -1,8 +1,8 @@ /* * Copyright or © or Copr. Moribus (2013) * Copyright or © or Copr. ProkopyL (2015) - * Copyright or © or Copr. Amaury Carrade (2016 – 2021) - * Copyright or © or Copr. Vlammar (2019 – 2021) + * Copyright or © or Copr. Amaury Carrade (2016 – 2022) + * Copyright or © or Copr. Vlammar (2019 – 2024) * * This software is a computer program whose purpose is to allow insertion of * custom images in a Minecraft world. @@ -36,77 +36,70 @@ package fr.moribus.imageonmap.commands.maptool; -import fr.moribus.imageonmap.ImageOnMap; import fr.moribus.imageonmap.Permissions; import fr.moribus.imageonmap.commands.IoMCommand; -import fr.moribus.imageonmap.map.ImageMap; -import fr.moribus.imageonmap.map.MapManager; +import fr.moribus.imageonmap.map.ImagePoster; +import fr.moribus.imageonmap.map.PosterManager; import fr.zcraft.quartzlib.components.commands.CommandException; import fr.zcraft.quartzlib.components.commands.CommandInfo; import fr.zcraft.quartzlib.components.i18n.I; import java.util.ArrayList; import java.util.List; +import java.util.UUID; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; -@CommandInfo(name = "get",usageParameters = "[player name]:") +@CommandInfo(name = "get", usageParameters = "[player name]:") public class GetCommand extends IoMCommand { @Override protected void run() throws CommandException { ArrayList arguments = getArgs(); - if (arguments.size() > 2) { - throwInvalidArgument(I.t("Too many parameters!")); - return; - } - if (arguments.size() < 1) { - throwInvalidArgument(I.t("Too few parameters!")); + boolean isTooMany = arguments.size() > 2; + boolean isTooFew = arguments.isEmpty(); + if (!checkArguments(isTooMany, isTooFew)) { return; } + final String playerName; - final String mapName; + final String posterName; final Player sender = playerSender(); if (arguments.size() == 1) { playerName = sender.getName(); - mapName = arguments.get(0); + posterName = arguments.get(0); } else { if (!Permissions.GETOTHER.grantedTo(sender)) { throwNotAuthorized(); return; } playerName = arguments.get(0); - mapName = arguments.get(1); + posterName = arguments.get(1); } + UUID uuid = getPlayerUUID(playerName); + if (!sender.isOnline()) { + return; + } + ImagePoster poster = PosterManager.getPoster(uuid, posterName); + if (poster == null) { + warning(sender, I.t("This map does not exist.")); + return; + } + if (poster.give(sender)) { + info(I.t("The requested map was too big to fit in your inventory.")); + info(I.t("Use '/maptool getremaining' to get the remaining maps.")); + } - retrieveUUID(playerName, uuid -> { - - if (!sender.isOnline()) { - return; - } - - ImageMap map = MapManager.getMap(uuid, mapName); - - if (map == null) { - warning(sender, I.t("This map does not exist.")); - return; - } - - if (map.give(sender)) { - info(I.t("The requested map was too big to fit in your inventory.")); - info(I.t("Use '/maptool getremaining' to get the remaining maps.")); - } - }); } @Override protected List complete() throws CommandException { if (args.length == 1) { - return getMatchingMapNames(playerSender(), args[0]); + return getMatchingPosterNames(playerSender(), args[0]); } return null; } diff --git a/src/main/java/fr/moribus/imageonmap/commands/maptool/GetRemainingCommand.java b/src/main/java/fr/moribus/imageonmap/commands/maptool/GetRemainingCommand.java index 7a8c29f5..439b9d6d 100644 --- a/src/main/java/fr/moribus/imageonmap/commands/maptool/GetRemainingCommand.java +++ b/src/main/java/fr/moribus/imageonmap/commands/maptool/GetRemainingCommand.java @@ -1,8 +1,8 @@ /* * Copyright or © or Copr. Moribus (2013) * Copyright or © or Copr. ProkopyL (2015) - * Copyright or © or Copr. Amaury Carrade (2016 – 2021) - * Copyright or © or Copr. Vlammar (2019 – 2021) + * Copyright or © or Copr. Amaury Carrade (2016 – 2022) + * Copyright or © or Copr. Vlammar (2019 – 2024) * * This software is a computer program whose purpose is to allow insertion of * custom images in a Minecraft world. @@ -38,7 +38,7 @@ import fr.moribus.imageonmap.Permissions; import fr.moribus.imageonmap.commands.IoMCommand; -import fr.moribus.imageonmap.ui.MapItemManager; +import fr.moribus.imageonmap.ui.PosterItemManager; import fr.zcraft.quartzlib.components.commands.CommandException; import fr.zcraft.quartzlib.components.commands.CommandInfo; import fr.zcraft.quartzlib.components.i18n.I; @@ -51,18 +51,18 @@ public class GetRemainingCommand extends IoMCommand { protected void run() throws CommandException { Player player = playerSender(); - if (MapItemManager.getCacheSize(player) <= 0) { + if (PosterItemManager.getCacheSize(player) <= 0) { info(I.t("You have no remaining map.")); return; } - int givenMaps = MapItemManager.giveCache(player); + int givenMaps = PosterItemManager.giveCache(player); if (givenMaps == 0) { error(I.t("Your inventory is full! Make some space before requesting the remaining maps.")); } else { info(I.tn("There is {0} map remaining.", "There are {0} maps remaining.", - MapItemManager.getCacheSize(player))); + PosterItemManager.getCacheSize(player))); } } diff --git a/src/main/java/fr/moribus/imageonmap/commands/maptool/GiveCommand.java b/src/main/java/fr/moribus/imageonmap/commands/maptool/GiveCommand.java index 2751ab23..046f9c4c 100644 --- a/src/main/java/fr/moribus/imageonmap/commands/maptool/GiveCommand.java +++ b/src/main/java/fr/moribus/imageonmap/commands/maptool/GiveCommand.java @@ -1,8 +1,8 @@ /* * Copyright or © or Copr. Moribus (2013) * Copyright or © or Copr. ProkopyL (2015) - * Copyright or © or Copr. Amaury Carrade (2016 – 2021) - * Copyright or © or Copr. Vlammar (2019 – 2021) + * Copyright or © or Copr. Amaury Carrade (2016 – 2022) + * Copyright or © or Copr. Vlammar (2019 – 2024) * * This software is a computer program whose purpose is to allow insertion of * custom images in a Minecraft world. @@ -36,88 +36,128 @@ package fr.moribus.imageonmap.commands.maptool; +import fr.moribus.imageonmap.Argument; import fr.moribus.imageonmap.Permissions; +import fr.moribus.imageonmap.Status; +import fr.moribus.imageonmap.Type; import fr.moribus.imageonmap.commands.IoMCommand; -import fr.moribus.imageonmap.map.ImageMap; -import fr.moribus.imageonmap.map.MapManager; +import fr.moribus.imageonmap.map.ImagePoster; +import fr.moribus.imageonmap.map.PosterManager; import fr.zcraft.quartzlib.components.commands.CommandException; import fr.zcraft.quartzlib.components.commands.CommandInfo; import fr.zcraft.quartzlib.components.i18n.I; +import fr.zcraft.quartzlib.tools.PluginLogger; import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.UUID; import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; -@CommandInfo(name = "give", usageParameters = " [playerFrom]:") +@CommandInfo(name = "give", usageParameters = " [playerFrom]: [quantity]") public class GiveCommand extends IoMCommand { + protected List commandPrototype() { + Argument target = new Argument("target", Type.STRING, Status.MANDATORY); + Argument from = new Argument("from", Type.STRING, Status.OPTIONAL_FOR_PLAYER_ONLY); + Argument posterName = new Argument("posterName", Type.STRING, Status.MANDATORY); + Argument quantity = new Argument("quantity", Type.INT, Status.OPTIONAL, "1"); + + List list = new ArrayList<>(); + list.add(target); + list.add(from); + list.add(posterName); + list.add(quantity); + + return list; + } + // /givemap Vlammar "Carte 1" 10 + // /maptool give Vlammar Vlammar:"Carte" @Override protected void run() throws CommandException { - if (args.length < 2) { - throwInvalidArgument(I.t("You must give a valid player name and a map name.")); + List cmdProto = commandPrototype(); + ArrayList arguments = getArgs(); + boolean isPlayer = sender != null; + final String posterName; + final String from; + final String playerName; + final int quantity; + try { + Map argMap = Argument.parseArguments(cmdProto, arguments, isPlayer); + posterName = argMap.get("posterName").getContent(); + from = argMap.get("from").getContent(); + playerName = argMap.get("target").getContent(); + quantity = argMap.get("quantity").getContent(); + } catch (Exception e) { + PluginLogger.error("Error while parsing command"); return; } - ArrayList arguments = getArgs(); - if (arguments.size() > 3) { - throwInvalidArgument(I.t("Too many parameters!")); + boolean isTooMany = arguments.size() > 4; + boolean isTooFew = arguments.size() < 2; + if (!checkArguments(isTooMany, isTooFew)) { return; } - if (arguments.size() < 1) { - throwInvalidArgument(I.t("Too few parameters!")); + + /* + final String posterName = Argument.findContent("posterName", args); + final String from = Argument.findContent("from", args); + final String playerName = Argument.findContent("target", args); + final int quantity = Argument.findContent("quantity", args); + */ + PluginLogger.info("quantity {0}", quantity); + if (posterName == null || from == null || playerName == null) { + PluginLogger.error("Error one argument is null, check argument names"); return; } - final String mapName; - final String from; - final String playerName; final Player playerSender; Player playerSender1; try { playerSender1 = playerSender(); } catch (CommandException ignored) { if (arguments.size() == 2) { - throwInvalidArgument(I.t("Player name is required from the console")); + throwInvalidArgument(I.t("Player name is required for the console")); } playerSender1 = null; } playerSender = playerSender1; - if (arguments.size() == 2) { + /*if (arguments.size() == 2) { from = playerSender.getName(); playerName = arguments.get(0); - mapName = arguments.get(1); + posterName = arguments.get(1); } else { if (arguments.size() == 3) { from = arguments.get(1); playerName = arguments.get(0); - mapName = arguments.get(2); + posterName = arguments.get(2); } else { from = ""; playerName = ""; - mapName = ""; + posterName = ""; } - } + }*/ final Player sender = playerSender(); + UUID uuid = getPlayerUUID(from); + UUID uuid2 = getPlayerUUID(playerName); - retrieveUUID(from, uuid -> { - final ImageMap map = MapManager.getMap(uuid, mapName); + final ImagePoster poster = PosterManager.getPoster(uuid, posterName); - if (map == null) { - warning(sender, I.t("This map does not exist.")); - return; - } + if (poster == null) { + warning(sender, I.t("This map does not exist.")); + return; + } - retrieveUUID(playerName, uuid2 -> { - if (Bukkit.getPlayer((uuid2)) != null && Bukkit.getPlayer((uuid2)).isOnline() - && map.give(Bukkit.getPlayer(uuid2))) { - info(I.t("The requested map was too big to fit in your inventory.")); - info(I.t("Use '/maptool getremaining' to get the remaining maps.")); - } - }); - }); + + if (Bukkit.getPlayer((uuid2)) != null && Bukkit.getPlayer((uuid2)).isOnline() + && poster.give(Bukkit.getPlayer(uuid2), quantity)) { + info(I.t("The requested map was too big to fit in your inventory.")); + info(I.t("Use '/maptool getremaining' to get the remaining maps.")); + } } diff --git a/src/main/java/fr/moribus/imageonmap/commands/maptool/ListCommand.java b/src/main/java/fr/moribus/imageonmap/commands/maptool/ListCommand.java index b1fdc333..59f21fb3 100644 --- a/src/main/java/fr/moribus/imageonmap/commands/maptool/ListCommand.java +++ b/src/main/java/fr/moribus/imageonmap/commands/maptool/ListCommand.java @@ -1,8 +1,8 @@ /* * Copyright or © or Copr. Moribus (2013) * Copyright or © or Copr. ProkopyL (2015) - * Copyright or © or Copr. Amaury Carrade (2016 – 2021) - * Copyright or © or Copr. Vlammar (2019 – 2021) + * Copyright or © or Copr. Amaury Carrade (2016 – 2022) + * Copyright or © or Copr. Vlammar (2019 – 2024) * * This software is a computer program whose purpose is to allow insertion of * custom images in a Minecraft world. @@ -36,19 +36,28 @@ package fr.moribus.imageonmap.commands.maptool; +import fr.moribus.imageonmap.Argument; import fr.moribus.imageonmap.Permissions; +import fr.moribus.imageonmap.Status; +import fr.moribus.imageonmap.Type; import fr.moribus.imageonmap.commands.IoMCommand; -import fr.moribus.imageonmap.map.ImageMap; -import fr.moribus.imageonmap.map.MapManager; +import fr.moribus.imageonmap.map.ImagePoster; +import fr.moribus.imageonmap.map.PosterManager; import fr.moribus.imageonmap.map.PosterMap; import fr.zcraft.quartzlib.components.commands.CommandException; import fr.zcraft.quartzlib.components.commands.CommandInfo; import fr.zcraft.quartzlib.components.i18n.I; import fr.zcraft.quartzlib.components.rawtext.RawText; import fr.zcraft.quartzlib.components.rawtext.RawTextPart; +import fr.zcraft.quartzlib.tools.PluginLogger; import fr.zcraft.quartzlib.tools.text.RawMessage; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.LinkedList; import java.util.List; +import java.util.Map; +import java.util.UUID; import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; @@ -58,62 +67,101 @@ public class ListCommand extends IoMCommand { @Override protected void run() throws CommandException { ArrayList arguments = getArgs(); - if (arguments.size() > 1) { - throwInvalidArgument(I.t("Too many parameters!")); + + + boolean isTooMany = arguments.size() > 1; + boolean isTooFew = false; + if (!checkArguments(isTooMany, isTooFew)) { return; } String playerName; + final boolean isHuman = (sender instanceof Player); + if (arguments.size() == 1) { - if (!Permissions.LISTOTHER.grantedTo(sender)) { + if (!Permissions.LISTOTHER.grantedTo(sender) && isHuman) { throwNotAuthorized(); return; } playerName = arguments.get(0); } else { - playerName = playerSender().getName(); + if (isHuman) { + playerName = playerSender().getName(); + } else { + PluginLogger.warning(I.t("You must give a player name")); + return; + } } + try { + List prototype = new ArrayList<>(); + prototype.add(new Argument("playerName", Type.STRING, Status.OPTIONAL, playerName)); + Map argMap = Argument.parseArguments(prototype, arguments, isHuman); - final Player sender = playerSender(); - - - retrieveUUID(playerName, uuid -> { - List mapList = MapManager.getMapList(uuid); - if (mapList.isEmpty()) { - info(sender, I.t("No map found.")); - return; + for (String key : argMap.keySet()) { + Argument arg = argMap.get(key); + PluginLogger.info("Arguments : \n name " + arg.getName() + "\n type " + arg.getType() + + "\n status " + arg.getStatus()); } - String message = I.tn("{white}{bold}{0} map found.", - "{white}{bold}{0} maps found.", - mapList.size()); + } catch (Exception e) { + PluginLogger.warning(e.toString()); + } + final Player playerSender; + if (isHuman) { + playerSender = playerSender(); + } else { + playerSender = null; + } + UUID uuid = getPlayerUUID(playerName); + List posterList = PosterManager.getPosterList(uuid); + if (posterList.isEmpty()) { + String msg = I.t("No map found."); + if (isHuman) { + info(playerSender, msg); + } else { + PluginLogger.info(msg); + } + return; + } + + String msg = I.tn("{white}{bold}{0} map found.", + "{white}{bold}{0} maps found.", + posterList.size()); + if (isHuman) { + info(playerSender, + msg); //TODO merge those into a common info(isHuman,msg) that print to a sender or the console + } else { + PluginLogger.info(msg); + } - info(sender, I.tn("{white}{bold}{0} map found.", "{white}{bold}{0} maps found.", mapList.size())); + RawTextPart rawText = new RawText(""); + rawText = addPoster(rawText, posterList.get(0)); - RawTextPart rawText = new RawText(""); - rawText = addMap(rawText, mapList.get(0)); + //TODO pagination chat + for (int i = 1, c = posterList.size(); i < c; i++) { + rawText = rawText.then(", ").color(ChatColor.GRAY); + rawText = addPoster(rawText, posterList.get(i)); + } + if (isHuman) { + RawMessage.send(playerSender, rawText.build()); + } else { + PluginLogger.info(rawText.build().toPlainText()); + } - //TODO pagination chat - for (int i = 1, c = mapList.size(); i < c; i++) { - rawText = rawText.then(", ").color(ChatColor.GRAY); - rawText = addMap(rawText, mapList.get(i)); - } - RawMessage.send(sender, rawText.build()); - }); } - private RawTextPart addMap(RawTextPart rawText, ImageMap map) { - final String size = map.getType() == ImageMap.Type.SINGLE ? "1 × 1" : - ((PosterMap) map).getColumnCount() + " × " + ((PosterMap) map).getRowCount(); + private RawTextPart addPoster(RawTextPart rawText, ImagePoster poster) { + final String size = poster.getType() == ImagePoster.Type.SINGLE ? "1 × 1" : + ((PosterMap) poster).getColumnCount() + " × " + ((PosterMap) poster).getRowCount(); return rawText - .then(map.getId()) + .then(poster.getId()) .color(ChatColor.WHITE) - .command(GetCommand.class, map.getId()) + .command(GetCommand.class, poster.getId()) .hover(new RawText() - .then(map.getName()).style(ChatColor.BOLD, ChatColor.GREEN).then("\n") - .then(map.getId() + ", " + size).color(ChatColor.GRAY).then("\n\n") + .then(poster.getName()).style(ChatColor.BOLD, ChatColor.GREEN).then("\n") + .then(poster.getId() + ", " + size).color(ChatColor.GRAY).then("\n\n") .then(I.t("{white}Click{gray} to get this map")) ); } diff --git a/src/main/java/fr/moribus/imageonmap/commands/maptool/MigrateCommand.java b/src/main/java/fr/moribus/imageonmap/commands/maptool/MigrateCommand.java index 993f3372..42cfdb4d 100644 --- a/src/main/java/fr/moribus/imageonmap/commands/maptool/MigrateCommand.java +++ b/src/main/java/fr/moribus/imageonmap/commands/maptool/MigrateCommand.java @@ -1,8 +1,8 @@ /* * Copyright or © or Copr. Moribus (2013) * Copyright or © or Copr. ProkopyL (2015) - * Copyright or © or Copr. Amaury Carrade (2016 – 2021) - * Copyright or © or Copr. Vlammar (2019 – 2021) + * Copyright or © or Copr. Amaury Carrade (2016 – 2022) + * Copyright or © or Copr. Vlammar (2019 – 2024) * * This software is a computer program whose purpose is to allow insertion of * custom images in a Minecraft world. diff --git a/src/main/java/fr/moribus/imageonmap/commands/maptool/NewCommand.java b/src/main/java/fr/moribus/imageonmap/commands/maptool/NewCommand.java index bef3a490..4068e110 100644 --- a/src/main/java/fr/moribus/imageonmap/commands/maptool/NewCommand.java +++ b/src/main/java/fr/moribus/imageonmap/commands/maptool/NewCommand.java @@ -1,8 +1,8 @@ /* * Copyright or © or Copr. Moribus (2013) * Copyright or © or Copr. ProkopyL (2015) - * Copyright or © or Copr. Amaury Carrade (2016 – 2021) - * Copyright or © or Copr. Vlammar (2019 – 2021) + * Copyright or © or Copr. Amaury Carrade (2016 – 2022) + * Copyright or © or Copr. Vlammar (2019 – 2024) * * This software is a computer program whose purpose is to allow insertion of * custom images in a Minecraft world. @@ -36,23 +36,28 @@ package fr.moribus.imageonmap.commands.maptool; +import fr.moribus.imageonmap.Argument; import fr.moribus.imageonmap.Permissions; +import fr.moribus.imageonmap.Status; +import fr.moribus.imageonmap.Type; import fr.moribus.imageonmap.commands.IoMCommand; import fr.moribus.imageonmap.image.ImageRendererExecutor; import fr.moribus.imageonmap.image.ImageUtils; -import fr.moribus.imageonmap.map.ImageMap; -import fr.moribus.imageonmap.map.MapManager; +import fr.moribus.imageonmap.map.ImagePoster; +import fr.moribus.imageonmap.map.PosterData; +import fr.moribus.imageonmap.map.PosterManager; import fr.moribus.imageonmap.map.PosterMap; import fr.zcraft.quartzlib.components.commands.CommandException; import fr.zcraft.quartzlib.components.commands.CommandInfo; import fr.zcraft.quartzlib.components.i18n.I; import fr.zcraft.quartzlib.components.worker.WorkerCallback; import fr.zcraft.quartzlib.tools.PluginLogger; -import fr.zcraft.quartzlib.tools.text.ActionBar; -import fr.zcraft.quartzlib.tools.text.MessageSender; -import java.net.MalformedURLException; import java.net.URL; -import org.bukkit.ChatColor; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import net.md_5.bungee.api.ChatMessageType; +import net.md_5.bungee.api.chat.TextComponent; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; @@ -77,111 +82,162 @@ private ImageUtils.ScalingType resizeMode() throws CommandException { } } - @Override - protected void run() throws CommandException { - final Player player = playerSender(); - ImageUtils.ScalingType scaling = ImageUtils.ScalingType.NONE; - URL url; - int width = 0; - int height = 0; - - if (args.length < 1) { - throwInvalidArgument(I.t("You must give an URL to take the image from.")); + private void imageLimitReached(Player player) { + int imageLimit = Permissions.NEW.getLimitPermission(player, Permissions.LimitType.image); + int imageCount = PosterManager.getplayerPosterStore(player.getUniqueId()).getImagesCount(); + if (!Permissions.BYPASS_IMAGE_LIMIT.grantedTo(player) && imageLimit <= imageCount) { + I.t("Your map limit is set to {0} and you currently have {1} loaded map(s)", + imageLimit, + imageCount); } - //Checking if the map limit and image limit - if (!Permissions.BYPASS_IMAGE_LIMIT.grantedTo(player)) { - int imageLimit = Permissions.NEW.getLimitPermission(player, Permissions.LimitType.image); - int imageCount = MapManager.getPlayerMapStore(player.getUniqueId()).getImagesCount(); - if (imageLimit <= imageCount) { - throwInvalidArgument( - I.t("Your image limit is set to {0} and you currently have {1} loaded image(s)", - imageLimit, - imageCount)); - } + + } + + private void posterLimitReached(Player player) { + + int posterLimit = Permissions.NEW.getLimitPermission(player, Permissions.LimitType.poster); + int posterCount = PosterManager.getplayerPosterStore(player.getUniqueId()).getPosterCount(); + if (!Permissions.BYPASS_POSTER_LIMIT.grantedTo(player) && posterLimit <= posterCount) { + I.t("Your map limit is set to {0} and you currently have {1} loaded map(s)", + posterLimit, + posterCount); } - if (!Permissions.BYPASS_MAP_LIMIT.grantedTo(player)) { - int mapLimit = Permissions.NEW.getLimitPermission(player, Permissions.LimitType.map); - int mapCount = MapManager.getPlayerMapStore(player.getUniqueId()).getMapCount(); - if (mapLimit <= mapCount) { - throwInvalidArgument( - I.t("Your map limit is set to {0} and you currently have {1} loaded map(s)", - mapLimit, - mapCount)); + } + + private PosterData parseSize(PosterData posterData) { + if (args.length >= 4) { + posterData.setWidth(Integer.parseInt(args[2])); + posterData.setHeight(Integer.parseInt(args[3])); + } else { + String[] size; + //Parse n*m and nxm where n and m are integer + if (args[2].contains("*") && !args[2].contains("x")) { + size = args[2].split("\\*"); + posterData.setWidth(Integer.parseInt(size[0])); + posterData.setHeight(Integer.parseInt(size[1])); } + if (!args[2].contains("*") && args[2].contains("x")) { + size = args[2].split("x"); + posterData.setWidth(Integer.parseInt(size[0])); + posterData.setHeight(Integer.parseInt(size[1])); + } + } + return posterData; + } + + @Override + protected void run() throws CommandException { try { - url = new URL(args[0]); - if (!Permissions.BYPASS_WHITELIST.grantedTo(player) && !checkHostnameWhitelist(url)) { + //TODO check if not too many args + final Player player = playerSender(); + if (args.length < 1) { + throwInvalidArgument(I.t("You must give an URL to take the image from.")); + } + //Checking if the poster limit and image limit + + imageLimitReached(player); + posterLimitReached(player); + boolean isPlayer = sender != null; + ArrayList arguments = getArgs(); + + + List prototype = new ArrayList<>(); + prototype.add(new Argument("URL", Type.STRING, Status.MANDATORY)); + Map argMap = Argument.parseArguments(prototype, arguments, isPlayer); + for (String key : argMap.keySet()) { + Argument arg = argMap.get(key); + PluginLogger.info("Arguments : \n name " + arg.getName() + "\n type " + arg.getType() + + "\n status " + arg.getStatus() + "\n arg content " + arg.getContent()); + } + + PluginLogger.info("qjksckhjkhqjskhjc quhshukchjkqs " + argMap.get("URL").getContent()); + URL url = new URL(argMap.get("URL").getContent()); + PosterData posterData = new PosterData(url); + posterData.setURL(url); //TODO WTF... the constructor doesn't work + PluginLogger.info("url qsjkh jsjc " + url); + + + if (!Permissions.BYPASS_WHITELIST.grantedTo(player) && !checkHostnameWhitelist(posterData.getURL())) { throwInvalidArgument(I.t("This hosting website is not trusted, if you think that this is an error " + " contact your server administrator")); return; } - } catch (MalformedURLException ex) { - throwInvalidArgument(I.t("Invalid URL.")); - return; - } - - if (args.length >= 2) { - if (args.length >= 3) { + // TODO Add a per-player toggle for the GUI. + if (args.length < 2 && isPlayer) { try { - if (args.length >= 4) { - width = Integer.parseInt(args[2]); - height = Integer.parseInt(args[3]); - } else { - String[] size; - if (args[2].contains("*") && !args[2].contains("x")) { - size = args[2].split("\\*"); - width = Integer.parseInt(size[0]); - height = Integer.parseInt(size[1]); - } - if (!args[2].contains("*") && args[2].contains("x")) { - size = args[2].split("x"); - width = Integer.parseInt(size[0]); - height = Integer.parseInt(size[1]); + String message = I.t("&1 test..."); + PluginLogger.info("URL quuhqsjhkcjhk " + posterData.getURL()); + TextComponent text = new TextComponent(message); + player.spigot().sendMessage(ChatMessageType.ACTION_BAR, text); + posterData.setHeight(0); + posterData.setWidth(0); + posterData.setScaling(ImageUtils.ScalingType.NONE); + } catch (Exception e) { + PluginLogger.info("Exception " + e.getMessage()); + } + + //Gui.open(player, new RenderGui(posterData.getURL()); + //ImageRendererExecutor.renderAndNotify(posterData.getURL(), posterData.getScaling(), + // player.getUniqueId(), + //posterData.getWidth(), posterData.getHeight()); + } else { + if (args.length >= 2) { + if (args.length >= 3) { + try { + posterData = parseSize(posterData); + } catch (NumberFormatException | ArrayIndexOutOfBoundsException e) { + throwInvalidArgument( + I.t("resize dimension as to be in format or or .")); + return; } } - } catch (NumberFormatException | ArrayIndexOutOfBoundsException e) { - throwInvalidArgument(I.t("resize dimension as to be in format or or .")); + posterData.setScaling(resizeMode()); + } + if (posterData.getWidth() < 0 || posterData.getWidth() > 256 + || posterData.getHeight() < 0 || posterData.getHeight() > 256) { + throwInvalidArgument(I.t("You need to specify a valid size. e.g. resize 4 5")); return; } } - scaling = resizeMode(); - } - if (width < 0 || height < 0) { - throwInvalidArgument(I.t("You need to specify a valid size. e.g. resize 4 5")); - return; - } - try { - ActionBar.sendPermanentMessage(player, ChatColor.DARK_GREEN + I.t("Rendering...")); - ImageRendererExecutor - .render(url, scaling, player.getUniqueId(), width, height, new WorkerCallback() { - @Override - public void finished(ImageMap result) { - ActionBar.removeMessage(player); - MessageSender - .sendActionBarMessage(player, ChatColor.DARK_GREEN + I.t("Rendering finished!")); - - if (result.give(player) - && (result instanceof PosterMap && !((PosterMap) result).hasColumnData())) { - info(I.t("The rendered map was too big to fit in your inventory.")); - info(I.t("Use '/maptool getremaining' to get the remaining maps.")); - } + try { + String message = I.t("&1 Rendering..."); + TextComponent text = new TextComponent(message); + player.spigot().sendMessage(ChatMessageType.ACTION_BAR, text); + ImageRendererExecutor.render(posterData, player.getUniqueId(), new WorkerCallback() { + @Override + public void finished(ImagePoster result) { + String message = I.t("&1 Rendering finished!"); + TextComponent text = new TextComponent(message); + player.spigot().sendMessage(ChatMessageType.ACTION_BAR, text); + + if (result.give(player) + && (result instanceof PosterMap && !((PosterMap) result).hasColumnData())) { + info(I.t("The rendered map was too big to fit in your inventory.")); + info(I.t("Use '/maptool getremaining' to get the remaining maps.")); } + } - @Override - public void errored(Throwable exception) { - player.sendMessage(I.t("{ce}Map rendering failed: {0}", exception.getMessage())); + @Override + public void errored(Throwable exception) { + player.sendMessage(I.t("{ce}Map rendering failed: {0}", exception.getMessage())); - PluginLogger.warning("Rendering from {0} failed: {1}: {2}", - player.getName(), - exception.getClass().getCanonicalName(), - exception.getMessage()); - } - }); - } finally { - ActionBar.removeMessage(player); + PluginLogger.warning("Rendering from {0} failed: {1}: {2}", + player.getName(), + exception.getClass().getCanonicalName(), + exception.getMessage()); + } + }); + } finally { + player.spigot().sendMessage(ChatMessageType.ACTION_BAR, new TextComponent("")); + } + + } catch (Exception e) { + PluginLogger.warning(e.toString()); + throwInvalidArgument(I.t("Invalid URL.")); } + } @Override diff --git a/src/main/java/fr/moribus/imageonmap/commands/maptool/RemotePlacingCommand.java b/src/main/java/fr/moribus/imageonmap/commands/maptool/RemotePlacingCommand.java new file mode 100644 index 00000000..ae78523e --- /dev/null +++ b/src/main/java/fr/moribus/imageonmap/commands/maptool/RemotePlacingCommand.java @@ -0,0 +1,126 @@ +/* + * Copyright or © or Copr. Moribus (2013) + * Copyright or © or Copr. ProkopyL (2015) + * Copyright or © or Copr. Amaury Carrade (2016 – 2022) + * Copyright or © or Copr. Vlammar (2019 – 2024) + * + * This software is a computer program whose purpose is to allow insertion of + * custom images in a Minecraft world. + * + * This software is governed by the CeCILL license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL license and that you accept its terms. + */ + +package fr.moribus.imageonmap.commands.maptool; + +import fr.moribus.imageonmap.Permissions; +import fr.moribus.imageonmap.commands.IoMCommand; +import fr.moribus.imageonmap.map.ImagePoster; +import fr.moribus.imageonmap.map.PlayerPosterStore; +import fr.moribus.imageonmap.map.PosterManager; +import fr.moribus.imageonmap.map.PosterMap; +import fr.zcraft.quartzlib.components.commands.CommandException; +import fr.zcraft.quartzlib.components.commands.CommandInfo; +import fr.zcraft.quartzlib.components.i18n.I; +import java.util.ArrayList; +import java.util.UUID; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.block.BlockFace; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.ItemFrame; + +@CommandInfo(name = "RemotePlacing", usageParameters = "[player name]:mapName worldname x y z [N|W|S|E]") +public class RemotePlacingCommand extends IoMCommand { + @Override + protected void run() throws CommandException { + ArrayList arguments = getArgs(); + String playerName; + String mapName; + if (arguments.get(1).contains(":")) { + playerName = arguments.get(1).split(":")[0]; + mapName = arguments.get(1).split(":")[1]; + } else { + playerName = playerSender().getName(); + mapName = arguments.get(1); + } + UUID uuid = Bukkit.getOfflinePlayer(playerName).getUniqueId(); + ImagePoster poster = PosterManager.getplayerPosterStore(uuid).getPoster(mapName); + + String worldName = arguments.get(1); + World world = Bukkit.getWorld(worldName); + Location loc = new Location(world, + Integer.parseInt(arguments.get(2)), + Integer.parseInt(arguments.get(3)), + Integer.parseInt(arguments.get(4))); + BlockFace bf = null; + //TODO add ground placement and bf for ground ceilling and wall + switch (arguments.get(5)) { + case "N": + case "n": + case "North": + case "north": + bf = BlockFace.NORTH; + break; + case "E": + case "e": + case "East": + case "east": + bf = BlockFace.EAST; + break; + case "W": + case "w": + case "West": + case "west": + bf = BlockFace.WEST; + break; + case "S": + case "s": + case "South": + case "south": + bf = BlockFace.SOUTH; + break; + default: + //or messagesender + throwInvalidArgument(I.t("Must specify a valid rotation N|W|S|E|")); + break; + } + ItemFrame i = world.spawn(loc, ItemFrame.class); + i.setFacingDirection(bf); + world.getBlockAt(loc); + + //summon item frame(location, rotation) + //if wall => need position and direction N/S/E/W + //else if floor or ceiling => same + rotation + } + + @Override + public boolean canExecute(CommandSender sender) { + return Permissions.REMOTE_PLACING.grantedTo(sender); + } + +} diff --git a/src/main/java/fr/moribus/imageonmap/commands/maptool/RenameCommand.java b/src/main/java/fr/moribus/imageonmap/commands/maptool/RenameCommand.java index 1059eb77..3ddbd046 100644 --- a/src/main/java/fr/moribus/imageonmap/commands/maptool/RenameCommand.java +++ b/src/main/java/fr/moribus/imageonmap/commands/maptool/RenameCommand.java @@ -1,8 +1,8 @@ /* * Copyright or © or Copr. Moribus (2013) * Copyright or © or Copr. ProkopyL (2015) - * Copyright or © or Copr. Amaury Carrade (2016 – 2021) - * Copyright or © or Copr. Vlammar (2019 – 2021) + * Copyright or © or Copr. Amaury Carrade (2016 – 2022) + * Copyright or © or Copr. Vlammar (2019 – 2024) * * This software is a computer program whose purpose is to allow insertion of * custom images in a Minecraft world. @@ -34,30 +34,12 @@ * knowledge of the CeCILL license and that you accept its terms. */ -/* - * Copyright (C) 2013 Moribus - * Copyright (C) 2015 ProkopyL - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - package fr.moribus.imageonmap.commands.maptool; import fr.moribus.imageonmap.Permissions; import fr.moribus.imageonmap.commands.IoMCommand; -import fr.moribus.imageonmap.map.ImageMap; -import fr.moribus.imageonmap.map.MapManager; +import fr.moribus.imageonmap.map.ImagePoster; +import fr.moribus.imageonmap.map.PosterManager; import fr.zcraft.quartzlib.components.commands.CommandException; import fr.zcraft.quartzlib.components.commands.CommandInfo; import fr.zcraft.quartzlib.components.i18n.I; @@ -70,27 +52,27 @@ public class RenameCommand extends IoMCommand { @Override protected void run() throws CommandException { + ArrayList arguments = getArgs(); - ArrayList argList = getArgs(); - - if (argList.size() != 2) { - warning(I.t("Not enough or too many arguments! Usage: /maptool rename ")); + boolean isTooMany = arguments.size() > 2; + boolean isTooFew = arguments.size() < 2; + if (!checkArguments(isTooMany, isTooFew)) { return; } - - ImageMap map = MapManager.getMap(playerSender().getUniqueId(), argList.get(0)); - if (map == null) { + String oldName = arguments.get(0); + ImagePoster poster = PosterManager.getPoster(playerSender().getUniqueId(), oldName); + if (poster == null) { error(I.t("This map does not exist.")); return; } - map.rename(argList.get(1)); + String newName = arguments.get(1); + poster.rename(newName); } @Override protected List complete() throws CommandException { - if (args.length == 1) { - return getMatchingMapNames(playerSender(), args[0]); + return getMatchingPosterNames(playerSender(), args[0]); } return null; } diff --git a/src/main/java/fr/moribus/imageonmap/commands/maptool/UpdateCommand.java b/src/main/java/fr/moribus/imageonmap/commands/maptool/UpdateCommand.java index 3e843b5d..f1dfcc9f 100644 --- a/src/main/java/fr/moribus/imageonmap/commands/maptool/UpdateCommand.java +++ b/src/main/java/fr/moribus/imageonmap/commands/maptool/UpdateCommand.java @@ -1,8 +1,8 @@ /* * Copyright or © or Copr. Moribus (2013) * Copyright or © or Copr. ProkopyL (2015) - * Copyright or © or Copr. Amaury Carrade (2016 – 2021) - * Copyright or © or Copr. Vlammar (2019 – 2021) + * Copyright or © or Copr. Amaury Carrade (2016 – 2022) + * Copyright or © or Copr. Vlammar (2019 – 2024) * * This software is a computer program whose purpose is to allow insertion of * custom images in a Minecraft world. @@ -40,19 +40,19 @@ import fr.moribus.imageonmap.commands.IoMCommand; import fr.moribus.imageonmap.image.ImageRendererExecutor; import fr.moribus.imageonmap.image.ImageUtils; -import fr.moribus.imageonmap.map.ImageMap; -import fr.moribus.imageonmap.map.MapManager; +import fr.moribus.imageonmap.map.ImagePoster; +import fr.moribus.imageonmap.map.PosterManager; import fr.zcraft.quartzlib.components.commands.CommandException; import fr.zcraft.quartzlib.components.commands.CommandInfo; import fr.zcraft.quartzlib.components.i18n.I; import fr.zcraft.quartzlib.components.worker.WorkerCallback; import fr.zcraft.quartzlib.tools.PluginLogger; -import fr.zcraft.quartzlib.tools.text.ActionBar; -import fr.zcraft.quartzlib.tools.text.MessageSender; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; -import org.bukkit.ChatColor; +import java.util.UUID; +import net.md_5.bungee.api.ChatMessageType; +import net.md_5.bungee.api.chat.TextComponent; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; @@ -62,21 +62,14 @@ public class UpdateCommand extends IoMCommand { protected void run() throws CommandException { //TODO fix the issue where to many quick usage of offlineNameFetch will return null ArrayList arguments = getArgs(); - String warningMsg; - if (arguments.size() > 4) { - warningMsg = "Too many parameters!" - + " Usage: /maptool update [player name]: [stretched|covered]"; - warning(I.t(warningMsg)); - return; - } - if (arguments.size() < 2) { - warningMsg = - "Too few parameters! Usage: /maptool update [player name]: [stretched|covered]"; - warning(I.t(warningMsg)); + + boolean isTooMany = arguments.size() > 4; + boolean isTooFew = arguments.size() < 2; + if (!checkArguments(isTooMany, isTooFew)) { return; } final String playerName; - final String mapName; + final String posterName; final String url; final String resize; final Player playerSender; @@ -101,7 +94,7 @@ protected void run() throws CommandException { if (arguments.size() == 2) { resize = ""; playerName = playerSender.getName(); - mapName = arguments.get(0); + posterName = arguments.get(0); url = arguments.get(1); } else { if (arguments.size() == 4) { @@ -110,14 +103,14 @@ protected void run() throws CommandException { return; } playerName = arguments.get(0); - mapName = arguments.get(1); + posterName = arguments.get(1); url = arguments.get(2); resize = arguments.get(3); } else { if (arguments.size() == 3) { if (arguments.get(2).equals("covered") || arguments.get(2).equals("stretched")) { playerName = playerSender.getName(); - mapName = arguments.get(0); + posterName = arguments.get(0); url = arguments.get(1); resize = arguments.get(2); } else { @@ -126,7 +119,7 @@ protected void run() throws CommandException { return; } playerName = arguments.get(0); - mapName = arguments.get(1); + posterName = arguments.get(1); url = arguments.get(2); resize = ""; } @@ -134,92 +127,89 @@ protected void run() throws CommandException { resize = ""; playerName = ""; url = ""; - mapName = ""; + posterName = ""; } } } - final ImageUtils.ScalingType scaling; + final ImageUtils.ScalingType scaling = ImageUtils.scalingTypeFromName(resize);//TODO test if nothing broke + // because I went from 3 to 4 by adding the none as default instead of the contained one. - switch (resize) { + UUID uuid = getPlayerUUID(playerName); + ImagePoster poster = PosterManager.getPoster(uuid, posterName); - case "stretched": - scaling = ImageUtils.ScalingType.STRETCHED; - break; - case "covered": - scaling = ImageUtils.ScalingType.COVERED; - break; - default: - scaling = ImageUtils.ScalingType.CONTAINED; + if (poster == null) { + warning(sender, I.t("This map does not exist.")); + return; } - //TODO passer en static - //ImageOnMap.getPlugin().getCommandWorker().offlineNameFetch(playerName, uuid -> { - retrieveUUID(playerName, uuid -> { - - ImageMap map = MapManager.getMap(uuid, mapName); - - if (map == null) { - warning(sender, I.t("This map does not exist.")); + URL url1; + try { + url1 = new URL(url); + if (!Permissions.BYPASS_WHITELIST.grantedTo(playerSender) && !checkHostnameWhitelist(url1)) { + throwInvalidArgument(I.t("This hosting website is not trusted, if you think that this is an error " + + " contact your server administrator")); return; } - URL url1; - try { - url1 = new URL(url); - if (!Permissions.BYPASS_WHITELIST.grantedTo(playerSender) && !checkHostnameWhitelist(url1)) { - throwInvalidArgument(I.t("This hosting website is not trusted, if you think that this is an error " - + " contact your server administrator")); - return; - } + //TODO replace by a check of the load status.(if not loaded load the mapmanager) + PosterManager.load(false);//we don't want to spam the console each time we reload the mapManager - //TODO replace by a check of the load status.(if not loaded load the mapmanager) - MapManager.load(false);//we don't want to spam the console each time we reload the mapManager + Integer[] size = {1, 1}; + if (poster.getType() == ImagePoster.Type.POSTER) { + size = poster.getSize(poster.getUserUUID(), poster.getId()); + } - Integer[] size = {1, 1}; - if (map.getType() == ImageMap.Type.POSTER) { - size = map.getSize(map.getUserUUID(), map.getId()); + if (size.length == 0) { + size = new Integer[] {1, 1}; + } + int width = size[0]; + int height = size[1]; + try { + String msg = I.t("Updating..."); + if (playerSender != null) { + //TODO test if player human + String message = I.t("&2 Updating..."); + TextComponent text = new TextComponent(message); + playerSender.spigot().sendMessage(ChatMessageType.ACTION_BAR, text); + } else { + PluginLogger.info(msg); } - - int width = size[0]; - int height = size[1]; - try { - if (playerSender != null) { - ActionBar.sendPermanentMessage(playerSender, ChatColor.DARK_GREEN + I.t("Updating...")); - } - ImageRendererExecutor - .update(url1, scaling, uuid, map, width, height, new WorkerCallback() { - @Override - public void finished(ImageMap result) { - if (playerSender != null) { - ActionBar.removeMessage(playerSender); - MessageSender.sendActionBarMessage(playerSender, - ChatColor.DARK_GREEN + I.t("The map was updated using the new image!")); - } + ImageRendererExecutor + .update(url1, scaling, uuid, poster, width, height, new WorkerCallback() { + @Override + public void finished(ImagePoster result) { + String msg = I.t("The map was updated using the new image!"); + if (playerSender != null) { + String message = I.t("&2 The map was updated using the new image!"); + TextComponent text = new TextComponent(message); + playerSender.spigot().sendMessage(ChatMessageType.ACTION_BAR, text); + } else { + PluginLogger.info(msg); } - - @Override - public void errored(Throwable exception) { - if (playerSender != null) { - playerSender - .sendMessage( - I.t("{ce}Map rendering failed: {0}", exception.getMessage())); - } - PluginLogger.warning("Rendering from {0} failed: {1}: {2}", - playerSender.getName(), - exception.getClass().getCanonicalName(), - exception.getMessage()); + } + + @Override + public void errored(Throwable exception) { + if (playerSender != null) { + String message = I.t("&C Map rendering failed: {0}", exception.getMessage()); + TextComponent text = new TextComponent(message); + playerSender.spigot().sendMessage(ChatMessageType.CHAT, text); } - }); - } finally { - if (playerSender != null) { - ActionBar.removeMessage(playerSender); - } + PluginLogger.warning("Rendering from {0} failed: {1}: {2}", + playerSender.getName(), + exception.getClass().getCanonicalName(), + exception.getMessage()); + } + }); + } finally { + if (playerSender != null) { + playerSender.spigot().sendMessage(ChatMessageType.ACTION_BAR,new TextComponent("")); } - } catch (MalformedURLException | CommandException ex) { - warning(sender, I.t("Invalid URL.")); } - }); + } catch (MalformedURLException | CommandException ex) { + warning(sender, I.t("Invalid URL.")); + } } diff --git a/src/main/java/fr/moribus/imageonmap/gui/ConfirmDeleteMapGui.java b/src/main/java/fr/moribus/imageonmap/gui/ConfirmDeletePosterGui.java similarity index 87% rename from src/main/java/fr/moribus/imageonmap/gui/ConfirmDeleteMapGui.java rename to src/main/java/fr/moribus/imageonmap/gui/ConfirmDeletePosterGui.java index 5695a33f..922c11e9 100644 --- a/src/main/java/fr/moribus/imageonmap/gui/ConfirmDeleteMapGui.java +++ b/src/main/java/fr/moribus/imageonmap/gui/ConfirmDeletePosterGui.java @@ -1,8 +1,8 @@ /* * Copyright or © or Copr. Moribus (2013) * Copyright or © or Copr. ProkopyL (2015) - * Copyright or © or Copr. Amaury Carrade (2016 – 2021) - * Copyright or © or Copr. Vlammar (2019 – 2021) + * Copyright or © or Copr. Amaury Carrade (2016 – 2022) + * Copyright or © or Copr. Vlammar (2019 – 2024) * * This software is a computer program whose purpose is to allow insertion of * custom images in a Minecraft world. @@ -37,9 +37,9 @@ package fr.moribus.imageonmap.gui; import fr.moribus.imageonmap.Permissions; -import fr.moribus.imageonmap.map.ImageMap; -import fr.moribus.imageonmap.map.MapManager; -import fr.moribus.imageonmap.map.MapManagerException; +import fr.moribus.imageonmap.map.ImagePoster; +import fr.moribus.imageonmap.map.PosterManager; +import fr.moribus.imageonmap.map.PosterManagerException; import fr.zcraft.quartzlib.components.gui.ActionGui; import fr.zcraft.quartzlib.components.gui.Gui; import fr.zcraft.quartzlib.components.gui.GuiAction; @@ -52,7 +52,7 @@ import org.bukkit.inventory.ItemStack; -public class ConfirmDeleteMapGui extends ActionGui { +public class ConfirmDeletePosterGui extends ActionGui { private static final int BUTTONS_WIDTH = 4; private static final int FIRST_SLOT_DELETE_BUTTON = 27; @@ -89,7 +89,7 @@ public class ConfirmDeleteMapGui extends ActionGui { /** * The map being deleted. */ - private final ImageMap mapToDelete; + private final ImagePoster posterToDelete; /** * A source of randomness. @@ -99,16 +99,16 @@ public class ConfirmDeleteMapGui extends ActionGui { /** - * @param mapToDelete The map being deleted. + * @param posterToDelete The poster being deleted. */ - public ConfirmDeleteMapGui(ImageMap mapToDelete) { - this.mapToDelete = mapToDelete; + public ConfirmDeletePosterGui(ImagePoster posterToDelete) { + this.posterToDelete = posterToDelete; } @Override protected void onUpdate() { - /// The title of the map deletion GUI. {0}: map name. - setTitle(I.t(getPlayerLocale(), "{0} » {black}Confirm deletion", mapToDelete.getName())); + /// The title of the poster deletion GUI. {0}: poster name. + setTitle(I.t(getPlayerLocale(), "{0} » {black}Confirm deletion", posterToDelete.getName())); setSize(6 * 9); @@ -120,9 +120,9 @@ protected void onUpdate() { /// The end, in the lore, of a title starting with “You're about to destroy this map...”. .lore(I.t(getPlayerLocale(), "{red}...{italic}forever{red}.")) .loreLine() - .lore(I.t(getPlayerLocale(), "{gray}Name: {white}{0}", mapToDelete.getName())) - .lore(I.t(getPlayerLocale(), "{gray}Map ID: {white}{0}", mapToDelete.getId())) - .lore(I.t(getPlayerLocale(), "{gray}Maps inside: {white}{0}", mapToDelete.getMapsIDs().length)) + .lore(I.t(getPlayerLocale(), "{gray}Name: {white}{0}", posterToDelete.getName())) + .lore(I.t(getPlayerLocale(), "{gray}Map ID: {white}{0}", posterToDelete.getId())) + .lore(I.t(getPlayerLocale(), "{gray}Maps inside: {white}{0}", posterToDelete.getPostersIDs().length)) .hideAllAttributes() ); @@ -163,25 +163,25 @@ protected void cancel() { @GuiAction("delete") protected void delete() { - // Does the player still have the permission to delete a map? + // Does the player still have the permission to delete a poster? if (!Permissions.DELETE.grantedTo(getPlayer())) { I.sendT(getPlayer(), "{ce}You are no longer allowed to do that."); close(); return; } - MapManager.clear(getPlayer().getInventory(), mapToDelete); + PosterManager.clear(getPlayer().getInventory(), posterToDelete); try { - MapManager.deleteMap(mapToDelete); + PosterManager.deletePoster(posterToDelete); getPlayer().sendMessage(I.t("{gray}Map successfully deleted.")); - } catch (MapManagerException ex) { + } catch (PosterManagerException ex) { PluginLogger.warning("Error while deleting map", ex); getPlayer().sendMessage(ChatColor.RED + ex.getMessage()); } - // We try to open the map list GUI, if the map was deleted, before the details GUI + // We try to open the poster list GUI, if the poster was deleted, before the details GUI // (so the grandparent GUI). if (getParent() != null && getParent().getParent() != null) { Gui.open(getPlayer(), getParent().getParent()); diff --git a/src/main/java/fr/moribus/imageonmap/gui/MapDetailGui.java b/src/main/java/fr/moribus/imageonmap/gui/PosterDetailGui.java similarity index 77% rename from src/main/java/fr/moribus/imageonmap/gui/MapDetailGui.java rename to src/main/java/fr/moribus/imageonmap/gui/PosterDetailGui.java index f224b192..ec240b06 100644 --- a/src/main/java/fr/moribus/imageonmap/gui/MapDetailGui.java +++ b/src/main/java/fr/moribus/imageonmap/gui/PosterDetailGui.java @@ -1,8 +1,8 @@ /* * Copyright or © or Copr. Moribus (2013) * Copyright or © or Copr. ProkopyL (2015) - * Copyright or © or Copr. Amaury Carrade (2016 – 2021) - * Copyright or © or Copr. Vlammar (2019 – 2021) + * Copyright or © or Copr. Amaury Carrade (2016 – 2022) + * Copyright or © or Copr. Vlammar (2019 – 2024) * * This software is a computer program whose purpose is to allow insertion of * custom images in a Minecraft world. @@ -36,11 +36,11 @@ package fr.moribus.imageonmap.gui; + import fr.moribus.imageonmap.Permissions; -import fr.moribus.imageonmap.map.ImageMap; +import fr.moribus.imageonmap.map.ImagePoster; import fr.moribus.imageonmap.map.PosterMap; -import fr.moribus.imageonmap.map.SingleMap; -import fr.moribus.imageonmap.ui.MapItemManager; +import fr.moribus.imageonmap.ui.PosterItemManager; import fr.zcraft.quartzlib.components.gui.ExplorerGui; import fr.zcraft.quartzlib.components.gui.Gui; import fr.zcraft.quartzlib.components.gui.GuiAction; @@ -53,25 +53,40 @@ import org.bukkit.Material; import org.bukkit.OfflinePlayer; import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; -public class MapDetailGui extends ExplorerGui { - private final ImageMap map; - private OfflinePlayer offplayer; - private String name; +public class PosterDetailGui extends ExplorerGui { + private final ImagePoster iposter; + private final OfflinePlayer offplayer; + private final String name; - public MapDetailGui(ImageMap map, OfflinePlayer p, String name) { + public PosterDetailGui(ImagePoster iposter, OfflinePlayer p, String name) { super(); - this.map = map; + this.iposter = iposter; this.offplayer = p; this.name = name; } + private void setPosterPartCustomModelData(ItemStack item) { + ItemMeta meta = item.getItemMeta(); + + //TODO make it prettier and fix issues with scrolling + if (iposter instanceof PosterMap) { + meta.setCustomModelData(1000);//TODO ID was 1000 maybe a bug there to check + item.setItemMeta(meta); + } + } + @Override protected ItemStack getViewItem(int x, int y) { final Material partMaterial = y % 2 == x % 2 ? Material.MAP : Material.PAPER; + ItemStackBuilder builder = new ItemStackBuilder(partMaterial); + ItemStack itemStack = builder.craftItem(); - final ItemStackBuilder builder = new ItemStackBuilder(partMaterial) + setPosterPartCustomModelData(itemStack); + builder = new ItemStackBuilder(itemStack); + builder = builder .title(I.t(getPlayerLocale(), "{green}Map part")) .lore(I.t(getPlayerLocale(), "{gray}Row: {white}{0}", y + 1)) .lore(I.t(getPlayerLocale(), "{gray}Column: {white}{0}", x + 1)); @@ -84,8 +99,8 @@ protected ItemStack getViewItem(int x, int y) { } @Override - protected ItemStack getViewItem(Integer mapId) { - final int index = ((PosterMap) map).getIndex(mapId); + protected ItemStack getViewItem(Integer posterId) { + final int index = ((PosterMap) iposter).getIndex(posterId); final Material partMaterial = index % 2 == 0 ? Material.MAP : Material.PAPER; final ItemStackBuilder builder = new ItemStackBuilder(partMaterial) @@ -105,50 +120,44 @@ protected ItemStack getPickedUpItem(int x, int y) { return null; } - if (map instanceof SingleMap) { - return MapItemManager.createMapItem((SingleMap) map, true); - } else if (map instanceof PosterMap) { - return MapItemManager.createMapItem((PosterMap) map, x, y); + if (iposter instanceof PosterMap) { + return PosterItemManager.createPosterItem((PosterMap) iposter, x, y); } - throw new IllegalStateException("Unsupported map type: " + map.getType()); + throw new IllegalStateException("Unsupported map type: " + iposter.getType()); } @Override - protected ItemStack getPickedUpItem(Integer mapId) { + protected ItemStack getPickedUpItem(Integer posterId) { if (!Permissions.GET.grantedTo(getPlayer())) { return null; } - final PosterMap poster = (PosterMap) map; - return MapItemManager.createMapItem(poster, poster.getIndex(mapId)); + final PosterMap poster = (PosterMap) iposter; + return PosterItemManager.createPosterItem(poster, poster.getIndex(posterId)); } @Override protected ItemStack getEmptyViewItem() { - if (map instanceof SingleMap) { - return getViewItem(0, 0); - } else { - return super.getEmptyViewItem(); - } + return super.getEmptyViewItem(); } @Override protected void onUpdate() { - /// Title of the map details GUI + /// Title of the poster details GUI if (offplayer.getUniqueId().equals(getPlayer().getUniqueId())) { - setTitle(I.t(getPlayerLocale(), "Your maps » {black}{0}", map.getName())); + setTitle(I.t(getPlayerLocale(), "Your maps » {black}{0}", iposter.getName())); } else { - setTitle(I.t(getPlayerLocale(), "{1}'s maps » {black}{0}", map.getName(), name)); + setTitle(I.t(getPlayerLocale(), "{1}'s maps » {black}{0}", iposter.getName(), name)); } setKeepHorizontalScrollingSpace(true); - if (map instanceof PosterMap) { - PosterMap poster = (PosterMap) map; + if (iposter instanceof PosterMap) { + PosterMap poster = (PosterMap) iposter; if (poster.hasColumnData()) { setDataShape(poster.getColumnCount(), poster.getRowCount()); } else { - setData(ArrayUtils.toObject(poster.getMapsIDs())); + setData(ArrayUtils.toObject(poster.getPostersIDs())); } } else { setDataShape(1, 1); @@ -190,7 +199,7 @@ protected void onUpdate() { if (!canRename && !canDelete) { backSlot = getSize() - 5; - } else if (map instanceof PosterMap && ((PosterMap) map).getColumnCount() <= INVENTORY_ROW_SIZE) { + } else if (iposter instanceof PosterMap && ((PosterMap) iposter).getColumnCount() <= INVENTORY_ROW_SIZE) { backSlot++; } @@ -220,20 +229,19 @@ public void rename() { I.sendT(getPlayer(), "{ce}Map names can't be empty."); return; } - if (newName.equals(map.getName())) { + if (newName.equals(iposter.getName())) { return; } - map.rename(newName); + iposter.rename(newName); I.sendT(getPlayer(), "{cs}Map successfully renamed."); if (getParent() != null) { RunTask.later(() -> Gui.open(getPlayer(), this), 1L); - } else { close(); } - }, map.getName(), this); + }, iposter.getName(), this); } catch (IllegalStateException e) { PluginLogger.error("Error while renaming map: ", e); @@ -247,7 +255,7 @@ public void delete() { update(); return; } - Gui.open(getPlayer(), new ConfirmDeleteMapGui(map), this); + Gui.open(getPlayer(), new ConfirmDeletePosterGui(iposter), this); } @GuiAction("back") diff --git a/src/main/java/fr/moribus/imageonmap/gui/MapListGui.java b/src/main/java/fr/moribus/imageonmap/gui/PosterListGui.java similarity index 60% rename from src/main/java/fr/moribus/imageonmap/gui/MapListGui.java rename to src/main/java/fr/moribus/imageonmap/gui/PosterListGui.java index 476ccd7a..e3b0ac28 100644 --- a/src/main/java/fr/moribus/imageonmap/gui/MapListGui.java +++ b/src/main/java/fr/moribus/imageonmap/gui/PosterListGui.java @@ -1,8 +1,8 @@ /* * Copyright or © or Copr. Moribus (2013) * Copyright or © or Copr. ProkopyL (2015) - * Copyright or © or Copr. Amaury Carrade (2016 – 2021) - * Copyright or © or Copr. Vlammar (2019 – 2021) + * Copyright or © or Copr. Amaury Carrade (2016 – 2022) + * Copyright or © or Copr. Vlammar (2019 – 2024) * * This software is a computer program whose purpose is to allow insertion of * custom images in a Minecraft world. @@ -38,63 +38,66 @@ import fr.moribus.imageonmap.Permissions; import fr.moribus.imageonmap.PluginConfiguration; -import fr.moribus.imageonmap.map.ImageMap; -import fr.moribus.imageonmap.map.MapManager; +import fr.moribus.imageonmap.map.ImagePoster; +import fr.moribus.imageonmap.map.PosterManager; import fr.moribus.imageonmap.map.PosterMap; -import fr.moribus.imageonmap.map.SingleMap; -import fr.moribus.imageonmap.ui.MapItemManager; -import fr.moribus.imageonmap.ui.SplatterMapManager; +import fr.moribus.imageonmap.ui.PosterItemManager; +import fr.moribus.imageonmap.ui.SplatterPosterManager; import fr.zcraft.quartzlib.components.gui.ExplorerGui; import fr.zcraft.quartzlib.components.gui.Gui; import fr.zcraft.quartzlib.components.i18n.I; +import fr.zcraft.quartzlib.tools.PluginLogger; import fr.zcraft.quartzlib.tools.items.ItemStackBuilder; import org.bukkit.Color; import org.bukkit.Material; import org.bukkit.OfflinePlayer; import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.inventory.meta.MapMeta; -public class MapListGui extends ExplorerGui { +public class PosterListGui extends ExplorerGui { private OfflinePlayer offplayer; private String name; - public MapListGui(OfflinePlayer sender) { + public PosterListGui(OfflinePlayer sender) { this.offplayer = sender; this.name = sender.getName(); } - public MapListGui(OfflinePlayer p, String name) { + public PosterListGui(OfflinePlayer p, String name) { this.offplayer = p; this.name = name; } @Override - protected ItemStack getViewItem(ImageMap map) { - String mapDescription; - if (map instanceof SingleMap) { - /// Displayed subtitle description of a single map on the list GUI - mapDescription = I.tl(getPlayerLocale(), "{white}Single map"); + protected ItemStack getViewItem(ImagePoster iposter) { + String posterDescription; + PosterMap poster = (PosterMap) iposter; + if (poster.hasColumnData()) { + /// Displayed subtitle description of a poster poster on the list GUI (columns × rows in english) + posterDescription = I.tl(getPlayerLocale(), "{white}Poster map ({0} × {1})", poster.getColumnCount(), + poster.getRowCount()); } else { - PosterMap poster = (PosterMap) map; - if (poster.hasColumnData()) { - /// Displayed subtitle description of a poster map on the list GUI (columns × rows in english) - mapDescription = I.tl(getPlayerLocale(), "{white}Poster map ({0} × {1})", poster.getColumnCount(), - poster.getRowCount()); - } else { - /// Displayed subtitle description of a poster map without column data on the list GUI - mapDescription = I.tl(getPlayerLocale(), "{white}Poster map ({0} parts)", poster.getMapCount()); - } + /// Displayed subtitle description of a poster map without column data on the list GUI + posterDescription = I.tl(getPlayerLocale(), "{white}Poster map ({0} parts)", poster.getPosterCount()); } - - ItemStackBuilder builder = new ItemStackBuilder(Material.FILLED_MAP) - /// Displayed title of a map on the list GUI - .title(I.tl(getPlayerLocale(), "{green}{bold}{0}", map.getName())) - - .lore(mapDescription) + ItemStackBuilder builder = + new ItemStackBuilder(Material.FILLED_MAP); + ItemStack item = builder.craftItem(); + ItemMeta itemMeta = item.getItemMeta(); + itemMeta.setCustomModelData(1); + item.setItemMeta(itemMeta); + PluginLogger.info(item.getItemMeta().toString()); + builder = new ItemStackBuilder(item); + builder = builder + /// Displayed title of a poster on the list GUI + .title(I.tl(getPlayerLocale(), "{green}{bold}{0}", poster.getName())) + + .lore(posterDescription) .loreLine() - /// Map ID displayed in the tooltip of a map on the list GUI - .lore(I.tl(getPlayerLocale(), "{gray}Map ID: {0}", map.getId())) + /// poster ID displayed in the tooltip of a poster on the list GUI + .lore(I.tl(getPlayerLocale(), "{gray}Map ID: {0}", poster.getId())) .loreLine(); if (Permissions.GET.grantedTo(getPlayer())) { @@ -103,13 +106,13 @@ protected ItemStack getViewItem(ImageMap map) { builder.lore(I.tl(getPlayerLocale(), "{gray}» {white}Right-click{gray} for details and options")); - final ItemStack mapItem = builder.item(); + final ItemStack posterItem = builder.item(); - final MapMeta meta = (MapMeta) mapItem.getItemMeta(); + final MapMeta meta = (MapMeta) posterItem.getItemMeta(); meta.setColor(Color.GREEN); - mapItem.setItemMeta(meta); + posterItem.setItemMeta(meta); - return mapItem; + return posterItem; } @Override @@ -132,8 +135,8 @@ protected ItemStack getEmptyViewItem() { } @Override - protected void onRightClick(ImageMap data) { - Gui.open(getPlayer(), new MapDetailGui(data, getPlayer(), name), this); + protected void onRightClick(ImagePoster data) { + Gui.open(getPlayer(), new PosterDetailGui(data, getPlayer(), name), this); } @Override @@ -142,66 +145,65 @@ protected void onClose() { } @Override - protected ItemStack getPickedUpItem(ImageMap map) { + protected ItemStack getPickedUpItem(ImagePoster iposter) { if (!Permissions.GET.grantedTo(getPlayer())) { return null; } - if (map instanceof SingleMap) { - return MapItemManager.createMapItem(map.getMapsIDs()[0], map.getName(), false, true); - } else if (map instanceof PosterMap) { - PosterMap poster = (PosterMap) map; + if (iposter instanceof PosterMap) { + PosterMap poster = (PosterMap) iposter; if (poster.hasColumnData()) { - return SplatterMapManager.makeSplatterMap((PosterMap) map); + return SplatterPosterManager.makeSplatterPoster(poster); } - MapItemManager.giveParts(getPlayer(), poster); + PosterItemManager.giveParts(getPlayer(), poster); return null; } - MapItemManager.give(getPlayer(), map); + PosterItemManager.give(getPlayer(), iposter); return null; } @Override protected void onUpdate() { - ImageMap[] maps = MapManager.getMaps(offplayer.getUniqueId()); - setData(maps); + ImagePoster[] posters = PosterManager.getPosters(offplayer.getUniqueId()); + setData(posters); /// The maps list GUI title //Equal if the person who send the command is the owner of the mapList if (offplayer.getUniqueId().equals(getPlayer().getUniqueId())) { - setTitle(I.tl(getPlayerLocale(), "{black}Your maps {reset}({0})", maps.length)); + setTitle(I.tl(getPlayerLocale(), "{black}Your maps {reset}({0})", posters.length)); } else { - setTitle(I.tl(getPlayerLocale(), "{black}{1}'s maps {reset}({0})", maps.length, name)); + setTitle(I.tl(getPlayerLocale(), "{black}{1}'s maps {reset}({0})", posters.length, name)); } setKeepHorizontalScrollingSpace(true); /* ** Statistics ** */ - int imagesCount = MapManager.getMapList(offplayer.getUniqueId()).size(); - int mapPartCount = MapManager.getMapPartCount(offplayer.getUniqueId()); - - int mapGlobalLimit = PluginConfiguration.MAP_GLOBAL_LIMIT.get(); - int mapPersonalLimit = PluginConfiguration.MAP_PLAYER_LIMIT.get(); - - int mapPartGloballyLeft = mapGlobalLimit - MapManager.getMapCount(); - int mapPartPersonallyLeft = mapPersonalLimit - mapPartCount; - - int mapPartLeft; - if (mapGlobalLimit <= 0 && mapPersonalLimit <= 0) { - mapPartLeft = -1; - } else if (mapGlobalLimit <= 0) { - mapPartLeft = mapPartPersonallyLeft; - } else if (mapPersonalLimit <= 0) { - mapPartLeft = mapPartGloballyLeft; + int imagesCount = PosterManager.getPosterList(offplayer.getUniqueId()).size(); + int posterPartCount = PosterManager.getPosterPartCount(offplayer.getUniqueId()); + + int posterGlobalLimit = PluginConfiguration.POSTER_GLOBAL_LIMIT.get(); + int posterPersonalLimit = PluginConfiguration.POSTER_PLAYER_LIMIT.get(); + + int posterPartGloballyLeft = posterGlobalLimit - PosterManager.getPosterCount(); + int posterPartPersonallyLeft = posterPersonalLimit - posterPartCount; + + int posterPartLeft; + if (posterGlobalLimit <= 0 && posterPersonalLimit <= 0) { + posterPartLeft = -1; + } else if (posterGlobalLimit <= 0) { + posterPartLeft = posterPartPersonallyLeft; + } else if (posterPersonalLimit <= 0) { + posterPartLeft = posterPartGloballyLeft; } else { - mapPartLeft = Math.min(mapPartGloballyLeft, mapPartPersonallyLeft); + posterPartLeft = Math.min(posterPartGloballyLeft, posterPartPersonallyLeft); } double percentageUsed = - mapPartLeft < 0 ? 0 : ((double) mapPartCount) / ((double) (mapPartCount + mapPartLeft)) * 100; + posterPartLeft < 0 ? 0 : + ((double) posterPartCount) / ((double) (posterPartCount + posterPartLeft)) * 100; ItemStackBuilder statistics = new ItemStackBuilder(Material.ENCHANTED_BOOK) .title(I.t(getPlayerLocale(), "{blue}Usage statistics")) @@ -209,22 +211,22 @@ protected void onUpdate() { .lore(I.tn(getPlayerLocale(), "{white}{0}{gray} image rendered", "{white}{0}{gray} images rendered", imagesCount)) .lore(I.tn(getPlayerLocale(), "{white}{0}{gray} Minecraft map used", - "{white}{0}{gray} Minecraft maps used", mapPartCount)); + "{white}{0}{gray} Minecraft maps used", posterPartCount)); - if (mapPartLeft >= 0) { + if (posterPartLeft >= 0) { statistics .lore("", I.t(getPlayerLocale(), "{blue}Minecraft maps limits"), "") - .lore(mapGlobalLimit == 0 + .lore(posterGlobalLimit == 0 ? I.t(getPlayerLocale(), "{gray}Server-wide limit: {white}unlimited") - : I.t(getPlayerLocale(), "{gray}Server-wide limit: {white}{0}", mapGlobalLimit)) - .lore(mapPersonalLimit == 0 + : I.t(getPlayerLocale(), "{gray}Server-wide limit: {white}{0}", posterGlobalLimit)) + .lore(posterPersonalLimit == 0 ? I.t(getPlayerLocale(), "{gray}Per-player limit: {white}unlimited") - : I.t(getPlayerLocale(), "{gray}Per-player limit: {white}{0}", mapPersonalLimit)) + : I.t(getPlayerLocale(), "{gray}Per-player limit: {white}{0}", posterPersonalLimit)) .loreLine() .lore(I.t(getPlayerLocale(), "{white}{0} %{gray} of your quota used", (int) Math.rint(percentageUsed))) .lore(I.tn(getPlayerLocale(), "{white}{0}{gray} map left", "{white}{0}{gray} maps left", - mapPartLeft)); + posterPartLeft)); } statistics.hideAllAttributes(); diff --git a/src/main/java/fr/moribus/imageonmap/gui/RenderGui.java b/src/main/java/fr/moribus/imageonmap/gui/RenderGui.java new file mode 100644 index 00000000..bc3cd656 --- /dev/null +++ b/src/main/java/fr/moribus/imageonmap/gui/RenderGui.java @@ -0,0 +1,183 @@ +/* + * Copyright or © or Copr. Moribus (2013) + * Copyright or © or Copr. ProkopyL (2015) + * Copyright or © or Copr. Amaury Carrade (2016 – 2022) + * Copyright or © or Copr. Vlammar (2019 – 2024) + * + * This software is a computer program whose purpose is to allow insertion of + * custom images in a Minecraft world. + * + * This software is governed by the CeCILL license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL license and that you accept its terms. + */ + + +package fr.moribus.imageonmap.gui; + +import fr.moribus.imageonmap.image.ImageUtils; +import fr.zcraft.quartzlib.components.gui.ActionGui; +import fr.zcraft.quartzlib.components.i18n.I; +import fr.zcraft.quartzlib.tools.items.ItemStackBuilder; +import fr.zcraft.quartzlib.tools.items.TextualBanners; +import java.net.URL; +import org.bukkit.ChatColor; +import org.bukkit.DyeColor; +import org.bukkit.Material; + + +public class RenderGui extends ActionGui { + final URL url; + + boolean resize = false; + int width = 0; + int height = 0; + + boolean originalSizeLoaded = false; + int originalWidth = 0; + int originalHeight = 0; + + ImageUtils.ScalingType scaling = ImageUtils.ScalingType.NONE; + + public RenderGui(URL url) { + this.url = url; + } + + @Override + protected void onUpdate() { + setTitle(I.t("Image Editor")); + setHeight(6); + + action("toggle_resize", 0, new ItemStackBuilder(Material.PAINTING) + .title(ChatColor.LIGHT_PURPLE, ChatColor.BOLD + I.t("Resize image")) + .loreLine(ChatColor.DARK_GRAY, resize ? I.t("Enabled") : I.t("Disabled")) + .loreSeparator() + .longLore(ChatColor.GRAY, + I.t("You can automatically resize the image to a certain number of blocks (or item frames).")) + .loreSeparator() + .loreLine(ChatColor.BLUE, I.t("Original size (in blocks)")) + .loreLine(ChatColor.GRAY, + originalSizeLoaded ? I.t("{0} × {1}", originalWidth, originalHeight) : I.t("Loading...")) + .loreSeparator() + .longLore(resize ? I.t("{gray}» {white}Click{gray} to disable resize") : + I.t("{gray}» {white}Click{gray} to enable resize")) + ); + + injectSizeEditor(2, true); + injectSizeEditor(11, false); + } + + /** + * Injects the size editor in the GUI. + * + * @param slot The slot where the editor must start. + * @param isWidth True to inject a width-size editor; false to inject a height-editor. + */ + private void injectSizeEditor(int slot, final boolean isWidth) { + final String action_key = isWidth ? "width_" : "height_"; + final String currentSize = ChatColor.DARK_GRAY + I.t("Current size: {0} × {1}", width, height); + + /*action(action_key + "_decrement_10", slot++, getBannerButton(false, true, resize) + .title(ChatColor.RED, I.t("- 10")) + .loreLine(currentSize) + .loreSeparator() + .longLore(isWidth + ? I.t("{gray}» {white}Click{gray} to decrease the image's width by 10 blocks") + : I.t("{gray}» {white}Click{gray} to decrease the image's height by 10 blocks") + ) + ); + + action(action_key + "_decrement_1", slot++, getBannerButton(false, false, resize) + .title(ChatColor.RED, I.t("- 1")) + .loreLine(currentSize) + .loreSeparator() + .longLore(isWidth + ? I.t("{gray}» {white}Click{gray} to decrease the image's width by one block") + : I.t("{gray}» {white}Click{gray} to decrease the image's height by one block") + ) + ); + + action(action_key + "_increment_1", slot++, getBannerButton(true, false, resize) + .title(ChatColor.GREEN, I.t("+ 1")) + .loreLine(currentSize) + .loreSeparator() + .longLore(isWidth + ? I.t("{gray}» {white}Click{gray} to increase the image's width by one block") + : I.t("{gray}» {white}Click{gray} to increase the image's height by one block") + ) + ); + + action(action_key + "_increment_10", slot++, getBannerButton(true, true, resize) + .title(ChatColor.GREEN, I.t("+ 10")) + .loreLine(currentSize) + .loreSeparator() + .longLore(isWidth + ? I.t("{gray}» {white}Click{gray} to increase the image's width by 10 blocks") + : I.t("{gray}» {white}Click{gray} to increase the image's height by 10 blocks") + ) + ); + + + /*(action_key + "_set_values", slot++, getBannerButton(false, false, resize) + .title(ChatColor.BLUE, I.t("set the size")) + .loreLine(currentSize) + .loreSeparator() + .longLore(isWidth + ? I.t("{gray}» {white}Click{gray} to set the image's width") + : I.t("{gray}» {white}Click{gray} to set the image's height") + ) + );*/ + slot++; + } + + /** + * Creates a banner for the +/- buttons. + * + are green, - are red + * short steps are light, long steps are dark + * disabled banners are in grayscale + * + * @param positive true for a + banner + * @param longStep true for a darker banner + * @param disabled true for a grayscale banner + * @return The banner in a builder. + */ + private ItemStackBuilder getBannerButton(boolean positive, boolean longStep, boolean disabled) { + //final char symbol = positive ? '+' : '-'; //TODO this need rework have something that work but need QL update + final char symbol = positive ? '*' : '-'; + final DyeColor background; + + if (disabled) { + background = longStep ? DyeColor.BLACK : DyeColor.GRAY; + } else { + if (positive) { + background = longStep ? DyeColor.GREEN : DyeColor.LIME; + } else { + background = longStep ? DyeColor.RED : DyeColor.PINK; + } + } + + return new ItemStackBuilder(TextualBanners.getCharBanner(symbol, background, DyeColor.BLACK)); + } +} diff --git a/src/main/java/fr/moribus/imageonmap/image/ImageIOExecutor.java b/src/main/java/fr/moribus/imageonmap/image/ImageIOExecutor.java index 7f6cd17c..a56c24fd 100644 --- a/src/main/java/fr/moribus/imageonmap/image/ImageIOExecutor.java +++ b/src/main/java/fr/moribus/imageonmap/image/ImageIOExecutor.java @@ -1,8 +1,8 @@ /* * Copyright or © or Copr. Moribus (2013) * Copyright or © or Copr. ProkopyL (2015) - * Copyright or © or Copr. Amaury Carrade (2016 – 2021) - * Copyright or © or Copr. Vlammar (2019 – 2021) + * Copyright or © or Copr. Amaury Carrade (2016 – 2022) + * Copyright or © or Copr. Vlammar (2019 – 2024) * * This software is a computer program whose purpose is to allow insertion of * custom images in a Minecraft world. @@ -37,7 +37,7 @@ package fr.moribus.imageonmap.image; import fr.moribus.imageonmap.ImageOnMap; -import fr.moribus.imageonmap.map.ImageMap; +import fr.moribus.imageonmap.map.ImagePoster; import fr.zcraft.quartzlib.components.worker.Worker; import fr.zcraft.quartzlib.components.worker.WorkerAttributes; import fr.zcraft.quartzlib.components.worker.WorkerRunnable; @@ -49,13 +49,13 @@ @WorkerAttributes(name = "Image IO") public class ImageIOExecutor extends Worker { - public static void loadImage(final File file, final Renderer mapRenderer) { + public static void loadImage(final File file, final Renderer posterRenderer) { submitQuery(new WorkerRunnable() { @Override public Void run() throws Exception { BufferedImage image = ImageIO.read(file); - mapRenderer.setImage(image); + posterRenderer.setImage(image); image.flush();//Safe to free return null; } @@ -72,22 +72,22 @@ public Void run() throws Throwable { }); } - public static void saveImage(int mapID, BufferedImage image) { - saveImage(ImageOnMap.getPlugin().getImageFile(mapID), image); + public static void saveImage(int posterID, BufferedImage image) { + saveImage(ImageOnMap.getPlugin().getImageFile(posterID), image); } - public static void saveImage(int[] mapsIDs, PosterImage image) { - for (int i = 0, c = mapsIDs.length; i < c; i++) { + public static void saveImage(int[] postersIDs, PosterImage image) { + for (int i = 0, c = postersIDs.length; i < c; i++) { BufferedImage img = image.getImageAt(i); - ImageIOExecutor.saveImage(ImageOnMap.getPlugin().getImageFile(mapsIDs[i]), img); + ImageIOExecutor.saveImage(ImageOnMap.getPlugin().getImageFile(postersIDs[i]), img); img.flush();//Safe to free } } - public static void deleteImage(ImageMap map) { - int[] mapsIDs = map.getMapsIDs(); - for (int i = 0, c = mapsIDs.length; i < c; i++) { - deleteImage(ImageOnMap.getPlugin().getImageFile(mapsIDs[i])); + public static void deleteImage(ImagePoster poster) { + int[] postersIDs = poster.getPostersIDs(); + for (int i = 0, c = postersIDs.length; i < c; i++) { + deleteImage(ImageOnMap.getPlugin().getImageFile(postersIDs[i])); } } diff --git a/src/main/java/fr/moribus/imageonmap/image/ImageRendererExecutor.java b/src/main/java/fr/moribus/imageonmap/image/ImageRendererExecutor.java index 2321f38f..fe8f3489 100644 --- a/src/main/java/fr/moribus/imageonmap/image/ImageRendererExecutor.java +++ b/src/main/java/fr/moribus/imageonmap/image/ImageRendererExecutor.java @@ -1,8 +1,8 @@ /* * Copyright or © or Copr. Moribus (2013) * Copyright or © or Copr. ProkopyL (2015) - * Copyright or © or Copr. Amaury Carrade (2016 – 2021) - * Copyright or © or Copr. Vlammar (2019 – 2021) + * Copyright or © or Copr. Amaury Carrade (2016 – 2022) + * Copyright or © or Copr. Vlammar (2019 – 2024) * * This software is a computer program whose purpose is to allow insertion of * custom images in a Minecraft world. @@ -36,15 +36,19 @@ package fr.moribus.imageonmap.image; + import fr.moribus.imageonmap.Permissions; import fr.moribus.imageonmap.PluginConfiguration; -import fr.moribus.imageonmap.map.ImageMap; -import fr.moribus.imageonmap.map.MapManager; +import fr.moribus.imageonmap.map.ImagePoster; +import fr.moribus.imageonmap.map.PosterData; +import fr.moribus.imageonmap.map.PosterManager; +import fr.moribus.imageonmap.map.PosterMap; import fr.zcraft.quartzlib.components.i18n.I; import fr.zcraft.quartzlib.components.worker.Worker; import fr.zcraft.quartzlib.components.worker.WorkerAttributes; import fr.zcraft.quartzlib.components.worker.WorkerCallback; import fr.zcraft.quartzlib.components.worker.WorkerRunnable; +import fr.zcraft.quartzlib.tools.PluginLogger; import java.awt.image.BufferedImage; import java.io.IOException; import java.io.InputStream; @@ -55,10 +59,52 @@ import java.util.concurrent.Callable; import java.util.concurrent.Future; import javax.imageio.ImageIO; +import net.md_5.bungee.api.ChatMessageType; +import net.md_5.bungee.api.chat.TextComponent; import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + @WorkerAttributes(name = "Image Renderer", queriesMainThread = true) public class ImageRendererExecutor extends Worker { + public static void renderAndNotify(final URL url, final ImageUtils.ScalingType scaling, final UUID playerUUID, + final int width, final int height) { + final Player player = Bukkit.getPlayer(playerUUID); + if (player == null) { + return; + } + player.spigot().sendMessage(ChatMessageType.ACTION_BAR, new TextComponent(I.t("&1 Rendering..."))); + + render(url, scaling, player.getUniqueId(), width, height, new WorkerCallback() { + @Override + public void finished(ImagePoster result) { + String message = I.t("&1 Rendering finished!"); + TextComponent text = new TextComponent(message); + player.spigot().sendMessage(ChatMessageType.ACTION_BAR, text); + + if (result.give(player) && (result instanceof PosterMap && !((PosterMap) result).hasColumnData())) { + player.spigot().sendMessage(ChatMessageType.CHAT, + new TextComponent(I.t("&7 The rendered map was too big to fit in your inventory."))); + player.spigot().sendMessage(ChatMessageType.CHAT, + new TextComponent(I.t("&7 Use '/maptool getremaining' to get the remaining maps."))); + } + } + + @Override + public void errored(Throwable exception) { + String message = I.t("&C Map rendering failed: {0}", exception.getMessage()); + TextComponent text = new TextComponent(message); + player.spigot().sendMessage(ChatMessageType.CHAT, text); + player.spigot().sendMessage(ChatMessageType.ACTION_BAR, new TextComponent("")); + + PluginLogger.warning("Rendering from {0} failed: {1}: {2}", + player.getName(), + exception.getClass().getCanonicalName(), + exception.getMessage()); + } + }); + } + private static URLConnection connecting(URL url) throws IOException { final URLConnection connection = url.openConnection(); connection.addRequestProperty("User-Agent", @@ -91,11 +137,18 @@ private static void checkSizeLimit(final UUID playerUUID, final BufferedImage im } } + public static void render(final PosterData posterData, final UUID playerUUID, + WorkerCallback callback) { + render(posterData.getURL(), posterData.getScaling(), playerUUID, posterData.getWidth(), posterData.getHeight(), + callback); + } + public static void render(final URL url, final ImageUtils.ScalingType scaling, final UUID playerUUID, - final int width, final int height, WorkerCallback callback) { - submitQuery(new WorkerRunnable() { + final int width, final int height, WorkerCallback callback) { + submitQuery(new WorkerRunnable() { + @Override - public ImageMap run() throws Throwable { + public ImagePoster run() throws Throwable { BufferedImage image = null; @@ -144,25 +197,22 @@ public ImageMap run() throws Throwable { } // Limits are in place and the player does NOT have rights to avoid them. checkSizeLimit(playerUUID, image); - if (scaling != ImageUtils.ScalingType.NONE && height <= 1 && width <= 1) { - ImageMap ret = renderSingle(scaling.resize(image, ImageMap.WIDTH, ImageMap.HEIGHT), playerUUID); - image.flush();//Safe to free - return ret; - } - final BufferedImage resizedImage = - scaling.resize(image, ImageMap.WIDTH * width, ImageMap.HEIGHT * height); + final BufferedImage resizedImage; + + resizedImage = scaling.resize(image, ImagePoster.WIDTH * width, ImagePoster.HEIGHT * height); image.flush();//Safe to free + return renderPoster(resizedImage, playerUUID); } }, callback); } public static void update(final URL url, final ImageUtils.ScalingType scaling, final UUID playerUUID, - final ImageMap map, final int width, final int height, - WorkerCallback callback) { - submitQuery(new WorkerRunnable() { + final ImagePoster poster, final int width, final int height, + WorkerCallback callback) { + submitQuery(new WorkerRunnable() { @Override - public ImageMap run() throws Throwable { + public ImagePoster run() throws Throwable { final URLConnection connection = connecting(url); final InputStream stream = connection.getInputStream(); @@ -176,85 +226,68 @@ public ImageMap run() throws Throwable { // Limits are in place and the player does NOT have rights to avoid them. checkSizeLimit(playerUUID, image); - updateMap(scaling.resize(image, width * 128, height * 128), playerUUID, map.getMapsIDs()); - return map; + updatePoster(scaling.resize(image, width * 128, height * 128), + playerUUID, poster.getPostersIDs()); + return poster; } }, callback); } - private static void updateMap(final BufferedImage image, final UUID playerUUID, int[] mapsIDs) throws Throwable { + private static void updatePoster(final BufferedImage image, final UUID playerUUID, int[] postersIDs) + throws Throwable { final PosterImage poster = new PosterImage(image); poster.splitImages(); - ImageIOExecutor.saveImage(mapsIDs, poster); + ImageIOExecutor.saveImage(postersIDs, poster); if (PluginConfiguration.SAVE_FULL_IMAGE.get()) { - ImageIOExecutor.saveImage(ImageMap.getFullImageFile(mapsIDs[0], mapsIDs[mapsIDs.length - 1]), image); + ImageIOExecutor.saveImage(ImagePoster.getFullImageFile(postersIDs[0], postersIDs[postersIDs.length - 1]), + image); } submitToMainThread(new Callable() { @Override public Void call() throws Exception { - Renderer.installRenderer(poster, mapsIDs); - return null; - } - }); - } - - private static ImageMap renderSingle(final BufferedImage image, final UUID playerUUID) throws Throwable { - MapManager.checkMapLimit(1, playerUUID); - final Future futureMapID = submitToMainThread(new Callable() { - @Override - public Integer call() throws Exception { - return MapManager.getNewMapsIds(1)[0]; - } - }); - - final int mapID = futureMapID.get(); - ImageIOExecutor.saveImage(mapID, image); - submitToMainThread(new Callable() { - @Override - public Void call() throws Exception { - Renderer.installRenderer(image, mapID); + Renderer.installRenderer(poster, postersIDs); return null; } }); - return MapManager.createMap(playerUUID, mapID); } - private static ImageMap renderPoster(final BufferedImage image, final UUID playerUUID) throws Throwable { + private static ImagePoster renderPoster(final BufferedImage image, final UUID playerUUID) throws Throwable { final PosterImage poster = new PosterImage(image); - final int mapCount = poster.getImagesCount(); - MapManager.checkMapLimit(mapCount, playerUUID); - final Future futureMapsIds = submitToMainThread(new Callable() { + final int posterCount = poster.getImagesCount(); + PosterManager.checkPosterLimit(posterCount, playerUUID); + final Future futurePostersIds = submitToMainThread(new Callable() { @Override public int[] call() throws Exception { - return MapManager.getNewMapsIds(mapCount); + return PosterManager.getNewPostersIds(posterCount); } }); poster.splitImages(); - final int[] mapsIDs = futureMapsIds.get(); - ImageIOExecutor.saveImage(mapsIDs, poster); + final int[] postersIDs = futurePostersIds.get(); + ImageIOExecutor.saveImage(postersIDs, poster); - ImageIOExecutor.saveImage(mapsIDs, poster); + ImageIOExecutor.saveImage(postersIDs, poster); if (PluginConfiguration.SAVE_FULL_IMAGE.get()) { - ImageIOExecutor.saveImage(ImageMap.getFullImageFile(mapsIDs[0], mapsIDs[mapsIDs.length - 1]), image); + ImageIOExecutor.saveImage(ImagePoster.getFullImageFile(postersIDs[0], postersIDs[postersIDs.length - 1]), + image); } submitToMainThread(new Callable() { @Override public Void call() throws Exception { - Renderer.installRenderer(poster, mapsIDs); + Renderer.installRenderer(poster, postersIDs); return null; } }); poster.getImage().flush();//Safe to free - return MapManager.createMap(poster, playerUUID, mapsIDs); + return PosterManager.createPoster(poster, playerUUID, postersIDs); } private enum Extension { diff --git a/src/main/java/fr/moribus/imageonmap/image/ImageUtils.java b/src/main/java/fr/moribus/imageonmap/image/ImageUtils.java index 191408ca..6dd41974 100644 --- a/src/main/java/fr/moribus/imageonmap/image/ImageUtils.java +++ b/src/main/java/fr/moribus/imageonmap/image/ImageUtils.java @@ -1,8 +1,8 @@ /* * Copyright or © or Copr. Moribus (2013) * Copyright or © or Copr. ProkopyL (2015) - * Copyright or © or Copr. Amaury Carrade (2016 – 2021) - * Copyright or © or Copr. Vlammar (2019 – 2021) + * Copyright or © or Copr. Amaury Carrade (2016 – 2022) + * Copyright or © or Copr. Vlammar (2019 – 2024) * * This software is a computer program whose purpose is to allow insertion of * custom images in a Minecraft world. @@ -125,6 +125,29 @@ private static BufferedImage drawImage(BufferedImage source, } + public static ScalingType scalingTypeFromName(String resize) { + switch (resize) { + case "stretch": + case "stretched": + case "resize-stretched": + return ScalingType.STRETCHED; + + case "cover": + case "covered": + case "resize-covered": + return ScalingType.COVERED; + + case "contain": + case "contained": + case "resize-contained": + case "resize": + return ScalingType.CONTAINED; + + default: + return ScalingType.NONE; + } + } + public enum ScalingType { NONE, CONTAINED, diff --git a/src/main/java/fr/moribus/imageonmap/image/PosterImage.java b/src/main/java/fr/moribus/imageonmap/image/PosterImage.java index 253530d7..ec2808e0 100644 --- a/src/main/java/fr/moribus/imageonmap/image/PosterImage.java +++ b/src/main/java/fr/moribus/imageonmap/image/PosterImage.java @@ -1,8 +1,8 @@ /* * Copyright or © or Copr. Moribus (2013) * Copyright or © or Copr. ProkopyL (2015) - * Copyright or © or Copr. Amaury Carrade (2016 – 2021) - * Copyright or © or Copr. Vlammar (2019 – 2021) + * Copyright or © or Copr. Amaury Carrade (2016 – 2022) + * Copyright or © or Copr. Vlammar (2019 – 2024) * * This software is a computer program whose purpose is to allow insertion of * custom images in a Minecraft world. @@ -113,10 +113,10 @@ public void splitImages() { } /** - * Generates the subimage that intersects with the given map rectangle. + * Generates the subimage that intersects with the given poster rectangle. * - * @param x X coordinate of top-left point of the map. - * @param y Y coordinate of top-left point of the map. + * @param x X coordinate of top-left point of the poster. + * @param y Y coordinate of top-left point of the poster. * @return the requested subimage. */ private BufferedImage makeSubImage(BufferedImage originalImage, int x, int y) { diff --git a/src/main/java/fr/moribus/imageonmap/image/MapInitEvent.java b/src/main/java/fr/moribus/imageonmap/image/PosterInitEvent.java similarity index 81% rename from src/main/java/fr/moribus/imageonmap/image/MapInitEvent.java rename to src/main/java/fr/moribus/imageonmap/image/PosterInitEvent.java index f2f27e80..4b891e6a 100644 --- a/src/main/java/fr/moribus/imageonmap/image/MapInitEvent.java +++ b/src/main/java/fr/moribus/imageonmap/image/PosterInitEvent.java @@ -1,8 +1,8 @@ /* * Copyright or © or Copr. Moribus (2013) * Copyright or © or Copr. ProkopyL (2015) - * Copyright or © or Copr. Amaury Carrade (2016 – 2021) - * Copyright or © or Copr. Vlammar (2019 – 2021) + * Copyright or © or Copr. Amaury Carrade (2016 – 2022) + * Copyright or © or Copr. Vlammar (2019 – 2024) * * This software is a computer program whose purpose is to allow insertion of * custom images in a Minecraft world. @@ -37,7 +37,7 @@ package fr.moribus.imageonmap.image; import fr.moribus.imageonmap.ImageOnMap; -import fr.moribus.imageonmap.map.MapManager; +import fr.moribus.imageonmap.map.PosterManager; import fr.zcraft.quartzlib.components.events.FutureEventHandler; import fr.zcraft.quartzlib.components.events.FutureEvents; import fr.zcraft.quartzlib.components.events.WrappedEvent; @@ -66,45 +66,46 @@ import org.bukkit.inventory.ItemStack; import org.bukkit.map.MapView; -public class MapInitEvent implements Listener { +public class PosterInitEvent implements Listener { public static void init() { - QuartzLib.registerEvents(new MapInitEvent()); + QuartzLib.registerEvents(new PosterInitEvent()); FutureEvents.registerFutureEvents(new EntitiesLoadListener()); for (World world : Bukkit.getWorlds()) { for (ItemFrame frame : world.getEntitiesByClass(ItemFrame.class)) { - initMap(frame.getItem()); + initPoster(frame.getItem()); } } for (Player player : Bukkit.getOnlinePlayers()) { - initMap(player.getInventory().getItemInMainHand()); + initPoster(player.getInventory().getItemInMainHand()); } } - public static void initMap(ItemStack item) { + public static void initPoster(ItemStack item) { if (item != null && item.getType() == Material.FILLED_MAP) { - initMap(MapManager.getMapIdFromItemStack(item)); + initPoster(PosterManager.getPosterIDFromItemStack(item)); } } - public static void initMap(int id) { - initMap(Bukkit.getServer().getMap(id)); + @Deprecated + public static void initPoster(int id) { + initPoster(Bukkit.getServer().getMap(id)); } - public static void initMap(MapView map) { - if (map == null) { + public static void initPoster(MapView view) { + if (view == null) { return; } - if (Renderer.isHandled(map)) { + if (Renderer.isHandled(view)) { return; } - File imageFile = ImageOnMap.getPlugin().getImageFile(map.getId()); + File imageFile = ImageOnMap.getPlugin().getImageFile(view.getId()); if (imageFile.isFile()) { - ImageIOExecutor.loadImage(imageFile, Renderer.installRenderer(map)); + ImageIOExecutor.loadImage(imageFile, Renderer.installRenderer(view)); } } @@ -113,7 +114,7 @@ public void onChunkLoad(ChunkLoadEvent event) { RunTask.later(() -> { for (Entity entity : event.getChunk().getEntities()) { if (entity instanceof ItemFrame) { - initMap(((ItemFrame) entity).getItem()); + initPoster(((ItemFrame) entity).getItem()); } } }, 5L); @@ -122,7 +123,7 @@ public void onChunkLoad(ChunkLoadEvent event) { @EventHandler public void onPlayerInv(PlayerItemHeldEvent event) { ItemStack item = event.getPlayer().getInventory().getItem(event.getNewSlot()); - initMap(item); + initPoster(item); } @EventHandler @@ -130,7 +131,7 @@ public void onPlayerPickup(EntityPickupItemEvent event) { if (!(event.getEntity() instanceof HumanEntity)) { return; } - initMap(event.getItem().getItemStack()); + initPoster(event.getItem().getItemStack()); } @EventHandler @@ -140,7 +141,7 @@ public void onPlayerInventoryPlace(InventoryClickEvent event) { case PLACE_ONE: case PLACE_SOME: case SWAP_WITH_CURSOR: - initMap(event.getCursor()); + initPoster(event.getCursor()); break; default: @@ -149,7 +150,7 @@ public void onPlayerInventoryPlace(InventoryClickEvent event) { @EventHandler public void onEntityDamageByEntityEvent(EntityDamageByEntityEvent event) { - //Negate entity interaction with item frame containing IoM maps. + //Negate entity interaction with item frame containing IoM posters. Entity entity = event.getEntity(); if (!(entity instanceof ItemFrame)) { @@ -157,13 +158,13 @@ public void onEntityDamageByEntityEvent(EntityDamageByEntityEvent event) { } Entity damager = event.getDamager(); if (damager instanceof Player) { - //can solve the dup with the map here by doing a better handling + //can solve the dup with the poster here by doing a better handling return; } ItemStack item = ((ItemFrame) entity).getItem(); if (item.getType() == Material.FILLED_MAP) { - //if the map exist we canceled the event - if (MapManager.getMap(item) != null) { + //if the poster exist we canceled the event + if (PosterManager.getPoster(item) != null) { event.setCancelled(true); } } @@ -171,7 +172,7 @@ public void onEntityDamageByEntityEvent(EntityDamageByEntityEvent event) { @EventHandler public void onEntityDamageByBlockEvent(EntityDamageByBlockEvent event) { - //Negate damage done to IoM maps by some blocks + //Negate damage done to IoM posters by some blocks Entity entity = event.getEntity(); if (!(entity instanceof ItemFrame)) { @@ -179,8 +180,8 @@ public void onEntityDamageByBlockEvent(EntityDamageByBlockEvent event) { } ItemStack item = ((ItemFrame) entity).getItem(); if (item.getType() == Material.FILLED_MAP) { - //if the map exist we canceled the event - if (MapManager.getMap(item) != null) { + //if the poster exist we canceled the event + if (PosterManager.getPoster(item) != null) { switch (event.getCause()) { case MAGIC: case ENTITY_EXPLOSION: @@ -209,8 +210,8 @@ public void onHangingBreakEvent(HangingBreakEvent event) { ItemStack item = ((ItemFrame) entity).getItem(); if (item.getType() == Material.FILLED_MAP) { - //if the map exist we canceled the event - if (MapManager.getMap(item) != null) { + //if the poster exist we canceled the event + if (PosterManager.getPoster(item) != null) { if (event.getCause() == HangingBreakEvent.RemoveCause.EXPLOSION) { //creeper goes boom event.setCancelled(true); @@ -224,13 +225,13 @@ protected static final class EntitiesLoadListener implements Listener { @FutureEventHandler(event = "world.EntitiesLoadEvent") public void onEntitiesLoad(WrappedEvent event) { //New in 1.17 - //Used to make sure map are really loaded in 1.17 on Paper (else some won't render or update properly) + //Used to make sure poster are really loaded in 1.17 on Paper (else some won't render or update properly) RunTask.later(() -> { try { List entities = (List) Reflection.call(event.getEvent(), "getEntities"); for (Entity entity : entities) { if (entity instanceof ItemFrame) { - initMap(((ItemFrame) entity).getItem()); + initPoster(((ItemFrame) entity).getItem()); } } } catch (Exception e) { diff --git a/src/main/java/fr/moribus/imageonmap/image/Renderer.java b/src/main/java/fr/moribus/imageonmap/image/Renderer.java index 48542123..68b9f554 100644 --- a/src/main/java/fr/moribus/imageonmap/image/Renderer.java +++ b/src/main/java/fr/moribus/imageonmap/image/Renderer.java @@ -1,8 +1,8 @@ /* * Copyright or © or Copr. Moribus (2013) * Copyright or © or Copr. ProkopyL (2015) - * Copyright or © or Copr. Amaury Carrade (2016 – 2021) - * Copyright or © or Copr. Vlammar (2019 – 2021) + * Copyright or © or Copr. Amaury Carrade (2016 – 2022) + * Copyright or © or Copr. Vlammar (2019 – 2024) * * This software is a computer program whose purpose is to allow insertion of * custom images in a Minecraft world. @@ -37,10 +37,12 @@ package fr.moribus.imageonmap.image; import fr.zcraft.quartzlib.tools.PluginLogger; +import java.awt.Color; import java.awt.image.BufferedImage; import org.bukkit.Bukkit; 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; @@ -55,11 +57,11 @@ protected Renderer(BufferedImage image) { this.image = image; } - public static boolean isHandled(MapView map) { - if (map == null) { + public static boolean isHandled(MapView poster) { + if (poster == null) { return false; } - for (MapRenderer renderer : map.getRenderers()) { + for (MapRenderer renderer : poster.getRenderers()) { if (renderer instanceof Renderer) { return true; } @@ -67,31 +69,32 @@ public static boolean isHandled(MapView map) { return false; } - public static void installRenderer(PosterImage image, int[] mapsIds) { - for (int i = 0; i < mapsIds.length; i++) { - installRenderer(image.getImageAt(i), mapsIds[i]); + public static void installRenderer(PosterImage image, int[] postersIds) { + for (int i = 0; i < postersIds.length; i++) { + installRenderer(image.getImageAt(i), postersIds[i]); } } - public static void installRenderer(BufferedImage image, int mapID) { - MapView map = Bukkit.getMap(mapID); - if (map == null) { - PluginLogger.warning("Could not install renderer for map {0}: the Minecraft map does not exist", mapID); + public static void installRenderer(BufferedImage image, int posterID) { + MapView poster = Bukkit.getMap(posterID); + if (poster == null) { + PluginLogger.warning("Could not install renderer for map {0}: the Minecraft map does not exist", + posterID); } else { - installRenderer(map).setImage(image); + installRenderer(poster).setImage(image); } } - public static Renderer installRenderer(MapView map) { + public static Renderer installRenderer(MapView poster) { Renderer renderer = new Renderer(); - removeRenderers(map); - map.addRenderer(renderer); + removeRenderers(poster); + poster.addRenderer(renderer); return renderer; } - public static void removeRenderers(MapView map) { - for (MapRenderer renderer : map.getRenderers()) { - map.removeRenderer(renderer); + public static void removeRenderers(MapView poster) { + for (MapRenderer renderer : poster.getRenderers()) { + poster.removeRenderer(renderer); } } diff --git a/src/main/java/fr/moribus/imageonmap/map/DATReader.java b/src/main/java/fr/moribus/imageonmap/map/DATReader.java new file mode 100644 index 00000000..5e70c4f3 --- /dev/null +++ b/src/main/java/fr/moribus/imageonmap/map/DATReader.java @@ -0,0 +1,224 @@ +/* + * Copyright or © or Copr. Moribus (2013) + * Copyright or © or Copr. ProkopyL (2015) + * Copyright or © or Copr. Amaury Carrade (2016 – 2022) + * Copyright or © or Copr. Vlammar (2019 – 2024) + * + * This software is a computer program whose purpose is to allow insertion of + * custom images in a Minecraft world. + * + * This software is governed by the CeCILL license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL license and that you accept its terms. + */ + +package fr.moribus.imageonmap.map; + +import fr.zcraft.quartzlib.tools.PluginLogger; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; +import javax.xml.crypto.Data; +import org.bukkit.util.io.BukkitObjectInputStream; +import org.bukkit.util.io.BukkitObjectOutputStream; + +public class DATReader { + + public boolean saveData(String filePath) { + try { + BukkitObjectOutputStream out = + new BukkitObjectOutputStream(new GZIPOutputStream(new FileOutputStream(filePath))); + out.writeObject(this); + out.close(); + return true; + } catch (IOException e) { + // TODO Auto-generated catch block + PluginLogger.error("" + e.getMessage()); + return false; + } + } + + public boolean loadData(String filePath) { + try { + BukkitObjectInputStream in = + new BukkitObjectInputStream(new GZIPInputStream(new FileInputStream(filePath))); + Data data = (Data) in.readObject(); + PluginLogger.info("loaded data" + data); + in.close(); + return true; + } catch (ClassNotFoundException | IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + return false; + } + } +} + +/* +package tutorial; + + import java.io.FileInputStream; + import java.io.FileOutputStream; + import java.io.IOException; + import java.io.Serializable; + import java.util.HashMap; + import java.util.HashSet; + import java.util.UUID; + import java.util.logging.Level; + import java.util.zip.GZIPInputStream; + import java.util.zip.GZIPOutputStream; + + import org.bukkit.Bukkit; + import org.bukkit.Location; + import org.bukkit.util.io.BukkitObjectInputStream; + import org.bukkit.util.io.BukkitObjectOutputStream; + + +public class Data implements Serializable { + private static transient final long serialVersionUID = -1681012206529286330L; + + public final HashMap blockSnapShot; + public final HashSet previouslyOnlinePlayers; + + // Can be used for saving + public Data(HashMap blockSnapShot, HashSet previouslyOnlinePlayers) { + this.blockSnapShot = blockSnapShot; + this.previouslyOnlinePlayers = previouslyOnlinePlayers; + } + // Can be used for loading + public Data(Data loadedData) { + this.blockSnapShot = loadedData.blockSnapShot; + this.previouslyOnlinePlayers = loadedData.previouslyOnlinePlayers; + } + + public boolean saveData(String filePath) { + try { + BukkitObjectOutputStream out = + new BukkitObjectOutputStream(new GZIPOutputStream(new FileOutputStream(filePath))); + out.writeObject(this); + out.close(); + return true; + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + return false; + } + } + public static Data loadData(String filePath) { + try { + BukkitObjectInputStream in = + new BukkitObjectInputStream(new GZIPInputStream(new FileInputStream(filePath))); + Data data = (Data) in.readObject(); + in.close(); + return data; + } catch (ClassNotFoundException | IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + return null; + } + } + public static void getBlocksPlayersAndSave() { + // HashMap used for storing blocks + HashMap blockSnapShot = new HashMap(); + // HashSet used for storing the online players + HashSet previouslyOnlinePlayers = new HashSet(); + // Grabs the spawn location of the first world the server finds + Location spawnLocation = Bukkit.getServer().getWorlds().iterator().next().getSpawnLocation(); + // One variable to store our blockLocation + Location blockLocation; + // Variables to store our x y z coordinates + int x, y, z; + // We will first retrieve all the currently online players + Bukkit.getServer().getOnlinePlayers().forEach(player -> previouslyOnlinePlayers.add(player.getUniqueId())); + // Next we will retrieve all block data in a 64 by 64 radius around the spawn. + + // While looping, using the new keyword and making declarations like "int x = 0;" + // will create garbage, and that garbage will start to pile up if the loop is + // extensive. We will take as much of a load off of the garbage collector as we + // can here by re-assigning x,y,z not re-declaring, and re-assigning the declared + // blockLocation to retrieve the block data. (we are going to store 262,144 blocks...) + for (x = 0; x <= 32; x++) { + for (y = 0; y <= 32; y++) { + for(z = 0; z <= 32; z++) { + blockSnapShot.put(blockLocation = new Location(spawnLocation.getWorld(), + spawnLocation.getX() - x, + spawnLocation.getY() - y, + spawnLocation.getZ() - z), blockLocation.getBlock().getBlockData().getAsString()); + blockSnapShot.put(blockLocation = new Location(spawnLocation.getWorld(), + spawnLocation.getX() + x, + spawnLocation.getY() - y, + spawnLocation.getZ() - z), blockLocation.getBlock().getBlockData().getAsString()); + blockSnapShot.put(blockLocation = new Location(spawnLocation.getWorld(), + spawnLocation.getX() - x, + spawnLocation.getY() + y, + spawnLocation.getZ() - z), blockLocation.getBlock().getBlockData().getAsString()); + blockSnapShot.put(blockLocation = new Location(spawnLocation.getWorld(), + spawnLocation.getX() - x, + spawnLocation.getY() - y, + spawnLocation.getZ() + z), blockLocation.getBlock().getBlockData().getAsString()); + blockSnapShot.put(blockLocation = new Location(spawnLocation.getWorld(), + spawnLocation.getX() + x, + spawnLocation.getY() + y, + spawnLocation.getZ() + z), blockLocation.getBlock().getBlockData().getAsString()); + blockSnapShot.put(blockLocation = new Location(spawnLocation.getWorld(), + spawnLocation.getX() - x, + spawnLocation.getY() + y, + spawnLocation.getZ() + z), blockLocation.getBlock().getBlockData().getAsString()); + blockSnapShot.put(blockLocation = new Location(spawnLocation.getWorld(), + spawnLocation.getX() + x, + spawnLocation.getY() - y, + spawnLocation.getZ() + z), blockLocation.getBlock().getBlockData().getAsString()); + blockSnapShot.put(blockLocation = new Location(spawnLocation.getWorld(), + spawnLocation.getX() + x, + spawnLocation.getY() + y, + spawnLocation.getZ() - z), blockLocation.getBlock().getBlockData().getAsString()); + } + } + } + // Finally we save the retrieved data to a file + + // You will most likely want to change the file location to your some other directory, + // like your plugin's data directory, instead of the Tutorial's. + new Data(blockSnapShot, previouslyOnlinePlayers).saveData("Tutorial.data"); + Bukkit.getServer().getLogger().log(Level.INFO, "Data Saved"); + } + public static void welcomePlayersAndResetBlocks() { + // Load the data from disc using our loadData method. + Data data = new Data(Data.loadData("Tutorial.data")); + // For each player that is current online send them a message + data.previouslyOnlinePlayers.forEach(playerId -> { + if (Bukkit.getServer().getPlayer(playerId).isOnline()) { + Bukkit.getServer().getPlayer(playerId). + sendMessage("Thanks for coming back after downtime. Hope you see the spawn blocks change!"); + } + }); + // Change all of the blocks around the spawn to what we have saved in our file. + data.blockSnapShot.keySet().forEach(location -> + location.getBlock().setBlockData(Bukkit.getServer().createBlockData(data.blockSnapShot.get(location)))); + Bukkit.getServer().getLogger().log(Level.INFO, "Data loaded"); + } +}*/ diff --git a/src/main/java/fr/moribus/imageonmap/map/ImageMap.java b/src/main/java/fr/moribus/imageonmap/map/ImagePoster.java similarity index 72% rename from src/main/java/fr/moribus/imageonmap/map/ImageMap.java rename to src/main/java/fr/moribus/imageonmap/map/ImagePoster.java index c6c8e5af..16f5e4a4 100644 --- a/src/main/java/fr/moribus/imageonmap/map/ImageMap.java +++ b/src/main/java/fr/moribus/imageonmap/map/ImagePoster.java @@ -1,8 +1,8 @@ /* * Copyright or © or Copr. Moribus (2013) * Copyright or © or Copr. ProkopyL (2015) - * Copyright or © or Copr. Amaury Carrade (2016 – 2021) - * Copyright or © or Copr. Vlammar (2019 – 2021) + * Copyright or © or Copr. Amaury Carrade (2016 – 2022) + * Copyright or © or Copr. Vlammar (2019 – 2024) * * This software is a computer program whose purpose is to allow insertion of * custom images in a Minecraft world. @@ -37,7 +37,7 @@ package fr.moribus.imageonmap.map; import fr.moribus.imageonmap.ImageOnMap; -import fr.moribus.imageonmap.ui.MapItemManager; +import fr.moribus.imageonmap.ui.PosterItemManager; import fr.zcraft.quartzlib.components.i18n.I; import java.io.File; import java.util.HashMap; @@ -51,23 +51,23 @@ import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; -public abstract class ImageMap implements ConfigurationSerializable { +public abstract class ImagePoster implements ConfigurationSerializable { public static final int WIDTH = 128; public static final int HEIGHT = 128; /// The default display name of a map public static final String DEFAULT_NAME = I.t("Map"); private final UUID userUUID; - private final Type mapType; + private final Type posterType; private String id; private String name; - protected ImageMap(UUID userUUID, Type mapType) { - this(userUUID, mapType, null, null); + protected ImagePoster(UUID userUUID, Type posterType) { + this(userUUID, posterType, null, null); } - protected ImageMap(UUID userUUID, Type mapType, String id, String name) { + protected ImagePoster(UUID userUUID, Type posterType, String id, String name) { this.userUUID = userUUID; - this.mapType = mapType; + this.posterType = posterType; this.id = id; this.name = name; @@ -75,32 +75,33 @@ protected ImageMap(UUID userUUID, Type mapType, String id, String name) { if (this.name == null) { this.name = DEFAULT_NAME; } - this.id = MapManager.getNextAvailableMapID(this.name, userUUID); + this.id = PosterManager.getNextAvailablePosterID(this.name, userUUID); } } - protected ImageMap(Map map, UUID userUUID, Type mapType) throws InvalidConfigurationException { - this(userUUID, mapType, + protected ImagePoster(Map map, UUID userUUID, Type posterType) + throws InvalidConfigurationException { + this(userUUID, posterType, (String) getNullableFieldValue(map, "id"), (String) getNullableFieldValue(map, "name")); } - public static File getFullImageFile(int mapIDstart, int mapIDend) { - return new File(ImageOnMap.getPlugin().getImagesDirectory(), "_" + mapIDstart + "-" + mapIDend + ".png"); + public static File getFullImageFile(int posterIDstart, int posterIDend) { + return new File(ImageOnMap.getPlugin().getImagesDirectory(), "_" + posterIDstart + + "-" + posterIDend + ".png"); } - public static ImageMap fromConfig(Map map, UUID userUUID) throws InvalidConfigurationException { - Type mapType; + public static ImagePoster fromConfig(Map map, UUID userUUID) throws InvalidConfigurationException { + Type posterType;//TODO refactor this try { - mapType = Type.valueOf((String) map.get("type")); + posterType = Type.valueOf((String) map.get("type")); } catch (ClassCastException ex) { throw new InvalidConfigurationException(ex); } - switch (mapType) { + switch (posterType) { case SINGLE: - return new SingleMap(map, userUUID); case POSTER: return new PosterMap(map, userUUID); default: @@ -111,14 +112,15 @@ public static ImageMap fromConfig(Map map, UUID userUUID) throws public static Integer[] getSize(UUID playerUUID, String id) { ConfigurationSection section = - MapManager.getPlayerMapStore(playerUUID).getToolConfig().getConfigurationSection("PlayerMapStore"); + PosterManager.getplayerPosterStore(playerUUID).getToolConfig() + .getConfigurationSection("playerPosterStore"); if (section == null) { - return null; + return new Integer[0]; } List> list = (List>) section.getList("mapList"); if (list == null) { - return null; + return new Integer[0]; } for (Map tmpMap : list) { @@ -126,7 +128,7 @@ public static Integer[] getSize(UUID playerUUID, String id) { return new Integer[] {(Integer) tmpMap.get("columns"), (Integer) tmpMap.get("rows")}; } } - return null; + return new Integer[0]; } protected static T getFieldValue(Map map, String fieldName) @@ -147,29 +149,38 @@ protected static T getNullableFieldValue(Map map, String fie } } - public abstract int[] getMapsIDs(); + public abstract int[] getPostersIDs(); + public abstract int getFirstPosterID(); /* ====== Serialization methods ====== */ - public abstract boolean managesMap(int mapID); + public abstract boolean managesPoster(int posterID); - public boolean managesMap(ItemStack item) { + public boolean managesPoster(ItemStack item) { if (item == null) { return false; } if (item.getType() != Material.FILLED_MAP) { return false; } - return managesMap(MapManager.getMapIdFromItemStack(item)); + return managesPoster(PosterManager.getPosterIDFromItemStack(item)); } // - public abstract int getMapCount(); + public abstract int getPosterCount(); // public boolean give(Player player) { - return MapItemManager.give(player, this); + return PosterItemManager.give(player, this); + } + + public boolean give(Player player, int quantity) { + boolean bool = true; + for (int i = 0; i < quantity; i++) { + bool = bool && PosterItemManager.give(player, this); + } + return bool; } protected abstract void postSerialize(Map map); @@ -178,7 +189,7 @@ public boolean give(Player player) { public Map serialize() { Map map = new HashMap(); map.put("id", getId()); - map.put("type", mapType.toString()); + map.put("type", posterType.toString()); map.put("name", getName()); this.postSerialize(map); return map; @@ -200,7 +211,7 @@ public synchronized String getId() { } public synchronized Type getType() { - return mapType; + return posterType; } public synchronized void rename(String id, String name) { @@ -212,7 +223,7 @@ public void rename(String name) { if (getName().equals(name)) { return; } - rename(MapManager.getNextAvailableMapID(name, getUserUUID()), name); + rename(PosterManager.getNextAvailablePosterID(name, getUserUUID()), name); } public enum Type { diff --git a/src/main/java/fr/moribus/imageonmap/map/PlayerMapStore.java b/src/main/java/fr/moribus/imageonmap/map/PlayerPosterStore.java similarity index 53% rename from src/main/java/fr/moribus/imageonmap/map/PlayerMapStore.java rename to src/main/java/fr/moribus/imageonmap/map/PlayerPosterStore.java index 3eb13bec..4aef1200 100644 --- a/src/main/java/fr/moribus/imageonmap/map/PlayerMapStore.java +++ b/src/main/java/fr/moribus/imageonmap/map/PlayerPosterStore.java @@ -1,8 +1,8 @@ /* * Copyright or © or Copr. Moribus (2013) * Copyright or © or Copr. ProkopyL (2015) - * Copyright or © or Copr. Amaury Carrade (2016 – 2021) - * Copyright or © or Copr. Vlammar (2019 – 2021) + * Copyright or © or Copr. Amaury Carrade (2016 – 2022) + * Copyright or © or Copr. Vlammar (2019 – 2024) * * This software is a computer program whose purpose is to allow insertion of * custom images in a Minecraft world. @@ -38,7 +38,7 @@ import fr.moribus.imageonmap.ImageOnMap; import fr.moribus.imageonmap.PluginConfiguration; -import fr.moribus.imageonmap.map.MapManagerException.Reason; +import fr.moribus.imageonmap.map.PosterManagerException.Reason; import fr.zcraft.quartzlib.tools.PluginLogger; import java.io.File; import java.io.IOException; @@ -54,126 +54,116 @@ import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.configuration.serialization.ConfigurationSerializable; import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; -public class PlayerMapStore implements ConfigurationSerializable { +public class PlayerPosterStore implements ConfigurationSerializable { private final UUID playerUUID; - private final ArrayList mapList = new ArrayList(); + private final ArrayList posterList = new ArrayList<>(); private boolean modified = false; - private int mapCount = 0; - private FileConfiguration mapConfig = null; - private File mapsFile = null; + private int posterCount = 0; + private FileConfiguration posterConfig = null; + private File postersFile = null; - public PlayerMapStore(UUID playerUUID) { + public PlayerPosterStore(UUID playerUUID) { this.playerUUID = playerUUID; } - public synchronized boolean managesMap(int mapID) { - for (ImageMap map : mapList) { - if (map.managesMap(mapID)) { + //TODO maybe usefull to merge with the other manages poster + public synchronized boolean managesPoster(int posterID) { + for (ImagePoster poster : posterList) { + if (poster.managesPoster(posterID)) { return true; } } return false; } - public synchronized boolean managesMap(ItemStack item) { + public synchronized boolean managesPoster(ItemStack item) { if (item == null) { return false; } if (item.getType() != Material.FILLED_MAP) { return false; } - - for (ImageMap map : mapList) { - if (map.managesMap(item)) { - return true; - } - } - return false; + return managesPoster(PosterManager.getPosterIDFromItemStack(item)); } - public synchronized void addMap(ImageMap map) throws MapManagerException { - checkMapLimit(map); - insertMap(map); + public synchronized void addPoster(ImagePoster poster) throws PosterManagerException { + checkPosterLimit(poster); + insertPoster(poster); } - public synchronized void insertMap(ImageMap map) { - add_Map(map); + public synchronized void insertPoster(ImagePoster poster) { + add_Poster(poster); notifyModification(); } - private void add_Map(ImageMap map) { - mapList.add(map); - mapCount += map.getMapCount(); + private void add_Poster(ImagePoster poster) { + posterList.add(poster); + posterCount += poster.getPosterCount(); } - public synchronized void deleteMap(ImageMap map) throws MapManagerException { - remove_Map(map); + public synchronized void deletePoster(ImagePoster poster) throws PosterManagerException { + delete_Poster(poster); notifyModification(); } - private void remove_Map(ImageMap map) throws MapManagerException { - if (!mapList.remove(map)) { - throw new MapManagerException(Reason.IMAGEMAP_DOES_NOT_EXIST); + private void delete_Poster(ImagePoster poster) throws PosterManagerException { + if (!posterList.remove(poster)) { + throw new PosterManagerException(Reason.IMAGEPOSTER_DOES_NOT_EXIST); } - mapCount -= map.getMapCount(); + posterCount -= poster.getPosterCount(); } - public synchronized boolean mapExists(String id) { - for (ImageMap map : mapList) { - if (map.getId().equals(id)) { - return true; - } - } - - return false; + public synchronized boolean posterExists(String posterId) { + return getPoster(posterId) != null; } - public String getNextAvailableMapID(String mapId) { - if (!mapExists(mapId)) { - return mapId; + public String getNextAvailablePosterID(String posterId) { + //TODO check if the value is always greater than the id count + if (!posterExists(posterId)) { + return posterId; } int id = 0; - do { id++; - } while (mapExists(mapId + "-" + id)); + } while (posterExists(posterId + "-" + id)); - return mapId + "-" + id; + return posterId + "-" + id; } - public synchronized List getMapList() { - return new ArrayList(mapList); + public synchronized List getPosterList() { + return new ArrayList<>(posterList); } - public synchronized ImageMap[] getMaps() { - return mapList.toArray(new ImageMap[mapList.size()]); + //TODO refactor to arraylist instead of an array + public synchronized ImagePoster[] getPosters() { + return posterList.toArray(new ImagePoster[0]); } - public synchronized ImageMap getMap(String mapId) { - for (ImageMap map : mapList) { - if (map.getId().equals(mapId)) { - return map; + public synchronized ImagePoster getPoster(String posterId) { + for (ImagePoster poster : posterList) { + if (poster.getId().equals(posterId)) { + return poster; } } - return null; } /* ===== Getters & Setters ===== */ - public void checkMapLimit(ImageMap map) throws MapManagerException { - checkMapLimit(map.getMapCount()); + public void checkPosterLimit(ImagePoster poster) throws PosterManagerException { + checkPosterLimit(poster.getPosterCount()); } - public void checkMapLimit(int newMapsCount) throws MapManagerException { - int limit = PluginConfiguration.MAP_PLAYER_LIMIT.get(); + public void checkPosterLimit(int newPostersCount) throws PosterManagerException { + int limit = PluginConfiguration.POSTER_PLAYER_LIMIT.get(); if (limit <= 0) { return; } - if (getMapCount() + newMapsCount > limit) { - throw new MapManagerException(Reason.MAXIMUM_PLAYER_MAPS_EXCEEDED, limit); + if (getPosterCount() + newPostersCount > limit) { + throw new PosterManagerException(Reason.MAXIMUM_PLAYER_POSTERS_EXCEEDED, limit); } } @@ -191,26 +181,26 @@ public synchronized void notifyModification() { /* ****** Serializing ***** */ - public synchronized int getMapCount() { - return this.mapCount; + public synchronized int getPosterCount() { + return this.posterCount; } public synchronized int getImagesCount() { - return this.mapList.size(); + return this.posterList.size(); } /* ****** Configuration Files management ***** */ @Override - public Map serialize() { - Map map = new HashMap(); - ArrayList list = new ArrayList(); + public @NotNull Map serialize() { + Map map = new HashMap<>(); + ArrayList list = new ArrayList<>(); synchronized (this) { - for (ImageMap tmpMap : mapList) { - list.add(tmpMap.serialize()); + for (ImagePoster tmpPoster : posterList) { + list.add(tmpPoster.serialize()); } } - map.put("mapList", list); + map.put("posterList", list); return map; } @@ -218,16 +208,16 @@ private void loadFromConfig(ConfigurationSection section) { if (section == null) { return; } - List> list = (List>) section.getList("mapList"); + List> list = (List>) section.getList("posterList"); if (list == null) { return; } for (Map tmpMap : list) { try { - ImageMap newMap = ImageMap.fromConfig(tmpMap, playerUUID); + ImagePoster newPoster = ImagePoster.fromConfig(tmpMap, playerUUID); synchronized (this) { - add_Map(newMap); + add_Poster(newPoster); } } catch (InvalidConfigurationException ex) { PluginLogger.warning("Could not load map data : ", ex); @@ -235,39 +225,39 @@ private void loadFromConfig(ConfigurationSection section) { } try { - checkMapLimit(0); - } catch (MapManagerException ex) { + checkPosterLimit(0); + } catch (PosterManagerException ex) { PluginLogger.warning("Map limit exceeded for player {0} ({1} maps loaded)", - playerUUID.toString(), mapList.size()); + playerUUID.toString(), posterList.size()); } } public FileConfiguration getToolConfig() { - if (mapConfig == null) { + if (posterConfig == null) { load(); } - return mapConfig; + return posterConfig; } public void load() { - if (mapsFile == null) { - mapsFile = new File(ImageOnMap.getPlugin().getMapsDirectory(), playerUUID.toString() + ".yml"); - if (!mapsFile.exists()) { + if (postersFile == null) { + postersFile = new File(ImageOnMap.getPlugin().getPostersDirectory(), playerUUID.toString() + ".yml"); + if (!postersFile.exists()) { save(); } } - mapConfig = YamlConfiguration.loadConfiguration(mapsFile); - loadFromConfig(getToolConfig().getConfigurationSection("PlayerMapStore")); + posterConfig = YamlConfiguration.loadConfiguration(postersFile); + loadFromConfig(getToolConfig().getConfigurationSection("playerPosterStore")); } public void save() { - if (mapsFile == null || mapConfig == null) { + if (postersFile == null || posterConfig == null) { return; } - getToolConfig().set("PlayerMapStore", this.serialize()); + getToolConfig().set("playerPosterStore", this.serialize()); try { - getToolConfig().save(mapsFile); + getToolConfig().save(postersFile); } catch (IOException ex) { PluginLogger.error("Could not save maps file for player '{0}'", ex, playerUUID.toString()); diff --git a/src/main/java/fr/moribus/imageonmap/map/PosterData.java b/src/main/java/fr/moribus/imageonmap/map/PosterData.java new file mode 100644 index 00000000..ad979925 --- /dev/null +++ b/src/main/java/fr/moribus/imageonmap/map/PosterData.java @@ -0,0 +1,55 @@ +package fr.moribus.imageonmap.map; + +import fr.moribus.imageonmap.image.ImageUtils; +import java.net.URL; + +public class PosterData { + private URL url; + private int width; + private int height; + private ImageUtils.ScalingType scaling; + + public PosterData(URL url, int width, int height, ImageUtils.ScalingType scaling) { + this.url = url; + this.width = width; + this.height = height; + this.scaling = scaling; + } + + public PosterData(URL url) { + new PosterData(url, 0, 0, scaling.NONE); + } + + public void setURL(java.net.URL url) { + this.url = url; + } + + public void setWidth(int width) { + this.width = width; + } + + public void setHeight(int height) { + this.height = height; + } + + public void setScaling(ImageUtils.ScalingType scaling) { + this.scaling = scaling; + } + + public java.net.URL getURL() { + return this.url; + } + + public int getWidth() { + return this.width; + } + + public int getHeight() { + return this.height; + } + + public ImageUtils.ScalingType getScaling() { + return this.scaling; + } + +} diff --git a/src/main/java/fr/moribus/imageonmap/map/SingleMap.java b/src/main/java/fr/moribus/imageonmap/map/PosterIndexes.java similarity index 62% rename from src/main/java/fr/moribus/imageonmap/map/SingleMap.java rename to src/main/java/fr/moribus/imageonmap/map/PosterIndexes.java index 442ef045..6e0771c9 100644 --- a/src/main/java/fr/moribus/imageonmap/map/SingleMap.java +++ b/src/main/java/fr/moribus/imageonmap/map/PosterIndexes.java @@ -1,82 +1,56 @@ -/* - * Copyright or © or Copr. Moribus (2013) - * Copyright or © or Copr. ProkopyL (2015) - * Copyright or © or Copr. Amaury Carrade (2016 – 2021) - * Copyright or © or Copr. Vlammar (2019 – 2021) - * - * This software is a computer program whose purpose is to allow insertion of - * custom images in a Minecraft world. - * - * This software is governed by the CeCILL license under French law and - * abiding by the rules of distribution of free software. You can use, - * modify and/ or redistribute the software under the terms of the CeCILL - * license as circulated by CEA, CNRS and INRIA at the following URL - * "http://www.cecill.info". - * - * As a counterpart to the access to the source code and rights to copy, - * modify and redistribute granted by the license, users are provided only - * with a limited warranty and the software's author, the holder of the - * economic rights, and the successive licensors have only limited - * liability. - * - * In this respect, the user's attention is drawn to the risks associated - * with loading, using, modifying and/or developing or reproducing the - * software by the user in light of its specific status of free software, - * that may mean that it is complicated to manipulate, and that also - * therefore means that it is reserved for developers and experienced - * professionals having in-depth computer knowledge. Users are therefore - * encouraged to load and test the software's suitability as regards their - * requirements in conditions enabling the security of their systems and/or - * data to be ensured and, more generally, to use and operate it in the - * same conditions as regards security. - * - * The fact that you are presently reading this means that you have had - * knowledge of the CeCILL license and that you accept its terms. - */ - -package fr.moribus.imageonmap.map; - -import java.util.Map; -import java.util.UUID; -import org.bukkit.configuration.InvalidConfigurationException; - -public class SingleMap extends ImageMap { - protected final int mapID; - - public SingleMap(UUID ownerUUID, int mapID, String id, String name) { - super(ownerUUID, Type.SINGLE, id, name); - this.mapID = mapID; - } - - public SingleMap(UUID ownerUUID, int mapID) { - this(ownerUUID, mapID, null, null); - } - - public SingleMap(Map map, UUID userUUID) throws InvalidConfigurationException { - super(map, userUUID, Type.SINGLE); - mapID = getFieldValue(map, "mapID"); - } - - @Override - public int[] getMapsIDs() { - return new int[] {mapID}; - } - - @Override - public boolean managesMap(int mapID) { - return this.mapID == mapID; - } - - /* ====== Serialization methods ====== */ - - @Override - public int getMapCount() { - return 1; - } - - @Override - protected void postSerialize(Map map) { - map.put("mapID", mapID); - } - -} +/* + * Copyright or © or Copr. Moribus (2013) + * Copyright or © or Copr. ProkopyL (2015) + * Copyright or © or Copr. Amaury Carrade (2016 – 2022) + * Copyright or © or Copr. Vlammar (2019 – 2024) + * + * This software is a computer program whose purpose is to allow insertion of + * custom images in a Minecraft world. + * + * This software is governed by the CeCILL license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL license and that you accept its terms. + */ + +package fr.moribus.imageonmap.map; + +public class PosterIndexes { + + private final int columnIndex; + private final int rowIndex; + + public PosterIndexes(int rowIndex, int columnIndex) { + this.rowIndex = rowIndex; + this.columnIndex = columnIndex; + } + + public int getColumnIndex() { + return columnIndex; + } + + public int getRowIndex() { + return rowIndex; + } +} diff --git a/src/main/java/fr/moribus/imageonmap/map/MapManager.java b/src/main/java/fr/moribus/imageonmap/map/PosterManager.java similarity index 52% rename from src/main/java/fr/moribus/imageonmap/map/MapManager.java rename to src/main/java/fr/moribus/imageonmap/map/PosterManager.java index 46452245..d56b8bbb 100644 --- a/src/main/java/fr/moribus/imageonmap/map/MapManager.java +++ b/src/main/java/fr/moribus/imageonmap/map/PosterManager.java @@ -1,8 +1,8 @@ /* * Copyright or © or Copr. Moribus (2013) * Copyright or © or Copr. ProkopyL (2015) - * Copyright or © or Copr. Amaury Carrade (2016 – 2021) - * Copyright or © or Copr. Vlammar (2019 – 2021) + * Copyright or © or Copr. Amaury Carrade (2016 – 2022) + * Copyright or © or Copr. Vlammar (2019 – 2024) * * This software is a computer program whose purpose is to allow insertion of * custom images in a Minecraft world. @@ -40,11 +40,12 @@ import fr.moribus.imageonmap.PluginConfiguration; import fr.moribus.imageonmap.image.ImageIOExecutor; import fr.moribus.imageonmap.image.PosterImage; -import fr.moribus.imageonmap.map.MapManagerException.Reason; +import fr.moribus.imageonmap.map.PosterManagerException.Reason; import fr.zcraft.quartzlib.tools.PluginLogger; import java.io.File; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.UUID; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -54,9 +55,9 @@ import org.bukkit.inventory.meta.MapMeta; import org.bukkit.scheduler.BukkitTask; -public abstract class MapManager { +public abstract class PosterManager { private static final long SAVE_DELAY = 200; - private static final ArrayList playerMaps = new ArrayList(); + private static final ArrayList playerPosters = new ArrayList(); private static BukkitTask autosaveTask; public static void init() { @@ -65,16 +66,17 @@ public static void init() { public static void exit() { save(); - playerMaps.clear(); + playerPosters.clear(); if (autosaveTask != null) { autosaveTask.cancel(); } } - public static boolean managesMap(int mapID) { - synchronized (playerMaps) { - for (PlayerMapStore mapStore : playerMaps) { - if (mapStore.managesMap(mapID)) { + @Deprecated + public static boolean managesPoster(int posterID) { + synchronized (playerPosters) { + for (PlayerPosterStore posterStore : playerPosters) { + if (posterStore.managesPoster(posterID)) { return true; } } @@ -82,7 +84,7 @@ public static boolean managesMap(int mapID) { return false; } - public static boolean managesMap(ItemStack item) { + public static boolean managesPoster(ItemStack item) { if (item == null) { return false; } @@ -90,9 +92,10 @@ public static boolean managesMap(ItemStack item) { return false; } - synchronized (playerMaps) { - for (PlayerMapStore mapStore : playerMaps) { - if (mapStore.managesMap(item)) { + synchronized (playerPosters) { + for (PlayerPosterStore posterStore : playerPosters) { + if (posterStore.managesPoster(item)) { + //duplicated ? maybe to remove from posterstore return true; } } @@ -100,30 +103,35 @@ public static boolean managesMap(ItemStack item) { return false; } - public static ImageMap createMap(UUID playerUUID, int mapID) throws MapManagerException { - ImageMap newMap = new SingleMap(playerUUID, mapID); - addMap(newMap); - return newMap; + public static ImagePoster createPoster(UUID playerUUID, int posterID) throws PosterManagerException { + //ImagePoster newPoster = new SinglePoster(playerUUID, posterID); + int[] ids = new int[] {posterID}; + ImagePoster newPoster = new PosterMap(playerUUID, ids, 1, 1); + addPoster(newPoster);//TODO refactor this + return newPoster; } - public static ImageMap createMap(PosterImage image, UUID playerUUID, int[] mapsIDs) throws MapManagerException { - ImageMap newMap; + public static ImagePoster createPoster(PosterImage image, UUID playerUUID, int[] postersIDs) + throws PosterManagerException { + ImagePoster newPoster; if (image.getImagesCount() == 1) { - newMap = new SingleMap(playerUUID, mapsIDs[0]); + newPoster = new PosterMap(playerUUID, postersIDs, 1, 1);//TODO refactor this + //newPoster = new SinglePoster(playerUUID, postersIDs[0]); } else { - newMap = new PosterMap(playerUUID, mapsIDs, image.getColumns(), image.getLines()); + PluginLogger.info("pb ici ? " + image.getColumns() + " " + image.getLines()); + newPoster = new PosterMap(playerUUID, postersIDs, image.getColumns(), image.getLines()); } - addMap(newMap); - return newMap; + addPoster(newPoster); + return newPoster; } - public static int[] getNewMapsIds(int amount) { - int[] mapsIds = new int[amount]; + public static int[] getNewPostersIds(int amount) { + int[] postersIDs = new int[amount]; for (int i = 0; i < amount; i++) { - mapsIds[i] = Bukkit.createMap(Bukkit.getWorlds().get(0)).getId(); + postersIDs[i] = Bukkit.createMap(Bukkit.getWorlds().get(0)).getId(); } - return mapsIds; + return postersIDs; } /** @@ -132,7 +140,7 @@ public static int[] getNewMapsIds(int amount) { * @param item The item stack * @return The map ID, or 0 if invalid. */ - public static int getMapIdFromItemStack(final ItemStack item) { + public static int getPosterIDFromItemStack(final ItemStack item) { final ItemMeta meta = item.getItemMeta(); if (!(meta instanceof MapMeta)) { return 0; @@ -141,36 +149,36 @@ public static int getMapIdFromItemStack(final ItemStack item) { return ((MapMeta) meta).hasMapId() ? ((MapMeta) meta).getMapId() : 0; } - public static void addMap(ImageMap map) throws MapManagerException { - getPlayerMapStore(map.getUserUUID()).addMap(map); + public static void addPoster(ImagePoster poster) throws PosterManagerException { + getplayerPosterStore(poster.getUserUUID()).addPoster(poster); } - public static void insertMap(ImageMap map) { - getPlayerMapStore(map.getUserUUID()).insertMap(map); + public static void insertPoster(ImagePoster poster) { + getplayerPosterStore(poster.getUserUUID()).insertPoster(poster); } - public static void deleteMap(ImageMap map) throws MapManagerException { - getPlayerMapStore(map.getUserUUID()).deleteMap(map); - ImageIOExecutor.deleteImage(map); + public static void deletePoster(ImagePoster poster) throws PosterManagerException { + getplayerPosterStore(poster.getUserUUID()).deletePoster(poster); + ImageIOExecutor.deleteImage(poster); } public static void notifyModification(UUID playerUUID) { - getPlayerMapStore(playerUUID).notifyModification(); + getplayerPosterStore(playerUUID).notifyModification(); if (autosaveTask == null) { - Bukkit.getScheduler().runTaskLater(ImageOnMap.getPlugin(), new AutosaveRunnable(), SAVE_DELAY); + Bukkit.getScheduler().runTaskLater(ImageOnMap.getPlugin(), new AutoSaveRunnable(), SAVE_DELAY); } } - public static String getNextAvailableMapID(String mapId, UUID playerUUID) { - return getPlayerMapStore(playerUUID).getNextAvailableMapID(mapId); + public static String getNextAvailablePosterID(String posterID, UUID playerUUID) { + return getplayerPosterStore(playerUUID).getNextAvailablePosterID(posterID); } - public static List getMapList(UUID playerUUID) { - return getPlayerMapStore(playerUUID).getMapList(); + public static List getPosterList(UUID playerUUID) { + return getplayerPosterStore(playerUUID).getPosterList(); } - public static ImageMap[] getMaps(UUID playerUUID) { - return getPlayerMapStore(playerUUID).getMaps(); + public static ImagePoster[] getPosters(UUID playerUUID) { + return getplayerPosterStore(playerUUID).getPosters(); } /** @@ -179,27 +187,27 @@ public static ImageMap[] getMaps(UUID playerUUID) { * @param playerUUID The player's UUID. * @return The count. */ - public static int getMapPartCount(UUID playerUUID) { - return getPlayerMapStore(playerUUID).getMapCount(); + public static int getPosterPartCount(UUID playerUUID) { + return getplayerPosterStore(playerUUID).getPosterCount(); } - public static ImageMap getMap(UUID playerUUID, String mapId) { - return getPlayerMapStore(playerUUID).getMap(mapId); + public static ImagePoster getPoster(UUID playerUUID, String posterID) { + return getplayerPosterStore(playerUUID).getPoster(posterID); } /** - * Returns the {@link ImageMap} this map belongs to. + * Returns the {@link ImagePoster} this map belongs to. * - * @param mapId The ID of the Minecraft map. - * @return The {@link ImageMap}. + * @param posterID The ID of the Minecraft map. + * @return The {@link ImagePoster}. */ - public static ImageMap getMap(int mapId) { - synchronized (playerMaps) { - for (PlayerMapStore mapStore : playerMaps) { - if (mapStore.managesMap(mapId)) { - for (ImageMap map : mapStore.getMapList()) { - if (map.managesMap(mapId)) { - return map; + public static ImagePoster getPoster(int posterID) { + synchronized (playerPosters) { + for (PlayerPosterStore posterStore : playerPosters) { + if (posterStore.managesPoster(posterID)) { + for (ImagePoster poster : posterStore.getPosterList()) { + if (poster.managesPoster(posterID)) { + return poster; } } } @@ -210,32 +218,32 @@ public static ImageMap getMap(int mapId) { } /** - * Returns the {@link ImageMap} this map belongs to. + * Returns the {@link ImagePoster} this map belongs to. * * @param item The map, as an {@link ItemStack}. - * @return The {@link ImageMap}. + * @return The {@link ImagePoster}. */ - public static ImageMap getMap(ItemStack item) { + public static ImagePoster getPoster(ItemStack item) { if (item == null) { return null; } if (item.getType() != Material.FILLED_MAP) { return null; } - return getMap(getMapIdFromItemStack(item)); + return getPoster(getPosterIDFromItemStack(item)); } public static void clear(Inventory inventory) { for (int i = 0, c = inventory.getSize(); i < c; i++) { - if (managesMap(inventory.getItem(i))) { + if (managesPoster(inventory.getItem(i))) { inventory.setItem(i, new ItemStack(Material.AIR)); } } } - public static void clear(Inventory inventory, ImageMap map) { + public static void clear(Inventory inventory, ImagePoster poster) { for (int i = 0, c = inventory.getSize(); i < c; i++) { - if (map.managesMap(inventory.getItem(i))) { + if (poster.managesPoster(inventory.getItem(i))) { inventory.setItem(i, new ItemStack(Material.AIR)); } } @@ -268,12 +276,12 @@ public static void load() { //Loading public static void load(boolean verbose) { int loadedFilesCount = 0; - for (File file : ImageOnMap.getPlugin().getMapsDirectory().listFiles()) { + for (File file : Objects.requireNonNull(ImageOnMap.getPlugin().getPostersDirectory().listFiles())) { UUID uuid = getUUIDFromFile(file); if (uuid == null) { continue; } - getPlayerMapStore(uuid); + getplayerPosterStore(uuid); ++loadedFilesCount; } @@ -283,25 +291,25 @@ public static void load(boolean verbose) { } public static void save() { - synchronized (playerMaps) { - for (PlayerMapStore tmpStore : playerMaps) { + synchronized (playerPosters) { + for (PlayerPosterStore tmpStore : playerPosters) { tmpStore.save(); } } } - public static void checkMapLimit(ImageMap map) throws MapManagerException { - checkMapLimit(map.getMapCount(), map.getUserUUID()); + public static void checkPosterLimit(ImagePoster poster) throws PosterManagerException { + checkPosterLimit(poster.getPosterCount(), poster.getUserUUID()); } - public static void checkMapLimit(int newMapsCount, UUID userUUID) throws MapManagerException { - int limit = PluginConfiguration.MAP_GLOBAL_LIMIT.get(); + public static void checkPosterLimit(int newPostersCount, UUID userUUID) throws PosterManagerException { + int limit = PluginConfiguration.POSTER_GLOBAL_LIMIT.get(); - if (limit > 0 && getMapCount() + newMapsCount > limit) { - throw new MapManagerException(Reason.MAXIMUM_SERVER_MAPS_EXCEEDED); + if (limit > 0 && getPosterCount() + newPostersCount > limit) { + throw new PosterManagerException(Reason.MAXIMUM_SERVER_POSTERS_EXCEEDED); } - getPlayerMapStore(userUUID).checkMapLimit(newMapsCount); + getplayerPosterStore(userUUID).checkPosterLimit(newPostersCount); } /** @@ -309,14 +317,14 @@ public static void checkMapLimit(int newMapsCount, UUID userUUID) throws MapMana * * @return The count. */ - public static int getMapCount() { - int mapCount = 0; - synchronized (playerMaps) { - for (PlayerMapStore tmpStore : playerMaps) { - mapCount += tmpStore.getMapCount(); + public static int getPosterCount() { + int posterCount = 0; + synchronized (playerPosters) { + for (PlayerPosterStore tmpStore : playerPosters) { + posterCount += tmpStore.getPosterCount(); } } - return mapCount; + return posterCount; } /** @@ -326,8 +334,8 @@ public static int getMapCount() { */ public static int getImagesCount() { int imagesCount = 0; - synchronized (playerMaps) { - for (PlayerMapStore tmpStore : playerMaps) { + synchronized (playerPosters) { + for (PlayerPosterStore tmpStore : playerPosters) { imagesCount += tmpStore.getImagesCount(); } } @@ -337,47 +345,47 @@ public static int getImagesCount() { /** * Returns if the given map ID is valid and exists in the current save. * - * @param mapId the map ID. + * @param posterID the map ID. * @return true if the given map ID is valid and exists in the current save, false otherwise. */ - public static boolean mapIdExists(int mapId) { + public static boolean posterIDExists(int posterID) { try { - return Bukkit.getMap(mapId) != null; + return Bukkit.getMap(posterID) != null; } catch (Throwable ex) { return false; } } - public static PlayerMapStore getPlayerMapStore(UUID playerUUID) { - PlayerMapStore store; - synchronized (playerMaps) { - store = getExistingPlayerMapStore(playerUUID); + public static PlayerPosterStore getplayerPosterStore(UUID playerUUID) { + PlayerPosterStore store; + synchronized (playerPosters) { + store = getExistingplayerPosterStore(playerUUID); if (store == null) { - store = new PlayerMapStore(playerUUID); + store = new PlayerPosterStore(playerUUID); - playerMaps.add(store); + playerPosters.add(store); store.load(); } } return store; } - private static PlayerMapStore getExistingPlayerMapStore(UUID playerUUID) { - synchronized (playerMaps) { - for (PlayerMapStore mapStore : playerMaps) { - if (mapStore.getUUID().equals(playerUUID)) { - return mapStore; + private static PlayerPosterStore getExistingplayerPosterStore(UUID playerUUID) { + synchronized (playerPosters) { + for (PlayerPosterStore posterStore : playerPosters) { + if (posterStore.getUUID().equals(playerUUID)) { + return posterStore; } } } return null; } - private static class AutosaveRunnable implements Runnable { + private static class AutoSaveRunnable implements Runnable { @Override public void run() { - synchronized (playerMaps) { - for (PlayerMapStore toolStore : playerMaps) { + synchronized (playerPosters) { + for (PlayerPosterStore toolStore : playerPosters) { if (toolStore.isModified()) { toolStore.save(); } diff --git a/src/main/java/fr/moribus/imageonmap/map/MapManagerException.java b/src/main/java/fr/moribus/imageonmap/map/PosterManagerException.java similarity index 83% rename from src/main/java/fr/moribus/imageonmap/map/MapManagerException.java rename to src/main/java/fr/moribus/imageonmap/map/PosterManagerException.java index 231483f4..d295ad3d 100644 --- a/src/main/java/fr/moribus/imageonmap/map/MapManagerException.java +++ b/src/main/java/fr/moribus/imageonmap/map/PosterManagerException.java @@ -1,8 +1,8 @@ /* * Copyright or © or Copr. Moribus (2013) * Copyright or © or Copr. ProkopyL (2015) - * Copyright or © or Copr. Amaury Carrade (2016 – 2021) - * Copyright or © or Copr. Vlammar (2019 – 2021) + * Copyright or © or Copr. Amaury Carrade (2016 – 2022) + * Copyright or © or Copr. Vlammar (2019 – 2024) * * This software is a computer program whose purpose is to allow insertion of * custom images in a Minecraft world. @@ -39,11 +39,11 @@ import fr.zcraft.quartzlib.components.i18n.I; import java.text.MessageFormat; -public class MapManagerException extends Exception { +public class PosterManagerException extends Exception { private final Reason reason; - public MapManagerException(Reason reason, Object... arguments) { + public PosterManagerException(Reason reason, Object... arguments) { super(reason.getReasonString(arguments)); this.reason = reason; } @@ -53,9 +53,9 @@ public Reason getReason() { } public enum Reason { - MAXIMUM_PLAYER_MAPS_EXCEEDED(I.t("You have too many maps (maximum : {0}).")), - MAXIMUM_SERVER_MAPS_EXCEEDED(I.t("The server ImageOnMap limit has been reached.")), - IMAGEMAP_DOES_NOT_EXIST(I.t("The given map does not exist.")); + MAXIMUM_PLAYER_POSTERS_EXCEEDED(I.t("You have too many maps (maximum : {0}).")), + MAXIMUM_SERVER_POSTERS_EXCEEDED(I.t("The server ImageOnMap limit has been reached.")), + IMAGEPOSTER_DOES_NOT_EXIST(I.t("The given map does not exist.")); private final String reasonString; diff --git a/src/main/java/fr/moribus/imageonmap/map/PosterMap.java b/src/main/java/fr/moribus/imageonmap/map/PosterMap.java index 72e1a05c..a41a74b6 100644 --- a/src/main/java/fr/moribus/imageonmap/map/PosterMap.java +++ b/src/main/java/fr/moribus/imageonmap/map/PosterMap.java @@ -1,8 +1,8 @@ /* * Copyright or © or Copr. Moribus (2013) * Copyright or © or Copr. ProkopyL (2015) - * Copyright or © or Copr. Amaury Carrade (2016 – 2021) - * Copyright or © or Copr. Vlammar (2019 – 2021) + * Copyright or © or Copr. Amaury Carrade (2016 – 2022) + * Copyright or © or Copr. Vlammar (2019 – 2024) * * This software is a computer program whose purpose is to allow insertion of * custom images in a Minecraft world. @@ -36,26 +36,32 @@ package fr.moribus.imageonmap.map; +import fr.zcraft.quartzlib.tools.PluginLogger; +import fr.zcraft.quartzlib.tools.world.WorldUtils; +import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.UUID; +import org.bukkit.Location; import org.bukkit.block.BlockFace; import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.entity.ItemFrame; +import org.bukkit.entity.Player; -public class PosterMap extends ImageMap { - protected final int[] mapsIDs; +public class PosterMap extends ImagePoster { + protected final int[] postersIDs; protected final int columnCount; protected final int rowCount; - public PosterMap(UUID userUUID, int[] mapsIDs, String id, String name, int columnCount, int rowCount) { + public PosterMap(UUID userUUID, int[] postersIDs, String id, String name, int columnCount, int rowCount) { super(userUUID, Type.POSTER, id, name); - this.mapsIDs = mapsIDs; + this.postersIDs = postersIDs; this.columnCount = Math.max(columnCount, 0); this.rowCount = Math.max(rowCount, 0); } - public PosterMap(UUID userUUID, int[] mapsIDs, int columnCount, int rowCount) { - this(userUUID, mapsIDs, null, null, columnCount, rowCount); + public PosterMap(UUID userUUID, int[] postersIDs, int columnCount, int rowCount) { + this(userUUID, postersIDs, null, null, columnCount, rowCount); } public PosterMap(Map map, UUID userUUID) throws InvalidConfigurationException { @@ -64,24 +70,34 @@ public PosterMap(Map map, UUID userUUID) throws InvalidConfigura columnCount = getFieldValue(map, "columns"); rowCount = getFieldValue(map, "rows"); - List idList = getFieldValue(map, "mapsIDs"); - mapsIDs = new int[idList.size()]; + List idList = getFieldValue(map, "postersIDs"); + postersIDs = new int[idList.size()]; for (int i = 0, c = idList.size(); i < c; i++) { - mapsIDs[i] = idList.get(i); + postersIDs[i] = idList.get(i); } } @Override - public int[] getMapsIDs() { - return mapsIDs; + public int[] getPostersIDs() { + return postersIDs; } + @Override + public int getFirstPosterID() { + int first = -1; + for (int id : postersIDs) { + if (first == -1 || first > id) { + first = id; + } + } + return first; + } /* ====== Serialization methods ====== */ @Override - public boolean managesMap(int mapID) { - for (int mapsID : mapsIDs) { - if (mapsID == mapID) { + public boolean managesPoster(int posterID) { + for (int postersID : postersIDs) { + if (posterID == postersID) { return true; } } @@ -93,7 +109,7 @@ public boolean managesMap(int mapID) { protected void postSerialize(Map map) { map.put("columns", columnCount); map.put("rows", rowCount); - map.put("mapsIDs", mapsIDs); + map.put("postersIDs", postersIDs); } /* ====== Getters & Setters ====== */ @@ -142,22 +158,23 @@ public int getIndexAt(int col, int row) { * @return The Minecraft map ID. * @throws ArrayIndexOutOfBoundsException if the given coordinates are too big (out of the poster). */ - public int getMapIdAt(int x, int y) { - return mapsIDs[y * columnCount + x]; + public int getPosterIdAt(int x, int y) { + return postersIDs[y * columnCount + x]; } - public int getMapIdAt(int index) { - return mapsIDs[index]; + public int getPosterIdAt(int index) { + return postersIDs[index]; } - public int getMapIdAtReverseY(int index) { + public int getPosterIdAtReverseY(int index) { int x = index % (columnCount); int y = index / (columnCount); - return getMapIdAt(x, rowCount - y - 1); + return getPosterIdAt(x, rowCount - y - 1); } - public int getMapIdAtReverseZ(int index, BlockFace orientation, BlockFace bf) { + public int getPosterIdAtReverseZ(int index, BlockFace orientation, BlockFace bf) { + //TODO maybe a bug there why don't use orientation? int x = 0; int y = 0; switch (bf) { @@ -172,29 +189,107 @@ public int getMapIdAtReverseZ(int index, BlockFace orientation, BlockFace bf) { default: } - return getMapIdAt(x, rowCount - y - 1); + return getPosterIdAt(x, rowCount - y - 1); } - public boolean hasColumnData() { return rowCount != 0 && columnCount != 0; } @Override - public int getMapCount() { - return mapsIDs.length; + public int getPosterCount() { + return postersIDs.length; } - public int getIndex(int mapID) { - for (int i = 0; i < mapsIDs.length; i++) { - if (mapsIDs[i] == mapID) { + public int getIndex(int posterID) { + for (int i = 0; i < postersIDs.length; i++) { + if (postersIDs[i] == posterID) { return i; } } + throw new IllegalArgumentException("Invalid poster ID"); + } + + public int getSortedIndex(int posterID) { + int[] ids = postersIDs.clone(); + Arrays.sort(ids); + for (int i : ids) { + PluginLogger.info("" + i); + } + + for (int i = 0; i < postersIDs.length; i++) { + if (ids[i] == posterID) { + return i; + } + } + throw new IllegalArgumentException("Invalid poster ID"); + } + + public PosterIndexes getIndexes(int posterID) { + int index = getSortedIndex(posterID); + PluginLogger.info(rowCount + " " + columnCount + " " + index); + return new PosterIndexes(index / columnCount, index % columnCount); + } - throw new IllegalArgumentException("Invalid map ID"); + public Location findLocationFirstFrame(ItemFrame frame, Player player) { + final ImagePoster iposter = PosterManager.getPoster(frame.getItem()); + if (!(iposter instanceof PosterMap)) { + return null; + } + PosterMap poster = (PosterMap) iposter; + if (!poster.hasColumnData()) { + return null; + } + int posterID = PosterManager.getPosterIDFromItemStack(frame.getItem()); + + BlockFace bf = WorldUtils.get4thOrientation(player.getLocation()); + + PosterIndexes posterindexes = getIndexes(posterID); + int row = posterindexes.getRowIndex(); + int column = posterindexes.getColumnIndex(); + Location loc = frame.getLocation(); + PluginLogger.info("\n\nlocalization of the initial clicked frame " + loc); + PluginLogger.info("row " + row + " col " + column); + switch (frame.getFacing().getOppositeFace()) { + case UP: + case DOWN: + switch (bf) { + case NORTH: + loc.add(-row, 0, column); + break; + case SOUTH: + loc.add(row, 0, -column); + break; + case WEST: + loc.add(row, 0, column); + break; + case EAST: + loc.add(-row, 0, -column); + break; + default: + throw new IllegalStateException("Unexpected value: " + bf); + } + break; + + case EAST: + loc.add(0, row, -column); + break; + case WEST: + loc.add(0, row, column); + break; + case NORTH: + loc.add(-column, row, 0); + break; + case SOUTH: + loc.add(column, row, 0); + break; + default: + throw new IllegalStateException("Unexpected value: " + bf); + } + PluginLogger.info("\n\nlocalization of the first frame " + loc); + return loc; } } diff --git a/src/main/java/fr/moribus/imageonmap/migration/MigratorExecutor.java b/src/main/java/fr/moribus/imageonmap/migration/MigratorExecutor.java index 1f681abd..c4c13ba5 100644 --- a/src/main/java/fr/moribus/imageonmap/migration/MigratorExecutor.java +++ b/src/main/java/fr/moribus/imageonmap/migration/MigratorExecutor.java @@ -1,8 +1,8 @@ /* * Copyright or © or Copr. Moribus (2013) * Copyright or © or Copr. ProkopyL (2015) - * Copyright or © or Copr. Amaury Carrade (2016 – 2021) - * Copyright or © or Copr. Vlammar (2019 – 2021) + * Copyright or © or Copr. Amaury Carrade (2016 – 2022) + * Copyright or © or Copr. Vlammar (2019 – 2024) * * This software is a computer program whose purpose is to allow insertion of * custom images in a Minecraft world. @@ -36,11 +36,10 @@ package fr.moribus.imageonmap.migration; -import fr.moribus.imageonmap.ImageOnMap; import fr.zcraft.quartzlib.components.i18n.I; import fr.zcraft.quartzlib.tools.PluginLogger; - +//TODO need to support new render folder and write on maps automatically public class MigratorExecutor { private static Thread migratorThread; @@ -49,7 +48,7 @@ public static void migrate() { PluginLogger.error(I.t("Migration is already running.")); return; } - migratorThread = new Thread(new V3Migrator(ImageOnMap.getPlugin()), "ImageOnMap-Migration"); + //migratorThread = new Thread(new V3Migrator(ImageOnMap.getPlugin()), "ImageOnMap-Migration"); migratorThread.start(); } diff --git a/src/main/java/fr/moribus/imageonmap/migration/OldSavedMap.java b/src/main/java/fr/moribus/imageonmap/migration/OldSavedMap.java deleted file mode 100644 index f4edbb69..00000000 --- a/src/main/java/fr/moribus/imageonmap/migration/OldSavedMap.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright or © or Copr. Moribus (2013) - * Copyright or © or Copr. ProkopyL (2015) - * Copyright or © or Copr. Amaury Carrade (2016 – 2021) - * Copyright or © or Copr. Vlammar (2019 – 2021) - * - * This software is a computer program whose purpose is to allow insertion of - * custom images in a Minecraft world. - * - * This software is governed by the CeCILL license under French law and - * abiding by the rules of distribution of free software. You can use, - * modify and/ or redistribute the software under the terms of the CeCILL - * license as circulated by CEA, CNRS and INRIA at the following URL - * "http://www.cecill.info". - * - * As a counterpart to the access to the source code and rights to copy, - * modify and redistribute granted by the license, users are provided only - * with a limited warranty and the software's author, the holder of the - * economic rights, and the successive licensors have only limited - * liability. - * - * In this respect, the user's attention is drawn to the risks associated - * with loading, using, modifying and/or developing or reproducing the - * software by the user in light of its specific status of free software, - * that may mean that it is complicated to manipulate, and that also - * therefore means that it is reserved for developers and experienced - * professionals having in-depth computer knowledge. Users are therefore - * encouraged to load and test the software's suitability as regards their - * requirements in conditions enabling the security of their systems and/or - * data to be ensured and, more generally, to use and operate it in the - * same conditions as regards security. - * - * The fact that you are presently reading this means that you have had - * knowledge of the CeCILL license and that you accept its terms. - */ - -package fr.moribus.imageonmap.migration; - -import fr.moribus.imageonmap.map.ImageMap; -import fr.moribus.imageonmap.map.MapManager; -import fr.moribus.imageonmap.map.SingleMap; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; -import org.bukkit.configuration.Configuration; -import org.bukkit.configuration.InvalidConfigurationException; - -class OldSavedMap { - private final short mapId; - private final String mapName; - private final String userName; - - public OldSavedMap(Object rawData) throws InvalidConfigurationException { - List data; - try { - data = (List) rawData; - } catch (ClassCastException ex) { - throw new InvalidConfigurationException("Invalid map data : " + ex.getMessage()); - } - - if (data.size() < 3) { - throw new InvalidConfigurationException("Map data too short (given : " + data.size() + ", expected 3)"); - } - try { - mapId = Short.parseShort(data.get(0)); - } catch (NumberFormatException ex) { - throw new InvalidConfigurationException("Invalid map ID : " + ex.getMessage()); - } - - mapName = data.get(1); - userName = data.get(2); - } - - public ImageMap toImageMap(UUID userUUID) { - return new SingleMap(userUUID, mapId, null, mapName); - } - - public void serialize(Configuration configuration) { - ArrayList data = new ArrayList(); - data.add(Short.toString(mapId)); - data.add(mapName); - data.add(userName); - configuration.set(mapName, data); - } - - public boolean isMapValid() { - return MapManager.mapIdExists(mapId); - } - - public short getMapId() { - return mapId; - } - - public String getUserName() { - return userName; - } -} diff --git a/src/main/java/fr/moribus/imageonmap/migration/OldSavedPoster.java b/src/main/java/fr/moribus/imageonmap/migration/OldSavedPoster.java deleted file mode 100644 index b471c267..00000000 --- a/src/main/java/fr/moribus/imageonmap/migration/OldSavedPoster.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright or © or Copr. Moribus (2013) - * Copyright or © or Copr. ProkopyL (2015) - * Copyright or © or Copr. Amaury Carrade (2016 – 2021) - * Copyright or © or Copr. Vlammar (2019 – 2021) - * - * This software is a computer program whose purpose is to allow insertion of - * custom images in a Minecraft world. - * - * This software is governed by the CeCILL license under French law and - * abiding by the rules of distribution of free software. You can use, - * modify and/ or redistribute the software under the terms of the CeCILL - * license as circulated by CEA, CNRS and INRIA at the following URL - * "http://www.cecill.info". - * - * As a counterpart to the access to the source code and rights to copy, - * modify and redistribute granted by the license, users are provided only - * with a limited warranty and the software's author, the holder of the - * economic rights, and the successive licensors have only limited - * liability. - * - * In this respect, the user's attention is drawn to the risks associated - * with loading, using, modifying and/or developing or reproducing the - * software by the user in light of its specific status of free software, - * that may mean that it is complicated to manipulate, and that also - * therefore means that it is reserved for developers and experienced - * professionals having in-depth computer knowledge. Users are therefore - * encouraged to load and test the software's suitability as regards their - * requirements in conditions enabling the security of their systems and/or - * data to be ensured and, more generally, to use and operate it in the - * same conditions as regards security. - * - * The fact that you are presently reading this means that you have had - * knowledge of the CeCILL license and that you accept its terms. - */ - -package fr.moribus.imageonmap.migration; - -import fr.moribus.imageonmap.map.ImageMap; -import fr.moribus.imageonmap.map.MapManager; -import fr.moribus.imageonmap.map.PosterMap; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.UUID; -import org.bukkit.configuration.Configuration; -import org.bukkit.configuration.InvalidConfigurationException; - -class OldSavedPoster { - private final String userName; - private final String posterName; - private final short[] mapsIds; - - public OldSavedPoster(Object rawData, String key) throws InvalidConfigurationException { - posterName = key; - List data; - try { - data = (List) rawData; - } catch (ClassCastException ex) { - throw new InvalidConfigurationException("Invalid map data : " + ex.getMessage()); - } - - if (data.size() < 2) { - throw new InvalidConfigurationException( - "Poster data too short (given : " + data.size() + ", expected at least 2)"); - } - userName = data.get(0); - mapsIds = new short[data.size() - 1]; - - for (int i = 1, c = data.size(); i < c; i++) { - try { - mapsIds[i - 1] = Short.parseShort(data.get(i)); - } catch (NumberFormatException ex) { - throw new InvalidConfigurationException("Invalid map ID : " + ex.getMessage()); - } - } - } - - public boolean contains(OldSavedMap map) { - short mapId = map.getMapId(); - - for (short mapsId : mapsIds) { - if (mapsId == mapId) { - return true; - } - } - - return false; - } - - public ImageMap toImageMap(UUID userUUID) { - // Converts the maps IDs to int as MC 1.13.2+ uses integer ids - final int[] mapsIdsInt = new int[mapsIds.length]; - Arrays.setAll(mapsIdsInt, i -> mapsIds[i]); - - return new PosterMap(userUUID, mapsIdsInt, null, "poster", 0, 0); - } - - public void serialize(Configuration configuration) { - ArrayList data = new ArrayList(); - data.add(userName); - - for (short mapId : mapsIds) { - data.add(Short.toString(mapId)); - } - - configuration.set(posterName, data); - - } - - public boolean isMapValid() { - for (short mapId : mapsIds) { - if (!MapManager.mapIdExists(mapId)) { - return false; - } - } - return true; - } - - public String getUserName() { - return userName; - } - - public short[] getMapsIds() { - return mapsIds; - } -} diff --git a/src/main/java/fr/moribus/imageonmap/migration/V3Migrator.java b/src/main/java/fr/moribus/imageonmap/migration/V3Migrator.java deleted file mode 100644 index 128baabb..00000000 --- a/src/main/java/fr/moribus/imageonmap/migration/V3Migrator.java +++ /dev/null @@ -1,619 +0,0 @@ -/* - * Copyright or © or Copr. Moribus (2013) - * Copyright or © or Copr. ProkopyL (2015) - * Copyright or © or Copr. Amaury Carrade (2016 – 2021) - * Copyright or © or Copr. Vlammar (2019 – 2021) - * - * This software is a computer program whose purpose is to allow insertion of - * custom images in a Minecraft world. - * - * This software is governed by the CeCILL license under French law and - * abiding by the rules of distribution of free software. You can use, - * modify and/ or redistribute the software under the terms of the CeCILL - * license as circulated by CEA, CNRS and INRIA at the following URL - * "http://www.cecill.info". - * - * As a counterpart to the access to the source code and rights to copy, - * modify and redistribute granted by the license, users are provided only - * with a limited warranty and the software's author, the holder of the - * economic rights, and the successive licensors have only limited - * liability. - * - * In this respect, the user's attention is drawn to the risks associated - * with loading, using, modifying and/or developing or reproducing the - * software by the user in light of its specific status of free software, - * that may mean that it is complicated to manipulate, and that also - * therefore means that it is reserved for developers and experienced - * professionals having in-depth computer knowledge. Users are therefore - * encouraged to load and test the software's suitability as regards their - * requirements in conditions enabling the security of their systems and/or - * data to be ensured and, more generally, to use and operate it in the - * same conditions as regards security. - * - * The fact that you are presently reading this means that you have had - * knowledge of the CeCILL license and that you accept its terms. - */ - -package fr.moribus.imageonmap.migration; - -import fr.moribus.imageonmap.ImageOnMap; -import fr.moribus.imageonmap.map.MapManager; -import fr.zcraft.quartzlib.components.i18n.I; -import fr.zcraft.quartzlib.tools.PluginLogger; -import fr.zcraft.quartzlib.tools.mojang.UUIDFetcher; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.StandardCopyOption; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Map; -import java.util.UUID; -import java.util.stream.Collectors; -import org.apache.commons.lang.ArrayUtils; -import org.apache.commons.lang.StringUtils; -import org.bukkit.configuration.InvalidConfigurationException; -import org.bukkit.configuration.file.FileConfiguration; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.plugin.Plugin; - -/** - * This class represents and executes the ImageOnMap v3.x migration process - */ -public class V3Migrator implements Runnable { - /** - * The name of the former images directory - */ - private static final String OLD_IMAGES_DIRECTORY_NAME = "Image"; - - /** - * The name of the former file that contained all the maps definitions (including posters) - */ - private static final String OLD_MAPS_FILE_NAME = "map.yml"; - - /** - * The name of the former file that contained all the posters definitions - */ - private static final String OLD_POSTERS_FILE_NAME = "poster.yml"; - - /** - * The name of the backup directory that will contain the pre-v3 files that - * were present before the migration started - */ - private static final String BACKUPS_PREV3_DIRECTORY_NAME = "backups_pre-v3"; - - /** - * The name of the backup directory that will contain the post-v3 files that - * were present before the migration started - */ - private static final String BACKUPS_POSTV3_DIRECTORY_NAME = "backups_post-v3"; - /** - * The plugin that is running the migration - */ - private final ImageOnMap plugin; - /** - * The backup directory that will contain the pre-v3 files that - * were present before the migration started - */ - private final File backupsPrev3Directory; - /** - * The backup directory that will contain the post-v3 files that - * were present before the migration started - */ - private final File backupsPostv3Directory; - /** - * The list of all the posters to migrate - */ - private final ArrayDeque postersToMigrate; - /** - * The list of all the single maps to migrate - */ - private final ArrayDeque mapsToMigrate; - /** - * The set of all the user names to retreive the UUID from Mojang - */ - private final HashSet userNamesToFetch; - /** - * The former file that contained all the posters definitions - */ - private File oldPostersFile; - /** - * The former file that contained all the maps definitions (including posters) - */ - private File oldMapsFile; - /** - * The map of all the usernames and their corresponding UUIDs - */ - private Map usersUUIDs; - /** - * Defines if the migration process is currently running - */ - private boolean isRunning = false; - - public V3Migrator(ImageOnMap plugin) { - this.plugin = plugin; - - File dataFolder = plugin.getDataFolder(); - - oldPostersFile = new File(dataFolder, OLD_POSTERS_FILE_NAME); - oldMapsFile = new File(dataFolder, OLD_MAPS_FILE_NAME); - - backupsPrev3Directory = new File(dataFolder, BACKUPS_PREV3_DIRECTORY_NAME); - backupsPostv3Directory = new File(dataFolder, BACKUPS_POSTV3_DIRECTORY_NAME); - - postersToMigrate = new ArrayDeque<>(); - mapsToMigrate = new ArrayDeque<>(); - userNamesToFetch = new HashSet<>(); - } - - /** - * Returns the former images directory of a given plugin - * - * @param plugin The plugin. - * @return the corresponding 'Image' directory - */ - public static File getOldImagesDirectory(Plugin plugin) { - return new File(plugin.getDataFolder(), OLD_IMAGES_DIRECTORY_NAME); - } - - /** - * Makes a standard file copy, and checks the integrity of the destination - * file after the copy - * - * @param sourceFile The file to copy - * @param destinationFile The destination file - * @throws IOException If the copy failed, if the integrity check failed, or if the destination file already exists - */ - private static void verifiedBackupCopy(File sourceFile, File destinationFile) throws IOException { - if (destinationFile.exists()) { - throw new IOException( - "Backup copy failed : destination file (" + destinationFile.getName() + ") already exists."); - } - - long sourceSize = sourceFile.length(); - String sourceCheckSum = fileCheckSum(sourceFile, "SHA1"); - - Path sourcePath = Paths.get(sourceFile.getAbsolutePath()); - Path destinationPath = Paths.get(destinationFile.getAbsolutePath()); - Files.copy(sourcePath, destinationPath, StandardCopyOption.REPLACE_EXISTING); - - long destinationSize = destinationFile.length(); - String destinationCheckSum = fileCheckSum(destinationFile, "SHA1"); - - if (sourceSize != destinationSize || !sourceCheckSum.equals(destinationCheckSum)) { - throw new IOException("Backup copy failed : source and destination files (" - + sourceFile.getName() - + ") differ after copy."); - } - - } - - /* ****** Actions ***** */ - - /** - * Calculates the checksum of a given file - * - * @param file The file to calculate the checksum of - * @param algorithmName The name of the algorithm to use - * @return The resulting checksum in hexadecimal format - * @throws IOException - **/ - private static String fileCheckSum(File file, String algorithmName) throws IOException { - MessageDigest instance; - try { - instance = MessageDigest.getInstance(algorithmName); - } catch (NoSuchAlgorithmException ex) { - throw new IOException( - "Could not check file integrity because of NoSuchAlgorithmException : " + ex.getMessage()); - } - - FileInputStream inputStream = new FileInputStream(file); - - byte[] data = new byte[1024]; - int read = 0; - - while ((read = inputStream.read(data)) != -1) { - instance.update(data); - } - - byte[] hashBytes = instance.digest(); - - StringBuilder buffer = new StringBuilder(); - char hexChar; - for (int i = 0; i < hashBytes.length; i++) { - hexChar = Integer.toHexString((hashBytes[i] & 0xff) + 0x100).charAt(0); - buffer.append(hexChar); - } - - return buffer.toString(); - } - - /** - * Executes the full migration - */ - private void migrate() { - try { - if (!spotFilesToMigrate()) { - return; - } - if (checkForExistingBackups()) { - return; - } - if (!loadOldFiles()) { - return; - } - backupMapData(); - fetchUUIDs(); - if (!fetchMissingUUIDs()) { - return; - } - } catch (Exception ex) { - PluginLogger.error(I.t("Error while preparing migration")); - PluginLogger.error(I.t("Aborting migration. No change has been made."), ex); - return; - } - - try { - mergeMapData(); - saveChanges(); - cleanup(); - } catch (Exception ex) { - PluginLogger.error(I.t("Error while migrating"), ex); - PluginLogger.error(I.t("Aborting migration. Some changes may already have been made.")); - PluginLogger.error(I.t( - "Before trying to migrate again, you must recover player files from the backups," - + " and then move the backups away from the plugin directory to avoid overwriting them.")); - } - } - - /** - * Checks if there is any of the former files to be migrated - * - * @return true if any former map or poster file exists, false otherwise - */ - private boolean spotFilesToMigrate() { - PluginLogger.info(I.t("Looking for configuration files to migrate...")); - - if (!oldPostersFile.exists()) { - oldPostersFile = null; - } else { - PluginLogger.info(I.t("Detected former posters file {0}", OLD_POSTERS_FILE_NAME)); - } - - if (!oldMapsFile.exists()) { - oldMapsFile = null; - } else { - PluginLogger.info(I.t("Detected former maps file {0}", OLD_MAPS_FILE_NAME)); - } - - if (oldPostersFile == null && oldMapsFile == null) { - PluginLogger.info(I.t("There is nothing to migrate. Stopping.")); - return false; - } else { - PluginLogger.info(I.t("Done.")); - return true; - } - } - - /** - * Checks if any existing backup directories exists - * - * @return true if a non-empty backup directory exists, false otherwise - */ - private boolean checkForExistingBackups() { - if ((backupsPrev3Directory.exists() && backupsPrev3Directory.list().length == 0) - || (backupsPostv3Directory.exists() && backupsPostv3Directory.list().length == 0)) { - PluginLogger.error(I.t("Backup directories already exists.")); - PluginLogger.error(I.t("This means that a migration has already been done," - + " or may not have ended well.")); - PluginLogger.error(I.t( - "To start a new migration," - + " you must move away the backup directories so they are not overwritten.")); - - return true; - } - return false; - } - - /** - * Creates backups of the former map files, and of the existing map stores - * - * @throws IOException - **/ - private void backupMapData() throws IOException { - PluginLogger.info(I.t("Backing up map data before migrating...")); - - if (!backupsPrev3Directory.exists()) { - backupsPrev3Directory.mkdirs(); - } - if (!backupsPostv3Directory.exists()) { - backupsPostv3Directory.mkdirs(); - } - - if (oldMapsFile != null && oldMapsFile.exists()) { - File oldMapsFileBackup = new File(backupsPrev3Directory, oldMapsFile.getName()); - verifiedBackupCopy(oldMapsFile, oldMapsFileBackup); - } - - if (oldPostersFile != null && oldPostersFile.exists()) { - File oldPostersFileBackup = new File(backupsPrev3Directory, oldPostersFile.getName()); - verifiedBackupCopy(oldPostersFile, oldPostersFileBackup); - } - - File backupFile; - for (File mapFile : plugin.getMapsDirectory().listFiles()) { - backupFile = new File(backupsPostv3Directory, mapFile.getName()); - verifiedBackupCopy(mapFile, backupFile); - } - - PluginLogger.info(I.t("Backup complete.")); - } - - /** - * An utility function to check if a map is actually part of a loaded poster - * - * @param map The single map. - * @return true if the map is part of a poster, false otherwise - */ - private boolean posterContains(OldSavedMap map) { - for (OldSavedPoster poster : postersToMigrate) { - if (poster.contains(map)) { - return true; - } - } - - return false; - } - - /** - * Loads the former files into the corresponding arrays - * Also fetches the names of all the users that have maps - * - * @return true if any of the files contained readable map data, false otherwise - */ - private boolean loadOldFiles() { - if (oldPostersFile != null) { - FileConfiguration oldPosters = YamlConfiguration.loadConfiguration(oldPostersFile); - - OldSavedPoster oldPoster; - for (String key : oldPosters.getKeys(false)) { - if ("IdCount".equals(key)) { - continue; - } - try { - oldPoster = new OldSavedPoster(oldPosters.get(key), key); - postersToMigrate.add(oldPoster); - if (!userNamesToFetch.contains(oldPoster.getUserName())) { - userNamesToFetch.add(oldPoster.getUserName()); - } - } catch (InvalidConfigurationException ex) { - PluginLogger.warning("Could not read poster data for key {0}", ex, key); - } - } - } - - if (oldMapsFile != null) { - FileConfiguration oldMaps = YamlConfiguration.loadConfiguration(oldMapsFile); - OldSavedMap oldMap; - - for (String key : oldMaps.getKeys(false)) { - try { - if ("IdCount".equals(key)) { - continue; - } - oldMap = new OldSavedMap(oldMaps.get(key)); - - if (!posterContains(oldMap)) { - mapsToMigrate.add(oldMap); - } - - if (!userNamesToFetch.contains(oldMap.getUserName())) { - userNamesToFetch.add(oldMap.getUserName()); - } - } catch (InvalidConfigurationException ex) { - PluginLogger.warning("Could not read poster data for key '{0}'", ex, key); - } - } - } - - return (postersToMigrate.size() > 0) || (mapsToMigrate.size() > 0); - } - - /** - * Fetches all the needed UUIDs from Mojang's UUID conversion service - * - * @throws IOException if the fetcher could not connect to Mojang's servers - * @throws InterruptedException if the thread was interrupted while fetching UUIDs - */ - private void fetchUUIDs() throws IOException, InterruptedException { - PluginLogger.info(I.t("Fetching UUIDs from Mojang...")); - try { - usersUUIDs = UUIDFetcher.fetch(new ArrayList(userNamesToFetch)); - } catch (IOException ex) { - PluginLogger.error(I.t("An error occurred while fetching the UUIDs from Mojang"), ex); - throw ex; - } catch (InterruptedException ex) { - PluginLogger.error(I.t("The migration worker has been interrupted"), ex); - throw ex; - } - PluginLogger.info(I.tn("Fetching done. {0} UUID have been retrieved.", - "Fetching done. {0} UUIDs have been retrieved.", usersUUIDs.size())); - } - - /** - * Fetches the UUIDs that could not be retrieved via Mojang's standard API - * - * @return true if at least one UUID has been retrieved, false otherwise - */ - private boolean fetchMissingUUIDs() throws IOException, InterruptedException { - if (usersUUIDs.size() == userNamesToFetch.size()) { - return true; - } - int remainingUsersCount = userNamesToFetch.size() - usersUUIDs.size(); - PluginLogger.info(I.tn("Mojang did not find UUIDs for {0} player at the current time.", - "Mojang did not find UUIDs for {0} players at the current time.", remainingUsersCount)); - PluginLogger.info(I.t("The Mojang servers limit requests rate at one per second, this may take some time...")); - - try { - UUIDFetcher.fetchRemaining(userNamesToFetch, usersUUIDs); - } catch (IOException ex) { - PluginLogger.error(I.t("An error occurred while fetching the UUIDs from Mojang")); - throw ex; - } catch (InterruptedException ex) { - PluginLogger.error(I.t("The migration worker has been interrupted")); - throw ex; - } - - if (usersUUIDs.size() != userNamesToFetch.size()) { - PluginLogger.warning(I.tn("Mojang did not find player data for {0} player", - "Mojang did not find player data for {0} players", - userNamesToFetch.size() - usersUUIDs.size())); - PluginLogger.warning(I.t("The following players do not exist or do not have paid accounts :")); - - String missingUsersList = ""; - - for (String user : userNamesToFetch) { - if (!usersUUIDs.containsKey(user)) { - missingUsersList += user + ", "; - } - } - missingUsersList = missingUsersList.substring(0, missingUsersList.length()); - - PluginLogger.info(missingUsersList); - } - - if (usersUUIDs.size() <= 0) { - PluginLogger.info(I.t("Mojang could not find any of the registered players.")); - PluginLogger.info(I.t("There is nothing to migrate. Stopping.")); - return false; - } - - return true; - } - - private void mergeMapData() { - PluginLogger.info(I.t("Merging map data...")); - - ArrayDeque remainingMaps = new ArrayDeque<>(); - ArrayDeque remainingPosters = new ArrayDeque<>(); - - ArrayDeque missingMapIds = new ArrayDeque<>(); - - UUID playerUUID; - OldSavedMap map; - while (!mapsToMigrate.isEmpty()) { - map = mapsToMigrate.pop(); - playerUUID = usersUUIDs.get(map.getUserName()); - if (playerUUID == null) { - remainingMaps.add(map); - } else if (!map.isMapValid()) { - missingMapIds.add((int) map.getMapId()); - } else { - MapManager.insertMap(map.toImageMap(playerUUID)); - } - } - mapsToMigrate.addAll(remainingMaps); - - OldSavedPoster poster; - while (!postersToMigrate.isEmpty()) { - poster = postersToMigrate.pop(); - playerUUID = usersUUIDs.get(poster.getUserName()); - if (playerUUID == null) { - remainingPosters.add(poster); - } else if (!poster.isMapValid()) { - missingMapIds.addAll(Arrays.stream(ArrayUtils.toObject(poster.getMapsIds())).map(id -> (int) id) - .collect(Collectors.toList())); - } else { - MapManager.insertMap(poster.toImageMap(playerUUID)); - } - } - postersToMigrate.addAll(remainingPosters); - - if (!missingMapIds.isEmpty()) { - PluginLogger.warning(I.tn("{0} registered minecraft map is missing from the save.", - "{0} registered minecraft maps are missing from the save.", missingMapIds.size())); - PluginLogger.warning( - I.t("These maps will not be migrated," - + " but this could mean the save has been altered or corrupted.")); - PluginLogger.warning(I.t("The following maps are missing : {0} ", - StringUtils.join(missingMapIds, ','))); - } - } - - /* ****** Utils ***** */ - - private void saveChanges() { - PluginLogger.info(I.t("Saving changes...")); - MapManager.save(); - } - - private void cleanup() throws IOException { - PluginLogger.info(I.t("Cleaning up old data files...")); - - //Cleaning maps file - if (oldMapsFile != null) { - if (mapsToMigrate.isEmpty()) { - PluginLogger.info(I.t("Deleting old map data file...")); - oldMapsFile.delete(); - } else { - PluginLogger.info(I.tn("{0} map could not be migrated.", "{0} maps could not be migrated.", - mapsToMigrate.size())); - YamlConfiguration mapConfig = new YamlConfiguration(); - mapConfig.set("IdCount", mapsToMigrate.size()); - - for (OldSavedMap map : mapsToMigrate) { - map.serialize(mapConfig); - } - - mapConfig.save(oldMapsFile); - } - } - - //Cleaning posters file - if (oldPostersFile != null) { - if (postersToMigrate.isEmpty()) { - PluginLogger.info(I.t("Deleting old poster data file...")); - oldPostersFile.delete(); - } else { - PluginLogger.info(I.tn("{0} poster could not be migrated.", "{0} posters could not be migrated.", - postersToMigrate.size())); - YamlConfiguration posterConfig = new YamlConfiguration(); - posterConfig.set("IdCount", postersToMigrate.size()); - - for (OldSavedPoster poster : postersToMigrate) { - poster.serialize(posterConfig); - } - - posterConfig.save(oldPostersFile); - } - } - - PluginLogger.info(I.t("Data that has not been migrated will be kept in the old data files.")); - } - - public synchronized boolean isRunning() { - return isRunning; - } - - private synchronized void setRunning(boolean running) { - this.isRunning = running; - } - - /** - * Executes the full migration, and defines the running status of the migration - */ - @Override - public void run() { - setRunning(true); - migrate(); - setRunning(false); - } - -} diff --git a/src/main/java/fr/moribus/imageonmap/ui/MapItemManager.java b/src/main/java/fr/moribus/imageonmap/ui/PosterItemManager.java similarity index 57% rename from src/main/java/fr/moribus/imageonmap/ui/MapItemManager.java rename to src/main/java/fr/moribus/imageonmap/ui/PosterItemManager.java index 90256f5c..8f196a89 100644 --- a/src/main/java/fr/moribus/imageonmap/ui/MapItemManager.java +++ b/src/main/java/fr/moribus/imageonmap/ui/PosterItemManager.java @@ -1,8 +1,8 @@ /* * Copyright or © or Copr. Moribus (2013) * Copyright or © or Copr. ProkopyL (2015) - * Copyright or © or Copr. Amaury Carrade (2016 – 2021) - * Copyright or © or Copr. Vlammar (2019 – 2021) + * Copyright or © or Copr. Amaury Carrade (2016 – 2022) + * Copyright or © or Copr. Vlammar (2019 – 2024) * * This software is a computer program whose purpose is to allow insertion of * custom images in a Minecraft world. @@ -36,17 +36,16 @@ package fr.moribus.imageonmap.ui; +import fr.moribus.imageonmap.ImageOnMap; import fr.moribus.imageonmap.Permissions; -import fr.moribus.imageonmap.map.ImageMap; -import fr.moribus.imageonmap.map.MapManager; +import fr.moribus.imageonmap.map.ImagePoster; +import fr.moribus.imageonmap.map.PosterManager; import fr.moribus.imageonmap.map.PosterMap; -import fr.moribus.imageonmap.map.SingleMap; import fr.zcraft.quartzlib.components.i18n.I; import fr.zcraft.quartzlib.core.QuartzLib; import fr.zcraft.quartzlib.tools.PluginLogger; import fr.zcraft.quartzlib.tools.items.ItemStackBuilder; import fr.zcraft.quartzlib.tools.items.ItemUtils; -import fr.zcraft.quartzlib.tools.runners.RunTask; import java.util.ArrayDeque; import java.util.HashMap; import java.util.Queue; @@ -70,13 +69,14 @@ import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.inventory.meta.MapMeta; +import org.bukkit.scheduler.BukkitScheduler; -public class MapItemManager implements Listener { +public class PosterItemManager implements Listener { private static HashMap> mapItemCache; public static void init() { mapItemCache = new HashMap<>(); - QuartzLib.registerEvents(new MapItemManager()); + QuartzLib.registerEvents(new PosterItemManager()); } public static void exit() { @@ -86,44 +86,38 @@ public static void exit() { mapItemCache = null; } - public static boolean give(Player player, ImageMap map) { - if (map instanceof PosterMap) { - return give(player, (PosterMap) map); - } else if (map instanceof SingleMap) { - return give(player, (SingleMap) map); + public static boolean give(Player player, ImagePoster poster) { + if (poster instanceof PosterMap) { + return give(player, (PosterMap) poster); } return false; } - public static boolean give(Player player, SingleMap map) { - return give(player, createMapItem(map, true)); - } - public static boolean give(Player player, PosterMap map) { - if (!map.hasColumnData()) { - return giveParts(player, map); + public static boolean give(Player player, PosterMap poster) { + if (!poster.hasColumnData()) { + return giveParts(player, poster); } - return give(player, SplatterMapManager.makeSplatterMap(map)); + return give(player, SplatterPosterManager.makeSplatterPoster(poster)); } private static boolean give(final Player player, final ItemStack item) { boolean given = ItemUtils.give(player, item); - if (given) { player.playSound(player.getLocation(), Sound.ENTITY_ITEM_PICKUP, SoundCategory.PLAYERS, 1, 1); } - return !given; } - public static boolean giveParts(Player player, PosterMap map) { + public static boolean giveParts(Player player, PosterMap poster) { boolean inventoryFull = false; - ItemStack mapPartItem; - for (int i = 0, c = map.getMapCount(); i < c; i++) { - mapPartItem = map.hasColumnData() ? createMapItem(map, map.getColumnAt(i), map.getRowAt(i)) : - createMapItem(map, i); - inventoryFull = give(player, mapPartItem) || inventoryFull; + ItemStack posterPartItem; + for (int i = 0, c = poster.getPosterCount(); i < c; i++) { + posterPartItem = poster.hasColumnData() ? createPosterItem(poster, poster.getColumnAt(i), + poster.getRowAt(i)) : + createPosterItem(poster, i); + inventoryFull = give(player, posterPartItem) || inventoryFull; } return inventoryFull; @@ -142,69 +136,66 @@ public static int giveCache(Player player) { return givenItemsCount; } - public static ItemStack createMapItem(SingleMap map) { - return createMapItem(map.getMapsIDs()[0], map.getName(), false); - } - - public static ItemStack createMapItem(SingleMap map, boolean goldTitle) { - return createMapItem(map.getMapsIDs()[0], map.getName(), false, goldTitle); + public static ItemStack createPosterItem(PosterMap poster, int index) { + return createPosterItem(poster.getPosterIdAt(index), getPosterTitle(poster, index), true); } - public static ItemStack createMapItem(PosterMap map, int index) { - return createMapItem(map.getMapIdAt(index), getMapTitle(map, index), true); + public static ItemStack createPosterItem(PosterMap poster, int x, int y) { + return createPosterItem(poster.getPosterIdAt(x, y), getPosterTitle(poster, y, x), true); } - public static ItemStack createMapItem(PosterMap map, int x, int y) { - return createMapItem(map.getMapIdAt(x, y), getMapTitle(map, y, x), true); + public static ItemStack createPosterItem(int posterID, String text, boolean isPosterPart) { + return createPosterItem(posterID, text, isPosterPart, false); } - public static ItemStack createMapItem(int mapID, String text, boolean isMapPart) { - return createMapItem(mapID, text, isMapPart, false); - } - - public static ItemStack createMapItem(int mapID, String text, boolean isMapPart, boolean goldTitle) { - ItemStack mapItem; - if (goldTitle) { - mapItem = new ItemStackBuilder(Material.FILLED_MAP) - .title(ChatColor.GOLD, text) + public static ItemStack createPosterItem(int posterID, String text, boolean isPosterPart, boolean goldTitle) { + ItemStack posterItem; + if (text == "") { + posterItem = new ItemStackBuilder(Material.FILLED_MAP) .hideAllAttributes() .item(); } else { - mapItem = new ItemStackBuilder(Material.FILLED_MAP) - .title(text) - .hideAllAttributes() - .item(); + if (goldTitle) { + posterItem = new ItemStackBuilder(Material.FILLED_MAP) + .title(ChatColor.GOLD, text) + .hideAllAttributes() + .item(); + } else { + posterItem = new ItemStackBuilder(Material.FILLED_MAP) + .title(text) + .hideAllAttributes() + .item(); + } } - final MapMeta meta = (MapMeta) mapItem.getItemMeta(); - meta.setMapId(mapID); - meta.setColor(isMapPart ? Color.LIME : Color.GREEN); - mapItem.setItemMeta(meta); - return mapItem; - } - public static String getMapTitle(PosterMap map, int row, int column) { - /// The name of a map item given to a player, if splatter maps are not used. 0 = map name; 1 = row; 2 = column. - return I.t("{0} (row {1}, column {2})", map.getName(), row + 1, column + 1); + final MapMeta meta = (MapMeta) posterItem.getItemMeta(); + meta.setMapId(posterID); + meta.setColor(isPosterPart ? Color.LIME : Color.GREEN); + posterItem.setItemMeta(meta); + return posterItem; } - public static String getMapTitle(PosterMap map, int index) { - /// The name of a map item given to a player, if splatter maps are not used. 0 = map name; 1 = index. - return I.t("{0} (part {1})", map.getName(), index + 1); + public static String getPosterTitle(PosterMap poster, int row, int column) { + // The name of a poster item given to a player, if splatter posters are not used. + // 0 = poster name; 1 = row; 2 = column. + return I.t("{0} (row {1}, column {2})", poster.getName(), row + 1, column + 1); } - private static String getMapTitle(ItemStack item) { - ImageMap map = MapManager.getMap(item); - if (map instanceof SingleMap) { - return map.getName(); - } else { - PosterMap poster = (PosterMap) map; - int index = poster.getIndex(MapManager.getMapIdFromItemStack(item)); - if (poster.hasColumnData()) { - return getMapTitle(poster, poster.getRowAt(index), poster.getColumnAt(index)); - } + public static String getPosterTitle(PosterMap poster, int index) { + // The name of a poster item given to a player, if splatter posters are not used. 0 = poster name; 1 = index. + return I.t("{0} (part {1})", poster.getName(), index + 1); + } - return getMapTitle(poster, index); + private static String getPosterTitle(ItemStack item) { + ImagePoster iposter = PosterManager.getPoster(item); + PosterMap poster = (PosterMap) iposter; + int index = poster.getIndex(PosterManager.getPosterIDFromItemStack(item)); + if (poster.hasColumnData()) { + return getPosterTitle(poster, poster.getRowAt(index), poster.getColumnAt(index)); } + + return getPosterTitle(poster, index); + //} } // @@ -212,21 +203,21 @@ private static String getMapTitle(ItemStack item) { /** * Returns the item to place to display the (col;row) part of the given poster. * - * @param map The map to take the part from. - * @param x The x coordinate of the part to display. Starts at 0. - * @param y The y coordinate of the part to display. Starts at 0. + * @param poster The map to take the part from. + * @param x The x coordinate of the part to display. Starts at 0. + * @param y The y coordinate of the part to display. Starts at 0. * @return The map. * @throws ArrayIndexOutOfBoundsException If x;y is not inside the map. */ - public static ItemStack createSubMapItem(ImageMap map, int x, int y) { - if (map instanceof PosterMap && ((PosterMap) map).hasColumnData()) { - return MapItemManager.createMapItem((PosterMap) map, x, y); + public static ItemStack createSubPosterItem(ImagePoster poster, int x, int y) { + if (poster instanceof PosterMap && ((PosterMap) poster).hasColumnData()) { + return PosterItemManager.createPosterItem((PosterMap) poster, x, y); } else { if (x != 0 || y != 0) { throw new ArrayIndexOutOfBoundsException(); // Coherence } - return createMapItem(map.getMapsIDs()[0], map.getName(), false); + return createPosterItem(poster.getPostersIDs()[0], poster.getName(), false); } } @@ -244,24 +235,24 @@ private static Queue getCache(Player player) { } private static void onItemFramePlace(ItemFrame frame, Player player, PlayerInteractEntityEvent event) { - final ItemStack mapItem = player.getInventory().getItemInMainHand(); + final ItemStack posterItem = player.getInventory().getItemInMainHand(); if (frame.getItem().getType() != Material.AIR) { return; } - if (!MapManager.managesMap(mapItem)) { + if (!PosterManager.managesPoster(posterItem)) { return; } - if (!Permissions.PLACE_SPLATTER_MAP.grantedTo(player)) { + if (!Permissions.PLACE_SPLATTER_POSTER.grantedTo(player)) { player.sendMessage(I.t(ChatColor.RED + "You do not have permission to place splatter maps.")); event.setCancelled(true); return; } frame.setItem(new ItemStack(Material.AIR)); - if (SplatterMapManager.hasSplatterAttributes(mapItem)) { - if (!SplatterMapManager.placeSplatterMap(frame, player, event)) { + if (SplatterPosterManager.hasSplatterAttributes(posterItem)) { + if (!SplatterPosterManager.placeSplatterPoster(frame, player)) { event.setCancelled(true); //In case of an error allow to cancel map placement return; @@ -277,18 +268,18 @@ private static void onItemFramePlace(ItemFrame frame, Player player, PlayerInter } - final ItemStack frameItem = mapItem.clone(); + final ItemStack frameItem = posterItem.clone(); final ItemMeta meta = frameItem.getItemMeta(); meta.setDisplayName(null); frameItem.setItemMeta(meta); - RunTask.later(() -> { + BukkitScheduler scheduler = ImageOnMap.getPlugin().getServer().getScheduler(); + scheduler.scheduleSyncDelayedTask(ImageOnMap.getPlugin(), () -> { frame.setItem(frameItem); frame.setRotation(Rotation.NONE); }, 5L); - } - //ItemUtils.consumeItem(player, mapItem); //useless no? + //ItemUtils.consumeItem(player, mapItem); //todo useless ? } private static void onItemFrameRemove(ItemFrame frame, Player player, EntityDamageByEntityEvent event) { @@ -297,27 +288,27 @@ private static void onItemFrameRemove(ItemFrame frame, Player player, EntityDama return; } - if (Permissions.REMOVE_SPLATTER_MAP.grantedTo(player)) { - if (player.isSneaking()) { - PosterMap poster = SplatterMapManager.removeSplatterMap(frame, player); - if (poster != null) { - event.setCancelled(true); - - if (player.getGameMode() != GameMode.CREATIVE - || !SplatterMapManager.hasSplatterMap(player, poster)) { - poster.give(player); - } - return; + if (Permissions.REMOVE_SPLATTER_POSTER.grantedTo(player) && player.isSneaking()) { + PluginLogger.info("Frame " + frame); + PosterMap poster = SplatterPosterManager.removeSplatterPoster(frame, player); + if (poster != null) { + event.setCancelled(true); + + if (player.getGameMode() != GameMode.CREATIVE + || !SplatterPosterManager.hasSplatterPoster(player, poster)) { + poster.give(player); } + return; } + } - if (!MapManager.managesMap(frame.getItem())) { + if (!PosterManager.managesPoster(frame.getItem())) { return; } - SplatterMapManager.removePropertiesFromFrames(player, frame); + SplatterPosterManager.removePropertiesFromFrames(frame); frame.setItem(new ItemStackBuilder(item) - .title(getMapTitle(item)) + .title(getPosterTitle(item)) .hideAllAttributes() .item()); diff --git a/src/main/java/fr/moribus/imageonmap/ui/PosterOnASurface.java b/src/main/java/fr/moribus/imageonmap/ui/PosterOnASurface.java index 1a7e3353..05c8d24a 100644 --- a/src/main/java/fr/moribus/imageonmap/ui/PosterOnASurface.java +++ b/src/main/java/fr/moribus/imageonmap/ui/PosterOnASurface.java @@ -1,8 +1,8 @@ /* * Copyright or © or Copr. Moribus (2013) * Copyright or © or Copr. ProkopyL (2015) - * Copyright or © or Copr. Amaury Carrade (2016 – 2021) - * Copyright or © or Copr. Vlammar (2019 – 2021) + * Copyright or © or Copr. Amaury Carrade (2016 – 2022) + * Copyright or © or Copr. Vlammar (2019 – 2024) * * This software is a computer program whose purpose is to allow insertion of * custom images in a Minecraft world. @@ -36,9 +36,12 @@ package fr.moribus.imageonmap.ui; -import fr.moribus.imageonmap.map.PosterMap; +import fr.moribus.imageonmap.map.ImagePoster; +import fr.moribus.imageonmap.map.PosterManager; import fr.zcraft.quartzlib.tools.world.FlatLocation; import fr.zcraft.quartzlib.tools.world.WorldUtils; +import java.util.HashMap; +import java.util.Map; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.block.BlockFace; @@ -48,121 +51,22 @@ import org.bukkit.inventory.ItemStack; public class PosterOnASurface { - public FlatLocation loc1; public FlatLocation loc2; public ItemFrame[] frames; - /** - * Return the list of map Frames associated with a specific map - */ - public static ItemFrame[] getMatchingMapFrames(PosterMap map, FlatLocation location, int mapId, BlockFace bf) { - int mapIndex = map.getIndex(mapId); - //int x = map.getColumnAt(mapIndex), y = map.getRowAt(mapIndex); - int x = 0; - int y = 0; - switch (bf) { - case EAST: - case WEST: - y = map.getColumnCount() - 1; - break; - case NORTH: - case SOUTH: - y = map.getRowCount() - 1; - break; - default: - throw new IllegalStateException("Unexpected value: " + bf); - } - return getMatchingMapFrames(map, location.clone().addH(x, y, bf), bf).clone(); - } - - public static ItemFrame[] getMatchingMapFrames(PosterMap map, FlatLocation location, BlockFace bf) { - ItemFrame[] frames = new ItemFrame[map.getMapCount()]; - FlatLocation loc = location.clone(); - - - int x = 0; - int y = 0; - switch (bf) { - case EAST: - case WEST: - //X=map.getRowCount(); - //Y=map.getColumnCount(); - //break; - case NORTH: - case SOUTH: - - y = map.getRowCount(); - x = map.getColumnCount(); - break; - - default: - throw new IllegalStateException("Unexpected value: " + bf); - } - - for (int j = 0; j < y; ++j) { - for (int i = 0; i < x; ++i) { - int mapIndex = map.getIndexAt(i, j); - - ItemFrame frame = getMapFrameAt(loc, map); - if (frame != null) { - frames[mapIndex] = frame; - } - switch (bf) { - case EAST: - case WEST: - loc.addH(0, -1, bf); - break; - case NORTH: - case SOUTH: - loc.addH(1, 0, bf); - break; - default: - throw new IllegalStateException("Unexpected value: " + bf); - } - - - } - - switch (bf) { - case EAST: - case WEST: - loc.addH(1, map.getColumnCount(), bf);//test - - break; - case NORTH: - case SOUTH: - loc.addH(-map.getColumnCount(), -1, bf); - break; - default: - throw new IllegalStateException("Unexpected value: " + bf); - } - - } - - return frames; - } - - public static ItemFrame getMapFrameAt(FlatLocation location, PosterMap map) { + public static ItemFrame getEmptyFrameAt(Location location, BlockFace facing) { Entity[] entities = location.getChunk().getEntities(); for (Entity entity : entities) { - if (!(entity instanceof ItemFrame)) { - continue; - } - if (!WorldUtils.blockEquals(location, entity.getLocation())) { + boolean notItemFrame = !(entity instanceof ItemFrame); + if (notItemFrame || !WorldUtils.blockEquals(location, entity.getLocation())) { continue; } ItemFrame frame = (ItemFrame) entity; - if (frame.getFacing() != location.getFacing()) { - continue; - } ItemStack item = frame.getItem(); - if (item.getType() != Material.FILLED_MAP) { - continue; - } - if (!map.managesMap(item)) { + if (frame.getFacing() != facing || item.getType() != Material.AIR) { continue; } return frame; @@ -171,22 +75,17 @@ public static ItemFrame getMapFrameAt(FlatLocation location, PosterMap map) { return null; } - public static ItemFrame getEmptyFrameAt(Location location, BlockFace facing) { + public static ItemFrame getFrameAt(Location location, BlockFace facing) { Entity[] entities = location.getChunk().getEntities(); for (Entity entity : entities) { - if (!(entity instanceof ItemFrame)) { - continue; - } - if (!WorldUtils.blockEquals(location, entity.getLocation())) { + boolean notItemFrame = !(entity instanceof ItemFrame); + if (notItemFrame || !WorldUtils.blockEquals(location, entity.getLocation())) { continue; } ItemFrame frame = (ItemFrame) entity; - if (frame.getFacing() != facing) { - continue; - } ItemStack item = frame.getItem(); - if (item.getType() != Material.AIR) { + if (frame.getFacing() != facing) { continue; } return frame; @@ -253,7 +152,111 @@ public boolean isValid(Player p) { return true; } - public void expand() { + public static Map getItemFramesLocation(Player p, Location startingLocation, + Location selectedLocation, BlockFace facing, + int rows, + int columns) { + Map itemFramesLocationMap = new HashMap(); + BlockFace bf = WorldUtils.get4thOrientation(p.getLocation()); + boolean isWall = + facing.equals(BlockFace.WEST) || facing.equals(BlockFace.EAST) || facing.equals(BlockFace.NORTH) + || facing.equals(BlockFace.SOUTH); + boolean isFloor = facing.equals(BlockFace.DOWN); + boolean isCeiling = facing.equals(BlockFace.UP); + Location loc = startingLocation; + + ItemFrame selectedFrame = getFrameAt(selectedLocation, facing); + + for (int r = 0; r < rows; r++) { + int x = 0; + int z = 0; + for (int c = 0; c < columns; c++) { + if (test(selectedFrame, loc, facing)) { + itemFramesLocationMap.put(loc.clone(), getFrameAt(loc, facing)); + } + //do a row + if (isWall || isFloor) { + switch (bf) { + case NORTH: + x++; + loc = loc.add(1, 0, 0); + break; + case SOUTH: + x--; + loc = loc.add(-1, 0, 0); + break; + case EAST: + z++; + loc = loc.add(0, 0, 1); + break; + case WEST: + z--; + loc = loc.add(0, 0, -1); + break; + default: + throw new IllegalStateException("Unexpected value: " + bf); + + } + } else if (isCeiling) { + switch (bf) { + case NORTH: + x--; + loc = loc.add(-1, 0, 0); + break; + case SOUTH: + x++; + loc = loc.add(1, 0, 0); + break; + case EAST: + z--; + loc = loc.add(0, 0, -1); + break; + case WEST: + z++; + loc = loc.add(0, 0, 1); + break; + default: + throw new IllegalStateException("Unexpected value: " + bf); + + } + } + + + } + if (isWall) { + loc = loc.add(-x, -1, -z); + } else if (isFloor || isCeiling) { + switch (bf) { + case NORTH: + case SOUTH: + loc = loc.add(-x, 0, 1); + break; + case EAST: + case WEST: + loc = loc.add(1, 0, -z); + break; + default: + throw new IllegalStateException("Unexpected value: " + bf); + } + x = 0; + z = 0; + } + } + return itemFramesLocationMap; + } + + //TODO add descr and move to correct class and rename + private static boolean test(ItemFrame selectedFrame, Location loc, BlockFace facing) { + ImagePoster fullPoster = PosterManager.getPoster(selectedFrame.getItem()); + for (int id : fullPoster.getPostersIDs()) { + if (getFrameAt(loc, facing) == null) { + continue; + } + if (id == PosterManager.getPosterIDFromItemStack(getFrameAt(loc, facing).getItem())) { + return true; + } + } + return false; } } diff --git a/src/main/java/fr/moribus/imageonmap/ui/PosterWall.java b/src/main/java/fr/moribus/imageonmap/ui/PosterWall.java index 0758ede5..c7287d72 100644 --- a/src/main/java/fr/moribus/imageonmap/ui/PosterWall.java +++ b/src/main/java/fr/moribus/imageonmap/ui/PosterWall.java @@ -1,8 +1,8 @@ /* * Copyright or © or Copr. Moribus (2013) * Copyright or © or Copr. ProkopyL (2015) - * Copyright or © or Copr. Amaury Carrade (2016 – 2021) - * Copyright or © or Copr. Vlammar (2019 – 2021) + * Copyright or © or Copr. Amaury Carrade (2016 – 2022) + * Copyright or © or Copr. Vlammar (2019 – 2024) * * This software is a computer program whose purpose is to allow insertion of * custom images in a Minecraft world. @@ -36,15 +36,11 @@ package fr.moribus.imageonmap.ui; -import fr.moribus.imageonmap.map.PosterMap; +import fr.zcraft.quartzlib.tools.PluginLogger; import fr.zcraft.quartzlib.tools.world.FlatLocation; -import fr.zcraft.quartzlib.tools.world.WorldUtils; import org.bukkit.Location; -import org.bukkit.Material; import org.bukkit.block.BlockFace; -import org.bukkit.entity.Entity; import org.bukkit.entity.ItemFrame; -import org.bukkit.inventory.ItemStack; public class PosterWall { @@ -53,96 +49,19 @@ public class PosterWall { public ItemFrame[] frames; - public static ItemFrame[] getMatchingMapFrames(PosterMap map, FlatLocation location, int mapId) { - int mapIndex = map.getIndex(mapId); - int x = map.getColumnAt(mapIndex); - int y = map.getRowAt(mapIndex); - - return getMatchingMapFrames(map, location.clone().add(-x, y)); - } - - public static ItemFrame[] getMatchingMapFrames(PosterMap map, FlatLocation location) { - ItemFrame[] frames = new ItemFrame[map.getMapCount()]; - FlatLocation loc = location.clone(); - - for (int y = 0; y < map.getRowCount(); ++y) { - for (int x = 0; x < map.getColumnCount(); ++x) { - int mapIndex = map.getIndexAt(x, y); - ItemFrame frame = getMapFrameAt(loc, map); - if (frame != null) { - frames[mapIndex] = frame; - } - loc.add(1, 0); - } - loc.setX(location.getX()); - loc.setZ(location.getZ()); - loc.add(0, -1); - } - - return frames; - } - - public static ItemFrame getMapFrameAt(FlatLocation location, PosterMap map) { - Entity[] entities = location.getChunk().getEntities(); - - for (Entity entity : entities) { - if (!(entity instanceof ItemFrame)) { - continue; - } - if (!WorldUtils.blockEquals(location, entity.getLocation())) { - continue; - } - ItemFrame frame = (ItemFrame) entity; - if (frame.getFacing() != location.getFacing()) { - continue; - } - ItemStack item = frame.getItem(); - if (item.getType() != Material.FILLED_MAP) { - continue; - } - if (!map.managesMap(item)) { - continue; - } - return frame; - } - - return null; - } - public static ItemFrame getEmptyFrameAt(Location location, BlockFace facing) { - Entity[] entities = location.getChunk().getEntities(); - - for (Entity entity : entities) { - if (!(entity instanceof ItemFrame)) { - continue; - } - if (!WorldUtils.blockEquals(location, entity.getLocation())) { - continue; - } - ItemFrame frame = (ItemFrame) entity; - if (frame.getFacing() != facing) { - continue; - } - ItemStack item = frame.getItem(); - if (item.getType() != Material.AIR) { - continue; - } - return frame; - } - - return null; + return PosterOnASurface.getEmptyFrameAt(location, facing); } public boolean isValid() { ItemFrame curFrame; + FlatLocation bottomLeft = FlatLocation.minMerged(loc1, loc2); FlatLocation loc = bottomLeft.clone(); - int distX = FlatLocation.flatBlockDistanceX(loc1, loc2); int distY = FlatLocation.flatBlockDistanceY(loc1, loc2); frames = new ItemFrame[distX * distY]; - for (int x = 0; x < distX; x++) { for (int y = 0; y < distY; y++) { curFrame = getEmptyFrameAt(loc, loc.getFacing()); @@ -159,7 +78,4 @@ public boolean isValid() { return true; } - public void expand() { - - } } diff --git a/src/main/java/fr/moribus/imageonmap/ui/SplatterMapManager.java b/src/main/java/fr/moribus/imageonmap/ui/SplatterPosterManager.java similarity index 52% rename from src/main/java/fr/moribus/imageonmap/ui/SplatterMapManager.java rename to src/main/java/fr/moribus/imageonmap/ui/SplatterPosterManager.java index d6c257cd..ade2d0e6 100644 --- a/src/main/java/fr/moribus/imageonmap/ui/SplatterMapManager.java +++ b/src/main/java/fr/moribus/imageonmap/ui/SplatterPosterManager.java @@ -1,8 +1,8 @@ /* * Copyright or © or Copr. Moribus (2013) * Copyright or © or Copr. ProkopyL (2015) - * Copyright or © or Copr. Amaury Carrade (2016 – 2021) - * Copyright or © or Copr. Vlammar (2019 – 2021) + * Copyright or © or Copr. Amaury Carrade (2016 – 2022) + * Copyright or © or Copr. Vlammar (2019 – 2024) * * This software is a computer program whose purpose is to allow insertion of * custom images in a Minecraft world. @@ -36,57 +36,54 @@ package fr.moribus.imageonmap.ui; -import com.google.common.collect.ImmutableMap; +import fr.moribus.imageonmap.ImageOnMap; import fr.moribus.imageonmap.Permissions; -import fr.moribus.imageonmap.image.MapInitEvent; -import fr.moribus.imageonmap.map.ImageMap; -import fr.moribus.imageonmap.map.MapManager; +import fr.moribus.imageonmap.image.PosterInitEvent; +import fr.moribus.imageonmap.map.ImagePoster; +import fr.moribus.imageonmap.map.PosterManager; import fr.moribus.imageonmap.map.PosterMap; import fr.zcraft.quartzlib.components.i18n.I; -import fr.zcraft.quartzlib.components.nbt.NBT; -import fr.zcraft.quartzlib.components.nbt.NBTCompound; -import fr.zcraft.quartzlib.components.nbt.NBTList; import fr.zcraft.quartzlib.tools.PluginLogger; -import fr.zcraft.quartzlib.tools.items.GlowEffect; import fr.zcraft.quartzlib.tools.items.ItemStackBuilder; -import fr.zcraft.quartzlib.tools.reflection.NMSException; -import fr.zcraft.quartzlib.tools.runners.RunTask; -import fr.zcraft.quartzlib.tools.text.MessageSender; import fr.zcraft.quartzlib.tools.world.FlatLocation; import fr.zcraft.quartzlib.tools.world.WorldUtils; -import java.lang.reflect.Method; +import java.util.Map; +import net.md_5.bungee.api.ChatMessageType; +import net.md_5.bungee.api.chat.TextComponent; import org.bukkit.ChatColor; import org.bukkit.Color; +import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.Rotation; import org.bukkit.block.BlockFace; +import org.bukkit.enchantments.Enchantment; import org.bukkit.entity.ItemFrame; import org.bukkit.entity.Player; import org.bukkit.event.player.PlayerInteractEntityEvent; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.MapMeta; +import org.bukkit.scheduler.BukkitScheduler; -//TODO rework splatter effect, using ID is far more stable than nbt tags. -// To update when adding small picture previsualization. -public abstract class SplatterMapManager { - private SplatterMapManager() { +// TODO update when adding small picture snapshot. +public abstract class SplatterPosterManager { + private SplatterPosterManager() { } - public static ItemStack makeSplatterMap(PosterMap map) { + public static ItemStack makeSplatterPoster(PosterMap poster) { - final ItemStack splatter = new ItemStackBuilder(Material.FILLED_MAP).title(ChatColor.GOLD, map.getName()) + final ItemStack splatter = new ItemStackBuilder(Material.FILLED_MAP).title(ChatColor.GOLD, poster.getName()) .title(ChatColor.DARK_GRAY, " - ").title(ChatColor.GRAY, I.t("Splatter Map")) .title(ChatColor.DARK_GRAY, " - ") - .title(ChatColor.GRAY, I.t("{0} × {1}", map.getColumnCount(), map.getRowCount())) - .loreLine(ChatColor.GRAY, map.getId()).loreLine() + .title(ChatColor.GRAY, I.t("{0} × {1}", poster.getColumnCount(), poster.getRowCount())) + .loreLine(ChatColor.GRAY, poster.getId()).loreLine() /// Title in a splatter map tooltip .loreLine(ChatColor.BLUE, I.t("Item frames needed")) /// Size of a map stored in a splatter map .loreLine(ChatColor.GRAY, - I.t("{0} × {1} (total {2} frames)", map.getColumnCount(), map.getRowCount(), - map.getColumnCount() * map.getRowCount())) + I.t("{0} × {1} (total {2} frames)", poster.getColumnCount(), poster.getRowCount(), + poster.getColumnCount() * poster.getRowCount())) .loreLine() /// Title in a splatter map tooltip .loreLine(ChatColor.BLUE, I.t("How to use this?")) @@ -103,7 +100,7 @@ public static ItemStack makeSplatterMap(PosterMap map) { .craftItem(); final MapMeta meta = (MapMeta) splatter.getItemMeta(); - meta.setMapId(map.getMapIdAt(0)); + meta.setMapId(poster.getPosterIdAt(0)); meta.setColor(Color.GREEN); splatter.setItemMeta(meta); @@ -128,7 +125,8 @@ public static ItemStack makeSplatterMap(PosterMap map) { * @return The modified item stack. The instance may be different if the passed item stack is not a craft itemstack. */ public static ItemStack addSplatterAttribute(final ItemStack itemStack) { - GlowEffect.addGlow(itemStack); + itemStack.addUnsafeEnchantment(Enchantment.LURE, 1); + //TODO ADD event to forbid xp duplication and usage in crafting table return itemStack; } @@ -140,50 +138,22 @@ public static ItemStack addSplatterAttribute(final ItemStack itemStack) { * @return True if the attribute was detected. */ public static boolean hasSplatterAttributes(ItemStack itemStack) { - - try { - final NBTCompound nbt = NBT.fromItemStack(itemStack); - if (!nbt.containsKey("Enchantments")) { - return false; - } - final Object enchantments = nbt.get("Enchantments"); - if (!(enchantments instanceof NBTList)) { - return false; - } - return !((NBTList) enchantments).isEmpty(); - } catch (NMSException e) { - PluginLogger.error("Unable to get Splatter Map attribute on item", e); - return false; - } - } - - /** - * Return true if it is a splatter map - * - * @param itemStack The item to check. - * @return True if is a splatter map - */ - public static boolean isSplatterMap(ItemStack itemStack) { - if (itemStack == null) { - return false; - } - return hasSplatterAttributes(itemStack) && MapManager.managesMap(itemStack); + return PosterManager.managesPoster(itemStack); } - /** * Return true if it has a specified splatter map * * @param player The player to check. - * @param map The map to check. + * @param poster The map to check. * @return True if the player has this map */ - public static boolean hasSplatterMap(Player player, PosterMap map) { + public static boolean hasSplatterPoster(Player player, PosterMap poster) { Inventory playerInventory = player.getInventory(); for (int i = 0; i < playerInventory.getSize(); ++i) { ItemStack item = playerInventory.getItem(i); - if (isSplatterMap(item) && map.managesMap(item)) { + if (hasSplatterAttributes(item) && poster.managesPoster(item)) { return true; } } @@ -198,13 +168,14 @@ public static boolean hasSplatterMap(Player player, PosterMap map) { * @param player Player placing map * @return true if the map was correctly placed */ - public static boolean placeSplatterMap(ItemFrame startFrame, Player player, PlayerInteractEntityEvent event) { - ImageMap map = MapManager.getMap(player.getInventory().getItemInMainHand()); - - if (!(map instanceof PosterMap)) { + @SuppressWarnings("checkstyle:VariableDeclarationUsageDistance") + public static boolean placeSplatterPoster(ItemFrame startFrame, Player player) { + ImagePoster iposter = PosterManager.getPoster(player.getInventory().getItemInMainHand()); + if (!(iposter instanceof PosterMap)) { return false; } - PosterMap poster = (PosterMap) map; + + PosterMap poster = (PosterMap) iposter; PosterWall wall = new PosterWall(); if (startFrame.getFacing().equals(BlockFace.DOWN) || startFrame.getFacing().equals(BlockFace.UP)) { @@ -218,71 +189,70 @@ public static boolean placeSplatterMap(ItemFrame startFrame, Player player, Play surface.loc2 = endLocation; if (!surface.isValid(player)) { - MessageSender.sendActionBarMessage(player, - I.t("{ce}There is not enough space to place this map ({0} × {1}).", - poster.getColumnCount(), - poster.getRowCount())); - - + String message = + I.t("§c There is not enough space to place this map ({0} × {1}).", poster.getColumnCount(), + poster.getRowCount()); + TextComponent text = new TextComponent(message); + player.spigot().sendMessage(ChatMessageType.ACTION_BAR, text); return false; } + Rotation r = Rotation.NONE; + BlockFace bf = WorldUtils.get4thOrientation(player.getLocation()); + PluginLogger.info("YAW calc {0} YAW {1} ", Math.abs(player.getLocation().getYaw()) - 180f, + player.getLocation().getYaw()); + switch (bf) { + case NORTH: + break; + case EAST: + r = r.rotateClockwise(); + break; + case WEST: + r = r.rotateCounterClockwise(); + break; + case SOUTH: + r = r.rotateClockwise(); + r = r.rotateClockwise(); + break; + + default: + throw new IllegalStateException("Unexpected value: " + bf); + } int i = 0; for (ItemFrame frame : surface.frames) { - BlockFace bf = WorldUtils.get4thOrientation(player.getLocation()); - int id = poster.getMapIdAtReverseZ(i, bf, startFrame.getFacing()); - Rotation rot = Rotation.NONE; + + bf = WorldUtils.get4thOrientation(player.getLocation()); + int id = poster.getPosterIdAtReverseZ(i, bf, startFrame.getFacing()); switch (frame.getFacing()) { case UP: break; case DOWN: - rot = Rotation.FLIPPED; + r = r.rotateClockwise().rotateClockwise(); //Invert break; default: - //throw new IllegalStateException("Unexpected value: " + frame.getFacing()); + throw new IllegalStateException("Unexpected value: " + frame.getFacing()); } - //Rotation management relative to player rotation the default position is North, - // when on ceiling we flipped the rotation - RunTask.later(() -> { - addPropertiesToFrames(player, frame); - frame.setItem( - new ItemStackBuilder(Material.FILLED_MAP).nbt(ImmutableMap.of("map", id)).craftItem()); - }, 5L); - - if (i == 0) { + frame.setRotation(r); + /*if (i == 0) { //First map need to be rotate one time CounterClockwise - rot = rot.rotateCounterClockwise(); - } - - switch (bf) { - case NORTH: - if (frame.getFacing() == BlockFace.DOWN) { - rot = rot.rotateClockwise(); - rot = rot.rotateClockwise(); - } - frame.setRotation(rot); - break; - case EAST: - rot = rot.rotateClockwise(); - frame.setRotation(rot); - break; - case SOUTH: - if (frame.getFacing() == BlockFace.UP) { - rot = rot.rotateClockwise(); - rot = rot.rotateClockwise(); - } - frame.setRotation(rot); - break; - case WEST: - rot = rot.rotateCounterClockwise(); - frame.setRotation(rot); - break; - default: - throw new IllegalStateException("Unexpected value: " + bf); - } - - - MapInitEvent.initMap(id); + switch (bf) { + case EAST: + frame.setRotation(r.rotateClockwise()); + break; + case WEST: + frame.setRotation(r.rotateCounterClockwise()); + break; + case SOUTH: + frame.setRotation(r.rotateClockwise().rotateClockwise()); + break; + case NORTH: + break; + default: + throw new IllegalStateException("Unexpected value: " + frame.getFacing()); + } + }*/ + + PosterInitEvent.initPoster(id); i++; } } else { @@ -294,33 +264,45 @@ public static boolean placeSplatterMap(ItemFrame startFrame, Player player, Play wall.loc2 = endLocation; if (!wall.isValid()) { - MessageSender.sendActionBarMessage(player, - I.t("{ce}There is not enough space to place this map ({0} × {1}).", poster.getColumnCount(), - poster.getRowCount())); + + String message = + I.t("§c There is not enough space to place this map ({0} × {1}).", poster.getColumnCount(), + poster.getRowCount()); + TextComponent text = new TextComponent(message); + player.spigot().sendMessage(ChatMessageType.ACTION_BAR, text); return false; } int i = 0; for (ItemFrame frame : wall.frames) { - int id = poster.getMapIdAtReverseY(i); + int id = poster.getPosterIdAtReverseY(i); - RunTask.later(() -> { - addPropertiesToFrames(player, frame); - frame.setItem( - new ItemStackBuilder(Material.FILLED_MAP).nbt(ImmutableMap.of("map", id)).craftItem()); - }, 5L); + + setupPoster(player, frame, id); //Force reset of rotation frame.setRotation(Rotation.NONE); - MapInitEvent.initMap(id); + PosterInitEvent.initPoster(id); ++i; } } return true; } + private static void setupPoster(Player player, ItemFrame frame, int id) { + BukkitScheduler scheduler = ImageOnMap.getPlugin().getServer().getScheduler(); + scheduler.scheduleSyncDelayedTask(ImageOnMap.getPlugin(), new Runnable() { + @Override + public void run() { + addPropertiesToFrames(player, frame); + ItemStack item = PosterItemManager.createPosterItem(id, "", true, false); + frame.setItem(item); + } + }, 5L); + } + /** * Remove splattermap * @@ -328,44 +310,27 @@ public static boolean placeSplatterMap(ItemFrame startFrame, Player player, Play * @param player The player removing the map * @return **/ - public static PosterMap removeSplatterMap(ItemFrame startFrame, Player player) { - final ImageMap map = MapManager.getMap(startFrame.getItem()); - if (!(map instanceof PosterMap)) { + public static PosterMap removeSplatterPoster(ItemFrame startFrame, Player player) { + final ImagePoster iposter = PosterManager.getPoster(startFrame.getItem()); + if (!(iposter instanceof PosterMap)) { return null; } - PosterMap poster = (PosterMap) map; + PosterMap poster = (PosterMap) iposter; if (!poster.hasColumnData()) { return null; } - FlatLocation loc = new FlatLocation(startFrame.getLocation(), startFrame.getFacing()); - ItemFrame[] matchingFrames = null; - - switch (startFrame.getFacing()) { - case UP: - case DOWN: - matchingFrames = PosterOnASurface.getMatchingMapFrames(poster, loc, - MapManager.getMapIdFromItemStack(startFrame.getItem()), - WorldUtils.get4thOrientation(player.getLocation()));//startFrame.getFacing()); - break; - - case NORTH: - case SOUTH: - case EAST: - case WEST: - matchingFrames = PosterWall.getMatchingMapFrames(poster, loc, - MapManager.getMapIdFromItemStack(startFrame.getItem())); - break; - default: - throw new IllegalStateException("Unexpected value: " + startFrame.getFacing()); - } - - if (matchingFrames == null) { - return null; - } - - for (ItemFrame frame : matchingFrames) { + //We search for the map on the top left corner + Location startingLocation = poster.findLocationFirstFrame(startFrame, player); + Map + itemFrameLocations = + PosterOnASurface.getItemFramesLocation(player, startingLocation, startFrame.getLocation(), + startFrame.getFacing(), + poster.getRowCount(), poster.getColumnCount()); + //TODO check if it is the correct map id and check the why it delete more than it should and out of place + for (Map.Entry entry : itemFrameLocations.entrySet()) { + ItemFrame frame = itemFrameLocations.get(entry.getKey()); if (frame != null) { - removePropertiesFromFrames(player, frame); + removePropertiesFromFrames(frame); frame.setItem(null); } } @@ -374,22 +339,12 @@ public static PosterMap removeSplatterMap(ItemFrame startFrame, Player player) { } public static void addPropertiesToFrames(Player player, ItemFrame frame) { - if (Permissions.PLACE_INVISIBLE_SPLATTER_MAP.grantedTo(player)) { - try { - Method setVisible = frame.getClass().getMethod("setVisible", boolean.class); - setVisible.invoke(frame, false); - } catch (Exception e) { - //1.16- - } + if (Permissions.PLACE_INVISIBLE_SPLATTER_POSTER.grantedTo(player)) { + frame.setVisible(false); } } - public static void removePropertiesFromFrames(Player player, ItemFrame frame) { - try { - Method setVisible = frame.getClass().getMethod("setVisible", boolean.class); - setVisible.invoke(frame, true); - } catch (Exception e) { - //1.16- - } + public static void removePropertiesFromFrames(ItemFrame frame) { + frame.setVisible(true); } } diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 58d19cb0..612ca6d0 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,20 +1,23 @@ name: ImageOnMap main: fr.moribus.imageonmap.ImageOnMap -version: "4.2.2" +version: "5.0.0" api-version: "1.13" commands: tomap: - description: render an image in a map + description: Render an image in a map usage: / [URL] maptool: description: Manage maps maps: description: Manage maps through a GUI givemap: - description: give a map to a player from a player map store, by default take from the player that make the command + description: Give a map to a player from a playerPosterStore, by default take from the player that make the command usage: / [player_from] player_to map_name + placemap: + description: Place a map remotely and deploy it + usage: / [player_from]: x y z permissions: imageonmap.*: @@ -42,6 +45,8 @@ permissions: imageonmap.bypassimagelimit: false imageonmap.bypasswhitelist: true imageonmap.placeinvisiblesplattermap: true + imageonmap.remoteplacing: false + imageonmap.repairmap: true imageonmap.userender: description: "Allows you to use /tomap and related commands (/maptool getremaining). Alias of imageonmap.new." @@ -120,7 +125,7 @@ permissions: default: op imageonmap.bypassimagelimit: - description: "Allows you to bypass permission node check for the number of images in the playerMapStore (by default users have an unlimited amount of images)." + description: "Allows you to bypass permission node check for the number of images in the playerPosterStore (by default users have an unlimited amount of images)." default: op imageonmap.bypasswhitelist: @@ -130,3 +135,7 @@ permissions: imageonmap.placeinvisiblesplattermap: description: "Allows you to make the item frame on which you placed your splatter map invisible." default: true + + imageonmap.remoteplacing: + description: "Allows you to remotly place a map from a playerPosterStore to a specific position." + default: op \ No newline at end of file