Merge branch 'indev'

This commit is contained in:
Adrien Prokopowicz 2015-06-03 21:56:13 +02:00
commit ec6b4ec997
63 changed files with 4791 additions and 2102 deletions

21
licenseheader.txt Normal file
View File

@ -0,0 +1,21 @@
<#if licenseFirst??>
${licenseFirst}
</#if>
${licensePrefix}Copyright (C) 2013 Moribus
${licensePrefix}Copyright (C) 2015 ProkopyL <prokopylmc@gmail.com>
${licensePrefix?replace(" +$", "", "r")}
${licensePrefix}This program is free software: you can redistribute it and/or modify
${licensePrefix}it under the terms of the GNU General Public License as published by
${licensePrefix}the Free Software Foundation, either version 3 of the License, or
${licensePrefix}(at your option) any later version.
${licensePrefix?replace(" +$", "", "r")}
${licensePrefix}This program is distributed in the hope that it will be useful,
${licensePrefix}but WITHOUT ANY WARRANTY; without even the implied warranty of
${licensePrefix}MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
${licensePrefix}GNU General Public License for more details.
${licensePrefix?replace(" +$", "", "r")}
${licensePrefix}You should have received a copy of the GNU General Public License
${licensePrefix}along with this program. If not, see <http://www.gnu.org/licenses/>.
<#if licenseLast??>
${licenseLast}
</#if>

18
nb-configuration.xml Normal file
View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<project-shared-configuration>
<!--
This file contains additional configuration written by modules in the NetBeans IDE.
The configuration is intended to be shared among all the users of project and
therefore it is assumed to be part of version control checkout.
Without this configuration present, some functionality in the IDE may be limited or fail altogether.
-->
<properties xmlns="http://www.netbeans.org/ns/maven-properties-data/1">
<!--
Properties that influence various parts of the IDE, especially code formatting and the like.
You can copy and paste the single properties, into the pom.xml file and the IDE will pick them up.
That way multiple projects can share the same settings (useful for formatting rules for example).
Any value defined here will override the pom.xml file value but is only applicable to the current project.
-->
<netbeans.hint.licensePath>${project.basedir}/licenseheader.txt</netbeans.hint.licensePath>
</properties>
</project-shared-configuration>

View File

@ -1,30 +1,48 @@
/*
* Copyright (C) 2013 Moribus
* Copyright (C) 2015 ProkopyL <prokopylmc@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package fr.moribus.imageonmap;
import fr.moribus.imageonmap.commands.MapToolCommand;
import fr.moribus.imageonmap.commands.ImageRenduCommande;
import fr.moribus.imageonmap.commands.Commands;
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.migration.MigratorExecutor;
import fr.moribus.imageonmap.migration.V3Migrator;
import fr.moribus.imageonmap.ui.MapItemManager;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Set;
import java.util.logging.Level;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.inventory.ItemStack;
import org.bukkit.plugin.java.JavaPlugin;
import fr.moribus.imageonmap.map.ImageMap;
import fr.moribus.imageonmap.map.SingleMap;
public final class ImageOnMap extends JavaPlugin
{
static private final String IMAGES_DIRECTORY_NAME = "images";
static private final String MAPS_DIRECTORY_NAME = "maps";
static private ImageOnMap plugin;
private File imagesDirectory;
private final File mapsDirectory;
public ImageOnMap()
{
imagesDirectory = new File(this.getDataFolder(), IMAGES_DIRECTORY_NAME);
mapsDirectory = new File(this.getDataFolder(), MAPS_DIRECTORY_NAME);
plugin = this;
}
@ -33,169 +51,62 @@ public final class ImageOnMap extends JavaPlugin
return plugin;
}
private boolean dossierCree;
private FileConfiguration customConfig = null;
private File customConfigFile = null;
/* liste contenant les maps ne pouvant être placé dans l'inventaire du joueur. Je le fous ici afin que ce soit
accessible de partout dans le plugin. */
private HashMap<String, ArrayList<ItemStack>> cache = new HashMap<String, ArrayList<ItemStack>>();
// Index des maps chargées sur le serveur
public ArrayList<Short> mapChargee = new ArrayList<Short>();
public File getImagesDirectory() {return imagesDirectory;}
public File getMapsDirectory() {return mapsDirectory;}
public File getImageFile(short mapID)
{
return new File(imagesDirectory, "map"+mapID+".png");
}
@Override
public void onEnable()
{
// On crée si besoin le dossier les images seront stockées
dossierCree = ImgUtility.CreeRepImg(this);
// On ajoute si besoin les params par défaut du plugin
ImgUtility.CreeSectionConfig(this);
if (getConfig().get("map_path") == null)
PluginLogger.init(this);
// Creating the images and maps directories if necessary
try
{
getConfig().set("map_path", getServer().getWorlds().get(0).getName());
imagesDirectory = checkPluginDirectory(imagesDirectory, V3Migrator.getOldImagesDirectory(this));
checkPluginDirectory(mapsDirectory);
}
else if (getConfig().get("map_path") != getServer().getWorlds().get(0).getName())
catch(IOException ex)
{
getConfig().set("map_path", getServer().getWorlds().get(0).getName());
}
if (getConfig().getBoolean("import-maps"))
{
ImgUtility.ImporterConfig(this);
}
if (this.getConfig().getBoolean("collect-data"))
{
try
{
MetricsLite metrics = new MetricsLite(this);
metrics.start();
getLogger().info("Metrics launched for ImageOnMap");
}
catch (IOException e)
{
PluginLogger.LogError("Failed to start Plugin metrics", e);
}
}
if (dossierCree)
{
getCommand("tomap").setExecutor(new ImageRenduCommande(this));
getCommand("maptool").setExecutor(new MapToolCommand(this));
getServer().getPluginManager().registerEvents(new SendMapOnFrameEvent(this), this);
getServer().getPluginManager().registerEvents(new SendMapOnInvEvent(this), this);
this.saveDefaultConfig();
//ChargerMap();
}
else
{
getLogger().info("[ImageOnMap] An error occured ! Unable to create Image folder. Plugin will NOT work !");
PluginLogger.error("FATAL : " + ex.getMessage(), null);
this.setEnabled(false);
return;
}
//Init all the things !
PluginConfiguration.init(this);
MetricsLite.startMetrics();
ImageIOExecutor.start();
ImageRendererExecutor.start();
MapManager.init();
Commands.init(this);
MapInitEvent.init(this);
MapItemManager.init();
}
@Override
public void onDisable()
{
getLogger().info("Stopping ImageOnMap");
ImageIOExecutor.stop();
ImageRendererExecutor.stop();
MapManager.exit();
MapItemManager.exit();
MigratorExecutor.waitForMigration();
PluginLogger.exit();
}
public void ChargerMap()
private File checkPluginDirectory(File primaryFile, File... alternateFiles) throws IOException
{
Set<String> cle = getCustomConfig().getKeys(false);
int nbMap = 0, nbErr = 0;
for (String s : cle)
if(primaryFile.exists()) return primaryFile;
for(File file : alternateFiles)
{
if (getCustomConfig().getStringList(s).size() >= 3)
{
ImageMap map;
String stringID = getCustomConfig().getStringList(s).get(0);
try
{
map = new SingleMap(Short.valueOf(stringID));
map.load();
nbMap++;
}
catch (NumberFormatException e)
{
PluginLogger.LogWarning("Could not parse map ID from config", e);
nbErr++;
}
catch (IOException e)
{
PluginLogger.LogError("Could not load map ID " + stringID, e);
nbErr++;
}
}
if(file.exists()) return file;
}
PluginLogger.LogInfo(nbMap + " maps successfuly loaded.");
if (nbErr != 0)
PluginLogger.LogWarning(nbErr + " couldn't be loaded.");
if(!primaryFile.mkdirs())
throw new IOException("Could not create '" + primaryFile.getName() + "' plugin directory.");
return primaryFile;
}
/* Méthodes pour charger / recharger / sauvegarder
* le fichier conf des maps (map.yml).
* Je les ai juste copié depuis un tuto du wiki Bukkit.
*/
@SuppressWarnings("deprecation")
public void reloadCustomConfig()
{
if (customConfigFile == null)
{
customConfigFile = new File(getDataFolder(), "map.yml");
}
customConfig = YamlConfiguration.loadConfiguration(customConfigFile);
// Look for defaults in the jar
InputStream defConfigStream = this.getResource("map.yml");
if (defConfigStream != null)
{
YamlConfiguration defConfig = YamlConfiguration.loadConfiguration(defConfigStream);
customConfig.setDefaults(defConfig);
}
}
public FileConfiguration getCustomConfig()
{
if (customConfig == null)
{
reloadCustomConfig();
}
return customConfig;
}
public void saveCustomConfig()
{
if (customConfig == null || customConfigFile == null)
{
return;
}
try
{
getCustomConfig().save(customConfigFile);
}
catch (IOException ex)
{
getLogger().log(Level.SEVERE, "Could not save config to " + customConfigFile, ex);
}
}
public ArrayList<ItemStack> getRemainingMaps(String j)
{
return cache.get(j);
}
public void setRemainingMaps(String j, ArrayList<ItemStack> remaining)
{
cache.put(j, remaining);
}
public void removeRemaingMaps(String j)
{
cache.remove(j);
}
}

View File

@ -1,266 +0,0 @@
package fr.moribus.imageonmap;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Set;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.command.BlockCommandSender;
import org.bukkit.command.CommandSender;
import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.map.MapView;
import fr.moribus.imageonmap.map.SingleMap;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.image.BufferedImage;
public class ImgUtility
{
// Vérifie que c'est bien un joueur qui exécute la commande
static public boolean VerifierIdentite(CommandSender sender)
{
if (sender instanceof Player)
{
return true;
}
else if (sender instanceof ConsoleCommandSender)
{System.out.println(ChatColor.RED + "Cette commande ne peut être utilisée dans la console !"); return false;}
else if (sender instanceof BlockCommandSender)
{System.out.println(ChatColor.RED + "Cette commande ne peut être utilisée par un bloc-commande !"); return false;}
else
{System.out.println(ChatColor.RED + "Cette commande ne peut être lancée de cette façon !"); return false;}
}
// Creation du dossier sera stocké les images
static public boolean CreeRepImg(ImageOnMap plugin)
{
File dossier;
dossier = new File(plugin.getDataFolder().getPath() + "/Image");
if (!dossier.exists())
{
return dossier.mkdirs();
}
else
return true;
}
static void CreeSectionConfig(ImageOnMap plugin)
{
if(plugin.getConfig().get("Limit-map-by-server") == null)
plugin.getConfig().set("Limit-map-by-server", 0);
if(plugin.getConfig().get("Limit-map-by-player") == null)
plugin.getConfig().set("Limit-map-by-player", 0);
if(plugin.getConfig().get("collect-data") == null)
plugin.getConfig().set("collect-data", true);
if(plugin.getConfig().get("import-maps") == null)
plugin.getConfig().set("import-maps", true);
if(plugin.getConfig().get("send-entire-maps") == null)
plugin.getConfig().set("send-entire-maps", 0);
plugin.saveConfig();
}
static int getNombreDeMaps(ImageOnMap plugin)
{
int nombre = 0;
Set<String> cle = plugin.getCustomConfig().getKeys(false);
for (String s: cle)
{
if(plugin.getCustomConfig().getStringList(s).size() >= 3)
{
nombre++;
}
}
return nombre;
}
static int getNombreDeMapsParJoueur(ImageOnMap plugin, String pseudo)
{
int nombre = 0;
Set<String> cle = plugin.getCustomConfig().getKeys(false);
for (String s: cle)
{
if(plugin.getCustomConfig().getStringList(s).size() >= 3 && plugin.getCustomConfig().getStringList(s).get(2).contentEquals(pseudo))
{
nombre++;
}
}
return nombre;
}
static boolean EstDansFichier(ImageOnMap plugin, short id)
{
Set<String> cle = plugin.getCustomConfig().getKeys(false);
for (String s: cle)
{
if(plugin.getCustomConfig().getStringList(s).size() >= 3 && Short.parseShort(plugin.getCustomConfig().getStringList(s).get(0)) == id)
{
return true;
}
}
return false;
}
public static boolean EstDansFichier(ImageOnMap plugin, short id, String pseudo)
{
Set<String> cle = plugin.getCustomConfig().getKeys(false);
for (String s: cle)
{
if(plugin.getCustomConfig().getStringList(s).size() >= 3 && Short.parseShort(plugin.getCustomConfig().getStringList(s).get(0)) == id && plugin.getCustomConfig().getStringList(s).get(2).contentEquals(pseudo))
{
return true;
}
}
return false;
}
static boolean ImporterConfig(ImageOnMap plugin)
{
Set<String> cle = plugin.getConfig().getKeys(false);
plugin.getLogger().info("Start importing maps config to maps.yml...");
int i = 0;
for (String s: cle)
{
if(plugin.getConfig().getStringList(s).size() >= 3)
{
//plugin.getLogger().info("Importing "+ plugin.getConfig().getStringList(s).get(1));
ArrayList<String> liste = new ArrayList<String>();
liste.add(String.valueOf(plugin.getConfig().getStringList(s).get(0)));
liste.add(plugin.getConfig().getStringList(s).get(1));
liste.add(plugin.getConfig().getStringList(s).get(2));
plugin.getCustomConfig().set(plugin.getConfig().getStringList(s).get(1), liste);
plugin.getConfig().set(s, null);
i++;
}
}
plugin.getLogger().info("Importing finished. "+ i+ "maps were imported");
plugin.getConfig().set("import-maps", false);
plugin.saveConfig();
plugin.saveCustomConfig();
return true;
}
// Fait la même chose que EstDansFichier() mais en retournant un objet MapView
@SuppressWarnings("deprecation")
static MapView getMap(ImageOnMap plugin, short id)
{
MapView map;
if(!ImgUtility.EstDansFichier(plugin, id))
{
return null;
}
map = Bukkit.getMap(id);
if(map == null)
{
plugin.getLogger().warning("Map#"+ id+ " exists in maps.yml but not in the world folder !");
return null;
}
return map;
}
static public boolean RemoveMap(ImageOnMap plugin, short id)
{
@SuppressWarnings("deprecation")
MapView carte = Bukkit.getMap(id);
Set<String> cle = plugin.getCustomConfig().getKeys(false);
for (String s: cle)
{
if(plugin.getCustomConfig().getStringList(s).size() >= 3)
{
if(carte == null && id == Short.parseShort(plugin.getCustomConfig().getStringList(s).get(0)))
{
//joueur.sendMessage("Suppression de la map dans fichier conf");
plugin.getCustomConfig().set(s, null);
plugin.saveCustomConfig();
File map = new File("./plugins/ImageOnMap/Image/" + s + ".png");
boolean isDeleted = map.delete();
//joueur.sendMessage("The picture have been deleted");
if(isDeleted)
return true;
else
{
plugin.getLogger().warning("Picture "+ s+ ".png cannot be deleted !");
return false;
}
}
else if(id == Short.parseShort(plugin.getCustomConfig().getStringList(s).get(0)))
{
//joueur.sendMessage("Suppression de la map dans fichier conf + fichier dat");
SingleMap.SupprRendu(carte);
/*if(plugin.getConfig().get("delete") != null);
{
ArrayList<String> ListeSuppr = (ArrayList<String>) plugin.getConfig().getStringList("delete");
ListeSuppr.add(plugin.getCustomConfig().getStringList(s).get(0));
plugin.getConfig().set("delete", ListeSuppr);
}*/
plugin.getCustomConfig().set(s, null);
plugin.saveCustomConfig();
plugin.saveConfig();
File map = new File("./plugins/ImageOnMap/Image/" + s + ".png");
boolean isDeleted = map.delete();
//joueur.sendMessage("DEBUG: booléen isDeleted :"+ isDeleted+ "; Nom de la map : "+ plugin.getServer().getWorlds().get(0).getName());
//joueur.sendMessage("The map has been deleted");
if(isDeleted)
return true;
else
{
plugin.getLogger().warning("Picture "+ s+ ".png cannot be deleted !");
return false;
}
}
}
}
//plugin.getLogger().info("No map with id"+ id+ " was found");
return false;
}
static public ArrayList<String> getListMapByPlayer(ImageOnMap plugin, String pseudo)
{
ArrayList<String> listeMap = new ArrayList<String>();
Set<String> cle = plugin.getCustomConfig().getKeys(false);
for (String s: cle)
{
if(plugin.getCustomConfig().getStringList(s).size() >= 3 && pseudo.equalsIgnoreCase(plugin.getCustomConfig().getStringList(s).get(2)))
{
listeMap.add(plugin.getCustomConfig().getStringList(s).get(0));
}
}
return listeMap;
}
static public void AddMap(ItemStack map, Inventory inv, ArrayList<ItemStack> restant)
{
HashMap<Integer,ItemStack> reste = inv.addItem(map);
if(!reste.isEmpty())
{
restant.add(reste.get(0));
}
}
public static BufferedImage scaleImage(Image image, int width, int height)
{
BufferedImage newImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = newImage.createGraphics();
g.drawImage(image, 0, 0, width, height, null);
g.dispose();
return newImage;
}
}

View File

