Migration is now fully functional !

* NEW: Refactored PluginLogger for multi-thread usage and message formatting.
* NEW: Posters and maps that could not be migrated are now saved back
  to their original files.
* NEW: Moved the maptool-migration command to the /maptool migrate subcommand.
* NEW: PosterMap can now be used without having row/column count information.
* NEW: Added a detailed help message for the migration command.
* BUG: Fixed UUIDFetcher's HTTP GET request sending.
* BUG: Fixed Name->UUID time-specific matching.
* OPT: Removed the CommandPermission structure, it is actually useless.
* OPT: Renamed Migrator to MigratorExecutor.
This commit is contained in:
Prokopyl 2015-04-12 01:28:05 +02:00
parent 6725c15002
commit 2a49d94d1e
26 changed files with 526 additions and 336 deletions

View File

@ -23,7 +23,7 @@ import fr.moribus.imageonmap.image.ImageIOExecutor;
import fr.moribus.imageonmap.image.ImageRendererExecutor; import fr.moribus.imageonmap.image.ImageRendererExecutor;
import fr.moribus.imageonmap.image.MapInitEvent; import fr.moribus.imageonmap.image.MapInitEvent;
import fr.moribus.imageonmap.map.MapManager; import fr.moribus.imageonmap.map.MapManager;
import fr.moribus.imageonmap.migration.Migrator; import fr.moribus.imageonmap.migration.MigratorExecutor;
import fr.moribus.imageonmap.migration.V3Migrator; import fr.moribus.imageonmap.migration.V3Migrator;
import fr.moribus.imageonmap.ui.MapItemManager; import fr.moribus.imageonmap.ui.MapItemManager;
import java.io.File; import java.io.File;
@ -61,6 +61,7 @@ public final class ImageOnMap extends JavaPlugin
@Override @Override
public void onEnable() public void onEnable()
{ {
PluginLogger.init(this);
// Creating the images and maps directories if necessary // Creating the images and maps directories if necessary
try try
{ {
@ -69,7 +70,7 @@ public final class ImageOnMap extends JavaPlugin
} }
catch(IOException ex) catch(IOException ex)
{ {
PluginLogger.LogError("FATAL : " + ex.getMessage(), null); PluginLogger.error("FATAL : " + ex.getMessage(), null);
this.setEnabled(false); this.setEnabled(false);
return; return;
} }
@ -83,7 +84,6 @@ public final class ImageOnMap extends JavaPlugin
Commands.init(this); Commands.init(this);
MapInitEvent.init(this); MapInitEvent.init(this);
MapItemManager.init(); MapItemManager.init();
Migrator.startWorker();
} }
@Override @Override
@ -93,7 +93,8 @@ public final class ImageOnMap extends JavaPlugin
ImageRendererExecutor.stop(); ImageRendererExecutor.stop();
MapManager.exit(); MapManager.exit();
MapItemManager.exit(); MapItemManager.exit();
Migrator.stopWorker(); MigratorExecutor.waitForMigration();
PluginLogger.exit();
} }
private File checkPluginDirectory(File primaryFile, File... alternateFiles) throws IOException private File checkPluginDirectory(File primaryFile, File... alternateFiles) throws IOException

View File

@ -65,7 +65,7 @@ public class MetricsLite
} }
catch (IOException e) catch (IOException e)
{ {
PluginLogger.LogError("Failed to start MetricsLite", e); PluginLogger.error("Failed to start MetricsLite", e);
} }
} }
@ -197,7 +197,7 @@ public class MetricsLite
firstPost = false; firstPost = false;
} catch (IOException e) { } catch (IOException e) {
if (debug) { if (debug) {
PluginLogger.LogWarning("[Metrics] ", e); PluginLogger.warning("[Metrics] ", e);
} }
} }
} }
@ -219,7 +219,7 @@ public class MetricsLite
configuration.load(getConfigFile()); configuration.load(getConfigFile());
} catch (IOException | InvalidConfigurationException ex) { } catch (IOException | InvalidConfigurationException ex) {
if (debug) { if (debug) {
PluginLogger.LogInfo("[Metrics] " + ex.getMessage()); PluginLogger.info("[Metrics] " + ex.getMessage());
} }
return true; return true;
} }
@ -408,7 +408,7 @@ public class MetricsLite
gzos = new GZIPOutputStream(baos); gzos = new GZIPOutputStream(baos);
gzos.write(input.getBytes("UTF-8")); gzos.write(input.getBytes("UTF-8"));
} catch (IOException e) { } catch (IOException e) {
PluginLogger.LogError("MetricsLite GZIP error : ", e); PluginLogger.error("MetricsLite GZIP error : ", e);
} finally { } finally {
if (gzos != null) try { if (gzos != null) try {
gzos.close(); gzos.close();

View File

@ -18,33 +18,112 @@
package fr.moribus.imageonmap; package fr.moribus.imageonmap;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger; import java.util.logging.Logger;
import org.bukkit.plugin.Plugin;
abstract public class PluginLogger 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() 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) @Override
public void log(LogRecord logRecord)
{ {
getLogger().log(Level.WARNING, message, ex); logRecord.setMessage(loggerName + logRecord.getMessage());
super.log(logRecord);
} }
static public void LogError(String message, Throwable ex)
{
getLogger().log(Level.SEVERE, message, ex);
} }
} }

View File

@ -65,18 +65,12 @@ abstract public class Command
return null; return null;
} }
public boolean hasPermission(CommandSender sender)
{
if(!commandGroup.getPermission().hasPermission(sender)) return false;
return canExecute(sender);
}
public void execute(CommandSender sender, String[] args) public void execute(CommandSender sender, String[] args)
{ {
this.sender = sender; this.args = args; this.sender = sender; this.args = args;
try try
{ {
if(!hasPermission(sender)) if(!canExecute(sender))
throw new CommandException(this, Reason.SENDER_NOT_AUTHORIZED); throw new CommandException(this, Reason.SENDER_NOT_AUTHORIZED);
run(); run();
} }

View File

@ -65,7 +65,7 @@ public class CommandException extends Exception
case SENDER_NOT_AUTHORIZED: case SENDER_NOT_AUTHORIZED:
return "You do not have the permission to use this command."; return "You do not have the permission to use this command.";
default: default:
PluginLogger.LogWarning("Unknown CommandException caught", this); PluginLogger.warning("Unknown CommandException caught", this);
return "An unknown error suddenly happened."; return "An unknown error suddenly happened.";
} }
} }

View File

@ -1,55 +0,0 @@
/*
* 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 org.bukkit.command.CommandSender;
import org.bukkit.plugin.Plugin;
public abstract class CommandPermission
{
abstract public boolean hasPermission(CommandSender sender);
static public final CommandPermission OP_ONLY = new CommandPermission()
{
@Override
public boolean hasPermission(CommandSender sender)
{
return sender.isOp();
}
};
static public CommandPermission bukkitPermission(final String permission)
{
return new CommandPermission()
{
@Override
public boolean hasPermission(CommandSender sender)
{
return sender.hasPermission(permission);
}
};
}
static public CommandPermission bukkitPermission(Plugin plugin, String permission)
{
final String permissionName = plugin.getName().toLowerCase()
+ "." + permission.toLowerCase();
return bukkitPermission(permissionName);
}
}

View File

@ -18,10 +18,9 @@
package fr.moribus.imageonmap.commands; package fr.moribus.imageonmap.commands;
import fr.moribus.imageonmap.ImageOnMap; import fr.moribus.imageonmap.commands.maptool.MigrateCommand;
import fr.moribus.imageonmap.PluginLogger; import fr.moribus.imageonmap.PluginLogger;
import fr.moribus.imageonmap.commands.maptool.*; import fr.moribus.imageonmap.commands.maptool.*;
import fr.moribus.imageonmap.commands.migration.*;
import java.io.InputStream; import java.io.InputStream;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.util.ArrayList; import java.util.ArrayList;
@ -36,10 +35,7 @@ import org.bukkit.command.CommandSender;
import org.bukkit.command.TabCompleter; import org.bukkit.command.TabCompleter;
import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.plugin.java.JavaPlugin;
/**
*
* @author Prokopyl<prokopylmc@gmail.com>
*/
public enum Commands implements TabCompleter, CommandExecutor public enum Commands implements TabCompleter, CommandExecutor
{ {
MAPTOOL(new String[]{"maptool"}, MAPTOOL(new String[]{"maptool"},
@ -48,22 +44,17 @@ public enum Commands implements TabCompleter, CommandExecutor
GetCommand.class, GetCommand.class,
DeleteConfirmCommand.class, DeleteConfirmCommand.class,
DeleteNoConfirmCommand.class, DeleteNoConfirmCommand.class,
GetRemainingCommand.class GetRemainingCommand.class,
MigrateCommand.class
), ),
TOMAP(MAPTOOL, NewCommand.class, "tomap"), TOMAP(MAPTOOL, NewCommand.class, "tomap");
MAPTOOL_MIGRATION(new String[]{"maptool-migration"}, CommandPermission.OP_ONLY,
StartCommand.class
);
static private JavaPlugin plugin;
static private final Commands[] commandGroups = Commands.class.getEnumConstants(); static private final Commands[] commandGroups = Commands.class.getEnumConstants();
private final Commands shortcutCommandGroup; private final Commands shortcutCommandGroup;
private final String[] names; private final String[] names;
private final Class<? extends Command>[] commandsClasses; private final Class<? extends Command>[] commandsClasses;
private final ArrayList<Command> commands = new ArrayList<>(); private final ArrayList<Command> commands = new ArrayList<>();
private final HashMap<String, String> commandsDescriptions = new HashMap<>(); private final HashMap<String, String> commandsDescriptions = new HashMap<>();
private final CommandPermission commandPermission;
private String description = ""; private String description = "";
private Commands(Commands shortcutCommandGroup, Class<? extends Command> commandClass, String ... names) private Commands(Commands shortcutCommandGroup, Class<? extends Command> commandClass, String ... names)
@ -71,23 +62,16 @@ public enum Commands implements TabCompleter, CommandExecutor
this.names = names; this.names = names;
this.commandsClasses = new Class[]{commandClass}; this.commandsClasses = new Class[]{commandClass};
this.shortcutCommandGroup = shortcutCommandGroup; this.shortcutCommandGroup = shortcutCommandGroup;
this.commandPermission = shortcutCommandGroup.getPermission();
initCommands();
}
private Commands(String[] names, CommandPermission permission, Class<? extends Command> ... commandsClasses)
{
this.names = names;
this.commandsClasses = commandsClasses;
this.shortcutCommandGroup = null;
this.commandPermission = permission;
initDescriptions();
initCommands(); initCommands();
} }
private Commands(String[] names, Class<? extends Command> ... commandsClasses) private Commands(String[] names, Class<? extends Command> ... commandsClasses)
{ {
this(names, CommandPermission.bukkitPermission(ImageOnMap.getPlugin(), names[0]), commandsClasses); this.names = names;
this.commandsClasses = commandsClasses;
this.shortcutCommandGroup = null;
initDescriptions();
initCommands();
} }
private void initDescriptions() private void initDescriptions()
@ -96,7 +80,7 @@ public enum Commands implements TabCompleter, CommandExecutor
InputStream stream = getClass().getClassLoader().getResourceAsStream(fileName); InputStream stream = getClass().getClassLoader().getResourceAsStream(fileName);
if(stream == null) if(stream == null)
{ {
PluginLogger.LogWarning("Could not load description file for the " + getUsualName() + " command"); PluginLogger.warning("Could not load description file for the " + getUsualName() + " command");
return; return;
} }
@ -154,7 +138,7 @@ public enum Commands implements TabCompleter, CommandExecutor
} }
catch (Exception ex) catch (Exception ex)
{ {
PluginLogger.LogWarning("Exception while initializing command", ex); PluginLogger.warning("Exception while initializing command", ex);
} }
} }
@ -341,6 +325,5 @@ public enum Commands implements TabCompleter, CommandExecutor
public String getDescription(String commandName) { return commandsDescriptions.get(commandName); } public String getDescription(String commandName) { return commandsDescriptions.get(commandName); }
public boolean isShortcutCommand() { return shortcutCommandGroup != null; } public boolean isShortcutCommand() { return shortcutCommandGroup != null; }
public Commands getShortcutCommandGroup() { return shortcutCommandGroup; } public Commands getShortcutCommandGroup() { return shortcutCommandGroup; }
public CommandPermission getPermission() { return commandPermission; }
} }