@ -47,10 +47,27 @@ import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.UUID;
import java.util.logging.Level;
import java.util.zip.GZIPOutputStream;
public class MetricsLite {
public class MetricsLite
{
/**
* Starts MetricsLite, unless disabled in config
*/
static public void startMetrics()
{
if(!PluginConfiguration.COLLECT_DATA.getBoolean()) return;
try
{
MetricsLite metrics = new MetricsLite(ImageOnMap.getPlugin());
metrics.start();
}
catch (IOException e)
{
PluginLogger.error("Failed to start MetricsLite", e);
}
}
/**
* The current revision number
@ -180,7 +197,7 @@ public class MetricsLite {
firstPost = false;
} catch (IOException e) {
if (debug) {
Bukkit.getLogger().log(Level.INFO, "[Metrics] " + e.getMessage());
PluginLogger.warning("[Metrics] ", e);
}
}
}
@ -200,14 +217,9 @@ public class MetricsLite {
try {
// Reload the metrics file
configuration.load(getConfigFile());
} catch (IOException ex) {
} catch (IOException | InvalidConfigurationException ex) {
if (debug) {
Bukkit.getLogger().log(Level.INFO, "[Metrics] " + ex.getMessage());
}
return true;
} catch (InvalidConfigurationException ex) {
if (debug) {
Bukkit.getLogger().log(Level.INFO, "[Metrics] " + ex.getMessage());
PluginLogger.info("[Metrics] " + ex.getMessage());
}
return true;
}
@ -263,7 +275,7 @@ public class MetricsLite {
*
* @return the File object for the config file
*/
public File getConfigFile() {
public final File getConfigFile() {
// I believe the easiest way to get the base folder (e.g craftbukkit set via -P) for plugins to use
// is to abuse the plugin object we already have
// plugin.getDataFolder() => base/plugins/PluginA/
@ -396,7 +408,7 @@ public class MetricsLite {
gzos = new GZIPOutputStream(baos);
gzos.write(input.getBytes("UTF-8"));
} catch (IOException e) {
e.printStackTrace();
PluginLogger.error("MetricsLite GZIP error : ", e);
} finally {
if (gzos != null) try {
gzos.close();
@ -489,7 +501,8 @@ public class MetricsLite {
default:
if (chr < ' ') {
String t = "000" + Integer.toHexString(chr);
builder.append("\\u" + t.substring(t.length() - 4));
builder.append("\\u");
builder.append(t.substring(t.length() - 4));
} else {
builder.append(chr);
}

View File

@ -1,43 +0,0 @@
package fr.moribus.imageonmap;
import java.util.HashMap;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerLoginEvent;
import org.bukkit.event.player.PlayerQuitEvent;
public class PlayerListener implements Listener
{
ImageOnMap plugin;
HashMap<String, SendEntireMapTask> hmap;
int send;
PlayerListener(ImageOnMap p)
{
plugin = p;
hmap = new HashMap<String, SendEntireMapTask>();
send = plugin.getConfig().getInt("send-entire-maps");
}
@EventHandler
public void OnPlayerLogin(PlayerLoginEvent event)
{
if(send > 0)
{
Player joueur = event.getPlayer();
hmap.put(joueur.getName(), new SendEntireMapTask(plugin, event.getPlayer()));
hmap.get(joueur.getName()).runTaskTimer(plugin, 40, 20);
}
}
@EventHandler
public void OnPlayerLeft(PlayerQuitEvent event)
{
if(send > 0)
{
Player joueur = event.getPlayer();
hmap.get(joueur.getName()).cancel();
}
}
}

View File

@ -0,0 +1,125 @@
/*
* Copyright (C) 2013 Moribus
* Copyright (C) 2015 ProkopyL <prokopylmc@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package fr.moribus.imageonmap;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.plugin.Plugin;
public enum PluginConfiguration
{
//Configuration field Names, with default values
COLLECT_DATA("collect-data", true),
MAP_GLOBAL_LIMIT("map-global-limit", 0, "Limit-map-by-server"),
MAP_PLAYER_LIMIT("map-player-limit", 0, "Limit-map-by-player");
private final String fieldName;
private final Object defaultValue;
private final String[] deprecatedNames;
private PluginConfiguration(String fieldName, Object defaultValue, String ... deprecatedNames)
{
this.fieldName = fieldName;
this.defaultValue = defaultValue;
this.deprecatedNames = deprecatedNames;
}
public Object get()
{
return getConfig().get(fieldName, defaultValue);
}
public Object getDefaultValue()
{
return defaultValue;
}
public boolean isDefined()
{
return getConfig().contains(fieldName);
}
@Override
public String toString()
{
return get().toString();
}
public String getString()
{
return getConfig().getString(fieldName, (String)defaultValue);
}
public int getInteger()
{
return getConfig().getInt(fieldName, (Integer)defaultValue);
}
public boolean getBoolean()
{
return getConfig().getBoolean(fieldName, (Boolean)defaultValue);
}
private boolean init()
{
boolean affected = false;
if(!isDefined())
{
getConfig().set(fieldName, defaultValue);
affected = true;
}
for(String deprecatedName : deprecatedNames)
{
if(getConfig().contains(deprecatedName))
{
getConfig().set(fieldName, getConfig().get(deprecatedName));
getConfig().set(deprecatedName, null);
affected = true;
}
}
return affected;
}
/* ===== Static API ===== */
static private Plugin plugin;
static public FileConfiguration getConfig()
{
return plugin.getConfig();
}
static public void init(Plugin plugin)
{
PluginConfiguration.plugin = plugin;
loadDefaultValues();
}
static private void loadDefaultValues()
{
boolean affected = false;
for(PluginConfiguration configField : PluginConfiguration.values())
{
if(configField.init()) affected = true;
}
if(affected) plugin.saveConfig();
}
}

View File

@ -1,10 +1,11 @@
/*
* Copyright (C) 2014 ProkopyL
* Copyright (C) 2013 Moribus
* Copyright (C) 2015 ProkopyL <prokopylmc@gmail.com>
*
* 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 2
* of the License, or (at your option) any later version.
* 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
@ -12,39 +13,117 @@
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package fr.moribus.imageonmap;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import org.bukkit.plugin.Plugin;
abstract public class PluginLogger
{
static private Plugin plugin;
static private Thread mainThread;
static private HashMap<Thread, PluginThreadLogger> loggers;
static public void init(Plugin plugin)
{
PluginLogger.plugin = plugin;
mainThread = Thread.currentThread();
loggers = new HashMap<>();
}
static public void exit()
{
plugin = null;
mainThread = null;
loggers = null;
}
static public void log(Level level, String message, Throwable ex)
{
getLogger().log(level, message, ex);
}
static public void log(Level level, String message, Object...args)
{
getLogger().log(level, message, args);
}
static public void log(Level level, String message, Throwable ex, Object... args)
{
log(level, message + " : " + ex.getMessage(), args);
}
static public void info(String message, Object...args)
{
log(Level.INFO, message, args);
}
static public void warning(String message, Object... args)
{
log(Level.WARNING, message, args);
}
static public void warning(String message, Throwable ex)
{
warning(message + " : " + ex.getMessage());
}
static public void error(String message)
{
log(Level.SEVERE, message);
}
static public void error(String message, Throwable ex)
{
log(Level.SEVERE, message, ex);
}
static public void error(String message, Throwable ex, Object... args)
{
log(Level.SEVERE, message, ex, args);
}
static private Logger getLogger()
{
return ImageOnMap.getPlugin().getLogger();
Thread currentThread = Thread.currentThread();
if(currentThread.equals(mainThread)) return plugin.getLogger();
return getLogger(currentThread);
}
static public void LogInfo(String message)
static private Logger getLogger(Thread thread)
{
getLogger().log(Level.INFO, message);
PluginThreadLogger logger = loggers.get(thread);
if(logger == null)
{
logger = new PluginThreadLogger(thread);
loggers.put(thread, logger);
}
return logger;
}
static public void LogWarning(String message)
static private class PluginThreadLogger extends Logger
{
getLogger().log(Level.WARNING, message);
}
private final String loggerName;
public PluginThreadLogger(Thread thread)
{
super(plugin.getClass().getCanonicalName(), null);
setParent(plugin.getLogger());
setLevel(Level.ALL);
loggerName = "[" + thread.getName() + "] ";
}
static public void LogWarning(String message, Throwable ex)
{
getLogger().log(Level.WARNING, message, ex);
}
static public void LogError(String message, Throwable ex)
{
getLogger().log(Level.SEVERE, message, ex);
@Override
public void log(LogRecord logRecord)
{
logRecord.setMessage(loggerName + logRecord.getMessage());
super.log(logRecord);
}
}
}

View File

@ -1,107 +0,0 @@
package fr.moribus.imageonmap;
import fr.moribus.imageonmap.image.Renderer;
import fr.moribus.imageonmap.image.ImageRendererThread;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Set;
import javax.imageio.ImageIO;
import org.bukkit.Bukkit;
import org.bukkit.map.MapPalette;
import org.bukkit.map.MapView;
public class SavedMap
{
ImageOnMap plugin;
String nomImg, nomJoueur, nomMonde;
short idMap;
BufferedImage image;
boolean loaded = false;
SavedMap(ImageOnMap plug, String nomJ, short id, BufferedImage img, String nomM)
{
plugin = plug;
nomJoueur = nomJ;
idMap = id;
image = img;
nomImg = "map" + id;
nomMonde = nomM;
loaded = true;
}
SavedMap(ImageOnMap plug, short id) throws IOException
{
idMap = id;
plugin = plug;
Set<String> cle = plugin.getCustomConfig().getKeys(false);
for (String s : cle)
{
if (plugin.getCustomConfig().getStringList(s).size() >= 3 && Short.valueOf(plugin.getCustomConfig().getStringList(s).get(0)) == id)
{
//System.out.println(tmp);
//MapView carte = Bukkit.getMap(Short.parseShort(plugin.getConfig().getStringList(s).get(0)));
nomImg = plugin.getCustomConfig().getStringList(s).get(1);
nomJoueur = plugin.getCustomConfig().getStringList(s).get(2);
image = ImageIO.read(new File("./plugins/ImageOnMap/Image/" + nomImg + ".png"));
loaded = true;
break;
}
}
if (!loaded)
{
//plugin.getLogger().info("No map was loaded");
}
}
public void saveMap() throws IOException
{
if (!loaded)
{
PluginLogger.LogWarning("Tried to save a map that wasn't loaded. ID:" + idMap);
return;
}
plugin.getLogger().info("Saving map " + idMap);
// Enregistrement de l'image sur le disque dur
File outputfile = new File("./plugins/ImageOnMap/Image/" + nomImg + ".png");
ImageIO.write(MapPalette.resizeImage(image), "png", outputfile);
// Enregistrement de la map dans la config
ArrayList<String> liste = new ArrayList<String>();
liste.add(String.valueOf(idMap));
liste.add(nomImg);
liste.add(nomJoueur);
liste.add(nomMonde);
plugin.getCustomConfig().set(nomImg, liste);
plugin.saveCustomConfig();
if (!plugin.mapChargee.contains(idMap))
{
plugin.mapChargee.add(idMap);
}
}
@SuppressWarnings("deprecation")
public Boolean LoadMap()
{
MapView carte = Bukkit.getMap(idMap);
if (carte != null && loaded)
{
ImageRendererThread.SupprRendu(carte);
carte.addRenderer(new Renderer(image));
if (!plugin.mapChargee.contains(idMap))
{
plugin.mapChargee.add(idMap);
}
return true;
}
else
{
return false;
}
}
}

View File

@ -1,155 +0,0 @@
package fr.moribus.imageonmap;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Set;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
public class SavedPoster
{
ImageOnMap plugin;
short[] ids;
private FileConfiguration customConfig = null;
private File customConfigFile = null;
String posterName, playerName;
public SavedPoster(ImageOnMap plugin, short[] ids, String playerName, String posterName)
{
this.ids = ids;
this.plugin = plugin;
this.playerName = playerName;
this.posterName = posterName;
}
public SavedPoster(ImageOnMap plugin, short[] ids, String playerName)
{
this(plugin, ids, playerName, null);
}
public SavedPoster(ImageOnMap plugin)
{
this(plugin, null, null, null);
}
public SavedPoster(ImageOnMap p, String id)
{
plugin = p;
posterName = id;
ArrayList<String> liste = (ArrayList<String>) getCustomConfig().getStringList(posterName);
if (!liste.isEmpty() || liste != null)
{
playerName = liste.get(0);
ids = new short[liste.size() - 1];
for (int i = 0; i < ids.length; i++)
{
ids[i] = Short.parseShort(liste.get(i + 1));
}
}
}
public String Save() throws IOException
{
int increment = increment();
ArrayList<String> liste = new ArrayList<String>();
liste.add(playerName);
for (int i = 0; i < ids.length; i++)
{
liste.add(String.valueOf(ids[i]));
}
posterName = "poster" + increment;
getCustomConfig().set(posterName, liste);
saveCustomConfig();
return posterName;
}
public void Remove() throws IOException
{
if (posterName == null || posterName.isEmpty()) return;
for (int i = 0; i < ids.length; i++)
{
ImgUtility.RemoveMap(plugin, ids[i]);
}
getCustomConfig().set(posterName, null);
saveCustomConfig();
}
int increment()
{
int i;
if (getCustomConfig().get("IdCount") != null)
{
i = getCustomConfig().getInt("IdCount");
}
else
{
i = 0;
}
i++;
this.getCustomConfig().set("IdCount", i);
return i;
}
String getId()
{
return posterName;
}
public ArrayList<String> getListMapByPlayer(ImageOnMap plugin, String pseudo)
{
ArrayList<String> listeMap = new ArrayList<String>();
Set<String> cle = getCustomConfig().getKeys(false);
for (String s : cle)
{
if (getCustomConfig().getStringList(s).size() > 1 && pseudo.equalsIgnoreCase(getCustomConfig().getStringList(s).get(0)))
{
listeMap.add(s);
}
}
return listeMap;
}
/* Méthodes pour charger / recharger / sauvegarder
* le fichier conf des Posters (poster.yml).
* Je les ai juste copié depuis un tuto du wiki Bukkit...
*/
@SuppressWarnings("deprecation")
public void reloadCustomConfig()
{
if (customConfigFile == null)
{
customConfigFile = new File(plugin.getDataFolder(), "poster.yml");
}
customConfig = YamlConfiguration.loadConfiguration(customConfigFile);
// Look for defaults in the jar
InputStream defConfigStream = plugin.getResource("poster.yml");
if (defConfigStream != null)
{
YamlConfiguration defConfig = YamlConfiguration.loadConfiguration(defConfigStream);
customConfig.setDefaults(defConfig);
}
}
public FileConfiguration getCustomConfig()
{
if (customConfig == null)
{
reloadCustomConfig();
}
return customConfig;
}
public void saveCustomConfig() throws IOException
{
if (customConfig == null || customConfigFile == null)
{
return;
}
getCustomConfig().save(customConfigFile);
}
}

View File

@ -1,81 +0,0 @@
package fr.moribus.imageonmap;
import java.util.ArrayList;
import java.util.List;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.map.MapView;
import org.bukkit.scheduler.BukkitRunnable;
public class SendEntireMapTask extends BukkitRunnable
{
ImageOnMap plugin;
Player joueur;
ArrayList<Short> listeMap;
List<Short> partListe;
boolean allsent;
int index, toIndex, nbSend;
SendEntireMapTask(ImageOnMap p, Player j)
{
plugin = p;
joueur = j;
allsent = false;
index = 0;
nbSend = plugin.getConfig().getInt("send-entire-maps");
toIndex = nbSend;
}
@SuppressWarnings("deprecation")
@Override
public void run()
{
if(nbSend > 0)
{
if(listeMap == null)
{
//plugin.getLogger().info("test de nullité");
listeMap = new ArrayList<Short>();
listeMap.addAll(plugin.mapChargee);
}
else if(listeMap.equals(plugin.mapChargee) != true)
{
//plugin.getLogger().info("taille des maps : " + listeMap.size()+ " ; "+ plugin.mapChargee.size());
//plugin.getLogger().info("test d'égalité");
listeMap.clear();
listeMap.addAll(plugin.mapChargee);
allsent = false;
}
if(!allsent)
{
//plugin.getLogger().info("test 1");
if(toIndex > listeMap.size())
{
//plugin.getLogger().info("test 3");
partListe = listeMap.subList(index, listeMap.size());
index = listeMap.size();
toIndex = listeMap.size() + nbSend;
allsent = true;
}
else
{
//plugin.getLogger().info("taille des index : " + index+ " ; "+ toIndex+ ". taille de la map : "
// + listeMap.size());
//plugin.getLogger().info("test 2");
partListe = listeMap.subList(index, toIndex);
index = toIndex;
toIndex += nbSend;
}
for(int i= 0; i< partListe.size(); i++)
{
//plugin.getLogger().info("test 4");
MapView map = Bukkit.getMap(partListe.get(i));
joueur.sendMap(map);
}
}
}
}
}

View File

@ -1,57 +0,0 @@
package fr.moribus.imageonmap;
import java.util.ArrayList;
import org.bukkit.Chunk;
import org.bukkit.Material;
import org.bukkit.entity.Entity;
import org.bukkit.entity.ItemFrame;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.world.ChunkLoadEvent;
import org.bukkit.inventory.ItemStack;
import fr.moribus.imageonmap.map.SingleMap;
public class SendMapOnFrameEvent implements Listener
{
ImageOnMap plugin;
Chunk chunk;
Entity[] entites;
ItemFrame frame;
SendMapOnFrameEvent(ImageOnMap plug)
{
plugin = plug;
}
@EventHandler
public void onChunkLoad(ChunkLoadEvent event)
{
chunk = event.getChunk();
entites = chunk.getEntities().clone();
for (Entity entite : entites)
{
if (entite instanceof ItemFrame)
{
ArrayList<Short> ListeId = plugin.mapChargee;
frame = (ItemFrame) entite;
ItemStack stack = frame.getItem();
if (stack.getType() == Material.MAP && !ListeId.contains(stack.getDurability()))
{
try
{
new SingleMap(stack.getDurability()).load();
}
catch (Exception e)
{
PluginLogger.LogWarning("Could not send frame map", e);
}
}
}
}
}
}

View File

@ -1,59 +0,0 @@
package fr.moribus.imageonmap;
import java.util.ArrayList;
import java.util.Set;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerItemHeldEvent;
import org.bukkit.inventory.ItemStack;
import fr.moribus.imageonmap.map.SingleMap;
public class SendMapOnInvEvent implements Listener
{
ImageOnMap plugin;
SendMapOnInvEvent(ImageOnMap p)
{
plugin = p;
}
@EventHandler
public void onPlayerInv(PlayerItemHeldEvent event)
{
Player joueur = event.getPlayer();
int slot = event.getNewSlot();
ItemStack stack = joueur.getInventory().getItem(slot);
if (stack != null && stack.getType() == Material.MAP)
{
ArrayList<Short> listeId = plugin.mapChargee;
Set<String> cle = plugin.getCustomConfig().getKeys(false);
for (String s : cle)
{
if (!listeId.contains(stack.getDurability()))
{
if (plugin.getCustomConfig().getStringList(s).get(0).equals(String.valueOf(stack.getDurability())))
{
try
{
new SingleMap(stack.getDurability()).load();
}
catch (Exception e)
{
PluginLogger.LogWarning("Could not send inventory map.", e);
}
}
}
}
}
}
}

View File

@ -1,29 +0,0 @@
package fr.moribus.imageonmap;
import java.awt.image.BufferedImage;
import org.bukkit.entity.Player;
import fr.moribus.imageonmap.map.ImageMap;
import java.io.IOException;
import java.net.URL;
public class TacheTraitementMajMap extends TacheTraitementMap
{
private ImageMap m;
public TacheTraitementMajMap(ImageMap m, URL url, Player joueur)
{
super(url);
setJoueur(joueur);
this.m = m;
}
@Override
public void traiterMap(BufferedImage img) throws IOException
{
m.setImage(img);
m.save();
m.send(getJoueur());
}
}

View File

@ -1,194 +0,0 @@
package fr.moribus.imageonmap;
import fr.moribus.imageonmap.image.DownloadImageThread;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
import org.bukkit.scheduler.BukkitRunnable;
public abstract class TacheTraitementMap extends BukkitRunnable
{
private Player joueur;
private DownloadImageThread renduImg;
private PlayerInventory inv;
private ItemStack map;
private ImageOnMap plugin;
private boolean resized, renamed;
private ExecutorService dlImg;
private Future<BufferedImage> futurDlImg;
private int compteurExec;
protected TacheTraitementMap(URL url)
{
renduImg = new DownloadImageThread(url);
dlImg = Executors.newSingleThreadExecutor();
futurDlImg = dlImg.submit(renduImg);
plugin = (ImageOnMap) Bukkit.getPluginManager().getPlugin("ImageOnMap");
}
public TacheTraitementMap(Player j, URL url, boolean rs, boolean rn)
{
this(url);
joueur = j;
inv = joueur.getInventory();
resized = rs;
renamed = rn;
}
@Override
public void run()
{
compteurExec = 0;
if (!futurDlImg.isDone())
{
compteurExec++;
if (compteurExec > 20)
{
joueur.sendMessage("TIMEOUT: the render took too many time");
futurDlImg.cancel(true);
cancel();
}
}
else
{
if (!futurDlImg.isCancelled())
{
cancel();
int nbImage = 1;
if (plugin.getConfig().getInt("Limit-map-by-server") != 0 && nbImage + ImgUtility.getNombreDeMaps(plugin) > plugin.getConfig().getInt("Limit-map-by-server"))
{
joueur.sendMessage("ERROR: cannot render " + nbImage + " picture(s): the limit of maps per server would be exceeded.");
return;
}
if (!joueur.hasPermission("imageonmap.nolimit"))
{
if (plugin.getConfig().getInt("Limit-map-by-player") != 0 && nbImage + ImgUtility.getNombreDeMapsParJoueur(plugin, joueur.getName()) > plugin.getConfig().getInt("Limit-map-by-player"))
{
joueur.sendMessage(ChatColor.RED + "ERROR: cannot render " + nbImage + " picture(s): the limit of maps allowed for you (per player) would be exceeded.");
return;
}
}
try
{
BufferedImage dlimg = futurDlImg.get();
traiterMap(dlimg);
joueur.sendMessage("Image successfuly downloaded !");
}
catch (InterruptedException ex)
{
joueur.sendMessage(ChatColor.RED + "Download task has been interrupted unexpectedly. Check server console for details.");
PluginLogger.LogError("Download task has been interrupted", ex);
}
catch (ExecutionException ex)
{
joueur.sendMessage(ChatColor.RED + "Download failed : " + ex.getMessage());
joueur.sendMessage(ChatColor.RED + "Please check your URL");
}
catch(IOException ex)
{
joueur.sendMessage(ChatColor.RED + "Failed to process the image. Check server console for details.");
PluginLogger.LogError("Image processing failed", ex);
}
}
else
{
joueur.sendMessage(ChatColor.RED + "An error occured. See the console for details");
}
}
}
public abstract void traiterMap(BufferedImage img) throws IOException;
protected Player getJoueur()
{
return joueur;
}
protected void setJoueur(Player joueur)
{
this.joueur = joueur;
}
protected DownloadImageThread getRenduImg()
{
return renduImg;
}
protected void setRenduImg(DownloadImageThread renduImg)
{
this.renduImg = renduImg;
}
protected PlayerInventory getInv()
{
return inv;
}
protected void setInv(PlayerInventory inv)
{
this.inv = inv;
}
protected ItemStack getMap()
{
return map;
}
protected void setMap(ItemStack map)
{
this.map = map;
}
protected ImageOnMap getPlugin()
{
return plugin;
}
protected void setPlugin(ImageOnMap plugin)
{
this.plugin = plugin;
}
protected boolean isResized()
{
return resized;
}
protected void setResized(boolean resized)
{
this.resized = resized;
}
protected boolean isRenamed()
{
return renamed;
}
protected void setRenamed(boolean renamed)
{
this.renamed = renamed;
}
protected int getCompteurExec()
{
return compteurExec;
}
protected void setCompteurExec(int compteurExec)
{
this.compteurExec = compteurExec;
}
}

View File

@ -1,32 +0,0 @@
package fr.moribus.imageonmap;
import java.awt.image.BufferedImage;
import org.bukkit.entity.Player;
import fr.moribus.imageonmap.map.ImageMap;
import fr.moribus.imageonmap.map.ImageMap.Type;
import java.io.IOException;
import java.net.URL;
public class TacheTraitementNouvelleMap extends TacheTraitementMap
{
private final Type type;
public TacheTraitementNouvelleMap(Player player, URL url, Type type, boolean rs, boolean rn)
{
super(player, url, rs, rn);
this.type = type;
}
@Override
public void traiterMap(BufferedImage img) throws IOException
{
ImageMap m = ImageMap.Type.createNewMap(type, img, getJoueur());
m.load();
m.give(getJoueur().getInventory());
m.save();
}
}

View File

@ -0,0 +1,268 @@
/*
* Copyright (C) 2013 Moribus
* Copyright (C) 2015 ProkopyL <prokopylmc@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package fr.moribus.imageonmap.commands;
import fr.moribus.imageonmap.commands.CommandException.Reason;
import fr.moribus.imageonmap.map.ImageMap;
import fr.moribus.imageonmap.map.MapManager;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
abstract public class Command
{
static private final String IMAGEONMAP_GLOBAL_PERMISSION = "imageonmap.userender";
protected final Commands commandGroup;
protected final String commandName;
protected final String usageParameters;
protected final String commandDescription;
protected final String[] aliases;
protected CommandSender sender;
protected String[] args;
abstract protected void run() throws CommandException;
public Command(Commands commandGroup)
{
this.commandGroup = commandGroup;
CommandInfo commandInfo = this.getClass().getAnnotation(CommandInfo.class);
if(commandInfo == null)
throw new IllegalArgumentException("Command has no CommandInfo annotation");
commandName = commandInfo.name().toLowerCase();
usageParameters = commandInfo.usageParameters();
commandDescription = commandGroup.getDescription(commandName);
aliases = commandInfo.aliases();
}
public boolean canExecute(CommandSender sender)
{
return sender.hasPermission("imageonmap." + commandGroup.getUsualName())
|| sender.hasPermission(IMAGEONMAP_GLOBAL_PERMISSION);
}
protected List<String> complete() throws CommandException
{
return null;
}
public void execute(CommandSender sender, String[] args)
{
this.sender = sender; this.args = args;
try
{
if(!canExecute(sender))
throw new CommandException(this, Reason.SENDER_NOT_AUTHORIZED);
run();
}
catch(CommandException ex)
{
warning(ex.getReasonString());
}
this.sender = null; this.args = null;
}
public List<String> tabComplete(CommandSender sender, String[] args)
{
List<String> result = null;
this.sender = sender; this.args = args;
try
{
if(canExecute(sender))
result = complete();
}
catch(CommandException ex){}
this.sender = null; this.args = null;
if(result == null) result = new ArrayList<String>();
return result;
}
public String getUsageString()
{
return "/" + commandGroup.getUsualName() + " " + commandName + " " + usageParameters;
}
public String getName()
{
return commandName;
}
public Commands getCommandGroup()
{
return commandGroup;
}
public String[] getAliases()
{
return aliases;
}
public boolean matches(String name)
{
if(commandName.equals(name.toLowerCase())) return true;
for(String alias : aliases)
{
if(alias.equals(name)) return true;
}
return false;
}
///////////// Common methods for commands /////////////
protected void throwInvalidArgument(String reason) throws CommandException
{
throw new CommandException(this, Reason.INVALID_PARAMETERS, reason);
}
protected Player playerSender() throws CommandException
{
if(!(sender instanceof Player))
throw new CommandException(this, Reason.COMMANDSENDER_EXPECTED_PLAYER);
return (Player)sender;
}
protected ImageMap getMapFromArgs() throws CommandException
{
return getMapFromArgs(playerSender(), 0, true);
}
protected ImageMap getMapFromArgs(Player player, int index, boolean expand) throws CommandException
{
if(args.length <= index) throwInvalidArgument("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("This map does not exist.");
return map;
}
///////////// Methods for command execution /////////////
static protected void info(CommandSender sender, String message)
{
sender.sendMessage("§7" + message);
}
protected void info(String message)
{
info(sender, message);
}
static protected void warning(CommandSender sender, String message)
{
sender.sendMessage("§c" + message);
}
protected void warning(String message)
{
info(sender, message);
}
protected void error(String message) throws CommandException
{
throw new CommandException(this, Reason.COMMAND_ERROR, message);
}
protected void tellRaw(String rawMessage) throws CommandException
{
Player player = playerSender();
Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(),
"tellraw " + player.getName() + " " + rawMessage);
}
///////////// Methods for autocompletion /////////////
protected List<String> getMatchingSubset(String prefix, String... list)
{
return getMatchingSubset(Arrays.asList(list), prefix);
}
protected List<String> getMatchingSubset(Iterable<? extends String> list, String prefix)
{
List<String> matches = new ArrayList<String>();
for(String item : list)
{
if(item.startsWith(prefix)) matches.add(item);
}
return matches;
}
protected List<String> getMatchingPlayerNames(String prefix)
{
return getMatchingPlayerNames(Bukkit.getOnlinePlayers(), prefix);
}
protected List<String> getMatchingPlayerNames(Iterable<? extends Player> players, String prefix)
{
List<String> matches = new ArrayList<String>();
for(Player player : players)
{
if(player.getName().startsWith(prefix)) matches.add(player.getName());
}
return matches;
}
protected List<String> getMatchingMapNames(Player player, String prefix)
{
return getMatchingToolNames(MapManager.getMapList(player.getUniqueId()), prefix);
}
protected List<String> getMatchingToolNames(Iterable<? extends ImageMap> maps, String prefix)
{
List<String> matches = new ArrayList<String>();
for(ImageMap map : maps)
{
if(map.getId().startsWith(prefix)) matches.add(map.getId());
}
return matches;
}
}

View File

@ -0,0 +1,72 @@
/*
* Copyright (C) 2013 Moribus
* Copyright (C) 2015 ProkopyL <prokopylmc@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package fr.moribus.imageonmap.commands;
import fr.moribus.imageonmap.PluginLogger;
public class CommandException extends Exception
{
public enum Reason
{
COMMANDSENDER_EXPECTED_PLAYER,
INVALID_PARAMETERS,
COMMAND_ERROR,
SENDER_NOT_AUTHORIZED
}
private final Reason reason;
private final Command command;
private final String extra;
public CommandException(Command command, Reason reason, String extra)
{
this.command = command;
this.reason = reason;
this.extra = extra;
}
public CommandException(Command command, Reason reason)
{
this(command, reason, "");
}
public Reason getReason() { return reason; }
public String getReasonString()
{
switch(reason)
{
case COMMANDSENDER_EXPECTED_PLAYER:
return "You must be a player to use this command.";
case INVALID_PARAMETERS:
return "Invalid arguments : " + extra +"\n§r" +
"Usage : " + command.getUsageString() + "\n" +
"For more information, use /" +
command.getCommandGroup().getUsualName() + " help " +
command.getName();
case COMMAND_ERROR:
return extra.isEmpty() ? "An unknown error suddenly happened." : extra;
case SENDER_NOT_AUTHORIZED:
return "You do not have the permission to use this command.";
default:
PluginLogger.warning("Unknown CommandException caught", this);
return "An unknown error suddenly happened.";
}
}
}

View File

@ -0,0 +1,30 @@
/*
* Copyright (C) 2013 Moribus
* Copyright (C) 2015 ProkopyL <prokopylmc@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package fr.moribus.imageonmap.commands;
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface CommandInfo
{
String name();
String usageParameters() default "";
String[] aliases() default {};
}

View File

@ -0,0 +1,329 @@
/*
* Copyright (C) 2013 Moribus
* Copyright (C) 2015 ProkopyL <prokopylmc@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package fr.moribus.imageonmap.commands;
import fr.moribus.imageonmap.commands.maptool.MigrateCommand;
import fr.moribus.imageonmap.PluginLogger;
import fr.moribus.imageonmap.commands.maptool.*;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Scanner;
import org.apache.commons.lang.StringUtils;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabCompleter;
import org.bukkit.plugin.java.JavaPlugin;
public enum Commands implements TabCompleter, CommandExecutor
{
MAPTOOL(new String[]{"maptool"},
NewCommand.class,
ListCommand.class,
GetCommand.class,
DeleteConfirmCommand.class,
DeleteNoConfirmCommand.class,
GetRemainingCommand.class,
MigrateCommand.class
),
TOMAP(MAPTOOL, NewCommand.class, "tomap");
static private final Commands[] commandGroups = Commands.class.getEnumConstants();
private final Commands shortcutCommandGroup;
private final String[] names;
private final Class<? extends Command>[] commandsClasses;
private final ArrayList<Command> commands = new ArrayList<>();
private final HashMap<String, String> commandsDescriptions = new HashMap<>();
private String description = "";
private Commands(Commands shortcutCommandGroup, Class<? extends Command> commandClass, String ... names)
{
this.names = names;
this.commandsClasses = new Class[]{commandClass};
this.shortcutCommandGroup = shortcutCommandGroup;
initCommands();
}
private Commands(String[] names, Class<? extends Command> ... commandsClasses)
{
this.names = names;
this.commandsClasses = commandsClasses;
this.shortcutCommandGroup = null;
initDescriptions();
initCommands();
}
private void initDescriptions()
{
String fileName = "help/" + getUsualName() + ".txt";
InputStream stream = getClass().getClassLoader().getResourceAsStream(fileName);
if(stream == null)
{
PluginLogger.warning("Could not load description file for the " + getUsualName() + " command");
return;
}
Scanner scanner = new Scanner(stream);
StringBuilder builder = new StringBuilder();
//Getting the group's description
//And then each command's description
int colonIndex, firstSpaceIndex;
boolean isGroupDescription = true;
while (scanner.hasNextLine())
{
String line = scanner.nextLine();
colonIndex = line.indexOf(':');
if(isGroupDescription)
{
firstSpaceIndex = line.indexOf(' ');
if(colonIndex > 0 && firstSpaceIndex > colonIndex)
isGroupDescription = false;
}
if(isGroupDescription)
{
builder.append(line).append('\n');
}
else
{
commandsDescriptions.put(line.substring(0, colonIndex).trim(),
line.substring(colonIndex + 1).trim());
}
}
scanner.close();
description = builder.toString().trim();
}
private void initCommands()
{
for (Class<? extends Command> commandClass : commandsClasses)
{
addCommand(commandClass);
}
if(!isShortcutCommand()) addCommand(HelpCommand.class);
}
private void addCommand(Class<? extends Command> commandClass)
{
Constructor<? extends Command> constructor;
try
{
constructor = commandClass.getConstructor(Commands.class);
commands.add(constructor.newInstance(isShortcutCommand() ? shortcutCommandGroup : this));
}
catch (Exception ex)
{
PluginLogger.warning("Exception while initializing command", ex);
}
}
public boolean executeMatchingCommand(CommandSender sender, String[] args)
{
if(isShortcutCommand())
{
commands.get(0).execute(sender, args);
return true;
}
if(args.length <= 0)
{
sender.sendMessage(getUsage()); return false;
}
String commandName = args[0];
String[] commandArgs = getCommandArgsFromGroupArgs(args);
return executeMatchingCommand(sender, commandName, commandArgs);
}
private boolean executeMatchingCommand(CommandSender sender, String commandName, String[] args)
{
Command command = getMatchingCommand(commandName);
if(command != null)
{
command.execute(sender, args);
}
else
{
sender.sendMessage(getUsage());
}
return command != null;
}
static public void init(JavaPlugin plugin)
{
org.bukkit.command.PluginCommand bukkitCommand;
for(Commands commandGroup : commandGroups)
{
bukkitCommand = plugin.getCommand(commandGroup.getUsualName());
bukkitCommand.setAliases(commandGroup.getAliases());
bukkitCommand.setExecutor(commandGroup);
bukkitCommand.setTabCompleter(commandGroup);
}
}
@Override
public List<String> onTabComplete(CommandSender sender, org.bukkit.command.Command cmd, String label, String[] args)
{
return tabComplete(sender, args);
}
@Override
public boolean onCommand(CommandSender sender, org.bukkit.command.Command cmd, String label, String[] args)
{
return executeMatchingCommand(sender, args);
}
public List<String> tabComplete(CommandSender sender, String[] args)
{
if(isShortcutCommand()) return commands.get(0).tabComplete(sender, args);
if(args.length <= 1) return tabComplete(sender, args.length == 1 ? args[0] : null);
String commandName = args[0];
String[] commandArgs = getCommandArgsFromGroupArgs(args);
return tabCompleteMatching(sender, commandName, commandArgs);
}
public List<String> tabComplete(CommandSender sender, String commandName)
{
ArrayList<String> matchingCommands = new ArrayList<String>();
for(Command command : commands)
{
if(!command.canExecute(sender)) continue;
if(commandName == null || command.getName().startsWith(commandName.toLowerCase()))
{
matchingCommands.add(command.getName());
}
}
return matchingCommands;
}
private List<String> tabCompleteMatching(CommandSender sender, String commandName, String[] args)
{
Command command = getMatchingCommand(commandName);
if(command != null)
{
return command.tabComplete(sender, args);
}
else
{
return new ArrayList<String>();
}
}
static public String[] getCommandArgsFromGroupArgs(String[] args)
{
String[] commandArgs = new String[args.length - 1];
for(int i = 0; i < commandArgs.length; i++)
{
commandArgs[i] = args[i + 1];
}
return commandArgs;
}
public Command getMatchingCommand(String commandName)
{
for(Command command : commands)
{
if(command.matches(commandName))
{
return command;
}
}
return null;
}
static public boolean execute(CommandSender sender, String commandName, String[] args)
{
Commands commandGroup = getMatchingCommandGroup(commandName);
if(commandGroup == null) return false;
commandGroup.executeMatchingCommand(sender, args);
return true;
}
static public List<String> tabComplete(CommandSender sender, String commandName, String[] args)
{
Commands commandGroup = getMatchingCommandGroup(commandName);
if(commandGroup == null) return new ArrayList<String>();
return commandGroup.tabComplete(sender, args);
}
static private Commands getMatchingCommandGroup(String commandName)
{
Commands commandGroup = null;
for(Commands tCommandGroup : commandGroups)
{
if(tCommandGroup.matches(commandName))
{
commandGroup = tCommandGroup;
break;
}
}
return commandGroup;
}
public boolean matches(String name)
{
name = name.toLowerCase();
for(String commandName : names)
{
if(commandName.equals(name)) return true;
}
return false;
}
public String[] getCommandsNames()
{
String[] commandsNames = new String[commands.size()];
for(int i = 0; i < commands.size(); i++)
{
commandsNames[i] = commands.get(i).getName();
}
return commandsNames;
}
protected String getUsage()
{
if(isShortcutCommand()) return "§cUsage : " + commands.get(0).getUsageString();
return "§cUsage : /" + getUsualName() +
" <" + StringUtils.join(getCommandsNames(), "|") + ">";
}
public String getUsualName() { return names[0]; }
public String[] getNames() { return names.clone(); }
public List<String> getAliases() { return Arrays.asList(names).subList(1, names.length);}
public Command[] getCommands() { return commands.toArray(new Command[commands.size()]);}
public String getDescription() { return description; }
public String getDescription(String commandName) { return commandsDescriptions.get(commandName); }
public boolean isShortcutCommand() { return shortcutCommandGroup != null; }
public Commands getShortcutCommandGroup() { return shortcutCommandGroup; }
}

View File

@ -0,0 +1,140 @@
/*
* Copyright (C) 2013 Moribus
* Copyright (C) 2015 ProkopyL <prokopylmc@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package fr.moribus.imageonmap.commands;
import fr.moribus.imageonmap.PluginLogger;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
@CommandInfo(name = "help", usageParameters = "<command name>")
public class HelpCommand extends Command
{
public HelpCommand(Commands commandGroup) {
super(commandGroup);
}
@Override
protected void run() throws CommandException
{
if(args.length < 1)
groupHelp();
else
commandHelp();
}
private void groupHelp() throws CommandException
{
sender.sendMessage(commandGroup.getDescription());
String tCommandName;
String tDescription;
for(Command tCommand: commandGroup.getCommands())
{
if(!tCommand.canExecute(sender)) continue;
tCommandName = tCommand.getName();
tDescription = commandGroup.getDescription(tCommandName);
tCommandName = commandGroup.getUsualName() + " " + tCommandName;
if(tDescription == null)
sender.sendMessage("§6/" + tCommandName + "§r");
else
sender.sendMessage("§6/" + tCommandName + " : §r" + tDescription);
}
}
private void commandHelp() throws CommandException
{
Command command = commandGroup.getMatchingCommand(args[0]);
if(command == null)
{
error("The specified command does not exist.");
return;
}
if(!command.canExecute(sender))
warning("You do not have the permission to use this command.");
String message = "§l§6 ||== ImageOnMap help ==||\n" +
"§l§6 |Usage : §r" + command.getUsageString();
try
{
String help = getHelpText(command);
if(help.isEmpty())
{
sender.sendMessage(message);
warning("There is no help message for this command.");
}
else
{
sender.sendMessage(message + "\n" + help);
}
}
catch(IOException ex)
{
sender.sendMessage(message);
warning("Could not read help for this command.");
PluginLogger.warning("Could not read help for the command : " + command.getName(), ex);
}
}
private String getHelpText(Command command) throws IOException
{
String fileName = "help/"+ commandGroup.getUsualName() +
"/" + command.getName() + ".txt";
StringBuilder result = new StringBuilder("");
InputStream stream = getClass().getClassLoader().getResourceAsStream(fileName);
if(stream == null) return "";
Scanner scanner = new Scanner(stream);
while (scanner.hasNextLine())
{
String line = scanner.nextLine();
result.append("§l§9 |§r").append(line).append("\n");
}
scanner.close();
return result.toString().trim();
}
@Override
protected List<String> complete() throws CommandException
{
if(args.length != 1) return null;
ArrayList<String> matches = new ArrayList<String>();
for(Command command : commandGroup.getCommands())
{
if(command.getName().startsWith(args[0]))
matches.add(command.getName());
}
return matches;
}
}

View File

@ -1,87 +0,0 @@
package fr.moribus.imageonmap.commands;
import fr.moribus.imageonmap.ImageOnMap;
import fr.moribus.imageonmap.ImgUtility;
import fr.moribus.imageonmap.TacheTraitementMap;
import fr.moribus.imageonmap.TacheTraitementNouvelleMap;
import fr.moribus.imageonmap.map.ImageMap;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.net.MalformedURLException;
import java.net.URL;
public class ImageRenduCommande implements CommandExecutor
{
Player joueur;
boolean renderName, imgSvg;
ImageOnMap plugin;
boolean resize, rename;
public ImageRenduCommande(ImageOnMap p)
{
plugin = p;
}
@Override
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args)
{
// On vérifie si celui qui exécute la commande est bien un joueur
if (!ImgUtility.VerifierIdentite(sender))
{
return false;
}
joueur = (Player) sender;
resize = false;
rename = true;
if (!joueur.hasPermission("imageonmap.userender"))
{
joueur.sendMessage("You are not allowed to use this command ( " + cmd.getName() + " )!");
return false;
}
if (args.length < 1)
{
joueur.sendMessage(ChatColor.RED + "You must enter image url.");
return false;
}
URL url;
try
{
url = new URL(args[0]);
}
catch (MalformedURLException ex)
{
joueur.sendMessage("§cInvalid URL.");
return false;
}
ImageMap.Type type = ImageMap.Type.POSTER;
if (args.length >= 2)
{
if(args[1].equals("resize"))
{
type = ImageMap.Type.SINGLE;
}
else
{
joueur.sendMessage("Invalid argument.");
return true;
}
}
TacheTraitementMap tache = new TacheTraitementNouvelleMap(joueur, url, type, resize, rename);
tache.runTaskTimer(plugin, 0, 5);
return true;
}
}

View File

@ -1,265 +0,0 @@
package fr.moribus.imageonmap.commands;
import fr.moribus.imageonmap.ImageOnMap;
import fr.moribus.imageonmap.ImgUtility;
import fr.moribus.imageonmap.PluginLogger;
import fr.moribus.imageonmap.SavedPoster;
import fr.moribus.imageonmap.TacheTraitementMajMap;
import java.util.ArrayList;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.map.MapView;
import fr.moribus.imageonmap.map.ImageMap;
import fr.moribus.imageonmap.map.PosterMap;
import fr.moribus.imageonmap.map.SingleMap;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
public class MapToolCommand implements CommandExecutor
{
short id;
ImageOnMap plugin;
MapView map;
Player joueur;
Inventory inv;
public MapToolCommand(ImageOnMap p)
{
plugin = p;
}
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args)
{
if (!ImgUtility.VerifierIdentite(sender))
{
return false;
}
joueur = (Player) sender;
inv = joueur.getInventory();
if (args.length < 1)
{
joueur.sendMessage("Map tools usage:"
+ "\n/" + ChatColor.GOLD + label + ChatColor.RESET + " get [id]: get the map corresponding to this id"
+ "\n/" + ChatColor.GOLD + label + ChatColor.RESET + " delete [id]: remove the map corresponding to this id"
+ "\n/" + ChatColor.GOLD + label + ChatColor.RESET + " list: show all ids of maps in your possession");
return true;
}
if (args[0].equalsIgnoreCase("get"))
{
try
{
id = Short.parseShort(args[1]);
}
catch (NumberFormatException err)
{
joueur.sendMessage("you must enter a number !");
return true;
}
SingleMap smap;
try
{
smap = new SingleMap(id);
smap.load();
if (inv.firstEmpty() == -1)
{
joueur.sendMessage("Your inventory is full, you can't take the map !");
return true;
}
smap.give(joueur.getInventory());
joueur.sendMessage("Map " + ChatColor.ITALIC + id + ChatColor.RESET + " was added in your inventory.");
}
catch (IllegalArgumentException ex)
{
joueur.sendMessage(ChatColor.RED + "Invalid argument : " + ex.getMessage());
}
catch(IOException ex)
{
joueur.sendMessage(ChatColor.RED + "Unable to load the map. Check server console for details.");
PluginLogger.LogError("Could not load the map", ex);
}
return true;
}
else if (args[0].equalsIgnoreCase("set"))
{
ImageMap smap;
try
{
if (args[1].startsWith("poster"))
{
smap = new PosterMap(args[1]);
}
else
{
id = Short.parseShort(args[1]);
smap = new SingleMap(id);
}
}
catch (NumberFormatException err)
{
joueur.sendMessage("you must enter a number !");
return true;
}
catch (Exception e)
{
e.printStackTrace();
joueur.sendMessage(ChatColor.RED + "ERROR while loading maps");
return true;
}
if(args.length < 3)
{
joueur.sendMessage("§cYou must enter a valid URL.");
return true;
}
URL url;
try
{
url = new URL(args[2]);
}
catch (MalformedURLException ex)
{
joueur.sendMessage("§Invalid URL.");
return true;
}
TacheTraitementMajMap tache = new TacheTraitementMajMap(smap, url, joueur);
tache.runTaskTimer(plugin, 0, 5);
return true;
}
else if (args[0].equalsIgnoreCase("delete"))
{
if (!joueur.hasPermission("imageonmap.usermmap"))
{
joueur.sendMessage("You are not allowed to delete map !");
return true;
}
if (args.length == 2 && args[1].startsWith("poster"))
{
SavedPoster poster = new SavedPoster(plugin, args[1]);
try
{
poster.Remove();
}
catch (IOException ex)
{
joueur.sendMessage("Unable to remove the entire poster, check the server log for more information");
PluginLogger.LogError("Unable to remove the entire poster", ex);
}
return true;
}
if (args.length <= 1)
{
if (joueur.getItemInHand().getType() == Material.MAP)
{
id = joueur.getItemInHand().getDurability();
}
else
{
joueur.sendMessage(ChatColor.RED + "You must hold a map or enter an id");
}
}
else
{
try
{
id = Short.parseShort(args[1]);
}
catch (NumberFormatException err)
{
joueur.sendMessage("you must enter a number !");
return true;
}
}
boolean success = ImgUtility.RemoveMap(plugin, id);
if (success)
{
joueur.sendMessage("Map#" + id + " was deleted");
return true;
}
else
{
joueur.sendMessage(ChatColor.RED + "Can't delete delete Map#" + id + ": check the server log");
return true;
}
}
else if (args[0].equalsIgnoreCase("list"))
{
String msg = "", msg2 = "";
int compteur = 0;
ArrayList<String> liste = new ArrayList<String>();
liste = ImgUtility.getListMapByPlayer(plugin, joueur.getName());
for (; compteur < liste.size(); compteur++)
{
msg += liste.get(compteur) + " ";
}
SavedPoster tmp = new SavedPoster(plugin);
ArrayList<String> listePoster = tmp.getListMapByPlayer(plugin, joueur.getName());
for (int i = 0; i < listePoster.size(); i++)
{
msg2 += listePoster.get(i) + " ";
}
joueur.sendMessage(msg
+ "\nYou have rendered " + ChatColor.DARK_PURPLE + (compteur + 1) + ChatColor.RESET + " pictures");
joueur.sendMessage("Your posters: \n" + msg2);
}
else if (args[0].equalsIgnoreCase("getrest"))
{
if (plugin.getRemainingMaps(joueur.getName()) == null)
{
joueur.sendMessage("All maps have already be placed in your inventory");
return true;
}
ArrayList<ItemStack> reste = plugin.getRemainingMaps(joueur.getName());
ArrayList<ItemStack> restant = new ArrayList<ItemStack>();
for (int i = 0; i < reste.size(); i++)
{
ImgUtility.AddMap(reste.get(i), inv, restant);
}
if (restant.isEmpty())
{
plugin.removeRemaingMaps(joueur.getName());
joueur.sendMessage("All maps have been placed in your inventory");
}
else
{
plugin.setRemainingMaps(joueur.getName(), restant);
joueur.sendMessage(restant.size() + " maps can't be placed in your inventory. Please run " + ChatColor.GOLD + "/maptool getrest again");
}
}
return true;
}
}

View File

@ -0,0 +1,52 @@
/*
* Copyright (C) 2013 Moribus
* Copyright (C) 2015 ProkopyL <prokopylmc@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package fr.moribus.imageonmap.commands.maptool;
import fr.moribus.imageonmap.commands.*;
import fr.moribus.imageonmap.map.ImageMap;
import java.util.List;
@CommandInfo(name = "delete", usageParameters = "[tool name]")
public class DeleteConfirmCommand extends Command
{
public DeleteConfirmCommand(Commands commandGroup) {
super(commandGroup);
}
@Override
protected void run() throws CommandException
{
ImageMap map = getMapFromArgs();
tellRaw("{text:\"You are going to delete \",extra:[{text:\""+ map.getId() +"\",color:gold},{text:\". Are you sure ? \",color:white}," +
"{text:\"[Confirm]\", color:green, clickEvent:{action:run_command,value:\"/maptool delete-noconfirm "+ map.getId() +"\"}, " +
"hoverEvent:{action:show_text,value:{text:\"This map will be deleted \",extra:[{text:\"forever\",color:red,bold:true,italic:true,underlined:true}, {text:\" !\", underlined:true}],underlined:true}}}]}");
}
@Override
protected List<String> complete() throws CommandException
{
if(args.length == 1)
return getMatchingMapNames(playerSender(), args[0]);
return null;
}
}

View File

@ -0,0 +1,64 @@
/*
* Copyright (C) 2013 Moribus
* Copyright (C) 2015 ProkopyL <prokopylmc@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package fr.moribus.imageonmap.commands.maptool;
import fr.moribus.imageonmap.PluginLogger;
import fr.moribus.imageonmap.commands.*;
import fr.moribus.imageonmap.map.ImageMap;
import fr.moribus.imageonmap.map.MapManager;
import fr.moribus.imageonmap.map.MapManagerException;
import java.util.List;
import org.bukkit.entity.Player;
@CommandInfo(name = "delete-noconfirm", usageParameters = "[map name]")
public class DeleteNoConfirmCommand extends Command
{
public DeleteNoConfirmCommand(Commands commandGroup) {
super(commandGroup);
}
@Override
protected void run() throws CommandException
{
Player player = playerSender();
ImageMap map = getMapFromArgs();
MapManager.clear(player.getInventory(), map);
try
{
MapManager.deleteMap(map);
info("Map successfully deleted.");
}
catch (MapManagerException ex)
{
PluginLogger.warning("A non-existent map was requested to be deleted", ex);
warning("This map does not exist.");
}
}
@Override
protected List<String> complete() throws CommandException
{
if(args.length == 1)
return getMatchingMapNames(playerSender(), args[0]);
return null;
}
}

View File

@ -0,0 +1,50 @@
/*
* Copyright (C) 2013 Moribus
* Copyright (C) 2015 ProkopyL <prokopylmc@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package fr.moribus.imageonmap.commands.maptool;
import fr.moribus.imageonmap.commands.*;
import java.util.List;
import org.bukkit.entity.Player;
@CommandInfo(name = "get")
public class GetCommand extends Command
{
public GetCommand(Commands commandGroup) {
super(commandGroup);
}
@Override
protected void run() throws CommandException
{
Player player = playerSender();
if(getMapFromArgs().give(player))
{
info("The requested map was too big to fit in your inventory.");
info("Use '/maptool getremaining' to get the remaining maps.");
}
}
@Override
protected List<String> complete() throws CommandException
{
if(args.length == 1)
return getMatchingMapNames(playerSender(), args[0]);
return null;
}
}

View File

@ -0,0 +1,57 @@
/*
* Copyright (C) 2013 Moribus
* Copyright (C) 2015 ProkopyL <prokopylmc@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package fr.moribus.imageonmap.commands.maptool;
import fr.moribus.imageonmap.commands.Command;
import fr.moribus.imageonmap.commands.CommandException;
import fr.moribus.imageonmap.commands.CommandInfo;
import fr.moribus.imageonmap.commands.Commands;
import fr.moribus.imageonmap.ui.MapItemManager;
import org.bukkit.entity.Player;
@CommandInfo(name = "getremaining", aliases = {"getrest"})
public class GetRemainingCommand extends Command
{
public GetRemainingCommand(Commands commandGroup) {
super(commandGroup);
}
@Override
protected void run() throws CommandException
{
Player player = playerSender();
if(MapItemManager.getCacheSize(player) <= 0)
{
info("You have no remaining map.");
return;
}
int givenMaps = MapItemManager.giveCache(player);
if(givenMaps == 0)
{
error("Your inventory is full ! Make some space before requesting the remaining maps.");
}
else
{
info("There are " + MapItemManager.getCacheSize(player) + " maps remaining.");
}
}
}

View File

@ -0,0 +1,56 @@
/*
* Copyright (C) 2013 Moribus
* Copyright (C) 2015 ProkopyL <prokopylmc@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package fr.moribus.imageonmap.commands.maptool;
import fr.moribus.imageonmap.commands.*;
import fr.moribus.imageonmap.map.ImageMap;
import fr.moribus.imageonmap.map.MapManager;
import java.util.List;
import org.bukkit.entity.Player;
@CommandInfo(name = "list")
public class ListCommand extends Command
{
public ListCommand(Commands commandGroup) {
super(commandGroup);
}
@Override
protected void run() throws CommandException
{
Player player = playerSender();
List<ImageMap> mapList = MapManager.getMapList(player.getUniqueId());
if(mapList.isEmpty())
{
info("No map found.");
return;
}
info(mapList.size() + " maps found.");
String sMapList = mapList.get(0).getId();
for(int i = 1, c = mapList.size(); i < c; i++)
{
sMapList += "§7,§r" + mapList.get(i).getId();
}
player.sendMessage(sMapList);
}
}

View File

@ -0,0 +1,52 @@
/*
* Copyright (C) 2013 Moribus
* Copyright (C) 2015 ProkopyL <prokopylmc@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package fr.moribus.imageonmap.commands.maptool;
import fr.moribus.imageonmap.commands.*;
import fr.moribus.imageonmap.migration.MigratorExecutor;
import org.bukkit.command.CommandSender;
@CommandInfo(name = "migrate")
public class MigrateCommand extends Command
{
public MigrateCommand(Commands commandGroup) {
super(commandGroup);
}
@Override
protected void run() throws CommandException
{
final CommandSender cmdSender = sender;
if(MigratorExecutor.isRunning())
{
error("A migration process is already running. Check console for details.");
}
else
{
info("Migration started. See console for details.");
MigratorExecutor.migrate();
}
}
@Override
public boolean canExecute(CommandSender sender)
{
return sender.isOp();
}
}

View File

@ -0,0 +1,87 @@
/*
* Copyright (C) 2013 Moribus
* Copyright (C) 2015 ProkopyL <prokopylmc@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package fr.moribus.imageonmap.commands.maptool;
import fr.moribus.imageonmap.PluginLogger;
import fr.moribus.imageonmap.commands.Command;
import fr.moribus.imageonmap.commands.CommandException;
import fr.moribus.imageonmap.commands.CommandInfo;
import fr.moribus.imageonmap.commands.Commands;
import fr.moribus.imageonmap.image.ImageRendererExecutor;
import fr.moribus.imageonmap.map.ImageMap;
import fr.moribus.imageonmap.worker.WorkerCallback;
import java.net.MalformedURLException;
import java.net.URL;
import org.bukkit.entity.Player;
@CommandInfo(name = "new", usageParameters = "<URL> [resize]")
public class NewCommand extends Command
{
public NewCommand(Commands commandGroup) {
super(commandGroup);
}
@Override
protected void run() throws CommandException
{
final Player player = playerSender();
boolean scaling = false;
URL url;
if(args.length < 1) throwInvalidArgument("You must give an URL to take the image from.");
try
{
url = new URL(args[0]);
}
catch(MalformedURLException ex)
{
throwInvalidArgument("Invalid URL.");
return;
}
if(args.length >= 2)
{
if(args[1].equals("resize")) scaling = true;
}
info("Rendering ...");
ImageRendererExecutor.Render(url, scaling, player.getUniqueId(), new WorkerCallback<ImageMap>()
{
@Override
public void finished(ImageMap result)
{
player.sendMessage("§7Rendering finished !");
if(result.give(player))
{
info("The rendered map was too big to fit in your inventory.");
info("Use '/maptool getremaining' to get the remaining maps.");
}
}
@Override
public void errored(Throwable exception)
{
player.sendMessage("§cMap rendering failed : " + exception.getMessage());
PluginLogger.warning("Rendering from '{0}' failed", exception, player.getName());
}
});
}
}

View File

@ -1,31 +0,0 @@
package fr.moribus.imageonmap.image;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.Callable;
import javax.imageio.ImageIO;
public class DownloadImageThread implements Callable<BufferedImage>
{
private final URL imageURL;
private BufferedImage imgSrc;
public DownloadImageThread(URL imageURL)
{
this.imageURL = imageURL;
}
@Override
public BufferedImage call() throws IOException
{
imgSrc = ImageIO.read(imageURL);
if(imgSrc == null) throw new IOException("URL does not points to a valid image.");
return imgSrc;
}
}

View File

@ -0,0 +1,115 @@
/*
* Copyright (C) 2013 Moribus
* Copyright (C) 2015 ProkopyL <prokopylmc@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package fr.moribus.imageonmap.image;
import fr.moribus.imageonmap.ImageOnMap;
import fr.moribus.imageonmap.map.ImageMap;
import fr.moribus.imageonmap.worker.Worker;
import fr.moribus.imageonmap.worker.WorkerCallback;
import fr.moribus.imageonmap.worker.WorkerRunnable;
import java.awt.image.BufferedImage;
import java.io.File;
import java.nio.file.Files;
import javax.imageio.ImageIO;
public class ImageIOExecutor extends Worker
{
static private ImageIOExecutor instance;
static public void start()
{
if(instance != null) stop();
instance = new ImageIOExecutor();
instance.init();
}
static public void stop()
{
instance.exit();
instance = null;
}
private ImageIOExecutor()
{
super("Image IO");
}
static public void loadImage(final File file, final Renderer mapRenderer)
{
instance.submitQuery(new WorkerRunnable<Void>()
{
@Override
public Void run() throws Exception
{
BufferedImage image = ImageIO.read(file);
mapRenderer.setImage(image);
return null;
}
});
}
static public void saveImage(final File file, final BufferedImage image)
{
instance.submitQuery(new WorkerRunnable<Void>()
{
@Override
public Void run() throws Throwable
{
ImageIO.write(image, "png", file);
return null;
}
});
}
static public void saveImage(short mapID, BufferedImage image)
{
saveImage(ImageOnMap.getPlugin().getImageFile(mapID), image);
}
static public void saveImage(short[] mapsIDs, PosterImage image)
{
for(int i = 0, c = mapsIDs.length; i < c; i++)
{
ImageIOExecutor.saveImage(ImageOnMap.getPlugin().getImageFile(mapsIDs[i]), image.getImageAt(i));
}
}
static public void deleteImage(ImageMap map)
{
short[] mapsIDs = map.getMapsIDs();
for(int i = 0, c = mapsIDs.length; i < c; i++)
{
deleteImage(ImageOnMap.getPlugin().getImageFile(mapsIDs[i]));
}
}
static public void deleteImage(final File file)
{
instance.submitQuery(new WorkerRunnable<Void>()
{
@Override
public Void run() throws Throwable
{
Files.delete(file.toPath());
return null;
}
});
}
}

View File

@ -0,0 +1,182 @@
/*
* Copyright (C) 2013 Moribus
* Copyright (C) 2015 ProkopyL <prokopylmc@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package fr.moribus.imageonmap.image;
import fr.moribus.imageonmap.PluginLogger;
import fr.moribus.imageonmap.map.ImageMap;
import fr.moribus.imageonmap.map.MapManager;
import fr.moribus.imageonmap.worker.Worker;
import fr.moribus.imageonmap.worker.WorkerCallback;
import fr.moribus.imageonmap.worker.WorkerRunnable;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import javax.imageio.ImageIO;
public class ImageRendererExecutor extends Worker
{
static private ImageRendererExecutor instance;
static public void start()
{
if(instance != null) stop();
instance = new ImageRendererExecutor();
instance.init();
}
static public void stop()
{
instance.exit();
instance = null;
}
private ImageRendererExecutor()
{
super("Image IO", true);
}
static public void Test(WorkerCallback callback)
{
instance.submitQuery(new WorkerRunnable<Void>()
{
@Override
public Void run() throws Throwable
{
Thread.sleep(5000);
return null;
}
}, callback);
}
static public void Render(final URL url, final boolean scaling, final UUID playerUUID, WorkerCallback<ImageMap> callback)
{
instance.submitQuery(new WorkerRunnable<ImageMap>()
{
@Override
public ImageMap run() throws Throwable
{
final BufferedImage image = ImageIO.read(url);
if(image == null) throw new IOException("The given URL is not a valid image");
if(scaling) return RenderSingle(image, playerUUID);
else return RenderPoster(image, playerUUID);
}
}, callback);
}
static private ImageMap RenderSingle(final BufferedImage image, final UUID playerUUID) throws Throwable
{
MapManager.checkMapLimit(1, playerUUID);
Future<Short> futureMapID = instance.submitToMainThread(new Callable<Short>()
{
@Override
public Short call() throws Exception
{
return MapManager.getNewMapsIds(1)[0];
}
});
final BufferedImage finalImage = ResizeImage(image, ImageMap.WIDTH, ImageMap.HEIGHT);
final short mapID = futureMapID.get();
ImageIOExecutor.saveImage(mapID, finalImage);
instance.submitToMainThread(new Callable<Void>()
{
@Override
public Void call() throws Exception
{
Renderer.installRenderer(finalImage, mapID);
return null;
}
});
return MapManager.createMap(playerUUID, mapID);
}
static private ImageMap 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<short[]> futureMapsIds = instance.submitToMainThread(new Callable<short[]>()
{
@Override
public short[] call() throws Exception
{
return MapManager.getNewMapsIds(mapCount);
}
});
poster.splitImages();
final short[] mapsIDs = futureMapsIds.get();
ImageIOExecutor.saveImage(mapsIDs, poster);
instance.submitToMainThread(new Callable<Void>()
{
@Override
public Void call() throws Exception
{
Renderer.installRenderer(poster, mapsIDs);
return null;
}
});
return MapManager.createMap(poster, playerUUID, mapsIDs);
}
static private BufferedImage ResizeImage(BufferedImage source, int destinationW, int destinationH)
{
float ratioW = (float)destinationW / (float)source.getWidth();
float ratioH = (float)destinationH / (float)source.getHeight();
int finalW, finalH;
if(ratioW < ratioH)
{
finalW = destinationW;
finalH = (int)(source.getHeight() * ratioW);
}
else
{
finalW = (int)(source.getWidth() * ratioH);
finalH = destinationH;
}
int x, y;
x = (destinationW - finalW) / 2;
y = (destinationH - finalH) / 2;
BufferedImage newImage = new BufferedImage(destinationW, destinationH, BufferedImage.TYPE_INT_ARGB);
Graphics graphics = newImage.getGraphics();
graphics.drawImage(source, x, y, finalW, finalH, null);
graphics.dispose();
return newImage;
}
}

View File

@ -1,152 +0,0 @@
package fr.moribus.imageonmap.image;
import fr.moribus.imageonmap.image.PosterImage;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import javax.imageio.ImageIO;
import org.bukkit.map.MapView;
public class ImageRendererThread extends Thread
{
private String URL;
private BufferedImage imgSrc;
private BufferedImage[] img;
private PosterImage poster;
private boolean estPrete = false, resized;
public boolean erreur = false;
public boolean isErreur()
{
return erreur;
}
public ImageRendererThread(String u, boolean r)
{
URL = u;
resized = r;
}
public BufferedImage[] getImg()
{
if (estPrete)
return img;
else
return null;
}
public Boolean getStatut()
{
return estPrete;
}
@Override
public void run()
{
URI uri = null;
java.net.URL url = null;
try
{
uri = URI.create(URL);
url = uri.toURL();
}
catch (IllegalArgumentException | MalformedURLException e) {
e.printStackTrace();
erreur = true;
return;
}
if(erreur != true)
{
try {
imgSrc = ImageIO.read(url.openStream());
} catch (IOException e) {
// TODO Auto-generated catch block
erreur = true;
e.printStackTrace();
}
if(resized)
{
img = new BufferedImage[1];
Image i = imgSrc.getScaledInstance(128, 128, Image.SCALE_SMOOTH);
BufferedImage imgScaled = new BufferedImage(128, 128, BufferedImage.TYPE_INT_ARGB);
imgScaled.getGraphics().drawImage(i, 0, 0 , null);
img[0] = imgScaled;
}
else
{
int width = imgSrc.getWidth();
int height = imgSrc.getHeight();
// Fonction qui cherche le multiple de 128 le plus proche
// de la hauteur / largeur de l'image
int tmpW = 0, tmpH = 0;
int i = 1;
while (tmpW < width)
{
tmpW = i * 128;
i++;
}
i = 0;
while (tmpH < height)
{
tmpH = i * 128;
i++;
}
// On crée un "canvas" = une image vide qui a une taille multiple de 128
// dans laquelle on dessinera l'image téléchargées
BufferedImage canvas = new BufferedImage(tmpW, tmpH, BufferedImage.TYPE_INT_ARGB);
// On récupère l'objet Grapics2D, servant à dessiner dans notre canvas
Graphics2D graph = canvas.createGraphics();
// Variable servant à cadrer l'image
int centerX = 0, centerY = 0;
centerX = (tmpW - imgSrc.getWidth()) / 2;
centerY = (tmpH - imgSrc.getHeight()) / 2;
//On déplace le point d'origine de graph afin que l'image soit dessinée au milieu du canvas
graph.translate(centerX, centerY);
//graph.rotate(45);
// on dessine l'image dans le canvas
graph.drawImage(imgSrc, null, null);
// on crée un Poster à partir de notre canvas
poster = new PosterImage(canvas);
img = poster.getImages();
}
estPrete = true;
}
}
static public void SupprRendu(MapView map)
{
if (map.getRenderers().size() > 0)
{
int i = 0, t = map.getRenderers().size();
while (i < t)
{
map.removeRenderer(map.getRenderers().get(i));
i++;
}
}
}
}

View File

@ -0,0 +1,95 @@
/*
* Copyright (C) 2013 Moribus
* Copyright (C) 2015 ProkopyL <prokopylmc@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package fr.moribus.imageonmap.image;
import fr.moribus.imageonmap.ImageOnMap;
import java.io.File;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.entity.Entity;
import org.bukkit.entity.ItemFrame;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerItemHeldEvent;
import org.bukkit.event.world.ChunkLoadEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.map.MapView;
import org.bukkit.plugin.Plugin;
public class MapInitEvent implements Listener
{
static public void init(Plugin plugin)
{
plugin.getServer().getPluginManager().registerEvents(new MapInitEvent(), plugin);
for(World world : Bukkit.getWorlds())
{
for(ItemFrame frame : world.getEntitiesByClass(ItemFrame.class))
{
initMap(frame.getItem());
}
}
for(Player player : Bukkit.getOnlinePlayers())
{
initMap(player.getItemInHand());
}
}
@EventHandler
public void onChunkLoad(ChunkLoadEvent event)
{
for (Entity entity : event.getChunk().getEntities())
{
if (entity instanceof ItemFrame)
{
initMap(((ItemFrame)entity).getItem());
}
}
}
@EventHandler
public void onPlayerInv(PlayerItemHeldEvent event)
{
ItemStack item = event.getPlayer().getInventory().getItem(event.getNewSlot());
initMap(item);
}
static protected void initMap(ItemStack item)
{
if (item != null && item.getType() == Material.MAP)
{
MapView map = Bukkit.getMap(item.getDurability());
initMap(map);
}
}
static protected void initMap(MapView map)
{
if(Renderer.isHandled(map)) return;
File imageFile = ImageOnMap.getPlugin().getImageFile(map.getId());
if(imageFile.isFile())
{
ImageIOExecutor.loadImage(imageFile, Renderer.installRenderer(map));
}
}
}

View File

@ -1,8 +1,24 @@
/*
* Copyright (C) 2013 Moribus
* Copyright (C) 2015 ProkopyL <prokopylmc@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package fr.moribus.imageonmap.image;
import fr.moribus.imageonmap.map.ImageMap;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
/**
@ -10,6 +26,10 @@ import java.awt.image.BufferedImage;
*/
public class PosterImage
{
static private final int WIDTH = 128;
static private final int HEIGHT = 128;
private BufferedImage originalImage;
private BufferedImage[] cutImages;
private int lines;
private int columns;
@ -17,43 +37,50 @@ public class PosterImage
private int remainderX, remainderY;
/**
* Creates and splits a new Poster from an entire image
* Creates a new Poster from an entire image
* @param originalImage the original image
*/
public PosterImage(BufferedImage originalImage)
{
splitImages(originalImage);
this.originalImage = originalImage;
calculateDimensions();
}
private void splitImages(BufferedImage originalImage)
private void calculateDimensions()
{
int originalWidth = originalImage.getWidth();
int originalHeight = originalImage.getHeight();
columns = (int) Math.ceil(originalWidth / ImageMap.WIDTH);
lines = (int) Math.ceil(originalHeight / ImageMap.HEIGHT);
columns = (int) Math.ceil(originalWidth / WIDTH);
lines = (int) Math.ceil(originalHeight / HEIGHT);
remainderX = originalWidth % ImageMap.WIDTH;
remainderY = originalHeight % ImageMap.HEIGHT;
remainderX = originalWidth % WIDTH;
remainderY = originalHeight % HEIGHT;
if(remainderX > 0) columns++;
if(remainderY > 0) lines++;
cutImagesCount = columns * lines;
}
public void splitImages()
{
cutImages = new BufferedImage[cutImagesCount];
int imageX;
int imageY = (remainderY > 0) ? (remainderY - ImageMap.HEIGHT) / 2 : 0;
int imageY = (remainderY - HEIGHT) / 2;
for(int i = 0; i < lines; i++)
{
imageX = (remainderX > 0) ? (remainderX - ImageMap.WIDTH) / 2 : 0;
imageX = (remainderX - WIDTH) / 2;
for(int j = 0; j < columns; j++)
{
cutImages[i * columns + j] = makeSubImage(originalImage, imageX, imageY);
imageX += ImageMap.WIDTH;
imageX += WIDTH;
}
imageY += ImageMap.HEIGHT;
imageY += HEIGHT;
}
originalImage = null;
}
/**
@ -64,7 +91,7 @@ public class PosterImage
*/
private BufferedImage makeSubImage(BufferedImage originalImage, int x, int y)
{
BufferedImage newImage = new BufferedImage(ImageMap.WIDTH, ImageMap.HEIGHT, BufferedImage.TYPE_INT_ARGB);
BufferedImage newImage = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_ARGB);
Graphics graphics = newImage.getGraphics();
@ -73,13 +100,6 @@ public class PosterImage
return newImage;
}
private int boundValue(int min, int value, int max)
{
return Math.max(Math.min(value, max), min);
}
/**
*
* @return the split images

View File

@ -1,6 +1,25 @@
/*
* Copyright (C) 2013 Moribus
* Copyright (C) 2015 ProkopyL <prokopylmc@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package fr.moribus.imageonmap.image;
import java.awt.image.BufferedImage;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.map.MapCanvas;
import org.bukkit.map.MapRenderer;
@ -8,11 +27,53 @@ import org.bukkit.map.MapView;
public class Renderer extends MapRenderer
{
private boolean isRendered;
private final BufferedImage image;
public Renderer(BufferedImage image)
static public boolean isHandled(MapView map)
{
for(MapRenderer renderer : map.getRenderers())
{
if(renderer instanceof Renderer) return true;
}
return false;
}
static public void installRenderer(PosterImage image, short[] mapsIds)
{
for(int i = 0; i < mapsIds.length; i++)
{
installRenderer(image.getImageAt(i), mapsIds[i]);
}
}
static public void installRenderer(BufferedImage image, short mapID)
{
installRenderer(Bukkit.getMap(mapID)).setImage(image);
}
static public Renderer installRenderer(MapView map)
{
Renderer renderer = new Renderer();
removeRenderers(map);
map.addRenderer(renderer);
return renderer;
}
static public void removeRenderers(MapView map)
{
for(MapRenderer renderer : map.getRenderers())
{
map.removeRenderer(renderer);
}
}
private BufferedImage image;
protected Renderer()
{
this(null);
}
protected Renderer(BufferedImage image)
{
isRendered = false;
this.image = image;
}
@ -20,10 +81,19 @@ public class Renderer extends MapRenderer
public void render(MapView v, final MapCanvas canvas, Player p)
{
//Render only once to avoid overloading the server
if (!isRendered)
{
canvas.drawImage(0, 0, image);
isRendered = true;
}
if (image == null) return;
canvas.drawImage(0, 0, image);
image = null;
}
public BufferedImage getImage()
{
return image;
}
public void setImage(BufferedImage image)
{
this.image = image;
}
}

View File

@ -1,125 +1,174 @@
/*
* Copyright (C) 2013 Moribus
* Copyright (C) 2015 ProkopyL <prokopylmc@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package fr.moribus.imageonmap.map;
import fr.moribus.imageonmap.image.PosterImage;
import java.awt.image.BufferedImage;
import java.io.IOException;
import fr.moribus.imageonmap.ui.MapItemManager;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import org.bukkit.Material;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.serialization.ConfigurationSerializable;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
public abstract class ImageMap
public abstract class ImageMap implements ConfigurationSerializable
{
static public enum Type
{
SINGLE, POSTER;
static public ImageMap createNewMap(Type type, BufferedImage image, Player player)
{
switch(type)
{
case POSTER:
return new PosterMap(new PosterImage(image), player);
default:
return new SingleMap(image, player);
}
}
static public Type fromString(String string)
{
switch(string.toLowerCase())
{
case "poster":
case "multi":
return POSTER;
case "single":
return SINGLE;
default:
return null;
}
}
}
};
static public final int WIDTH = 128;
static public final int HEIGHT = 128;
static public final String DEFAULT_NAME = "Map";
protected String imageName;
protected String ownerName;
protected String worldName;
private String id;
private final UUID userUUID;
private final Type mapType;
private String name;
public abstract void load() throws IOException;
public abstract void save() throws IOException;
public abstract void give(Inventory inv);
public abstract void setImage(BufferedImage image);
public abstract void send(Player joueur);
public ImageMap()
protected ImageMap(UUID userUUID, Type mapType)
{
this(null, null, null);
this(userUUID, mapType, null, null);
}
public ImageMap(String imageName, String ownerName, String worldName)
protected ImageMap(UUID userUUID, Type mapType, String id, String name)
{
this.imageName = imageName;
this.ownerName = ownerName;
this.worldName = worldName;
}
this.userUUID = userUUID;
this.mapType = mapType;
this.id = id;
this.name = name;
protected void give(Inventory inventory, short mapID)
{
give(inventory, mapID, getImageName());
}
protected void give(Inventory inventory, short mapID, String itemName)
{
ItemStack itemMap = new ItemStack(Material.MAP, 1, mapID);
if(itemName != null)
if(this.id == null)
{
ItemMeta meta = itemMap.getItemMeta();
meta.setDisplayName(itemName);
itemMap.setItemMeta(meta);
if(this.name == null) this.name = DEFAULT_NAME;
this.id = MapManager.getNextAvailableMapID(this.name, userUUID);
}
inventory.addItem(itemMap);
}
// Getters & Setters
public String getImageName()
public abstract short[] getMapsIDs();
public abstract boolean managesMap(short mapID);
public abstract int getMapCount();
public boolean managesMap(ItemStack item)
{
return imageName;
if(item == null) return false;
if(item.getType() != Material.MAP) return false;
return managesMap(item.getDurability());
}
public void setImageName(String imageName)
public boolean give(Player player)
{
this.imageName = imageName;
return MapItemManager.give(player, this);
}
public boolean isNamed()
/* ====== Serialization methods ====== */
static public ImageMap fromConfig(Map<String, Object> map, UUID userUUID) throws InvalidConfigurationException
{
return imageName != null;
Type mapType;
try
{
mapType = Type.valueOf((String) map.get("type"));
}
catch(ClassCastException ex)
{
throw new InvalidConfigurationException(ex);
}
switch(mapType)
{
case SINGLE: return new SingleMap(map, userUUID);
case POSTER: return new PosterMap(map, userUUID);
default: throw new IllegalArgumentException("Unhandled map type given");
}
}
public String getOwnerName()
protected ImageMap(Map<String, Object> map, UUID userUUID, Type mapType) throws InvalidConfigurationException
{
return ownerName;
this(userUUID, mapType,
(String) getNullableFieldValue(map, "id"),
(String) getNullableFieldValue(map, "name"));
}
public void setOwnerName(String ownerName)
protected abstract void postSerialize(Map<String, Object> map);
@Override
public Map<String, Object> serialize()
{
this.ownerName = ownerName;
Map<String, Object> map = new HashMap<String, Object>();
map.put("id", getId());
map.put("type", mapType.toString());
map.put("name", getName());
this.postSerialize(map);
return map;
}
public String getWorldName()
static protected <T> T getFieldValue(Map<String, Object> map, String fieldName) throws InvalidConfigurationException
{
return worldName;
T value = getNullableFieldValue(map, fieldName);
if(value == null) throw new InvalidConfigurationException("Field value not found for \"" + fieldName + "\"");
return value;
}
public void setWorldName(String worldName)
static protected <T> T getNullableFieldValue(Map<String, Object> map, String fieldName) throws InvalidConfigurationException
{
this.worldName = worldName;
try
{
return (T)map.get(fieldName);
}
catch(ClassCastException ex)
{
throw new InvalidConfigurationException("Invalid field \"" + fieldName + "\"", ex);
}
}
/* ====== Getters & Setters ====== */
public UUID getUserUUID()
{
return userUUID;
}
public synchronized String getName()
{
return name;
}
public synchronized String getId()
{
return id;
}
public synchronized void rename(String id, String name)
{
this.id = id;
this.name = name;
}
public void rename(String name)
{
if(getName().equals(name)) return;
rename(MapManager.getNextAvailableMapID(name, getUserUUID()), name);
}
}

View File

@ -0,0 +1,254 @@
/*
* Copyright (C) 2013 Moribus
* Copyright (C) 2015 ProkopyL <prokopylmc@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package fr.moribus.imageonmap.map;
import fr.moribus.imageonmap.ImageOnMap;
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 java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.scheduler.BukkitTask;
abstract public class MapManager
{
static private final long SAVE_DELAY = 200;
static private final ArrayList<PlayerMapStore> playerMaps = new ArrayList<PlayerMapStore>();
static private BukkitTask autosaveTask;
static public void init()
{
}
static public void exit()
{
save();
playerMaps.clear();
if(autosaveTask != null) autosaveTask.cancel();
}
static public boolean managesMap(short mapID)
{
synchronized(playerMaps)
{
for(PlayerMapStore mapStore : playerMaps)
{
if(mapStore.managesMap(mapID)) return true;
}
}
return false;
}
static public boolean managesMap(ItemStack item)
{
synchronized(playerMaps)
{
for(PlayerMapStore mapStore : playerMaps)
{
if(mapStore.managesMap(item)) return true;
}
}
return false;
}
static public ImageMap createMap(UUID playerUUID, short mapID) throws MapManagerException
{
ImageMap newMap = new SingleMap(playerUUID, mapID);
addMap(newMap);
return newMap;
}
static public ImageMap createMap(PosterImage image, UUID playerUUID, short[] mapsIDs) throws MapManagerException
{
ImageMap newMap;
if(image.getImagesCount() == 1)
{
newMap = new SingleMap(playerUUID, mapsIDs[0]);
}
else
{
newMap = new PosterMap(playerUUID, mapsIDs, image.getColumns(), image.getLines());
}
addMap(newMap);
return newMap;
}
static public short[] getNewMapsIds(int amount)
{
short[] mapsIds = new short[amount];
for(int i = 0; i < amount; i++)
{
mapsIds[i] = Bukkit.createMap(Bukkit.getWorlds().get(0)).getId();
}
return mapsIds;
}
static public void addMap(ImageMap map) throws MapManagerException
{
getPlayerMapStore(map.getUserUUID()).addMap(map);
}
static public void insertMap(ImageMap map)
{
getPlayerMapStore(map.getUserUUID()).insertMap(map);
}
static public void deleteMap(ImageMap map) throws MapManagerException
{
getPlayerMapStore(map.getUserUUID()).deleteMap(map);
ImageIOExecutor.deleteImage(map);
}
static public void notifyModification(UUID playerUUID)
{
getPlayerMapStore(playerUUID).notifyModification();
if(autosaveTask == null)
Bukkit.getScheduler().runTaskLater(ImageOnMap.getPlugin(), new AutosaveRunnable(), SAVE_DELAY);
}
static public String getNextAvailableMapID(String mapId, UUID playerUUID)
{
return getPlayerMapStore(playerUUID).getNextAvailableMapID(mapId);
}
static public List<ImageMap> getMapList(UUID playerUUID)
{
return getPlayerMapStore(playerUUID).getMapList();
}
static public ImageMap getMap(UUID playerUUID, String mapId)
{
return getPlayerMapStore(playerUUID).getMap(mapId);
}
static public void clear(Inventory inventory)
{
for(int i = 0, c = inventory.getSize(); i < c; i++)
{
if(managesMap(inventory.getItem(i)))
{
inventory.setItem(i, new ItemStack(Material.AIR));
}
}
}
static public void clear(Inventory inventory, ImageMap map)
{
for(int i = 0, c = inventory.getSize(); i < c; i++)
{
if(map.managesMap(inventory.getItem(i)))
{
inventory.setItem(i, new ItemStack(Material.AIR));
}
}
}
static public void save()
{
synchronized(playerMaps)
{
for(PlayerMapStore tStore : playerMaps)
{
tStore.save();
}
}
}
static public void checkMapLimit(ImageMap map) throws MapManagerException
{
checkMapLimit(map.getMapCount(), map.getUserUUID());
}
static public void checkMapLimit(int newMapsCount, UUID userUUID) throws MapManagerException
{
int limit = PluginConfiguration.MAP_GLOBAL_LIMIT.getInteger();
if(limit > 0)
{
if(getMapCount() + newMapsCount > limit)
throw new MapManagerException(Reason.MAXIMUM_SERVER_MAPS_EXCEEDED);
}
getPlayerMapStore(userUUID).checkMapLimit(newMapsCount);
}
static public int getMapCount()
{
int mapCount = 0;
synchronized(playerMaps)
{
for(PlayerMapStore tStore : playerMaps)
{
mapCount += tStore.getMapCount();
}
}
return mapCount;
}
static private PlayerMapStore getPlayerMapStore(UUID playerUUID)
{
PlayerMapStore store;
synchronized(playerMaps)
{
store = getExistingPlayerMapStore(playerUUID);
if(store == null)
{
store = new PlayerMapStore(playerUUID);
playerMaps.add(store);
store.load();
}
}
return store;
}
static private PlayerMapStore getExistingPlayerMapStore(UUID playerUUID)
{
synchronized(playerMaps)
{
for(PlayerMapStore mapStore : playerMaps)
{
if(mapStore.getUUID().equals(playerUUID)) return mapStore;
}
}
return null;
}
static private class AutosaveRunnable implements Runnable
{
@Override
public void run()
{
synchronized(playerMaps)
{
for(PlayerMapStore toolStore : playerMaps)
{
if(toolStore.isModified()) toolStore.save();
}
autosaveTask = null;
}
}
}
}

View File

@ -0,0 +1,53 @@
/*
* Copyright (C) 2013 Moribus
* Copyright (C) 2015 ProkopyL <prokopylmc@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package fr.moribus.imageonmap.map;
import java.text.MessageFormat;
public class MapManagerException extends Exception
{
public enum Reason
{
MAXIMUM_PLAYER_MAPS_EXCEEDED("You have too many maps (maximum : {0})."),
MAXIMUM_SERVER_MAPS_EXCEEDED("The server ImageOnMap limit has been reached."),
IMAGEMAP_DOES_NOT_EXIST("The given map does not exist.");
private final String reasonString;
private Reason(String reasonString)
{
this.reasonString = reasonString;
}
public String getReasonString(Object ...arguments)
{
return MessageFormat.format(reasonString, arguments);
}
}
private final Reason reason;
public MapManagerException(Reason reason, Object ...arguments)
{
super(reason.getReasonString(arguments));
this.reason = reason;
}
public Reason getReason() { return reason; }
}

View File

@ -0,0 +1,258 @@
/*
* Copyright (C) 2013 Moribus
* Copyright (C) 2015 ProkopyL <prokopylmc@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package fr.moribus.imageonmap.map;
import fr.moribus.imageonmap.ImageOnMap;
import fr.moribus.imageonmap.PluginConfiguration;
import fr.moribus.imageonmap.PluginLogger;
import fr.moribus.imageonmap.map.MapManagerException.Reason;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.configuration.serialization.ConfigurationSerializable;
import org.bukkit.inventory.ItemStack;
public class PlayerMapStore implements ConfigurationSerializable
{
private final UUID playerUUID;
private final ArrayList<ImageMap> mapList = new ArrayList<ImageMap>();
private boolean modified = false;
private int mapCount = 0;
public PlayerMapStore(UUID playerUUID)
{
this.playerUUID = playerUUID;
}
public synchronized boolean managesMap(short mapID)
{
for(ImageMap map : mapList)
{
if(map.managesMap(mapID)) return true;
}
return false;
}
public synchronized boolean managesMap(ItemStack item)
{
for(ImageMap map : mapList)
{
if(map.managesMap(item)) return true;
}
return false;
}
public synchronized void addMap(ImageMap map) throws MapManagerException
{
checkMapLimit(map);
insertMap(map);
}
public synchronized void insertMap(ImageMap map)
{
_addMap(map);
notifyModification();
}
private void _addMap(ImageMap map)
{
mapList.add(map);
mapCount += map.getMapCount();
}
public synchronized void deleteMap(ImageMap map) throws MapManagerException
{
_removeMap(map);
notifyModification();
}
private void _removeMap(ImageMap map) throws MapManagerException
{
if(!mapList.remove(map))
{
throw new MapManagerException(Reason.IMAGEMAP_DOES_NOT_EXIST);
}
mapCount -= map.getMapCount();
}
public synchronized boolean mapExists(String id)
{
for(ImageMap map : mapList)
{
if(map.getId().equals(id)) return true;
}
return false;
}
public String getNextAvailableMapID(String mapId)
{
if(!mapExists(mapId)) return mapId;
int id = 0;
do
{
id++;
}while(mapExists(mapId + "-" + id));
return mapId + "-" + id;
}
public synchronized List<ImageMap> getMapList()
{
return new ArrayList(mapList);
}
public synchronized ImageMap getMap(String mapId)
{
for(ImageMap map : mapList)
{
if(map.getId().equals(mapId)) return map;
}
return null;
}
public void checkMapLimit(ImageMap map) throws MapManagerException
{
checkMapLimit(map.getMapCount());
}
public void checkMapLimit(int newMapsCount) throws MapManagerException
{
int limit = PluginConfiguration.MAP_PLAYER_LIMIT.getInteger();
if(limit <= 0) return;
if(getMapCount() + newMapsCount > limit)
throw new MapManagerException(Reason.MAXIMUM_PLAYER_MAPS_EXCEEDED, limit);
}
/* ===== Getters & Setters ===== */
public UUID getUUID()
{
return playerUUID;
}
public synchronized boolean isModified()
{
return modified;
}
public synchronized void notifyModification()
{
this.modified = true;
}
public synchronized int getMapCount()
{
return this.mapCount;
}
/* ****** Serializing ***** */
@Override
public Map<String, Object> serialize()
{
Map<String, Object> map = new HashMap<String, Object>();
ArrayList<Map> list = new ArrayList<Map>();
synchronized(this)
{
for(ImageMap tMap : mapList)
{
list.add(tMap.serialize());
}
}
map.put("mapList", list);
return map;
}
private void loadFromConfig(ConfigurationSection section)
{
if(section == null) return;
List<Map<String, Object>> list = (List<Map<String, Object>>) section.getList("mapList");
if(list == null) return;
for(Map<String, Object> tMap : list)
{
try
{
ImageMap newMap = ImageMap.fromConfig(tMap, playerUUID);
synchronized(this) {_addMap(newMap);}
}
catch(InvalidConfigurationException ex)
{
PluginLogger.warning("Could not load map data : ", ex);
}
}
try { checkMapLimit(0); }
catch(MapManagerException ex)
{
PluginLogger.warning("Map limit exceeded for player '{0}' ({1} maps loaded)",
playerUUID.toString(),mapList.size());
}
}
/* ****** Configuration Files management ***** */
private FileConfiguration mapConfig = null;
private File mapsFile = null;
private FileConfiguration getToolConfig()
{
if(mapConfig == null) load();
return mapConfig;
}
public void load()
{
if(mapsFile == null)
{
mapsFile = new File(ImageOnMap.getPlugin().getMapsDirectory(), playerUUID.toString() + ".yml");
if(!mapsFile.exists()) save();
}
mapConfig = YamlConfiguration.loadConfiguration(mapsFile);
loadFromConfig(getToolConfig().getConfigurationSection("PlayerMapStore"));
}
public void save()
{
if(mapsFile == null || mapConfig == null) return;
getToolConfig().set("PlayerMapStore", this.serialize());
try
{
getToolConfig().save(mapsFile);
}
catch (IOException ex)
{
PluginLogger.error("Could not save maps file for player '{0}'", ex, playerUUID.toString());
}
synchronized(this) {modified = false;}
}
}

View File

@ -1,158 +1,130 @@
/*
* Copyright (C) 2013 Moribus
* Copyright (C) 2015 ProkopyL <prokopylmc@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package fr.moribus.imageonmap.map;
import fr.moribus.imageonmap.ImageOnMap;
import fr.moribus.imageonmap.image.Renderer;
import java.awt.image.BufferedImage;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import fr.moribus.imageonmap.image.PosterImage;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import javax.imageio.ImageIO;
import org.bukkit.Bukkit;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.map.MapView;
import java.util.Map;
import java.util.UUID;
import org.bukkit.configuration.InvalidConfigurationException;
public class PosterMap extends ImageMap
{
private PosterImage image;
private final short[] mapsIDs;
protected final short[] mapsIDs;
protected final int columnCount;
protected final int rowCount;
private FileConfiguration customConfig = null;
private File customConfigFile = null;
public PosterMap(PosterImage image, Player player)
public PosterMap(UUID userUUID, short[] mapsIDs, String id, String name, int columnCount, int rowCount)
{
super(null, player.getName(), player.getWorld().getName());
this.image = image;
mapsIDs = new short[image.getImagesCount()];
for (int i = 0; i < mapsIDs.length; i++)
{
mapsIDs[i] = Bukkit.createMap(player.getWorld()).getId();
}
super(userUUID, Type.POSTER, id, name);
this.mapsIDs = mapsIDs;
this.columnCount = Math.max(columnCount, 0);
this.rowCount = Math.max(rowCount, 0);
}
public PosterMap(String id) throws Exception
public PosterMap(UUID userUUID, short[] mapsIDs, int columnCount, int rowCount)
{
this.imageName = id;
List<String> svg = getCustomConfig().getStringList(imageName);
if(svg != null && !svg.isEmpty())
{
this.ownerName = svg.get(0);
mapsIDs = new short[svg.size()-1];
for(int i = 0; i < mapsIDs.length; i++)
{
mapsIDs[i] = Short.parseShort(svg.get(i+1));
}
}
else
{
throw new Exception("Le poster est introuvable.");
}
this(userUUID, mapsIDs, null, null, columnCount, rowCount);
}
@Override
public void load()
public short[] getMapsIDs()
{
return mapsIDs;
}
@Override
public boolean managesMap(short mapID)
{
for(int i = 0; i < mapsIDs.length; i++)
{
MapView map = Bukkit.getMap(mapsIDs[i]);
SingleMap.SupprRendu(map);
map.addRenderer(new Renderer(image.getImageAt(i)));
if(mapsIDs[i] == mapID) return true;
}
return false;
}
/* ====== Serialization methods ====== */
public PosterMap(Map<String, Object> map, UUID userUUID) throws InvalidConfigurationException
{
super(map, userUUID, Type.POSTER);
columnCount = getFieldValue(map, "columns");
rowCount = getFieldValue(map, "rows");
List<Integer> idList = getFieldValue(map, "mapsIDs");
mapsIDs = new short[idList.size()];
for(int i = 0, c = idList.size(); i < c; i++)
{
mapsIDs[i] = (short) ((int) idList.get(i));
}
}
@Override
public void save() throws IOException
protected void postSerialize(Map<String, Object> map)
{
ImageOnMap plugin = ImageOnMap.getPlugin();
map.put("columns", columnCount);
map.put("rows", rowCount);
map.put("mapsIDs", mapsIDs);
}
for(int i = 0; i < mapsIDs.length; i++)
{
short mapID = mapsIDs[i];
String mapName = "map" + mapID;
File outputfile = new File("./plugins/ImageOnMap/Image/" + mapName + ".png");
ImageIO.write(image.getImageAt(i), "png", outputfile);
/* ====== Getters & Setters ====== */
// Enregistrement de la map dans la config
ArrayList<String> liste = new ArrayList<String>();
liste.add(String.valueOf(mapID));
liste.add(mapName);
liste.add(ownerName);
liste.add(worldName);
plugin.getCustomConfig().set(mapName, liste);
}
/**
* Returns the amount of columns in the poster map
* @return The number of columns, or 0 if this data is missing
*/
public int getColumnCount()
{
return columnCount;
}
plugin.saveCustomConfig();
/**
* Returns the amount of rows in the poster map
* @return The number of rows, or 0 if this data is missing
*/
public int getRowCount()
{
return rowCount;
}
public int getColumnAt(int i)
{
if(columnCount == 0) return 0;
return (i % columnCount) + 1;
}
public int getRowAt(int i)
{
if(columnCount == 0) return 0;
return (i / columnCount) + 1;
}
public boolean hasColumnData()
{
return rowCount != 0 && columnCount != 0;
}
@Override
public void give(Inventory inv)
public int getMapCount()
{
String itemName;
for(int i = 0; i < mapsIDs.length; i++)
{
itemName = "Map (row " + (image.getLineAt(i) + 1) + ", column " + (image.getColumnAt(i) + 1) + ")";
give(inv, mapsIDs[i], itemName);
}
}
@Override
public void setImage(BufferedImage image)
{
}
@Override
public void send(Player player)
{
for(short mapID: mapsIDs)
{
player.sendMap(Bukkit.getMap(mapID));
}
}
private void reloadCustomConfig()
{
ImageOnMap plugin = (ImageOnMap)Bukkit.getPluginManager().getPlugin("ImageOnMap");
if (customConfigFile == null)
{
customConfigFile = new File(plugin.getDataFolder(), "poster.yml");
}
customConfig = YamlConfiguration.loadConfiguration(customConfigFile);
// Look for defaults in the jar
InputStream defConfigStream = plugin.getResource("poster.yml");
if (defConfigStream != null)
{
YamlConfiguration defConfig = YamlConfiguration.loadConfiguration(defConfigStream);
customConfig.setDefaults(defConfig);
}
}
private FileConfiguration getCustomConfig()
{
if (customConfig == null)
{
reloadCustomConfig();
}
return customConfig;
}
private void saveCustomConfig() throws IOException
{
ImageOnMap plugin = (ImageOnMap)Bukkit.getPluginManager().getPlugin("ImageOnMap");
if (customConfig == null || customConfigFile == null) {
return;
}
getCustomConfig().save(customConfigFile);
return mapsIDs.length;
}
}

View File

@ -1,118 +1,73 @@
/*
* Copyright (C) 2013 Moribus
* Copyright (C) 2015 ProkopyL <prokopylmc@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package fr.moribus.imageonmap.map;
import fr.moribus.imageonmap.ImageOnMap;
import fr.moribus.imageonmap.ImgUtility;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.map.MapView;
import fr.moribus.imageonmap.image.Renderer;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.imageio.ImageIO;
import org.bukkit.map.MapRenderer;
import java.util.Map;
import java.util.UUID;
import org.bukkit.configuration.InvalidConfigurationException;
public class SingleMap extends ImageMap
{
private final short mapID;
private BufferedImage image;
protected final short mapID;
public SingleMap(BufferedImage img, Player player)
{
this(img, null, player);
}
public SingleMap(BufferedImage image, String imageName, Player player)
{
super(imageName, player.getName(), player.getWorld().getName());
this.mapID = Bukkit.createMap(player.getWorld()).getId();
this.image = ImgUtility.scaleImage(image, WIDTH, HEIGHT);
}
public SingleMap(short mapID) throws IOException, IllegalArgumentException
public SingleMap(UUID ownerUUID, short mapID, String id, String name)
{
super(ownerUUID, Type.SINGLE, id, name);
this.mapID = mapID;
}
//Testing if the map id exists
MapView map = Bukkit.getMap(mapID);
if(map == null)
throw new IllegalArgumentException("Map ID '" + mapID + "' doesn't exist");
List<String> svg = ImageOnMap.getPlugin().getCustomConfig().getStringList("map" + mapID);
String nomImg = svg.get(1);
ownerName = svg.get(2);
image = ImageIO.read(new File("./plugins/ImageOnMap/Image/" + nomImg + ".png"));
public SingleMap(UUID ownerUUID, short mapID)
{
this(ownerUUID, mapID, null, null);
}
@Override
public void save() throws IOException
public short[] getMapsIDs()
{
String mapName = "map" + mapID;
ImageOnMap plugin = ImageOnMap.getPlugin();
File outputfile = new File("./plugins/ImageOnMap/Image/" + mapName + ".png");
ImageIO.write(image, "png", outputfile);
// Enregistrement de la map dans la config
ArrayList<String> liste = new ArrayList<String>();
liste.add(String.valueOf(mapID));
liste.add(mapName);
liste.add(ownerName);
liste.add(worldName);
plugin.getCustomConfig().set(mapName, liste);
plugin.saveCustomConfig();
}
@SuppressWarnings("deprecation")
@Override
public void give(Inventory inventory)
{
give(inventory, mapID);
return new short[]{mapID};
}
@Override
public void load()
public boolean managesMap(short mapID)
{
MapView map = Bukkit.getMap(mapID);
SingleMap.SupprRendu(map);
map.addRenderer(new Renderer(image));
return this.mapID == mapID;
}
@Override
public boolean isNamed()
public int getMapCount()
{
return imageName != null;
return 1;
}
public short getId()
/* ====== Serialization methods ====== */
public SingleMap(Map<String, Object> map, UUID userUUID) throws InvalidConfigurationException
{
return mapID;
super(map, userUUID, Type.SINGLE);
int _mapID = getFieldValue(map, "mapID");
mapID = (short) _mapID;//Meh
}
@Override
public void setImage(BufferedImage image)
protected void postSerialize(Map<String, Object> map)
{
this.image = image;
load();
}
@Override
public void send(Player joueur)
{
joueur.sendMap(Bukkit.getMap(mapID));
}
public static void SupprRendu(MapView map)
{
for(MapRenderer renderer : map.getRenderers())
{
map.removeRenderer(renderer);
}
map.put("mapID", mapID);
}
}

View File

@ -0,0 +1,59 @@
/*
* Copyright (C) 2013 Moribus
* Copyright (C) 2015 ProkopyL <prokopylmc@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package fr.moribus.imageonmap.migration;
import fr.moribus.imageonmap.ImageOnMap;
import fr.moribus.imageonmap.PluginLogger;
public class MigratorExecutor
{
static private Thread migratorThread;
static public void migrate()
{
if(isRunning())
{
PluginLogger.error("Migration is already running.");
return;
}
migratorThread = new Thread(new V3Migrator(ImageOnMap.getPlugin()), "ImageOnMap-Migration");
migratorThread.start();
}
static public boolean isRunning()
{
return migratorThread != null && migratorThread.isAlive();
}
static public void waitForMigration()
{
if(isRunning())
{
PluginLogger.info("Waiting for migration to finish ...");
try
{
migratorThread.join();
}
catch(InterruptedException ex)
{
PluginLogger.error("Migration thread has been interrupted while wating to finish. It may not have ended correctly.");
}
}
}
}

View File

@ -0,0 +1,78 @@
/*
* Copyright (C) 2013 Moribus
* Copyright (C) 2015 ProkopyL <prokopylmc@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package fr.moribus.imageonmap.migration;
import fr.moribus.imageonmap.map.ImageMap;
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<String> data;
try
{
data = (List<String>) 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<String> data = new ArrayList<String>();
data.add(Short.toString(mapId));
data.add(mapName);
data.add(userName);
configuration.set(mapName, data);
}
public short getMapId() {return mapId;}
public String getUserName() {return userName;}
}

View File

@ -0,0 +1,98 @@
/*
* Copyright (C) 2013 Moribus
* Copyright (C) 2015 ProkopyL <prokopylmc@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package fr.moribus.imageonmap.migration;
import fr.moribus.imageonmap.map.ImageMap;
import fr.moribus.imageonmap.map.PosterMap;
import java.util.ArrayList;
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<String> data;
try
{
data = (List<String>) 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(int i = 0, c = mapsIds.length; i < c; i++)
{
if(mapsIds[i] == mapId) return true;
}
return false;
}
public ImageMap toImageMap(UUID userUUID)
{
return new PosterMap(userUUID, mapsIds, null, "poster", 0, 0);
}
public void serialize(Configuration configuration)
{
ArrayList<String> data = new ArrayList<String>();
data.add(userName);
for(short mapId : mapsIds)
{
data.add(Short.toString(mapId));
}
configuration.set(posterName, data);
}
public String getUserName() {return userName;}
}

View File

@ -0,0 +1,222 @@
/*
* Copyright (C) 2013 Moribus
* Copyright (C) 2015 ProkopyL <prokopylmc@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package fr.moribus.imageonmap.migration;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
abstract public class UUIDFetcher
{
/**
* The maximal amount of usernames to send to mojang per request
* This allows not to overload mojang's service with too many usernames at a time
*/
static private final int MOJANG_USERNAMES_PER_REQUEST = 100;
/**
* The maximal amount of requests to send to Mojang
* The time limit for this amount is MOJANG_MAX_REQUESTS_TIME
* Read : You can only send MOJANG_MAX_REQUESTS in MOJANG_MAX_REQUESTS_TIME seconds
*/
static private final int MOJANG_MAX_REQUESTS = 600;
/**
* The timeframe for the Mojang request limit (in seconds)
*/
static private final int MOJANG_MAX_REQUESTS_TIME = 600;
/**
* The minimum time between two requests to mojang (in milliseconds)
*/
static private final int TIME_BETWEEN_REQUESTS = 200;
/**
* The (approximative) timestamp of the date when Mojang name changing feature
* was announced to be released
*/
static private final int NAME_CHANGE_TIMESTAMP = 1420844400;
static private final String PROFILE_URL = "https://api.mojang.com/profiles/minecraft";
static private final String TIMED_PROFILE_URL = "https://api.mojang.com/users/profiles/minecraft/";
static public Map<String, UUID> fetch(List<String> names) throws IOException, InterruptedException
{
return fetch(names, MOJANG_USERNAMES_PER_REQUEST);
}
static public Map<String, UUID> fetch(List<String> names, int limitByRequest) throws IOException, InterruptedException
{
Map<String, UUID> UUIDs = new HashMap<String, UUID>();
int requests = (names.size() / limitByRequest) + 1;
List<String> tempNames;
Map<String, UUID> tempUUIDs;
for(int i = 0; i < requests; i++)
{
tempNames = names.subList(limitByRequest * i, Math.min((limitByRequest * (i+1)) - 1, names.size()));
tempUUIDs = rawFetch(tempNames);
UUIDs.putAll(tempUUIDs);
Thread.sleep(TIME_BETWEEN_REQUESTS);
}
return UUIDs;
}
static private Map<String, UUID> rawFetch(List<String> names) throws IOException
{
Map<String, UUID> uuidMap = new HashMap<String, UUID>();
HttpURLConnection connection = getPOSTConnection(PROFILE_URL);
writeBody(connection, names);
JSONArray array;
try
{
array = (JSONArray) readResponse(connection);
}
catch(ParseException ex)
{
throw new IOException("Invalid response from server, unable to parse received JSON : " + ex.toString());
}
for (Object profile : array)
{
JSONObject jsonProfile = (JSONObject) profile;
String id = (String) jsonProfile.get("id");
String name = (String) jsonProfile.get("name");
uuidMap.put(name, fromMojangUUID(id));
}
return uuidMap;
}
static public void fetchRemaining(Collection<String> names, Map<String, UUID> uuids) throws IOException, InterruptedException
{
ArrayList<String> remainingNames = new ArrayList<>();
for(String name : names)
{
if(!uuids.containsKey(name)) remainingNames.add(name);
}
int timeBetweenRequests;
if(remainingNames.size() > MOJANG_MAX_REQUESTS)
{
timeBetweenRequests = (MOJANG_MAX_REQUESTS / MOJANG_MAX_REQUESTS_TIME) * 1000;
}
else
{
timeBetweenRequests = TIME_BETWEEN_REQUESTS;
}
User user;
for(String name : remainingNames)
{
user = fetchOriginalUUID(name);
uuids.put(name, user.uuid);
Thread.sleep(timeBetweenRequests);
}
}
static private User fetchOriginalUUID(String name) throws IOException
{
HttpURLConnection connection = getGETConnection(TIMED_PROFILE_URL + name + "?at=" + NAME_CHANGE_TIMESTAMP);
JSONObject object;
try
{
object = (JSONObject) readResponse(connection);
}
catch(ParseException ex)
{
throw new IOException("Invalid response from server, unable to parse received JSON : " + ex.toString());
}
User user = new User();
user.name = (String) object.get("name");
user.uuid = fromMojangUUID((String)object.get("id"));
return user;
}
static private HttpURLConnection getPOSTConnection(String url) throws IOException
{
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type", "application/json");
connection.setUseCaches(false);
connection.setDoInput(true);
connection.setDoOutput(true);
return connection;
}
static private HttpURLConnection getGETConnection(String url) throws IOException
{
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
connection.setRequestMethod("GET");
connection.setUseCaches(false);
connection.setDoInput(true);
connection.setDoOutput(true);
return connection;
}
private static void writeBody(HttpURLConnection connection, List<String> names) throws IOException
{
OutputStream stream = connection.getOutputStream();
String body = JSONArray.toJSONString(names);
stream.write(body.getBytes());
stream.flush();
stream.close();
}
private static Object readResponse(HttpURLConnection connection) throws IOException, ParseException
{
return new JSONParser().parse(new InputStreamReader(connection.getInputStream()));
}
private static UUID fromMojangUUID(String id) //Mojang sends string UUIDs without dashes ...
{
return UUID.fromString(id.substring(0, 8) + "-" + id.substring(8, 12) + "-" +
id.substring(12, 16) + "-" + id.substring(16, 20) + "-" +
id.substring(20, 32));
}
static private class User
{
public String name;
public UUID uuid;
}
}