View File

@ -73,25 +73,27 @@ public class HelpCommand extends Command
if(!command.canExecute(sender)) if(!command.canExecute(sender))
warning("You do not have the permission to use this command."); warning("You do not have the permission to use this command.");
sender.sendMessage("§l§6 ||== ImageOnMap help ==||\n" + String message = "§l§6 ||== ImageOnMap help ==||\n" +
"§l§6 |Usage : §r" + command.getUsageString()); "§l§6 |Usage : §r" + command.getUsageString();
try try
{ {
String help = getHelpText(command); String help = getHelpText(command);
if(help.isEmpty()) if(help.isEmpty())
{ {
sender.sendMessage(message);
warning("There is no help message for this command."); warning("There is no help message for this command.");
} }
else else
{ {
sender.sendMessage(help); sender.sendMessage(message + "\n" + help);
} }
} }
catch(IOException ex) catch(IOException ex)
{ {
sender.sendMessage(message);
warning("Could not read help for this command."); warning("Could not read help for this command.");
PluginLogger.LogWarning("Could not read help for the command : " + command.getName(), ex); PluginLogger.warning("Could not read help for the command : " + command.getName(), ex);
} }
} }

View File

@ -48,7 +48,7 @@ public class DeleteNoConfirmCommand extends Command
} }
catch (MapManagerException ex) catch (MapManagerException ex)
{ {
PluginLogger.LogWarning("A non-existent map was requested to be deleted", ex); PluginLogger.warning("A non-existent map was requested to be deleted", ex);
warning("This map does not exist."); warning("This map does not exist.");
} }
} }

View File

@ -16,17 +16,16 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package fr.moribus.imageonmap.commands.migration; package fr.moribus.imageonmap.commands.maptool;
import fr.moribus.imageonmap.commands.*; import fr.moribus.imageonmap.commands.*;
import fr.moribus.imageonmap.migration.Migrator; import fr.moribus.imageonmap.migration.MigratorExecutor;
import fr.moribus.imageonmap.worker.WorkerCallback;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
@CommandInfo(name = "start") @CommandInfo(name = "migrate")
public class StartCommand extends Command public class MigrateCommand extends Command
{ {
public StartCommand(Commands commandGroup) { public MigrateCommand(Commands commandGroup) {
super(commandGroup); super(commandGroup);
} }
@ -34,28 +33,20 @@ public class StartCommand extends Command
protected void run() throws CommandException protected void run() throws CommandException
{ {
final CommandSender cmdSender = sender; final CommandSender cmdSender = sender;
if(Migrator.isMigrationRunning()) if(MigratorExecutor.isRunning())
{ {
error("A migration process is already running. Check console for details."); error("A migration process is already running. Check console for details.");
} }
else else
{ {
Migrator.runMigration(new WorkerCallback<Void>()
{
@Override
public void finished(Void result)
{
info(cmdSender, "Migration finished. See console for details.");
}
@Override
public void errored(Throwable exception)
{
warning(cmdSender, "Migration ended unexpectedly. See console for details.");
}
});
info("Migration started. See console for details."); info("Migration started. See console for details.");
MigratorExecutor.migrate();
} }
} }
@Override
public boolean canExecute(CommandSender sender)
{
return sender.isOp();
}
} }