View File

@ -0,0 +1,611 @@
/*
* Copyright (C) 2013 Moribus
* Copyright (C) 2015 ProkopyL <prokopylmc@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package fr.moribus.imageonmap.migration;
import fr.moribus.imageonmap.ImageOnMap;
import fr.moribus.imageonmap.PluginLogger;
import fr.moribus.imageonmap.map.MapManager;
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.HashSet;
import java.util.Map;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;
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
*/
static private final String OLD_IMAGES_DIRECTORY_NAME = "Image";
/**
* The name of the former file that contained all the maps definitions (including posters)
*/
static private final String OLD_MAPS_FILE_NAME = "map.yml";
/**
* The name of the former file that contained all the posters definitions
*/
static private 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
*/
static private 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
*/
static private final String BACKUPS_POSTV3_DIRECTORY_NAME = "backups_post-v3";
/**
* Returns the former images directory of a given plugin
* @param plugin The plugin.
* @return the corresponding 'Image' directory
*/
static public File getOldImagesDirectory(Plugin plugin)
{
return new File(plugin.getDataFolder(), OLD_IMAGES_DIRECTORY_NAME);
}
/**
* The plugin that is running the migration
*/
private final ImageOnMap plugin;
/**
* 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 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<OldSavedPoster> postersToMigrate;
/**
* The list of all the single maps to migrate
*/
private final ArrayDeque<OldSavedMap> mapsToMigrate;
/**
* The set of all the user names to retreive the UUID from Mojang
*/
private final HashSet<String> userNamesToFetch;
/**
* The map of all the usernames and their corresponding UUIDs
*/
private Map<String, UUID> 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<>();
}
/**
* 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("Error while preparing migration");
PluginLogger.error("Aborting migration. No change has been made.", ex);
return;
}
try
{
mergeMapData();
saveChanges();
cleanup();
}
catch(Exception ex)
{
PluginLogger.error("Error while migrating", ex);
PluginLogger.error("Aborting migration. Some changes may already have been made.");
PluginLogger.error("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.");
Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, null, ex);
}
}
/* ****** Actions ***** */
/**
* 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("Looking for configuration files to migrate ...");
if(!oldPostersFile.exists()) oldPostersFile = null;
else PluginLogger.info("Detected former posters file {0}", OLD_POSTERS_FILE_NAME);
if(!oldMapsFile.exists()) oldMapsFile = null;
else PluginLogger.info("Detected former maps file {0}", OLD_MAPS_FILE_NAME);
if(oldPostersFile == null && oldMapsFile == null)
{
PluginLogger.info("There is nothing to migrate. Stopping.");
return false;
}
else
{
PluginLogger.info("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("Backup directories already exists.");
PluginLogger.error("This means that a migration has already been done, or may not have ended well.");
PluginLogger.error("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("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("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);
userNamesToFetch.add(oldPoster.getUserName());
postersToMigrate.add(oldPoster);
}
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);
}
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("Fetching UUIDs from Mojang ...");
try
{
usersUUIDs = UUIDFetcher.fetch(new ArrayList<String>(userNamesToFetch));
}
catch(IOException ex)
{
PluginLogger.error("An error occured while fetching the UUIDs from Mojang", ex);
throw ex;
}
catch(InterruptedException ex)
{
PluginLogger.error("The migration worker has been interrupted", ex);
throw ex;
}
PluginLogger.info("Fetching done. {0} UUIDs have been retreived.", usersUUIDs.size());
}
/**
* Fetches the UUIDs that could not be retreived via Mojang's standard API
* @return true if at least one UUID has been retreived, false otherwise
*/
private boolean fetchMissingUUIDs() throws IOException, InterruptedException
{
if(usersUUIDs.size() == userNamesToFetch.size()) return true;
int remainingUsersCount = userNamesToFetch.size() - usersUUIDs.size();
PluginLogger.info("Mojang did not find UUIDs for {0} players at the current time.", remainingUsersCount);
PluginLogger.info("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("An error occured while fetching the UUIDs from Mojang");
throw ex;
}
catch(InterruptedException ex)
{
PluginLogger.error("The migration worker has been interrupted");
throw ex;
}
if(usersUUIDs.size() != userNamesToFetch.size())
{
PluginLogger.warning("Mojang did not find player data for {0} players",
userNamesToFetch.size() - usersUUIDs.size());
PluginLogger.warning("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("Mojang could not find any of the registered players.");
PluginLogger.info("There is nothing to migrate. Stopping.");
return false;
}
return true;
}
private void mergeMapData()
{
PluginLogger.info("Merging map data ...");
ArrayDeque<OldSavedMap> remainingMaps = new ArrayDeque<>();
ArrayDeque<OldSavedPoster> remainingPosters = new ArrayDeque<>();
UUID playerUUID;
OldSavedMap map;
while(!mapsToMigrate.isEmpty())
{
map = mapsToMigrate.pop();
playerUUID = usersUUIDs.get(map.getUserName());
if(playerUUID == null)
{
remainingMaps.add(map);
}
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
{
MapManager.insertMap(poster.toImageMap(playerUUID));
}
}
postersToMigrate.addAll(remainingPosters);
}
private void saveChanges()
{
PluginLogger.info("Saving changes ...");
MapManager.save();
}
private void cleanup() throws IOException
{
PluginLogger.info("Cleaning up old data files ...");
//Cleaning maps file
if(oldMapsFile != null)
{
if(mapsToMigrate.isEmpty())
{
PluginLogger.info("Deleting old map data file ...");
oldMapsFile.delete();
}
else
{
PluginLogger.info("{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("Deleting old poster data file ...");
oldPostersFile.delete();
}
else
{
PluginLogger.info("{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("Data that has not been migrated will be kept in the old data files.");
}
/* ****** Utils ***** */
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);
}
/**
* 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
*/
static private 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.");
}
}
/**
* 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
*/
static private 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();
}
}