View File

@ -79,7 +79,7 @@ public class NewCommand extends Command
public void errored(Throwable exception) public void errored(Throwable exception)
{ {
player.sendMessage("§cMap rendering failed : " + exception.getMessage()); player.sendMessage("§cMap rendering failed : " + exception.getMessage());
PluginLogger.LogWarning("Rendering from '" + player.getName() + "' failed", exception); PluginLogger.warning("Rendering from '{0}' failed", exception, player.getName());
} }
}); });
} }

View File

@ -170,7 +170,6 @@ public class ImageRendererExecutor extends Worker
int x, y; int x, y;
x = (destinationW - finalW) / 2; x = (destinationW - finalW) / 2;
y = (destinationH - finalH) / 2; y = (destinationH - finalH) / 2;
PluginLogger.LogInfo(finalW + " " + finalH + " : " + x + " " + y);
BufferedImage newImage = new BufferedImage(destinationW, destinationH, BufferedImage.TYPE_INT_ARGB); BufferedImage newImage = new BufferedImage(destinationW, destinationH, BufferedImage.TYPE_INT_ARGB);

View File

@ -111,6 +111,11 @@ abstract public class MapManager
getPlayerMapStore(map.getUserUUID()).addMap(map); getPlayerMapStore(map.getUserUUID()).addMap(map);
} }
static public void insertMap(ImageMap map)
{
getPlayerMapStore(map.getUserUUID()).insertMap(map);
}
static public void deleteMap(ImageMap map) throws MapManagerException static public void deleteMap(ImageMap map) throws MapManagerException
{ {
getPlayerMapStore(map.getUserUUID()).deleteMap(map); getPlayerMapStore(map.getUserUUID()).deleteMap(map);

View File

@ -69,6 +69,11 @@ public class PlayerMapStore implements ConfigurationSerializable
public synchronized void addMap(ImageMap map) throws MapManagerException public synchronized void addMap(ImageMap map) throws MapManagerException
{ {
checkMapLimit(map); checkMapLimit(map);
insertMap(map);
}
public synchronized void insertMap(ImageMap map)
{
_addMap(map); _addMap(map);
notifyModification(); notifyModification();
} }
@ -201,15 +206,15 @@ public class PlayerMapStore implements ConfigurationSerializable
} }
catch(InvalidConfigurationException ex) catch(InvalidConfigurationException ex)
{ {
PluginLogger.LogWarning("Could not load map data : " + ex.getMessage()); PluginLogger.warning("Could not load map data : ", ex);
} }
} }
try { checkMapLimit(0); } try { checkMapLimit(0); }
catch(MapManagerException ex) catch(MapManagerException ex)
{ {
PluginLogger.LogWarning("Map limit exceeded for player " + playerUUID.toString() + PluginLogger.warning("Map limit exceeded for player '{0}' ({1} maps loaded)",
" (" + mapList.size() + " maps loaded)."); playerUUID.toString(),mapList.size());
} }
} }
@ -246,9 +251,8 @@ public class PlayerMapStore implements ConfigurationSerializable
} }
catch (IOException ex) catch (IOException ex)
{ {
PluginLogger.LogError("Could not save maps file for player " + playerUUID.toString(), ex); PluginLogger.error("Could not save maps file for player '{0}'", ex, playerUUID.toString());
} }
PluginLogger.LogInfo("Saving maps file for " + playerUUID.toString());
synchronized(this) {modified = false;} synchronized(this) {modified = false;}
} }
} }

View File

@ -33,16 +33,13 @@ public class PosterMap extends ImageMap
{ {
super(userUUID, Type.POSTER, id, name); super(userUUID, Type.POSTER, id, name);
this.mapsIDs = mapsIDs; this.mapsIDs = mapsIDs;
this.columnCount = columnCount; this.columnCount = Math.max(columnCount, 0);
this.rowCount = rowCount; this.rowCount = Math.max(rowCount, 0);
} }
public PosterMap(UUID userUUID, short[] mapsIDs, int columnCount, int rowCount) public PosterMap(UUID userUUID, short[] mapsIDs, int columnCount, int rowCount)
{ {
super(userUUID, Type.POSTER, null, null); this(userUUID, mapsIDs, null, null, columnCount, rowCount);
this.mapsIDs = mapsIDs;
this.columnCount = columnCount;
this.rowCount = rowCount;
} }
@Override @Override
@ -89,11 +86,19 @@ public class PosterMap extends ImageMap
/* ====== Getters & Setters ====== */ /* ====== Getters & Setters ====== */
/**
* Returns the amount of columns in the poster map
* @return The number of columns, or 0 if this data is missing
*/
public int getColumnCount() public int getColumnCount()
{ {
return columnCount; return columnCount;
} }
/**
* Returns the amount of rows in the poster map
* @return The number of rows, or 0 if this data is missing
*/
public int getRowCount() public int getRowCount()
{ {
return rowCount; return rowCount;
@ -101,14 +106,21 @@ public class PosterMap extends ImageMap
public int getColumnAt(int i) public int getColumnAt(int i)
{ {
if(columnCount == 0) return 0;
return (i % columnCount) + 1; return (i % columnCount) + 1;
} }
public int getRowAt(int i) public int getRowAt(int i)
{ {
if(columnCount == 0) return 0;
return (i / columnCount) + 1; return (i / columnCount) + 1;
} }
public boolean hasColumnData()
{
return rowCount != 0 && columnCount != 0;
}
@Override @Override
public int getMapCount() public int getMapCount()
{ {

View File

@ -26,10 +26,15 @@ public class SingleMap extends ImageMap
{ {
protected final short mapID; protected final short mapID;
public SingleMap(UUID ownerUUID, short mapID, String id, String name)
{
super(ownerUUID, Type.SINGLE, id, name);
this.mapID = mapID;
}
public SingleMap(UUID ownerUUID, short mapID) public SingleMap(UUID ownerUUID, short mapID)
{ {
super(ownerUUID, Type.SINGLE); this(ownerUUID, mapID, null, null);
this.mapID = mapID;
} }
@Override @Override

View File

@ -1,74 +0,0 @@
/*
* 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.worker.Worker;
import fr.moribus.imageonmap.worker.WorkerCallback;
import fr.moribus.imageonmap.worker.WorkerRunnable;
public class Migrator extends Worker
{
static private Migrator instance;
static private V3Migrator migrator;
static public void startWorker()
{
if(instance != null) stopWorker();
instance = new Migrator();
instance.init();
}
static public void stopWorker()
{
instance.exit();
instance = null;
}
private Migrator()
{
super("Migration");
}
static public boolean isMigrationStarted()
{
return migrator != null;
}
static public boolean isMigrationRunning()
{
return migrator != null && migrator.isRunning();
}
static public boolean runMigration(WorkerCallback<Void> callback)
{
if(isMigrationRunning()) return false;
if(!isMigrationStarted()) migrator = new V3Migrator(ImageOnMap.getPlugin());
instance.submitQuery(new WorkerRunnable<Void>()
{
@Override
public Void run() throws Throwable
{
migrator.run();
return null;
}
}, callback);
return true;
}
}

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

@ -18,7 +18,12 @@
package fr.moribus.imageonmap.migration; 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.List;
import java.util.UUID;
import org.bukkit.configuration.Configuration;
import org.bukkit.configuration.InvalidConfigurationException; import org.bukkit.configuration.InvalidConfigurationException;
class OldSavedMap class OldSavedMap
@ -54,6 +59,20 @@ class OldSavedMap
userName = data.get(2); 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 short getMapId() {return mapId;}
public String getUserName() {return userName;} public String getUserName() {return userName;}
} }

View File

@ -18,16 +18,23 @@
package fr.moribus.imageonmap.migration; 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.List;
import java.util.UUID;
import org.bukkit.configuration.Configuration;
import org.bukkit.configuration.InvalidConfigurationException; import org.bukkit.configuration.InvalidConfigurationException;
class OldSavedPoster class OldSavedPoster
{ {
private String userName; private final String userName;
private short[] mapsIds; private final String posterName;
private final short[] mapsIds;
public OldSavedPoster(Object rawData) throws InvalidConfigurationException public OldSavedPoster(Object rawData, String key) throws InvalidConfigurationException
{ {
posterName = key;
List<String> data; List<String> data;
try try
{ {
@ -68,5 +75,24 @@ class OldSavedPoster
return false; 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;} public String getUserName() {return userName;}
} }

View File

@ -142,7 +142,7 @@ abstract public class UUIDFetcher
for(String name : remainingNames) for(String name : remainingNames)
{ {
user = fetchOriginalUUID(name); user = fetchOriginalUUID(name);
uuids.put(user.name, user.uuid); uuids.put(name, user.uuid);
Thread.sleep(timeBetweenRequests); Thread.sleep(timeBetweenRequests);
} }
@ -151,7 +151,6 @@ abstract public class UUIDFetcher
static private User fetchOriginalUUID(String name) throws IOException static private User fetchOriginalUUID(String name) throws IOException
{ {
HttpURLConnection connection = getGETConnection(TIMED_PROFILE_URL + name + "?at=" + NAME_CHANGE_TIMESTAMP); HttpURLConnection connection = getGETConnection(TIMED_PROFILE_URL + name + "?at=" + NAME_CHANGE_TIMESTAMP);
sendRequest(connection);
JSONObject object; JSONObject object;
@ -187,14 +186,9 @@ abstract public class UUIDFetcher
connection.setRequestMethod("GET"); connection.setRequestMethod("GET");
connection.setUseCaches(false); connection.setUseCaches(false);
connection.setDoInput(true); connection.setDoInput(true);
return connection; connection.setDoOutput(true);
}
static private void sendRequest(HttpURLConnection connection) throws IOException return connection;
{
OutputStream stream = connection.getOutputStream();
stream.flush();
stream.close();
} }
private static void writeBody(HttpURLConnection connection, List<String> names) throws IOException private static void writeBody(HttpURLConnection connection, List<String> names) throws IOException

View File

@ -19,6 +19,8 @@
package fr.moribus.imageonmap.migration; package fr.moribus.imageonmap.migration;
import fr.moribus.imageonmap.ImageOnMap; import fr.moribus.imageonmap.ImageOnMap;
import fr.moribus.imageonmap.PluginLogger;
import fr.moribus.imageonmap.map.MapManager;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
@ -28,6 +30,7 @@ import java.nio.file.Paths;
import java.nio.file.StandardCopyOption; import java.nio.file.StandardCopyOption;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.util.ArrayDeque;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map; import java.util.Map;
@ -42,7 +45,7 @@ import org.bukkit.plugin.Plugin;
/** /**
* This class represents and executes the ImageOnMap v3.x migration process * This class represents and executes the ImageOnMap v3.x migration process
*/ */
public class V3Migrator public class V3Migrator implements Runnable
{ {
/** /**
* The name of the former images directory * The name of the former images directory
@ -111,12 +114,12 @@ public class V3Migrator
/** /**
* The list of all the posters to migrate * The list of all the posters to migrate
*/ */
private final ArrayList<OldSavedPoster> postersToMigrate; private final ArrayDeque<OldSavedPoster> postersToMigrate;
/** /**
* The list of all the single maps to migrate * The list of all the single maps to migrate
*/ */
private final ArrayList<OldSavedMap> mapsToMigrate; private final ArrayDeque<OldSavedMap> mapsToMigrate;
/** /**
* The set of all the user names to retreive the UUID from Mojang * The set of all the user names to retreive the UUID from Mojang
@ -145,8 +148,8 @@ public class V3Migrator
backupsPrev3Directory = new File(dataFolder, BACKUPS_PREV3_DIRECTORY_NAME); backupsPrev3Directory = new File(dataFolder, BACKUPS_PREV3_DIRECTORY_NAME);
backupsPostv3Directory = new File(dataFolder, BACKUPS_POSTV3_DIRECTORY_NAME); backupsPostv3Directory = new File(dataFolder, BACKUPS_POSTV3_DIRECTORY_NAME);
postersToMigrate = new ArrayList<>(); postersToMigrate = new ArrayDeque<>();
mapsToMigrate = new ArrayList<>(); mapsToMigrate = new ArrayDeque<>();
userNamesToFetch = new HashSet<>(); userNamesToFetch = new HashSet<>();
} }
@ -166,21 +169,22 @@ public class V3Migrator
} }
catch(Exception ex) catch(Exception ex)
{ {
logError("Error while preparing migration", ex); PluginLogger.error("Error while preparing migration");
logError("Aborting migration. No change has been made."); PluginLogger.error("Aborting migration. No change has been made.", ex);
Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, null, ex);
return; return;
} }
try try
{ {
mergeMapData();
saveChanges();
cleanup();
} }
catch(Exception ex) catch(Exception ex)
{ {
logError("Error while migrating", ex); PluginLogger.error("Error while migrating", ex);
logError("Aborting migration. Some changes may already have been made."); PluginLogger.error("Aborting migration. Some changes may already have been made.");
logError("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."); 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); Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, null, ex);
} }
@ -194,22 +198,22 @@ public class V3Migrator
*/ */
private boolean spotFilesToMigrate() private boolean spotFilesToMigrate()
{ {
logInfo("Looking for configuration files to migrate ..."); PluginLogger.info("Looking for configuration files to migrate ...");
if(!oldPostersFile.exists()) oldPostersFile = null; if(!oldPostersFile.exists()) oldPostersFile = null;
else logInfo("Detected former posters file " + OLD_POSTERS_FILE_NAME); else PluginLogger.info("Detected former posters file {0}", OLD_POSTERS_FILE_NAME);
if(!oldMapsFile.exists()) oldMapsFile = null; if(!oldMapsFile.exists()) oldMapsFile = null;
else logInfo("Detected former maps file " + OLD_POSTERS_FILE_NAME); else PluginLogger.info("Detected former maps file {0}", OLD_MAPS_FILE_NAME);
if(oldPostersFile == null && oldMapsFile == null) if(oldPostersFile == null && oldMapsFile == null)
{ {
logInfo("There is nothing to migrate. Stopping."); PluginLogger.info("There is nothing to migrate. Stopping.");
return false; return false;
} }
else else
{ {
logInfo("Done."); PluginLogger.info("Done.");
return true; return true;
} }
} }
@ -223,9 +227,9 @@ public class V3Migrator
if((backupsPrev3Directory.exists() && backupsPrev3Directory.list().length == 0) if((backupsPrev3Directory.exists() && backupsPrev3Directory.list().length == 0)
|| (backupsPostv3Directory.exists() && backupsPostv3Directory.list().length == 0)) || (backupsPostv3Directory.exists() && backupsPostv3Directory.list().length == 0))
{ {
logError("Backup directories already exists."); PluginLogger.error("Backup directories already exists.");
logError("This means that a migration has already been done, or may not have ended well."); PluginLogger.error("This means that a migration has already been done, or may not have ended well.");
logError("To start a new migration, you must move away the backup directories so they are not overwritten."); PluginLogger.error("To start a new migration, you must move away the backup directories so they are not overwritten.");
return true; return true;
} }
@ -238,18 +242,18 @@ public class V3Migrator
*/ */
private void backupMapData() throws IOException private void backupMapData() throws IOException
{ {
logInfo("Backing up map data before migrating ..."); PluginLogger.info("Backing up map data before migrating ...");
if(!backupsPrev3Directory.exists()) backupsPrev3Directory.mkdirs(); if(!backupsPrev3Directory.exists()) backupsPrev3Directory.mkdirs();
if(!backupsPostv3Directory.exists()) backupsPostv3Directory.mkdirs(); if(!backupsPostv3Directory.exists()) backupsPostv3Directory.mkdirs();
if(oldMapsFile.exists()) if(oldMapsFile != null && oldMapsFile.exists())
{ {
File oldMapsFileBackup = new File(backupsPrev3Directory, oldMapsFile.getName()); File oldMapsFileBackup = new File(backupsPrev3Directory, oldMapsFile.getName());
verifiedBackupCopy(oldMapsFile, oldMapsFileBackup); verifiedBackupCopy(oldMapsFile, oldMapsFileBackup);
} }
if(oldPostersFile.exists()) if(oldPostersFile != null && oldPostersFile.exists())
{ {
File oldPostersFileBackup = new File(backupsPrev3Directory, oldPostersFile.getName()); File oldPostersFileBackup = new File(backupsPrev3Directory, oldPostersFile.getName());
verifiedBackupCopy(oldPostersFile, oldPostersFileBackup); verifiedBackupCopy(oldPostersFile, oldPostersFileBackup);
@ -262,7 +266,7 @@ public class V3Migrator
verifiedBackupCopy(mapFile, backupFile); verifiedBackupCopy(mapFile, backupFile);
} }
logInfo("Backup complete."); PluginLogger.info("Backup complete.");
} }
/** /**
@ -286,24 +290,30 @@ public class V3Migrator
* @return true if any of the files contained readable map data, false otherwise * @return true if any of the files contained readable map data, false otherwise
*/ */
private boolean loadOldFiles() private boolean loadOldFiles()
{
if(oldPostersFile != null)
{ {
FileConfiguration oldPosters = YamlConfiguration.loadConfiguration(oldPostersFile); FileConfiguration oldPosters = YamlConfiguration.loadConfiguration(oldPostersFile);
OldSavedPoster oldPoster; OldSavedPoster oldPoster;
for(String key : oldPosters.getKeys(false)) for(String key : oldPosters.getKeys(false))
{ {
if("IdCount".equals(key)) continue;
try try
{ {
oldPoster = new OldSavedPoster(oldPosters.get(key)); oldPoster = new OldSavedPoster(oldPosters.get(key), key);
userNamesToFetch.add(oldPoster.getUserName()); userNamesToFetch.add(oldPoster.getUserName());
postersToMigrate.add(oldPoster); postersToMigrate.add(oldPoster);
} }
catch(InvalidConfigurationException ex) catch(InvalidConfigurationException ex)
{ {
logWarning("Could not read poster data for key " + key, ex); PluginLogger.warning("Could not read poster data for key '{0}'", ex, key);
}
} }
} }
if(oldMapsFile != null)
{
FileConfiguration oldMaps = YamlConfiguration.loadConfiguration(oldMapsFile); FileConfiguration oldMaps = YamlConfiguration.loadConfiguration(oldMapsFile);
OldSavedMap oldMap; OldSavedMap oldMap;
@ -311,13 +321,15 @@ public class V3Migrator
{ {
try try
{ {
if("IdCount".equals(key)) continue;
oldMap = new OldSavedMap(oldMaps.get(key)); oldMap = new OldSavedMap(oldMaps.get(key));
if(!posterContains(oldMap)) mapsToMigrate.add(oldMap); if(!posterContains(oldMap)) mapsToMigrate.add(oldMap);
} }
catch(InvalidConfigurationException ex) catch(InvalidConfigurationException ex)
{ {
logWarning("Could not read poster data for key " + key, ex); PluginLogger.warning("Could not read poster data for key '{0}'", ex, key);
}
} }
} }
@ -331,22 +343,22 @@ public class V3Migrator
*/ */
private void fetchUUIDs() throws IOException, InterruptedException private void fetchUUIDs() throws IOException, InterruptedException
{ {
logInfo("Fetching UUIDs from Mojang ..."); PluginLogger.info("Fetching UUIDs from Mojang ...");
try try
{ {
usersUUIDs = UUIDFetcher.fetch(new ArrayList<String>(userNamesToFetch)); usersUUIDs = UUIDFetcher.fetch(new ArrayList<String>(userNamesToFetch));
} }
catch(IOException ex) catch(IOException ex)
{ {
logError("An error occured while fetching the UUIDs from Mojang", ex); PluginLogger.error("An error occured while fetching the UUIDs from Mojang", ex);
throw ex; throw ex;
} }
catch(InterruptedException ex) catch(InterruptedException ex)
{ {
logError("The migration worker has been interrupted", ex); PluginLogger.error("The migration worker has been interrupted", ex);
throw ex; throw ex;
} }
logInfo("Fetching done. " + usersUUIDs.size() + " UUIDs have been retreived."); PluginLogger.info("Fetching done. {0} UUIDs have been retreived.", usersUUIDs.size());
} }
/** /**
@ -357,8 +369,8 @@ public class V3Migrator
{ {
if(usersUUIDs.size() == userNamesToFetch.size()) return true; if(usersUUIDs.size() == userNamesToFetch.size()) return true;
int remainingUsersCount = userNamesToFetch.size() - usersUUIDs.size(); int remainingUsersCount = userNamesToFetch.size() - usersUUIDs.size();
logInfo("Mojang did not find UUIDs for "+remainingUsersCount+" players."); PluginLogger.info("Mojang did not find UUIDs for {0} players at the current time.", remainingUsersCount);
logInfo("The Mojang servers limit requests rate at one per second, this may take some time..."); PluginLogger.info("The Mojang servers limit requests rate at one per second, this may take some time...");
try try
{ {
@ -366,50 +378,142 @@ public class V3Migrator
} }
catch(IOException ex) catch(IOException ex)
{ {
logError("An error occured while fetching the UUIDs from Mojang", ex); PluginLogger.error("An error occured while fetching the UUIDs from Mojang");
throw ex; throw ex;
} }
catch(InterruptedException ex) catch(InterruptedException ex)
{ {
logError("The migration worker has been interrupted", ex); PluginLogger.error("The migration worker has been interrupted");
throw ex; 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) if(usersUUIDs.size() <= 0)
{ {
logInfo("Mojang could not find any of the registered players."); PluginLogger.info("Mojang could not find any of the registered players.");
logInfo("There is nothing to migrate. Stopping."); PluginLogger.info("There is nothing to migrate. Stopping.");
return false; return false;
} }
return true; 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 ***** */ /* ****** Utils ***** */
static public void logInfo(String message)
{
System.out.println("[ImageOnMap-Migration][INFO] " + message);
}
static public void logWarning(String message)
{
System.err.println("[ImageOnMap-Migration][WARN] " + message);
}
static public void logWarning(String message, Exception ex)
{
logWarning(message + " : " + ex.getMessage());
}
static public void logError(String message)
{
System.err.println("[ImageOnMap-Migration][ERROR] " + message);
}
static public void logError(String message, Exception ex)
{
logError(message + " : " + ex.getMessage());
}
public synchronized boolean isRunning() public synchronized boolean isRunning()
{ {
@ -424,6 +528,7 @@ public class V3Migrator
/** /**
* Executes the full migration, and defines the running status of the migration * Executes the full migration, and defines the running status of the migration
*/ */
@Override
public void run() public void run()
{ {
setRunning(true); setRunning(true);

View File

@ -64,12 +64,20 @@ public class MapItemManager implements Listener
short[] mapsIDs = map.getMapsIDs(); short[] mapsIDs = map.getMapsIDs();
boolean inventoryFull = false; boolean inventoryFull = false;
String mapName;
for(int i = 0, c = mapsIDs.length; i < c; i++) for(int i = 0, c = mapsIDs.length; i < c; i++)
{ {
inventoryFull = give(player, if(map.hasColumnData())
createMapItem(mapsIDs[i], map.getName() + {
mapName = map.getName() +
" (row " + map.getRowAt(i) + " (row " + map.getRowAt(i) +
", column " + map.getColumnAt(i) + ")")) || inventoryFull; ", column " + map.getColumnAt(i) + ")";
}
else
{
mapName = map.getName();
}
inventoryFull = give(player, createMapItem(mapsIDs[i], mapName)) || inventoryFull;
} }
return inventoryFull; return inventoryFull;

View File

@ -48,7 +48,7 @@ public abstract class Worker
{ {
if(thread != null && thread.isAlive()) if(thread != null && thread.isAlive())
{ {
PluginLogger.LogWarning("Restarting " + name + " thread."); PluginLogger.warning("Restarting '{0}' thread.", name);
exit(); exit();
} }
callbackManager.init(); callbackManager.init();
@ -118,7 +118,7 @@ public abstract class Worker
private Thread createThread() private Thread createThread()
{ {
return new Thread("ImageOnMap " + name) return new Thread("ImageOnMap-" + name)
{ {
@Override @Override
public void run() public void run()

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

@ -8,9 +8,6 @@ commands:
usage: /<command> [URL] usage: /<command> [URL]
maptool: maptool:
description: manage maps description: manage maps
maptool-migration:
description: Manages the ImageOnMap migrations
permission: op
permissions: permissions:
imageonmap.userender: imageonmap.userender:
description: Allows you to use /tomap description: Allows you to use /tomap