View File

@ -0,0 +1,141 @@
/*
* Copyright (C) 2013 Moribus
* Copyright (C) 2015 ProkopyL <prokopylmc@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package fr.moribus.imageonmap.ui;
import fr.moribus.imageonmap.map.ImageMap;
import fr.moribus.imageonmap.map.PosterMap;
import fr.moribus.imageonmap.map.SingleMap;
import java.util.ArrayDeque;
import java.util.HashMap;
import java.util.Queue;
import java.util.UUID;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
public class MapItemManager implements Listener
{
static private HashMap<UUID, Queue<ItemStack>> mapItemCache;
static public void init()
{
mapItemCache = new HashMap();
}
static public void exit()
{
if(mapItemCache != null) mapItemCache.clear();
mapItemCache = null;
}
static public 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);
return false;
}
static public boolean give(Player player, SingleMap map)
{
return give(player, createMapItem(map.getMapsIDs()[0], map.getName()));
}
static public boolean give(Player player, PosterMap map)
{
short[] mapsIDs = map.getMapsIDs();
boolean inventoryFull = false;
String mapName;
for(int i = 0, c = mapsIDs.length; i < c; i++)
{
if(map.hasColumnData())
{
mapName = map.getName() +
" (row " + map.getRowAt(i) +
", column " + map.getColumnAt(i) + ")";
}
else
{
mapName = map.getName();
}
inventoryFull = give(player, createMapItem(mapsIDs[i], mapName)) || inventoryFull;
}
return inventoryFull;
}
static public int giveCache(Player player)
{
Queue<ItemStack> cache = getCache(player);
Inventory inventory = player.getInventory();
int givenItemsCount = 0;
while(inventory.firstEmpty() >= 0 && !cache.isEmpty())
{
inventory.addItem(cache.poll());
givenItemsCount++;
}
return givenItemsCount;
}
static private boolean give(Player player, ItemStack item)
{
if(player.getInventory().firstEmpty() <= -1)
{
getCache(player).add(item);
return true;
}
else
{
player.getInventory().addItem(item);
return false;
}
}
static public ItemStack createMapItem(short mapID, String text)
{
ItemStack itemMap = new ItemStack(Material.MAP, 1, mapID);
ItemMeta meta = itemMap.getItemMeta();
meta.setDisplayName(text);
itemMap.setItemMeta(meta);
return itemMap;
}
static public int getCacheSize(Player player)
{
return getCache(player).size();
}
static private Queue<ItemStack> getCache(Player player)
{
Queue<ItemStack> cache = mapItemCache.get(player.getUniqueId());
if(cache == null)
{
cache = new ArrayDeque<>();
mapItemCache.put(player.getUniqueId(), cache);
}
return cache;
}
}

View File

@ -0,0 +1,132 @@
/*
* Copyright (C) 2013 Moribus
* Copyright (C) 2015 ProkopyL <prokopylmc@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package fr.moribus.imageonmap.worker;
import fr.moribus.imageonmap.PluginLogger;
import java.util.ArrayDeque;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
public abstract class Worker
{
private final String name;
private final ArrayDeque<WorkerRunnable> runQueue = new ArrayDeque<>();
private final WorkerCallbackManager callbackManager;
private final WorkerMainThreadExecutor mainThreadExecutor;
private Thread thread;
protected Worker(String name)
{
this(name, false);
}
protected Worker(String name, boolean runMainThreadExecutor)
{
this.name = name;
this.callbackManager = new WorkerCallbackManager(name);
this.mainThreadExecutor = runMainThreadExecutor ? new WorkerMainThreadExecutor(name) : null;
}
protected void init()
{
if(thread != null && thread.isAlive())
{
PluginLogger.warning("Restarting '{0}' thread.", name);
exit();
}
callbackManager.init();
if(mainThreadExecutor != null) mainThreadExecutor.init();
thread = createThread();
thread.start();
}
protected void exit()
{
thread.interrupt();
callbackManager.exit();
if(mainThreadExecutor != null) mainThreadExecutor.exit();
thread = null;
}
private void run()
{
WorkerRunnable currentRunnable;
while(!Thread.interrupted())
{
synchronized(runQueue)
{
try
{
while(runQueue.isEmpty()) runQueue.wait();
}
catch(InterruptedException ex)
{
break;
}
currentRunnable = runQueue.pop();
}
try
{
callbackManager.callback(currentRunnable, currentRunnable.run());
}
catch(Throwable ex)
{
callbackManager.callback(currentRunnable, null, ex);
}
}
}
protected void submitQuery(WorkerRunnable runnable)
{
synchronized(runQueue)
{
runQueue.add(runnable);
runQueue.notify();
}
}
protected void submitQuery(WorkerRunnable runnable, WorkerCallback callback)
{
callbackManager.setupCallback(runnable, callback);
submitQuery(runnable);
}
protected <T> Future<T> submitToMainThread(Callable<T> callable)
{
if(mainThreadExecutor != null) return mainThreadExecutor.submit(callable);
return null;
}
private Thread createThread()
{
return new Thread("ImageOnMap-" + name)
{
@Override
public void run()
{
Worker.this.run();
}
};
}
}

View File

@ -0,0 +1,24 @@
/*
* Copyright (C) 2013 Moribus
* Copyright (C) 2015 ProkopyL <prokopylmc@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package fr.moribus.imageonmap.worker;
public interface WorkerCallback<T>
{
public void finished(T result);
public void errored(Throwable exception);
}

View File

@ -0,0 +1,152 @@
/*
* Copyright (C) 2013 Moribus
* Copyright (C) 2015 ProkopyL <prokopylmc@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package fr.moribus.imageonmap.worker;
import fr.moribus.imageonmap.ImageOnMap;
import java.util.ArrayDeque;
import java.util.HashMap;
import org.bukkit.Bukkit;
import org.bukkit.scheduler.BukkitTask;
class WorkerCallbackManager implements Runnable
{
static private final int WATCH_LOOP_DELAY = 5;
private final HashMap<WorkerRunnable, WorkerRunnableInfo> callbacks;
private final ArrayDeque<WorkerRunnableInfo> callbackQueue;
private final String name;
private BukkitTask selfTask;
public WorkerCallbackManager(String name)
{
callbacks = new HashMap<>();
callbackQueue = new ArrayDeque<>();
this.name = name;
}
public void init()
{
selfTask = Bukkit.getScheduler().runTaskTimer(ImageOnMap.getPlugin(), this, 0, WATCH_LOOP_DELAY);
}
public void setupCallback(WorkerRunnable runnable, WorkerCallback callback)
{
synchronized(callbacks)
{
callbacks.put(runnable, new WorkerRunnableInfo(callback));
}
}
public <T> void callback(WorkerRunnable<T> runnable, T result)
{
callback(runnable, result, null);
}
public <T> void callback(WorkerRunnable<T> runnable, T result, Throwable exception)
{
WorkerRunnableInfo<T> runnableInfo;
synchronized(callbacks)
{
runnableInfo = callbacks.get(runnable);
}
if(runnableInfo == null) return;
runnableInfo.setRunnableException(exception);
runnableInfo.setResult(result);
enqueueCallback(runnableInfo);
}
public void exit()
{
if(selfTask != null) selfTask.cancel();
}
private void enqueueCallback(WorkerRunnableInfo runnableInfo)
{
synchronized(callbackQueue)
{
callbackQueue.add(runnableInfo);
}
}
@Override
public void run()
{
WorkerRunnableInfo currentRunnableInfo;
synchronized(callbackQueue)
{
if(callbackQueue.isEmpty()) return;
currentRunnableInfo = callbackQueue.pop();
}
currentRunnableInfo.runCallback();
}
private class WorkerRunnableInfo<T>
{
private final WorkerCallback<T> callback;
private T result;
private Throwable runnableException;
public WorkerRunnableInfo(WorkerCallback callback)
{
this.callback = callback;
this.runnableException = null;
}
public WorkerCallback getCallback()
{
return callback;
}
public void runCallback()
{
if(runnableCrashed())
{
callback.errored(runnableException);
}
else
{
callback.finished(result);
}
}
public void setResult(T result)
{
this.result = result;
}
public Throwable getRunnableException()
{
return runnableException;
}
public void setRunnableException(Throwable runnableException)
{
this.runnableException = runnableException;
}
public boolean runnableCrashed()
{
return this.runnableException != null;
}
}
}

View File

@ -0,0 +1,199 @@
/*
* Copyright (C) 2013 Moribus
* Copyright (C) 2015 ProkopyL <prokopylmc@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package fr.moribus.imageonmap.worker;
import fr.moribus.imageonmap.ImageOnMap;
import java.util.ArrayDeque;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.bukkit.Bukkit;
import org.bukkit.scheduler.BukkitTask;
class WorkerMainThreadExecutor implements Runnable
{
static private final int WATCH_LOOP_DELAY = 1;
private final String name;
private final ArrayDeque<WorkerFuture> mainThreadQueue = new ArrayDeque<>();
private BukkitTask mainThreadTask;
public WorkerMainThreadExecutor(String name)
{
this.name = name;
}
public void init()
{
mainThreadTask = Bukkit.getScheduler().runTaskTimer(ImageOnMap.getPlugin(), this, 0, WATCH_LOOP_DELAY);
}
public void exit()
{
mainThreadTask.cancel();
mainThreadTask = null;
}
public <T> Future<T> submit(Callable<T> callable)
{
WorkerFuture<T> future = new WorkerFuture<T>(callable);
synchronized(mainThreadQueue)
{
mainThreadQueue.add(future);
}
return future;
}
@Override
public void run()
{
WorkerFuture currentFuture;
synchronized(mainThreadQueue)
{
if(mainThreadQueue.isEmpty()) return;
currentFuture = mainThreadQueue.pop();
}
currentFuture.runCallable();
}
private class WorkerFuture<T> implements Future<T>
{
private final Callable<T> callable;
private boolean isCancelled;
private boolean isDone;
private Exception executionException;
private T value;
public WorkerFuture(Callable<T> callable)
{
this.callable = callable;
}
public void runCallable()
{
try
{
value = callable.call();
}
catch(Exception ex)
{
executionException = ex;
}
finally
{
isDone = true;
synchronized(this){this.notifyAll();}
}
}
@Override
public boolean cancel(boolean mayInterruptIfRunning)
{
if(this.isCancelled || this.isDone) return false;
this.isCancelled = true;
this.isDone = true;
return true;
}
@Override
public boolean isCancelled()
{
return this.isCancelled;
}
@Override
public boolean isDone()
{
return this.isDone;
}
@Override
public T get() throws InterruptedException, ExecutionException
{
waitForCompletion();
if(executionException != null) throw new ExecutionException(executionException);
return value;
}
@Override
public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException
{
waitForCompletion(timeout, unit);
if(executionException != null) throw new ExecutionException(executionException);
return value;
}
private void waitForCompletion(long timeout) throws InterruptedException, TimeoutException
{
synchronized(this)
{
long remainingTime;
long timeoutTime = System.currentTimeMillis() + timeout;
while(!isDone)
{
remainingTime = timeoutTime - System.currentTimeMillis();
if(remainingTime <= 0) throw new TimeoutException();
this.wait(remainingTime);
}
}
}
private void waitForCompletion() throws InterruptedException
{
synchronized(this)
{
while(!isDone) this.wait();
}
}
private void waitForCompletion(long timeout, TimeUnit unit) throws InterruptedException, TimeoutException
{
long millis = 0;
switch(unit)
{
case NANOSECONDS:
millis = timeout / 10^6;
break;
case MICROSECONDS:
millis = timeout / 10^3;
break;
case MILLISECONDS:
millis = timeout;
break;
case SECONDS:
millis = timeout * 10^3;
break;
case MINUTES:
millis = timeout * 10^3 * 60;
break;
case HOURS:
millis = timeout * 10^3 * 3600;
break;
case DAYS:
millis = timeout * 10^3 * 3600 * 24;
}
waitForCompletion(millis);
}
}
}

View File

@ -0,0 +1,23 @@
/*
* Copyright (C) 2013 Moribus
* Copyright (C) 2015 ProkopyL <prokopylmc@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package fr.moribus.imageonmap.worker;
public interface WorkerRunnable<T>
{
public T run() throws Throwable;
}

View File

@ -0,0 +1,8 @@
This command manages and creates ImagesOnMaps.
new: Creates a new ImageOnMap
delete: Deletes a map.
delete-noconfirm: Deletes a map. Deletion is permanent and made without confirmation
get: Gives you a map.
getremaining: Gives you the remaining maps that could not fit in your inventory
list: Lists all the map you currently have.
help : Use help for more information about a command.

View File

@ -0,0 +1,4 @@
Deletes a map, and removes all parts and copies from your inventory.
§cWARNING: §r THERE WILL BE NO CONFIRMATION. MAP DELETION IS §lPERMANENT§r.
[map name] : The name of the map you want to delete.

View File

@ -0,0 +1,5 @@
Deletes a map, and removes all parts and copies from your inventory.
As deletion is permanent, a confirmation message will be shown
to the sending player.
[map name] : The name of the map you want to delete.

View File

@ -0,0 +1,3 @@
Gives you a map, by putting it directly in your inventory.
<map name> : The name of the map you want to get.

View File

@ -0,0 +1,5 @@
Gives you the remaining maps that could not fit in your inventory.
If you requested a multi-part (poster) map, and all the parts can't fit in
your inventory, then only first ones are given to you, and the remaining
ones are saved. You can then use this command to retreive them.

View File

@ -0,0 +1 @@
Gives you a list of all the maps you currently have.

View File

@ -0,0 +1,36 @@
Migrates the Map database to the new V3.x format, that uses UUIDs
instead of player names to designate players (among other improvements).
Migration runs in a separate thread, therefore its progress can only be
watched from the server console.
The migration will run the following steps :
- Checking if there are files to migrate. If not, the migration stops.
- Checking if there are backups from a previous migration.
If there are, the migration stops.
- Loading the old map and poster data to memory.
- Backing up old files and new files, to the backups_pre-v3 and
backups_post-v3 subdirectories respectively.
Backup's integrity are chacked using file size and SHA1 checksum.
If integrity could not be proved, the migration stops.
- Retreiving the UUIDs of the players from Mojang's servers.
- Checking if some UUIDs could not be retreived.
If there are, this means some of your players may have changed names
before the migration started. The plugin will therefore try to retreive
them specifying a time, back when usernames could not be changed.
If some names could still not be matched to their UUIDs, then these are
probably non-paid accounts.
If no UUID has been retreived at all, the migration stops.
§c--- From this step, changes to disk will be made, and you will have to use§r
§c--- backups if you want to revert back from before the migration started.§r
- Merging the old map data with the new one, if there is any
(which can be the case if your player started to use newer
versions of ImageOnMap before the migration started).
- Saving all this merged map data to disk.
- Removing the old map data from their former files, leaving only the data
that could not be migrated due to usernames that could not be matched to
their UUIDs.
Original data is still present in the appropriate backup directory,
just in case.
Note that this plugin will NEVER delete nor overwrite any backup directory.
Moving or deleting these backups is left to the administrator's responsibility.

View File

@ -0,0 +1 @@
Creates a new ImageOnMap.

View File

@ -8,8 +8,6 @@ commands:
usage: /<command> [URL]
maptool:
description: manage maps
usage: /<command>
permissions:
imageonmap.userender:
description: Allows you to use /tomap