ImageOnMap is now a zLib plugin.

* NEW: added zLib dependency.
* NEW: removed all classes moved to the zLib, and updated the references to these classes to the zLib ones.
* BUG: fixed some GUI navigation problems.
* NEW: implemented the items renaming into the details GUI.
This commit is contained in:
Amaury Carrade 2015-12-29 19:12:36 +01:00
parent d25bf9bd0e
commit 2443574316
46 changed files with 538 additions and 5421 deletions

49
pom.xml
View File

@ -5,24 +5,65 @@
<artifactId>ImageOnMap</artifactId>
<version>2.99</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.3</version>
<configuration>
<artifactSet>
<includes>
<include>fr.zcraft:zlib</include>
</includes>
</artifactSet>
<relocations>
<relocation>
<pattern>fr.zcraft.zlib</pattern>
<shadedPattern>fr.moribus.imageonmap</shadedPattern>
</relocation>
</relocations>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/groups/public/</url>
</repository>
<repository>
<id>zDevelopers</id>
<url>http://maven.carrade.eu/artifactory/snapshots</url>
</repository>
</repositories>
<dependencies>
<!-- Dependency information -->
<dependencies>
<dependency>
<groupId>org.bukkit</groupId>
<artifactId>bukkit</artifactId>
<version>1.8-R0.1-SNAPSHOT</version>
<version>1.8.3-R0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>fr.zcraft</groupId>
<artifactId>zlib</artifactId>
<version>0.99-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
</project>

View File

@ -18,9 +18,14 @@
package fr.moribus.imageonmap;
import fr.moribus.imageonmap.commands.Commands;
import fr.moribus.imageonmap.gui.core.*;
import fr.moribus.imageonmap.guiproko.core.Gui;
import fr.moribus.imageonmap.commands.maptool.DeleteConfirmCommand;
import fr.moribus.imageonmap.commands.maptool.DeleteNoConfirmCommand;
import fr.moribus.imageonmap.commands.maptool.ExploreCommand;
import fr.moribus.imageonmap.commands.maptool.GetCommand;
import fr.moribus.imageonmap.commands.maptool.GetRemainingCommand;
import fr.moribus.imageonmap.commands.maptool.ListCommand;
import fr.moribus.imageonmap.commands.maptool.MigrateCommand;
import fr.moribus.imageonmap.commands.maptool.NewCommand;
import fr.moribus.imageonmap.image.ImageIOExecutor;
import fr.moribus.imageonmap.image.ImageRendererExecutor;
import fr.moribus.imageonmap.image.MapInitEvent;
@ -28,11 +33,15 @@ 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 fr.zcraft.zlib.components.commands.Commands;
import fr.zcraft.zlib.components.gui.Gui;
import fr.zcraft.zlib.core.ZPlugin;
import fr.zcraft.zlib.tools.PluginLogger;
import java.io.File;
import java.io.IOException;
import org.bukkit.plugin.java.JavaPlugin;
public final class ImageOnMap extends JavaPlugin
public final class ImageOnMap extends ZPlugin
{
static private final String IMAGES_DIRECTORY_NAME = "images";
static private final String MAPS_DIRECTORY_NAME = "maps";
@ -60,10 +69,10 @@ public final class ImageOnMap extends JavaPlugin
return new File(imagesDirectory, "map"+mapID+".png");
}
@SuppressWarnings ("unchecked")
@Override
public void onEnable()
{
PluginLogger.init(this);
// Creating the images and maps directories if necessary
try
{
@ -76,30 +85,40 @@ public final class ImageOnMap extends JavaPlugin
this.setEnabled(false);
return;
}
loadComponents(Gui.class, Commands.class, ImageIOExecutor.class, ImageRendererExecutor.class);
//Init all the things !
PluginConfiguration.init(this);
MetricsLite.startMetrics();
ImageIOExecutor.start();
ImageRendererExecutor.start();
MapManager.init();
Commands.init(this);
MapInitEvent.init(this);
MapItemManager.init();
GuiManager.init(this);
Gui.init(this);
Commands.register(
"maptool",
NewCommand.class,
ListCommand.class,
GetCommand.class,
DeleteConfirmCommand.class,
DeleteNoConfirmCommand.class,
GetRemainingCommand.class,
ExploreCommand.class,
MigrateCommand.class
);
Commands.registerShortcut("maptool", NewCommand.class, "tomap");
Commands.registerShortcut("maptool", ExploreCommand.class, "maps");
}
@Override
public void onDisable()
{
ImageIOExecutor.stop();
ImageRendererExecutor.stop();
MapManager.exit();
MapItemManager.exit();
MigratorExecutor.waitForMigration();
PluginLogger.exit();
Gui.exit();
super.onDisable();
}
private File checkPluginDirectory(File primaryFile, File... alternateFiles) throws IOException
@ -113,5 +132,4 @@ public final class ImageOnMap extends JavaPlugin
throw new IOException("Could not create '" + primaryFile.getName() + "' plugin directory.");
return primaryFile;
}
}

View File

@ -28,6 +28,7 @@ package fr.moribus.imageonmap;
* either expressed or implied, of anybody else.
*/
import fr.zcraft.zlib.tools.PluginLogger;
import org.bukkit.Bukkit;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.YamlConfiguration;

View File

@ -1,130 +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;
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, args);
log(level, "Exception : ", ex);
}
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()
{
Thread currentThread = Thread.currentThread();
if(currentThread.equals(mainThread)) return plugin.getLogger();
return getLogger(currentThread);
}
static private Logger getLogger(Thread thread)
{
PluginThreadLogger logger = loggers.get(thread);
if(logger == null)
{
logger = new PluginThreadLogger(thread);
loggers.put(thread, logger);
}
return logger;
}
static private class PluginThreadLogger extends Logger
{
private final String loggerName;
public PluginThreadLogger(Thread thread)
{
super(plugin.getClass().getCanonicalName(), null);
setParent(plugin.getLogger());
setLevel(Level.ALL);
loggerName = "[" + thread.getName() + "] ";
}
@Override
public void log(LogRecord logRecord)
{
logRecord.setMessage(loggerName + logRecord.getMessage());
super.log(logRecord);
}
}
}

View File

@ -1,268 +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 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

@ -1,72 +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 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

@ -1,30 +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 java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface CommandInfo
{
String name();
String usageParameters() default "";
String[] aliases() default {};
}

View File

@ -1,324 +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 fr.moribus.imageonmap.*;
import fr.moribus.imageonmap.commands.maptool.*;
import org.apache.commons.lang.*;
import org.bukkit.command.*;
import org.bukkit.plugin.java.*;
import java.io.*;
import java.lang.reflect.*;
import java.util.*;
public enum Commands implements TabCompleter, CommandExecutor
{
MAPTOOL(new String[]{"maptool"},
NewCommand.class,
ListCommand.class,
GetCommand.class,
DeleteConfirmCommand.class,
DeleteNoConfirmCommand.class,
GetRemainingCommand.class,
ExploreCommand.class,
MigrateCommand.class
),
TOMAP(MAPTOOL, NewCommand.class, "tomap"),
MAPS(MAPTOOL, ExploreCommand.class, "maps");
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

@ -1,140 +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 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

@ -0,0 +1,77 @@
/*
* 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.map.ImageMap;
import fr.moribus.imageonmap.map.MapManager;
import fr.zcraft.zlib.components.commands.Command;
import fr.zcraft.zlib.components.commands.CommandException;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.List;
public abstract class IoMCommand extends Command
{
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;
}
protected List<String> getMatchingMapNames(Player player, String prefix)
{
return getMatchingMapNames(MapManager.getMapList(player.getUniqueId()), prefix);
}
protected List<String> getMatchingMapNames(Iterable<? extends ImageMap> maps, String prefix)
{
List<String> matches = new ArrayList<>();
for(ImageMap map : maps)
{
if(map.getId().startsWith(prefix)) matches.add(map.getId());
}
return matches;
}
}

View File

@ -18,20 +18,18 @@
package fr.moribus.imageonmap.commands.maptool;
import fr.moribus.imageonmap.commands.*;
import fr.moribus.imageonmap.commands.IoMCommand;
import fr.moribus.imageonmap.map.ImageMap;
import fr.zcraft.zlib.components.commands.CommandException;
import fr.zcraft.zlib.components.commands.CommandInfo;
import java.util.List;
@CommandInfo(name = "delete", usageParameters = "[tool name]")
public class DeleteConfirmCommand extends Command
@CommandInfo (name = "delete", usageParameters = "[tool name]")
public class DeleteConfirmCommand extends IoMCommand
{
public DeleteConfirmCommand(Commands commandGroup) {
super(commandGroup);
}
@Override
protected void run() throws CommandException
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}," +
@ -45,8 +43,7 @@ public class DeleteConfirmCommand extends Command
{
if(args.length == 1)
return getMatchingMapNames(playerSender(), args[0]);
return null;
}
}

View File

@ -18,25 +18,23 @@
package fr.moribus.imageonmap.commands.maptool;
import fr.moribus.imageonmap.PluginLogger;
import fr.moribus.imageonmap.commands.*;
import fr.moribus.imageonmap.commands.IoMCommand;
import fr.moribus.imageonmap.map.ImageMap;
import fr.moribus.imageonmap.map.MapManager;
import fr.moribus.imageonmap.map.MapManagerException;
import java.util.List;
import fr.zcraft.zlib.components.commands.CommandException;
import fr.zcraft.zlib.components.commands.CommandInfo;
import fr.zcraft.zlib.tools.PluginLogger;
import org.bukkit.entity.Player;
import java.util.List;
@CommandInfo(name = "delete-noconfirm", usageParameters = "[map name]")
public class DeleteNoConfirmCommand extends Command
@CommandInfo (name = "delete-noconfirm", usageParameters = "[map name]")
public class DeleteNoConfirmCommand extends IoMCommand
{
public DeleteNoConfirmCommand(Commands commandGroup) {
super(commandGroup);
}
@Override
protected void run() throws CommandException
protected void run() throws CommandException
{
Player player = playerSender();
ImageMap map = getMapFromArgs();

View File

@ -19,19 +19,16 @@
package fr.moribus.imageonmap.commands.maptool;
import fr.moribus.imageonmap.commands.*;
import fr.moribus.imageonmap.guiproko.list.MapListGui;
import fr.moribus.imageonmap.guiproko.core.Gui;
import fr.moribus.imageonmap.commands.IoMCommand;
import fr.moribus.imageonmap.gui.MapListGui;
import fr.zcraft.zlib.components.commands.CommandException;
import fr.zcraft.zlib.components.commands.CommandInfo;
import fr.zcraft.zlib.components.gui.Gui;
@CommandInfo(name = "explore")
public class ExploreCommand extends Command
@CommandInfo (name = "explore")
public class ExploreCommand extends IoMCommand
{
public ExploreCommand(Commands commandGroup)
{
super(commandGroup);
}
@Override
protected void run() throws CommandException
{

View File

@ -18,17 +18,16 @@
package fr.moribus.imageonmap.commands.maptool;
import fr.moribus.imageonmap.commands.*;
import java.util.List;
import fr.moribus.imageonmap.commands.IoMCommand;
import fr.zcraft.zlib.components.commands.CommandException;
import fr.zcraft.zlib.components.commands.CommandInfo;
import org.bukkit.entity.Player;
@CommandInfo(name = "get")
public class GetCommand extends Command
import java.util.List;
@CommandInfo (name = "get")
public class GetCommand extends IoMCommand
{
public GetCommand(Commands commandGroup) {
super(commandGroup);
}
@Override
protected void run() throws CommandException
{

View File

@ -18,20 +18,15 @@
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.commands.IoMCommand;
import fr.moribus.imageonmap.ui.MapItemManager;
import fr.zcraft.zlib.components.commands.CommandException;
import fr.zcraft.zlib.components.commands.CommandInfo;
import org.bukkit.entity.Player;
@CommandInfo(name = "getremaining", aliases = {"getrest"})
public class GetRemainingCommand extends Command
@CommandInfo (name = "getremaining", aliases = {"getrest"})
public class GetRemainingCommand extends IoMCommand
{
public GetRemainingCommand(Commands commandGroup) {
super(commandGroup);
}
@Override
protected void run() throws CommandException
{

View File

@ -18,19 +18,18 @@
package fr.moribus.imageonmap.commands.maptool;
import fr.moribus.imageonmap.commands.*;
import fr.moribus.imageonmap.commands.IoMCommand;
import fr.moribus.imageonmap.map.ImageMap;
import fr.moribus.imageonmap.map.MapManager;
import java.util.List;
import fr.zcraft.zlib.components.commands.CommandException;
import fr.zcraft.zlib.components.commands.CommandInfo;
import org.bukkit.entity.Player;
@CommandInfo(name = "list")
public class ListCommand extends Command
import java.util.List;
@CommandInfo (name = "list")
public class ListCommand extends IoMCommand
{
public ListCommand(Commands commandGroup) {
super(commandGroup);
}
@Override
protected void run() throws CommandException
{
@ -52,5 +51,4 @@ public class ListCommand extends Command
}
player.sendMessage(sMapList);
}
}

View File

@ -18,21 +18,18 @@
package fr.moribus.imageonmap.commands.maptool;
import fr.moribus.imageonmap.commands.*;
import fr.moribus.imageonmap.commands.IoMCommand;
import fr.moribus.imageonmap.migration.MigratorExecutor;
import fr.zcraft.zlib.components.commands.CommandException;
import fr.zcraft.zlib.components.commands.CommandInfo;
import org.bukkit.command.CommandSender;
@CommandInfo(name = "migrate")
public class MigrateCommand extends Command
@CommandInfo (name = "migrate")
public class MigrateCommand extends IoMCommand
{
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.");

View File

@ -18,25 +18,21 @@
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.commands.IoMCommand;
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 fr.zcraft.zlib.components.commands.CommandException;
import fr.zcraft.zlib.components.commands.CommandInfo;
import fr.zcraft.zlib.components.worker.WorkerCallback;
import fr.zcraft.zlib.tools.PluginLogger;
import org.bukkit.entity.Player;
@CommandInfo(name = "new", usageParameters = "<URL> [resize]")
public class NewCommand extends Command
import java.net.MalformedURLException;
import java.net.URL;
@CommandInfo (name = "new", usageParameters = "<URL> [resize]")
public class NewCommand extends IoMCommand
{
public NewCommand(Commands commandGroup) {
super(commandGroup);
}
@Override
protected void run() throws CommandException
{
@ -83,5 +79,4 @@ public class NewCommand extends Command
}
});
}
}

View File

@ -16,17 +16,24 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package fr.moribus.imageonmap.guiproko.list;
package fr.moribus.imageonmap.gui;
import fr.moribus.imageonmap.*;
import fr.moribus.imageonmap.guiproko.core.*;
import fr.moribus.imageonmap.map.*;
import org.bukkit.*;
import org.bukkit.inventory.*;
import org.bukkit.inventory.meta.*;
import org.bukkit.material.*;
import fr.moribus.imageonmap.map.ImageMap;
import fr.moribus.imageonmap.map.MapManager;
import fr.moribus.imageonmap.map.MapManagerException;
import fr.zcraft.zlib.components.gui.ActionGui;
import fr.zcraft.zlib.components.gui.Gui;
import fr.zcraft.zlib.components.gui.GuiAction;
import fr.zcraft.zlib.tools.PluginLogger;
import org.bukkit.ChatColor;
import org.bukkit.DyeColor;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.material.Dye;
import java.util.*;
import java.util.Arrays;
import java.util.Random;
public class ConfirmDeleteMapGui extends ActionGui
@ -42,12 +49,6 @@ public class ConfirmDeleteMapGui extends ActionGui
*/
private ImageMap mapToDelete;
/**
* The previously-viewed page of the list GUI.
* Used to be able to bring the user back to the same page.
*/
private int currentPage;
/**
* The messages randomly displayed in the lore of the delete buttons.
*/
@ -69,12 +70,10 @@ public class ConfirmDeleteMapGui extends ActionGui
/**
*
* @param mapToDelete The map being deleted.
* @param currentPage The previously-viewed page of the list GUI.
*/
public ConfirmDeleteMapGui(ImageMap mapToDelete, int currentPage)
public ConfirmDeleteMapGui(ImageMap mapToDelete)
{
this.mapToDelete = mapToDelete;
this.currentPage = currentPage;
deleteMessages = new String[]{
"Please", "I'm still alive", "Don't do that", "I'm still loving you", "I want to live",
@ -166,13 +165,15 @@ public class ConfirmDeleteMapGui extends ActionGui
subButton.setItemMeta(meta);
return subButton;
}
protected void action_cancel()
@GuiAction ("cancel")
protected void cancel()
{
Gui.open(getPlayer(), new MapDetailGui(null /*mapToDelete, currentPage */));
close();
}
protected void action_delete()
@GuiAction ("delete")
protected void delete()
{
MapManager.clear(getPlayer().getInventory(), mapToDelete);
@ -187,6 +188,12 @@ public class ConfirmDeleteMapGui extends ActionGui
getPlayer().sendMessage(ChatColor.RED + "This map does not exists.");
}
Gui.open(getPlayer(), new MapListGui(/* currentPage */));
// We try to open the map list GUI, if the map was deleted, before the details GUI
// (so the grandparent GUI)..
if (getParent() != null && getParent().getParent() != null)
Gui.open(getPlayer(), getParent().getParent());
else
close();
}
}

View File

@ -16,15 +16,24 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package fr.moribus.imageonmap.guiproko.list;
package fr.moribus.imageonmap.gui;
import fr.moribus.imageonmap.guiproko.core.*;
import fr.moribus.imageonmap.map.*;
import org.bukkit.*;
import org.bukkit.inventory.*;
import org.bukkit.inventory.meta.*;
import fr.moribus.imageonmap.map.ImageMap;
import fr.moribus.imageonmap.map.PosterMap;
import fr.moribus.imageonmap.map.SingleMap;
import fr.zcraft.zlib.components.gui.ExplorerGui;
import fr.zcraft.zlib.components.gui.Gui;
import fr.zcraft.zlib.components.gui.GuiAction;
import fr.zcraft.zlib.components.gui.GuiUtils;
import fr.zcraft.zlib.components.gui.PromptGui;
import fr.zcraft.zlib.tools.Callback;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import java.util.*;
import java.util.Arrays;
import java.util.Collections;
public class MapDetailGui extends ExplorerGui<Void>
@ -80,38 +89,18 @@ public class MapDetailGui extends ExplorerGui<Void>
setData(null); // Fallback to the empty view item.
ItemStack back = new ItemStack(Material.EMERALD);
ItemMeta meta = back.getItemMeta();
meta.setDisplayName(ChatColor.GREEN + "« Back");
meta.setLore(Collections.singletonList(
ChatColor.GRAY + "Go back to the list."
));
back.setItemMeta(meta);
ItemStack rename = new ItemStack(Material.BOOK_AND_QUILL);
meta = rename.getItemMeta();
meta.setDisplayName(ChatColor.BLUE + "Rename this image");
meta.setLore(Arrays.asList(
action("rename", getSize() - 7, GuiUtils.makeItem(Material.BOOK_AND_QUILL, ChatColor.BLUE + "Rename this image", Arrays.asList(
ChatColor.GRAY + "Click here to rename this image;",
ChatColor.GRAY + "this is used for your own organization."
));
rename.setItemMeta(meta);
)));
ItemStack delete = new ItemStack(Material.BARRIER);
meta = delete.getItemMeta();
meta.setDisplayName(ChatColor.RED + "Delete this image");
meta.setLore(Arrays.asList(
action("delete", getSize() - 6, GuiUtils.makeItem(Material.BARRIER, ChatColor.RED + "Delete this image", Arrays.asList(
ChatColor.GRAY + "Deletes this map " + ChatColor.WHITE + "forever" + ChatColor.GRAY + ".",
ChatColor.GRAY + "This action cannot be undone!",
"",
ChatColor.GRAY + "You will be asked to confirm your",
ChatColor.GRAY + "choice if you click here."
));
delete.setItemMeta(meta);
action("rename", getSize() - 7, rename);
action("delete", getSize() - 6, delete);
)));
// To keep the controls centered, the back button is shifted to the right when the
@ -121,6 +110,33 @@ public class MapDetailGui extends ExplorerGui<Void>
if(map instanceof PosterMap && ((PosterMap) map).getColumnCount() <= INVENTORY_ROW_SIZE)
backSlot++;
action("back", backSlot, back);
action("back", backSlot, GuiUtils.makeItem(Material.EMERALD, ChatColor.GREEN + "« Back", Collections.singletonList(
ChatColor.GRAY + "Go back to the list."
)));
}
@GuiAction ("rename")
public void rename()
{
Gui.open(getPlayer(), new PromptGui(new Callback<String>() {
@Override
public void call(String name)
{
map.rename(name);
}
}, map.getName()), this);
}
@GuiAction ("delete")
public void delete()
{
Gui.open(getPlayer(), new ConfirmDeleteMapGui(map), this);
}
@GuiAction ("back")
public void back()
{
close();
}
}

View File

@ -0,0 +1,185 @@
/*
* 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.gui;
import fr.moribus.imageonmap.PluginConfiguration;
import fr.moribus.imageonmap.map.ImageMap;
import fr.moribus.imageonmap.map.MapManager;
import fr.moribus.imageonmap.map.PosterMap;
import fr.moribus.imageonmap.map.SingleMap;
import fr.moribus.imageonmap.ui.MapItemManager;
import fr.zcraft.zlib.components.gui.ExplorerGui;
import fr.zcraft.zlib.components.gui.Gui;
import fr.zcraft.zlib.components.gui.GuiUtils;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
public class MapListGui extends ExplorerGui<ImageMap>
{
private final NumberFormat bigNumbersFormatter = new DecimalFormat("###,###,###,###", DecimalFormatSymbols.getInstance(Locale.ENGLISH));
@Override
protected ItemStack getViewItem(ImageMap map)
{
String mapDescription;
if (map instanceof SingleMap)
mapDescription = "Single map";
else
mapDescription = "Poster map (" + ((PosterMap) map).getColumnCount() + "×" + ((PosterMap) map).getRowCount() + ")";
ItemStack icon = GuiUtils.makeItem(Material.MAP, ChatColor.GREEN + "" + ChatColor.BOLD + map.getName(), Arrays.asList(
ChatColor.WHITE + mapDescription,
"",
ChatColor.GRAY + "Map ID: " + map.getId(),
"",
ChatColor.GRAY + "» Left-click to get this map",
ChatColor.GRAY + "» Right-click for details and options"
));
return GuiUtils.hideItemAttributes(icon);
}
@Override
protected ItemStack getEmptyViewItem()
{
ItemStack empty = new ItemStack(Material.BARRIER);
ItemMeta meta = empty.getItemMeta();
meta.setDisplayName(ChatColor.RED + "You don't have any map.");
meta.setLore(Arrays.asList(
ChatColor.GRAY + "Get started by creating a new one",
ChatColor.GRAY + "using " + ChatColor.WHITE + "/tomap <URL> [resize]" + ChatColor.GRAY + "!"
));
empty.setItemMeta(meta);
return empty;
}
@Override
protected void onRightClick(ImageMap data)
{
Gui.open(getPlayer(), new MapDetailGui(data), this);
}
@Override
protected ItemStack getPickedUpItem(ImageMap map)
{
if (map instanceof SingleMap)
{
return MapItemManager.createMapItem(map.getMapsIDs()[0], map.getName());
}
MapItemManager.give(getPlayer(), map);
return null;
}
@Override
protected void onUpdate()
{
ImageMap[] maps = MapManager.getMaps(getPlayer().getUniqueId());
setData(maps);
setTitle(ChatColor.BLACK + "Your maps " + ChatColor.RESET + "(" + maps.length + ")");
setKeepHorizontalScrollingSpace(true);
/* ** Statistics ** */
int imagesCount = MapManager.getMapList(getPlayer().getUniqueId()).size();
int mapPartCount = MapManager.getMapPartCount(getPlayer().getUniqueId());
int mapGlobalLimit = PluginConfiguration.MAP_GLOBAL_LIMIT.getInteger();
int mapPersonalLimit = PluginConfiguration.MAP_PLAYER_LIMIT.getInteger();
int mapPartGloballyLeft = mapGlobalLimit - MapManager.getMapCount();
int mapPartPersonallyLeft = mapPersonalLimit - mapPartCount;
int mapPartLeft;
if (mapGlobalLimit <= 0 && mapPersonalLimit <= 0)
mapPartLeft = -1;
else if (mapGlobalLimit <= 0)
mapPartLeft = mapPartPersonallyLeft;
else if (mapPersonalLimit <= 0)
mapPartLeft = mapPartGloballyLeft;
else
mapPartLeft = Math.min(mapPartGloballyLeft, mapPartPersonallyLeft);
double percentageUsed = mapPartLeft < 0 ? 0 : ((double) mapPartCount) / ((double) (mapPartCount + mapPartLeft)) * 100;
ItemStack statistics = new ItemStack(Material.ENCHANTED_BOOK);
ItemMeta meta = statistics.getItemMeta();
meta.setDisplayName(ChatColor.BLUE + "Usage statistics");
meta.setLore(Arrays.asList(
"",
getStatisticText("Images rendered", imagesCount),
getStatisticText("Minecraft maps used", mapPartCount)
));
if (mapPartLeft >= 0)
{
List<String> lore = meta.getLore();
lore.add("");
lore.add(ChatColor.BLUE + "Minecraft maps limits");
lore.add("");
lore.add(getStatisticText("Server-wide limit", mapGlobalLimit, true));
lore.add(getStatisticText("Per-player limit", mapPersonalLimit, true));
lore.add("");
lore.add(getStatisticText("Current consumption", ((int) Math.rint(percentageUsed)) + " %"));
lore.add(getStatisticText("Maps left", mapPartLeft));
meta.setLore(lore);
}
GuiUtils.hideItemAttributes(meta);
statistics.setItemMeta(meta);
action("", getSize() - 5, statistics);
}
private String getStatisticText(String title, Integer value)
{
return getStatisticText(title, value, false);
}
private String getStatisticText(String title, Integer value, boolean zeroIsUnlimited)
{
return getStatisticText(title, zeroIsUnlimited && value <= 0 ? "unlimited" : bigNumbersFormatter.format(value));
}
private String getStatisticText(String title, String value)
{
return ChatColor.GRAY + title + ": " + ChatColor.WHITE + value;
}
}

View File

@ -1,345 +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.gui.core;
import org.bukkit.*;
import org.bukkit.entity.*;
import org.bukkit.event.inventory.*;
import org.bukkit.inventory.*;
import org.bukkit.inventory.meta.*;
import java.util.*;
/**
* @author IamBlueSlime, Amaury Carrade
*/
public abstract class AbstractGui {
protected TreeMap<Integer, String> actions = new TreeMap<>();
protected Inventory inventory;
/**
* This method is called when the inventory is open through the
* {@link GuiManager}. Use this to populate the GUI.
*
* You will have to store the inventory inside the {@link AbstractGui#inventory} protected attribute,
* as this attribute will be used by all other methods.
*
* @param player The player this GUI will be displayed to.
*/
public abstract void display(Player player);
/**
* A standard way to update the GUI when it is not closed. This method is
* never called automatically, you have to call it when needed.
*
* @param player The player this GUI is displayed to.
*/
public void update(Player player)
{
}
/**
* Call this to open the GUI. The GUI is not automatically open, allowing you
* to open it at the best moment (before or after populating it, as example).
*
* @param player The player this GUI will be displayed to.
*/
public void open(Player player)
{
player.openInventory(inventory);
}
/**
* This method will be called when the GUI is closed.
*
* @param player The player this GUI was displayed to.
*/
public void onClose(Player player)
{
}
/**
* This method will be called when the player clicks on the GUI.
*
* @param player The player who clicked on the GUI.
* @param stack The clicked {@link ItemStack}.
* @param action The action associated with this slot using {@link AbstractGui#setSlotData(ItemStack, int, String)}
* or other similar methods.
* @param clickType The click.
* @param invAction The inventory action.
* @param event The full {@link InventoryClickEvent} triggered by the player.
*/
public void onClick(Player player, ItemStack stack, String action, ClickType clickType, InventoryAction invAction, InventoryClickEvent event)
{
this.onClick(player, stack, action, clickType, invAction);
}
/**
* This method will be called when the player clicks on the GUI.
*
* @param player The player who clicked on the GUI.
* @param stack The clicked {@link ItemStack}.
* @param action The action associated with this slot using {@link AbstractGui#setSlotData(ItemStack, int, String)}
* or other similar methods.
* @param clickType The click.
* @param invAction The inventory action.
*/
public void onClick(Player player, ItemStack stack, String action, ClickType clickType, InventoryAction invAction)
{
this.onClick(player, stack, action, clickType);
}
/**
* This method will be called when the player clicks on the GUI.
*
* @param player The player who clicked on the GUI.
* @param stack The clicked {@link ItemStack}.
* @param action The action associated with this slot using {@link AbstractGui#setSlotData(ItemStack, int, String)}
* or other similar methods.
* @param clickType The click.
*/
public void onClick(Player player, ItemStack stack, String action, ClickType clickType)
{
this.onClick(player, stack, action);
}
/**
* This method will be called when the player clicks on the GUI.
*
* @param player The player who clicked on the GUI.
* @param stack The clicked {@link ItemStack}.
* @param action The action associated with this slot using {@link AbstractGui#setSlotData(ItemStack, int, String)}
* or other similar methods.
*/
public void onClick(Player player, ItemStack stack, String action) {}
/**
* This method will be called when the user places an item in the GUI, either by shift-click or directly.
*
* Use the {@link InventoryAction} to distinguish these cases.
*
* @param player The played who moved the stack to the GUI.
* @param stack The moved {@link ItemStack}.
* @param clickType The click.
* @param invAction The inventory action.
* @param event The full {@link InventoryClickEvent} triggered by the player.
*/
public void onItemDeposit(Player player, ItemStack stack, ClickType clickType, InventoryAction invAction, InventoryClickEvent event)
{
onItemDeposit(player, stack, clickType, invAction);
}
/**
* This method will be called when the user places an item in the GUI, either by shift-click or directly.
*
* Use the {@link InventoryAction} to distinguish these cases.
*
* @param player The played who moved the stack to the GUI.
* @param stack The moved {@link ItemStack}.
* @param clickType The click.
* @param invAction The inventory action.
*/
public void onItemDeposit(Player player, ItemStack stack, ClickType clickType, InventoryAction invAction)
{
onItemDeposit(player, stack, clickType);
}
/**
* This method will be called when the user places an item in the GUI, either by shift-click or directly.
*
* Use the {@link InventoryAction} to distinguish these cases.
*
* @param player The played who moved the stack to the GUI.
* @param stack The moved {@link ItemStack}.
* @param clickType The click.
*/
public void onItemDeposit(Player player, ItemStack stack, ClickType clickType)
{
onItemDeposit(player, stack);
}
/**
* This method will be called when the user places an item in the GUI, either by shift-click or directly.
*
* Use the {@link InventoryAction} to distinguish these cases.
*
* @param player The played who moved the stack to the GUI.
* @param stack The moved {@link ItemStack}.
*/
public void onItemDeposit(Player player, ItemStack stack)
{
}
/**
* Registers a slot as a managed one.
*
* When such a slot is clicked by the player, the {@link #onClick(Player, ItemStack, String, ClickType, InventoryAction, InventoryClickEvent)}
* method is called (and all methods with the same name and less arguments).
*
* @param inv The inventory the item will be added to.
* @param name The display name of the item.
* @param material The material of this item.
* @param slot The slot this item will be added to.
* @param description The description (lore) of the item (one line per {@link String} in the array).
* @param action The action associated with this slot, retrieved with the {@link #onClick(Player, ItemStack, String)] methods.
*/
public void setSlotData(Inventory inv, String name, Material material, int slot, String[] description, String action)
{
this.setSlotData(inv, name, new ItemStack(material, 1), slot, description, action);
}
/**
* Registers a slot as a managed one, using the default inventory.
*
* When such a slot is clicked by the player, the {@link #onClick(Player, ItemStack, String, ClickType, InventoryAction, InventoryClickEvent)}
* method is called (and all methods with the same name and less arguments).
*
* @param name The display name of the item.
* @param material The material of this item.
* @param slot The slot this item will be added to.
* @param description The description (lore) of the item (one line per {@link String} in the array).
* @param action The action associated with this slot, retrieved with the {@link #onClick(Player, ItemStack, String)] methods.
*/
public void setSlotData(String name, Material material, int slot, String[] description, String action)
{
this.setSlotData(this.inventory, name, new ItemStack(material, 1), slot, description, action);
}
/**
* Registers a slot as a managed one, using the default inventory.
*
* When such a slot is clicked by the player, the {@link #onClick(Player, ItemStack, String, ClickType, InventoryAction, InventoryClickEvent)}
* method is called (and all methods with the same name and less arguments).
*
* @param name The display name of the item.
* @param item The {@link ItemStack} displayed in this slot.
* @param slot The slot this item will be added to.
* @param description The description (lore) of the item (one line per {@link String} in the array).
* @param action The action associated with this slot, retrieved with the {@link #onClick(Player, ItemStack, String)] methods.
*/
public void setSlotData(String name, ItemStack item, int slot, String[] description, String action)
{
this.setSlotData(this.inventory, name, item, slot, description, action);
}
/**
* Registers a slot as a managed one.
*
* When such a slot is clicked by the player, the {@link #onClick(Player, ItemStack, String, ClickType, InventoryAction, InventoryClickEvent)}
* method is called (and all methods with the same name and less arguments).
*
* @param inv The inventory the item will be added to.
* @param name The display name of the item.
* @param item The {@link ItemStack} displayed in this slot.
* @param slot The slot this item will be added to.
* @param description The description (lore) of the item (one line per {@link String} in the array).
* @param action The action associated with this slot, retrieved with the {@link #onClick(Player, ItemStack, String)] methods.
*/
public void setSlotData(Inventory inv, String name, ItemStack item, int slot, String[] description, String action)
{
this.actions.put(slot, action);
ItemMeta meta = item.getItemMeta();
meta.setDisplayName(name);
if (description != null)
meta.setLore(Arrays.asList(description));
item.setItemMeta(meta);
inv.setItem(slot, item);
}
/**
* Registers a slot as a managed one.
*
* When such a slot is clicked by the player, the {@link #onClick(Player, ItemStack, String, ClickType, InventoryAction, InventoryClickEvent)}
* method is called (and all methods with the same name and less arguments).
*
* @param inv The inventory the item will be added to.
* @param item The {@link ItemStack} displayed in this slot.
* @param slot The slot this item will be added to.
* @param action The action associated with this slot, retrieved with the {@link #onClick(Player, ItemStack, String)] methods.
*/
public void setSlotData(Inventory inv, ItemStack item, int slot, String action)
{
this.actions.put(slot, action);
inv.setItem(slot, item);
}
/**
* Registers a slot as a managed one, using the default inventory.
*
* When such a slot is clicked by the player, the {@link #onClick(Player, ItemStack, String, ClickType, InventoryAction, InventoryClickEvent)}
* method is called (and all methods with the same name and less arguments).
*
* @param item The {@link ItemStack} displayed in this slot.
* @param slot The slot this item will be added to.
* @param action The action associated with this slot, retrieved with the {@link #onClick(Player, ItemStack, String)] methods.
*/
public void setSlotData(ItemStack item, int slot, String action)
{
setSlotData(this.inventory, item, slot, action);
}
/**
* Returns the registered action associated with the given slot.
*
* @param slot The slot.
*
* @return The action; {@code null} if there isn't any action registered to this slot.
*/
public String getAction(int slot)
{
if (!this.actions.containsKey(slot))
return null;
return this.actions.get(slot);
}
/**
* Returns the slot registered to the given action.
*
* @param action The action.
*
* @return The slot; {@code -1} if this action is not registered.
*/
public int getSlot(String action)
{
for (int slot : this.actions.keySet())
if (this.actions.get(slot).equals(action))
return slot;
return -1;
}
/**
* Returns the default inventory.
*
* @return The inventory.
*/
public Inventory getInventory()
{
return this.inventory;
}
}

View File

@ -1,108 +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.gui.core;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryCloseEvent;
import org.bukkit.event.inventory.InventoryDragEvent;
import org.bukkit.inventory.PlayerInventory;
import org.bukkit.plugin.Plugin;
/**
* @author IamBlueSlime, Amaury Carrade
*
* Changes by Amaury Carrade to use statics (beh, code style, these things).
*/
public class GuiListener implements Listener {
public static void init(Plugin plugin)
{
plugin.getServer().getPluginManager().registerEvents(new GuiListener(), plugin);
}
@EventHandler
public void onInventoryClick(InventoryClickEvent event)
{
if (event.getWhoClicked() instanceof Player)
{
Player player = (Player) event.getWhoClicked();
AbstractGui gui = GuiManager.getPlayerGui(player);
if(gui == null)
return;
if (event.getInventory() instanceof PlayerInventory)
return;
/* *** Click from player inventory (with shift) *** */
if(event.getRawSlot() != event.getSlot())
{
if(event.isShiftClick())
{
gui.onItemDeposit(player, event.getCurrentItem(), event.getClick(), event.getAction(), event);
}
return;
}
/* *** Click on the GUI inventory *** */
if(event.getCursor() != null && event.getCursor().getType() != Material.AIR)
{
gui.onItemDeposit(player, event.getCursor(), event.getClick(), event.getAction(), event);
}
else
{
String action = gui.getAction(event.getSlot());
if (action != null)
gui.onClick(player, event.getCurrentItem(), action, event.getClick(), event.getAction(), event);
}
event.setCancelled(true);
}
}
@EventHandler
public void onInventoryDrag(InventoryDragEvent event)
{
Player player = (Player) event.getWhoClicked();
AbstractGui gui = GuiManager.getPlayerGui(player);
if (gui != null)
{
event.setCancelled(true);
}
}
@EventHandler
public void onInventoryClose(InventoryCloseEvent event)
{
if (GuiManager.getPlayerGui(event.getPlayer()) != null)
GuiManager.removeClosedGui((Player) event.getPlayer());
}
}

View File

@ -1,120 +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.gui.core;
import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
/**
* A tool to easily manage GUIs.
*
* @author IamBlueSlime and Amaury Carrade.
*/
public class GuiManager {
protected static Map<UUID, AbstractGui> currentGUIs;
/**
* Call this when the plugin is enabled.
*
* @param plugin The plugin using this.
*/
public static void init(Plugin plugin)
{
currentGUIs = new ConcurrentHashMap<>();
GuiListener.init(plugin);
}
/**
* Opens a GUI for the given player.
*
* Closes any GUI already open for this player.
*
* @param player The player this GUI will be open to.
* @param gui The GUI to open.
*/
public static void openGui(Player player, AbstractGui gui)
{
if (currentGUIs.containsKey(player.getUniqueId()))
closeGui(player);
currentGUIs.put(player.getUniqueId(), gui);
gui.display(player);
}
/**
* Closes the currently open GUI of this player, if it exists.
*
* Without any open GUI for this player, does nothing.
*
* @param player The player.
*/
public static void closeGui(Player player)
{
player.closeInventory();
removeClosedGui(player);
}
/**
* Calls the {@link AbstractGui#onClose(Player)} method of the currently open GUI of the
* given {@link Player} and unregisters it as open.
*
* @param player The player
*/
public static void removeClosedGui(Player player)
{
if (currentGUIs.containsKey(player.getUniqueId()))
{
//noinspection ConstantConditions
getPlayerGui(player).onClose(player);
currentGUIs.remove(player.getUniqueId());
}
}
/**
* Returns the currently open {@link AbstractGui} of the given {@link HumanEntity}.
*
* @param player The HumanEntity.
* @return The open GUI, or {@code null} if no GUI are open.
*/
public static AbstractGui getPlayerGui(HumanEntity player)
{
if (currentGUIs.containsKey(player.getUniqueId()))
return currentGUIs.get(player.getUniqueId());
return null;
}
/**
* Returns all open GUIs.
*
* @return The GUI (map: player's {@link UUID} {@link AbstractGui}).
*/
public static Map<UUID, AbstractGui> getPlayersGui()
{
return currentGUIs;
}
}

View File

@ -1,85 +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.gui.core;
import fr.moribus.imageonmap.PluginLogger;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class GuiUtils {
private static boolean supported = true;
private static Object[] itemFlags;
static
{
try {
Class<?> itemFlagEnumClass = Class.forName("org.bukkit.inventory.ItemFlag");
Method valuesMethod = itemFlagEnumClass.getDeclaredMethod("values");
itemFlags = (Object[]) valuesMethod.invoke(null);
} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException e) {
// Not supported :c
supported = false;
} catch (InvocationTargetException e) {
PluginLogger.error("Exception occurred while loading the ItemFlags.", e);
}
}
/**
* Removes the vanilla informations displayed on the tooltips of the given item,
* like enchantments, map infos, etc.
*
* @param stack The item.
*/
public static void removeVanillaInfos(ItemStack stack)
{
ItemMeta meta = stack.getItemMeta();
removeVanillaInfos(meta);
stack.setItemMeta(meta);
}
/**
* Removes the vanilla informations displayed on the tooltips of the given item,
* like enchantments, map infos, etc.
*
* @param meta The item's metadata.
*/
public static void removeVanillaInfos(ItemMeta meta)
{
if(!supported) return;
try {
Method addItemFlagsMethod = meta.getClass().getMethod("addItemFlags", itemFlags.getClass());
addItemFlagsMethod.setAccessible(true);
addItemFlagsMethod.invoke(meta, (Object) itemFlags);
} catch (NoSuchMethodException | IllegalAccessException ignored) {
// Should never happens, or only with breaking changes in the Bukkit API.
} catch (InvocationTargetException e) {
PluginLogger.error("Exception occurred while invoking the ItemMeta.addItemFlags method.", e);
}
}
}

View File

@ -1,206 +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.gui.list;
import fr.moribus.imageonmap.PluginLogger;
import fr.moribus.imageonmap.gui.core.AbstractGui;
import fr.moribus.imageonmap.gui.core.GuiManager;
import fr.moribus.imageonmap.map.ImageMap;
import fr.moribus.imageonmap.map.MapManager;
import fr.moribus.imageonmap.map.MapManagerException;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.DyeColor;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.material.Dye;
import java.util.Arrays;
import java.util.Random;
public class ConfirmDeleteMapGui extends AbstractGui
{
static private final int BUTTONS_WIDTH = 4;
static private final int FIRST_SLOT_DELETE_BUTTON = 27;
static private final int SHIFT_CANCEL_BUTTON = 5;
/**
* The map being deleted.
*/
private ImageMap mapToDelete;
/**
* The previously-viewed page of the list GUI.
* Used to be able to bring the user back to the same page.
*/
private int currentPage;
/**
* The messages randomly displayed in the lore of the delete buttons.
*/
private String[] deleteMessages;
/**
* The messages randomly displayed in the lore of the cancel buttons.
*/
private String[] cancelMessages;
/**
* A source of randomness.
*
* Yes, this javadoc comment is REALLY useful.
*/
private Random random = new Random();
/**
*
* @param mapToDelete The map being deleted.
* @param currentPage The previously-viewed page of the list GUI.
*/
public ConfirmDeleteMapGui(ImageMap mapToDelete, int currentPage) {
this.mapToDelete = mapToDelete;
this.currentPage = currentPage;
deleteMessages = new String[]{
"Please", "I'm still alive", "Don't do that", "I'm still loving you", "I want to live",
"Please please", "Please please please", "What are you doing?!", "Nooooo!",
"Click and I'll be dead", "Why?", "Please don't do that", "Think about my family",
"Click, I don't like you anymore.", "I don't hate you.", "Click, I'm ready.",
"I'm a green button.", "I'm different.", "Thanks anyway.", "Excuse me.", "Get mad.",
"Sorry!", "My fault!", "I don't blame you.", "No hard feelings.",
"But I need to protect the humans!", "Noooo!", "I'm scared!", "What are you doing?",
"It burns.", "This is not good.", "Can't breathe.", "Thanks anyway.", "These things happen.",
"That was nobody's fault.", "I probably deserved it.", "I blame myself."
};
cancelMessages = new String[] {
"Yay!", "Still aliiiive!", "Click click click", "Yes do that", "I'm a red button.",
"Please click here", "The other button is ugly", "Save me", "This is the good choice",
"Click, I want to live!", "I'll be dead another day anyway", "Are you sure?",
"So you're still loving me?", "Please save me", "Take me with you.",
"Excuse me.", "Don't make lemonade.", "Sleep mode activated.", "Hybernating.",
"Your business is appreciated.", "Hey! It's me! Don't shoot!", "Wheee!", "Hurray!",
"You have excellent aim!", "Please please please"
};
}
@Override
public void display(Player player)
{
inventory = Bukkit.createInventory(player, 6 * 9, ChatColor.BLACK + "Are you sure?");
/* ** Item representation of the image being deleted ** */
ItemStack beingDeleted = new ItemStack(Material.EMPTY_MAP);
ItemMeta meta = beingDeleted.getItemMeta();
meta.setDisplayName(ChatColor.RED + "You're about to destroy this map...");
meta.setLore(Arrays.asList(
ChatColor.RED + "..." + ChatColor.ITALIC + "forever" + ChatColor.RED + ".",
"",
ChatColor.GRAY + "Name: " + ChatColor.WHITE + mapToDelete.getName(),
ChatColor.GRAY + "Map ID: " + ChatColor.WHITE + mapToDelete.getId(),
ChatColor.GRAY + "Maps inside: " + ChatColor.WHITE + mapToDelete.getMapsIDs().length
));
beingDeleted.setItemMeta(meta);
setSlotData(beingDeleted, 13, "");
/* ** Buttons ** */
int slot = FIRST_SLOT_DELETE_BUTTON;
for(; slot < inventory.getSize() - (9 - BUTTONS_WIDTH); slot++)
{
setSlotData(createDeleteSubButton(), slot, "delete");
setSlotData(createCancelSubButton(), slot + SHIFT_CANCEL_BUTTON, "cancel");
if((slot + 1) % 9 == (9 - BUTTONS_WIDTH - 1))
slot += 5;
}
player.openInventory(inventory);
}
private ItemStack createDeleteSubButton()
{
// Orange? Nooo. In the real world this is red. True story.
return createSubButton(DyeColor.ORANGE, ChatColor.RED + "Delete the map", deleteMessages);
}
private ItemStack createCancelSubButton()
{
// YES. Purple = lime. BECAUSE. Just accept it.
return createSubButton(DyeColor.PURPLE, ChatColor.GREEN + "Cancel", cancelMessages);
}
private ItemStack createSubButton(DyeColor color, String title, String[] messages)
{
Dye pane = new Dye(Material.STAINED_GLASS_PANE);
pane.setColor(color);
ItemStack subButton = pane.toItemStack(1);
ItemMeta meta = subButton.getItemMeta();
meta.setDisplayName(title);
meta.setLore(Arrays.asList(
"",
ChatColor.GRAY + messages[random.nextInt(messages.length)]
));
subButton.setItemMeta(meta);
return subButton;
}
@Override
public void onClick(Player player, ItemStack stack, String action) {
switch(action)
{
case "cancel":
GuiManager.openGui(player, new MapDetailGui(mapToDelete, currentPage));
return;
case "delete":
MapManager.clear(player.getInventory(), mapToDelete);
try
{
MapManager.deleteMap(mapToDelete);
player.sendMessage(ChatColor.GRAY + "Map successfully deleted.");
}
catch (MapManagerException ex)
{
PluginLogger.warning("A non-existent map was requested to be deleted", ex);
player.sendMessage(ChatColor.RED + "This map does not exists.");
}
GuiManager.openGui(player, new MapListGui(currentPage));
}
}
}

View File

@ -1,370 +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.gui.list;
import fr.moribus.imageonmap.gui.core.AbstractGui;
import fr.moribus.imageonmap.gui.core.GuiManager;
import fr.moribus.imageonmap.guiproko.core.Gui;
import fr.moribus.imageonmap.map.ImageMap;
import fr.moribus.imageonmap.map.PosterMap;
import fr.moribus.imageonmap.ui.MapItemManager;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.ClickType;
import org.bukkit.event.inventory.InventoryAction;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import java.util.Arrays;
import java.util.Collections;
public class MapDetailGui extends AbstractGui
{
/**
* The max width of the window open on the image.
*/
private final int MAX_WINDOW_WIDTH = 8;
/**
* The max height of the window open on the image.
*/
private final int MAX_WINDOW_HEIGHT = 5;
/**
* The map displayed in this GUI.
*/
private ImageMap map;
/**
* The previously-viewed page of the list GUI.
* Used to be able to bring the user back to the same page.
*/
private int currentPage;
/**
* The row currently at the top of the window open on the displayed map.
*/
private int topRow = 0;
/**
* The column currently at the top of the window open on the displayed map.
*/
private int topColumn = 0;
/**
*
* @param map The map displayed in this GUI.
*/
public MapDetailGui(ImageMap map)
{
this(map, 0);
}
/**
*
* @param map The map displayed in this GUI.
* @param currentPage The previously-viewed page of the list GUI.
*/
public MapDetailGui(ImageMap map, int currentPage)
{
this.map = map;
this.currentPage = currentPage;
}
@Override
public void display(Player player)
{
inventory = Bukkit.createInventory(player, 6 * 9, ChatColor.BLACK + "Your maps");
ItemStack back = new ItemStack(Material.EMERALD);
ItemMeta meta = back.getItemMeta();
meta.setDisplayName(ChatColor.GREEN + "« Back");
meta.setLore(Collections.singletonList(
ChatColor.GRAY + "Go back to the list."
));
back.setItemMeta(meta);
ItemStack rename = new ItemStack(Material.BOOK_AND_QUILL);
meta = rename.getItemMeta();
meta.setDisplayName(ChatColor.BLUE + "Rename this image");
meta.setLore(Arrays.asList(
ChatColor.GRAY + "Click here to rename this image;",
ChatColor.GRAY + "this is used for your own organization."
));
rename.setItemMeta(meta);
ItemStack delete = new ItemStack(Material.BARRIER);
meta = delete.getItemMeta();
meta.setDisplayName(ChatColor.RED + "Delete this image");
meta.setLore(Arrays.asList(
ChatColor.GRAY + "Deletes this map " + ChatColor.WHITE + "forever" + ChatColor.GRAY + ".",
ChatColor.GRAY + "This action cannot be undone!",
"",
ChatColor.GRAY + "You will be asked to confirm your",
ChatColor.GRAY + "choice if you click here."
));
delete.setItemMeta(meta);
setSlotData(rename, inventory.getSize() - 7, "rename");
setSlotData(delete, inventory.getSize() - 6, "delete");
// To keep the controls centered, the back button is shifted to the right when the
// arrow isn't displayed, so when the map fit on the grid without sliders.
int backSlot = inventory.getSize() - 4;
if(map instanceof PosterMap && ((PosterMap) map).getColumnCount() <= MAX_WINDOW_WIDTH)
backSlot++;
setSlotData(back, backSlot, "back");
update(player);
open(player);
}
@Override
public void update(Player player)
{
if(map instanceof PosterMap && ((PosterMap) map).hasColumnData()) {
int slot = 0;
for (int row = topRow; row < topRow + MAX_WINDOW_HEIGHT; row++)
{
for (int col = topColumn; col < topColumn + MAX_WINDOW_WIDTH; col++)
{
if(col < ((PosterMap) map).getColumnCount() && row < ((PosterMap) map).getRowCount())
{
setSlotData(getMapPartRepresentation(col, row), slot, col + ";" + row);
}
else
{
setSlotData(new ItemStack(Material.AIR), slot, "");
}
slot++;
}
slot++;
}
placeArrowSlider('\u2B07', canGoDown() , ((PosterMap) map).getRowCount() > MAX_WINDOW_HEIGHT, 44, "down" );
placeArrowSlider('\u2B06', canGoUp() , ((PosterMap) map).getRowCount() > MAX_WINDOW_HEIGHT, 8 , "up" );
placeArrowSlider('«' , canGoLeft() , ((PosterMap) map).getColumnCount() > MAX_WINDOW_WIDTH , 45, "left" );
placeArrowSlider('»' , canGoRight(), ((PosterMap) map).getColumnCount() > MAX_WINDOW_WIDTH , 52, "right");
}
else
{
setSlotData(getMapPartRepresentation(0, 0), 13, "0;0");
}
}
private ItemStack getMapPartRepresentation(int col, int row)
{
Material partMaterial = Material.PAPER;
if((col % 2 == 0 && row % 2 == 0) || (col % 2 == 1 && row % 2 == 1))
partMaterial = Material.EMPTY_MAP;
ItemStack part = new ItemStack(partMaterial);
ItemMeta meta = part.getItemMeta();
meta.setDisplayName(ChatColor.GREEN + "Map part");
meta.setLore(Arrays.asList(
ChatColor.GRAY + "Column: " + ChatColor.WHITE + (col + 1),
ChatColor.GRAY + "Row: " + ChatColor.WHITE + (row + 1),
"",
ChatColor.GRAY + "» Click to get only this part"
));
part.setItemMeta(meta);
return part;
}
private void placeArrowSlider(Character nameCharacter, boolean active, boolean availableInThisView, int slot, String action)
{
/* ** Item ** */
if(!availableInThisView)
{
setSlotData(new ItemStack(Material.AIR), slot, "");
return;
}
ItemStack slider = new ItemStack(active ? Material.ARROW : Material.STICK);
ItemMeta meta = slider.getItemMeta();
String title = "";
for(int i = 0; i < 5; i++)
title += nameCharacter;
meta.setDisplayName((active ? ChatColor.GREEN : ChatColor.GRAY) + title);
slider.setItemMeta(meta);
/* ** Placement ** */
setSlotData(slider, slot, active ? action : "");
}
@Override
public void onClick(Player player, ItemStack stack, String action, ClickType click, InventoryAction invAction, InventoryClickEvent ev)
{
switch (action)
{
case "down":
goDown(player);
return;
case "up":
goUp(player);
return;
case "left":
goLeft(player);
return;
case "right":
goRight(player);
return;
case "back":
goBack(player);
return;
case "rename":
// TODO (no GUI ready for that yet)
return;
case "delete":
GuiManager.openGui(player, new ConfirmDeleteMapGui(map, currentPage));
//Gui.open(player, new fr.moribus.imageonmap.guiproko.list.ConfirmDeleteMapGui());
return;
default:
Integer col;
Integer row;
if(map instanceof PosterMap && ((PosterMap) map).hasColumnData()) {
String[] coords = action.split(";");
if (coords.length != 2) return; // Other unhandled action.
try {
col = Integer.valueOf(coords[0]);
row = Integer.valueOf(coords[1]);
} catch (NumberFormatException e) {
return; // Other unhandled action.
}
}
else
{
col = row = 0;
}
ev.setCursor(MapItemManager.createSubMapItem(map, col, row));
}
}
private void goDown(Player player)
{
if (!(map instanceof PosterMap)) return;
if(canGoDown())
{
topRow++;
update(player);
}
}
private void goUp(Player player)
{
if (!(map instanceof PosterMap)) return;
if(canGoUp())
{
topRow--;
update(player);
}
}
private void goRight(Player player)
{
if (!(map instanceof PosterMap)) return;
if(canGoRight())
{
topColumn++;
update(player);
}
}
private void goLeft(Player player)
{
if (!(map instanceof PosterMap)) return;
if(canGoLeft())
{
topColumn--;
update(player);
}
}
private void goBack(Player player)
{
GuiManager.openGui(player, new MapListGui(currentPage));
}
private boolean canGoDown()
{
return topRow + MAX_WINDOW_HEIGHT < ((PosterMap) map).getRowCount();
}
private boolean canGoUp()
{
return topRow > 0;
}
private boolean canGoRight()
{
return topColumn + MAX_WINDOW_WIDTH < ((PosterMap) map).getColumnCount();
}
private boolean canGoLeft()
{
return topColumn > 0;
}
}

View File

@ -1,354 +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.gui.list;
import fr.moribus.imageonmap.*;
import fr.moribus.imageonmap.gui.core.*;
import fr.moribus.imageonmap.map.*;
import fr.moribus.imageonmap.ui.MapItemManager;
import org.bukkit.*;
import org.bukkit.entity.*;
import org.bukkit.event.inventory.*;
import org.bukkit.inventory.*;
import org.bukkit.inventory.meta.*;
import java.text.*;
import java.util.*;
public class MapListGui extends AbstractGui
{
private final Integer MAPS_PER_PAGE = 7 * 3;
private final NumberFormat bigNumbersFormatter = new DecimalFormat("###,###,###,###", DecimalFormatSymbols.getInstance(Locale.ENGLISH));
private List<ImageMap> maps = new ArrayList<>();
private int currentPage = 0;
private int lastPage = 0;
public MapListGui() {
}
public MapListGui(Integer initialPage) {
currentPage = initialPage;
}
@Override
public void display(Player player) {
inventory = Bukkit.createInventory(player, 6 * 9, ChatColor.BLACK + "Your maps");
player.openInventory(getInventory());
/* ** Statistics ** */
int imagesCount = MapManager.getMapList(player.getUniqueId()).size();
int mapPartCount = MapManager.getMapPartCount(player.getUniqueId());
int mapGlobalLimit = PluginConfiguration.MAP_GLOBAL_LIMIT.getInteger();
int mapPersonalLimit = PluginConfiguration.MAP_PLAYER_LIMIT.getInteger();
int mapPartGloballyLeft = mapGlobalLimit - MapManager.getMapCount();
int mapPartPersonallyLeft = mapPersonalLimit - mapPartCount;
int mapPartLeft;
if(mapGlobalLimit <= 0 && mapPersonalLimit <= 0)
mapPartLeft = -1;
else if(mapGlobalLimit <= 0)
mapPartLeft = mapPartPersonallyLeft;
else if(mapPersonalLimit <= 0)
mapPartLeft = mapPartGloballyLeft;
else
mapPartLeft = Math.min(mapPartGloballyLeft, mapPartPersonallyLeft);
double percentageUsed = mapPartLeft < 0 ? 0 : ((double) mapPartCount) / ((double) (mapPartCount + mapPartLeft)) * 100;
ItemStack statistics = new ItemStack(Material.ENCHANTED_BOOK);
ItemMeta meta = statistics.getItemMeta();
meta.setDisplayName(ChatColor.BLUE + "Usage statistics");
meta.setLore(Arrays.asList(
"",
getStatisticText("Images rendered", imagesCount),
getStatisticText("Minecraft maps used", mapPartCount)
));
if(mapPartLeft >= 0)
{
List<String> lore = meta.getLore();
lore.add("");
lore.add(ChatColor.BLUE + "Minecraft maps limits");
lore.add("");
lore.add(getStatisticText("Server-wide limit", mapGlobalLimit, true));
lore.add(getStatisticText("Per-player limit", mapPersonalLimit, true));
lore.add("");
lore.add(getStatisticText("Current consumption", ((int) Math.rint(percentageUsed)) + " %"));
lore.add(getStatisticText("Maps left", mapPartLeft));
lore.add("");
meta.setLore(lore);
}
GuiUtils.removeVanillaInfos(meta);
statistics.setItemMeta(meta);
setSlotData(statistics, inventory.getSize() - 5, "");
/* ** Maps ** */
update(player);
}
@Override
public void update(Player player) {
update(player, false);
}
public void update(final Player player, final Boolean noCache) {
if (maps == null || maps.isEmpty() || noCache) {
updateMapCache(player);
}
if(maps.isEmpty()) {
ItemStack empty = new ItemStack(Material.BARRIER);
ItemMeta meta = empty.getItemMeta();
meta.setDisplayName(ChatColor.RED + "Nothing to display here");
meta.setLore(Arrays.asList(
ChatColor.GRAY + "You don't have any map.",
ChatColor.GRAY + "Get started by creating a new one",
ChatColor.GRAY + "with " + ChatColor.WHITE + "/tomap <URL> [resize]" + ChatColor.GRAY + "!"
));
empty.setItemMeta(meta);
setSlotData(empty, 13, "");
return;
}
int index = currentPage * MAPS_PER_PAGE;
int lastIndex = index + MAPS_PER_PAGE;
int slot = 10;
ImageMap map;
for (; index < lastIndex; index++) {
try {
map = maps.get(index);
setSlotData(getMapIcon(map), slot, map.getId());
} catch(IndexOutOfBoundsException e) {
setSlotData(new ItemStack(Material.AIR), slot, "");
}
if (slot % 9 == 7) slot += 3;
else slot++;
}
if (currentPage != 0)
setSlotData(getPageIcon(currentPage - 1), inventory.getSize() - 9, "previousPage");
else
setSlotData(new ItemStack(Material.AIR), inventory.getSize() - 9, "");
if (currentPage != lastPage)
setSlotData(getPageIcon(currentPage + 1), inventory.getSize() - 1, "nextPage");
else
setSlotData(new ItemStack(Material.AIR), inventory.getSize() - 1, "");
}
@Override
public void onClick(Player player, ItemStack stack, String action, ClickType clickType, InventoryAction invAction, InventoryClickEvent ev)
{
switch (action)
{
case "previousPage":
previousPage(player);
return;
case "nextPage":
nextPage(player);
return;
default:
// The action is the map ID
ImageMap map = null;
for(ImageMap lookupMap : maps)
{
if(lookupMap.getId().equals(action))
{
map = lookupMap;
break;
}
}
if(map == null) return;
switch (clickType)
{
case LEFT:
case SHIFT_LEFT:
if(map.getType() == ImageMap.Type.SINGLE)
{
if(invAction == InventoryAction.MOVE_TO_OTHER_INVENTORY)
{
map.give(player);
}
else
{
ev.setCursor(MapItemManager.createMapItem(map.getMapsIDs()[0], map.getName()));
}
}
else
{
if (map.give(player)) {
player.sendMessage(ChatColor.GRAY + "The requested map was too big to fit in your inventory.");
player.sendMessage(ChatColor.GRAY + "Use '/maptool getremaining' to get the remaining maps.");
}
}
break;
case RIGHT:
case SHIFT_RIGHT:
GuiManager.openGui(player, new MapDetailGui(map, currentPage));
break;
}
}
}
@Override
public void onItemDeposit(Player player, ItemStack stack, ClickType clickType, InventoryAction invAction, InventoryClickEvent ev) {
ev.setCancelled(true);
if (stack.getType() == Material.MAP && MapManager.managesMap(stack))
{
ImageMap map = MapManager.getMap(stack);
if (map != null)
{
MapManager.clear(player.getInventory(), map);
// Deprecated? Yes. Alternatives? No, as usual...
ev.setCursor(new ItemStack(Material.AIR));
}
}
}
private void nextPage(Player player)
{
if(currentPage < lastPage) currentPage++;
update(player);
}
private void previousPage(Player player)
{
if(currentPage > 0) currentPage--;
update(player);
}
private void updateMapCache(Player player)
{
maps = MapManager.getMapList(player.getUniqueId());
lastPage = (int) Math.ceil(((double) maps.size()) / ((double) MAPS_PER_PAGE)) - 1;
if(currentPage > lastPage)
currentPage = lastPage;
}
private ItemStack getMapIcon(ImageMap map)
{
ItemStack icon = new ItemStack(Material.MAP);
ItemMeta meta = icon.getItemMeta();
meta.setDisplayName(ChatColor.GREEN + "" + ChatColor.BOLD + map.getName());
meta.setLore(Arrays.asList(
ChatColor.WHITE + "" + map.getMapCount() + " map" + (map.getMapCount() != 1 ? "s" : ""),
"",
ChatColor.GRAY + "Map ID: " + map.getId(),
"",
ChatColor.GRAY + "» Left-click to get this map",
ChatColor.GRAY + "» Right-click for details"
));
GuiUtils.removeVanillaInfos(meta);
icon.setItemMeta(meta);
return icon;
}
private ItemStack getPageIcon(Integer targetPage)
{
ItemStack icon = new ItemStack(Material.ARROW);
ItemMeta meta = icon.getItemMeta();
if(currentPage < targetPage) { // next page
meta.setDisplayName(ChatColor.GREEN + "Next page");
}
else {
meta.setDisplayName(ChatColor.GREEN + "Previous page");
}
meta.setLore(Collections.singletonList(
ChatColor.GRAY + "Go to page " + ChatColor.WHITE + (targetPage + 1) + ChatColor.GRAY + " of " + ChatColor.WHITE + (lastPage + 1)
));
icon.setItemMeta(meta);
return icon;
}
private String getStatisticText(String title, Integer value)
{
return getStatisticText(title, value, false);
}
private String getStatisticText(String title, Integer value, boolean zeroIsUnlimited)
{
return getStatisticText(title, zeroIsUnlimited && value <= 0 ? "unlimited" : bigNumbersFormatter.format(value));
}
private String getStatisticText(String title, String value)
{
return ChatColor.GRAY + title + ": " + ChatColor.WHITE + value;
}
}

View File

@ -1,382 +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.guiproko.core;
import fr.moribus.imageonmap.*;
import org.bukkit.*;
import org.bukkit.event.inventory.*;
import org.bukkit.inventory.*;
import org.bukkit.inventory.meta.*;
import java.lang.reflect.*;
import java.util.*;
/**
* This class implements an action-based GUI.
* Actions are buttons which trigger an event when getting clicked on by the user.
* They are represented by (customizable) items, which are immutable by the user.
*
* Events handlers are (usually private) methods implemented in the derived
* class(es). They are named using the pattern 'action_[action name]', and
* are called when the associated action is triggered. They take an optional
* argument (add it if you need it): the {@link InventoryClickEvent} triggered.
*
* @author ProkopyL (main) and Amaury Carrade
*/
abstract public class ActionGui extends Gui
{
/**
* The prefix for action handlers.
*/
static private final String ACTION_HANDLER_NAME = "action_";
/**
* The class of this GUI.
* Useful to retrieve methods from the derived classes.
*/
private final Class<? extends ActionGui> guiClass = this.getClass();
/**
* A map containing all the actions defined by the derived class, indexed by
* their position in the inventory.
*/
private final HashMap<Integer, Action> actions = new HashMap<>();
/* ===== Protected API ===== */
/**
* Creates a new action, represented by the given item.
* The item's metadata is changed to use the given title and lore.
*
* @param name The identifier of the action.
* @param slot The slot the action will be placed on.
* @param material The material used to represent the action.
* @param title The title the item will show.
* @param loreLines The lore the item will show.
*/
protected void action(String name, int slot, Material material, String title, String ... loreLines)
{
action(name, slot, new ItemStack(material), title, Arrays.asList(loreLines));
}
/**
* Creates a new action, represented by the given item.
* The item's metadata is changed to use the given title and lore.
*
* @param name The identifier of the action.
* @param slot The slot the action will be placed on.
* @param item The item used to represent the action.
* @param title The title the item will show.
* @param loreLines The lore the item will show.
*/
protected void action(String name, int slot, ItemStack item, String title, String ... loreLines)
{
action(name, slot, item, title, Arrays.asList(loreLines));
}
/**
* Creates a new action, represented by the given item.
* The item's metadata is changed to use the given title and lore.
*
* @param name The identifier of the action.
* @param slot The slot the action will be placed on.
* @param item The item used to represent the action.
* @param title The title the item will show.
* @param loreLines The lore the item will show.
*/
protected void action(String name, int slot, ItemStack item, String title, List<String> loreLines)
{
action(name, slot, GuiUtils.makeItem(item, title, loreLines));
}
/**
* Creates a new action, represented by the given material.
*
* @param name The identifier of the action.
* @param slot The slot the action will be placed on.
* @param material The material used to represent the action.
*/
protected void action(String name, int slot, Material material)
{
action(name, slot, GuiUtils.makeItem(material));
}
/**
* Creates a new action, represented by no item.
* This action will not be rendered to the user until
* {@link #updateAction(java.lang.String, org.bukkit.inventory.ItemStack, java.lang.String)}
* is called.
*
* @param name The identifier of the action.
* @param slot The slot the action will be placed on.
*/
protected void action(String name, int slot)
{
action(name, slot, (ItemStack) null);
}
/**
* Creates a new action, and adds it to the GUI.
*
* @param name The identifier of the action.
* @param slot The slot the action will be placed on.
* @param item The item used to represent the action.
*/
protected void action(String name, int slot, ItemStack item)
{
if(slot > getSize() || slot < 0)
throw new IllegalArgumentException("Illegal slot ID");
action(new Action(name, slot, item, getActionHandler(guiClass, name)));
}
/**
* Adds an action to the GUI.
*
* @param action The {@link fr.moribus.imageonmap.guiproko.core.ActionGui.Action} to register.
*/
private void action(Action action)
{
actions.put(action.slot, action);
}
/**
* Updates the action represented by the given name.
*
* @param name The name of the action to update.
* @param item The new material to affect to the action.
* @param title The new title to affect to the action.
* @throws IllegalArgumentException If no action has the given name.
*/
protected void updateAction(String name, Material item, String title)
{
updateAction(name, new ItemStack(item), title);
}
/**
* Updates the action represented by the given name.
*
* @param name The name of the action to update.
* @param item The new item to affect to the action.
* @param title The new title to affect to the action.
* @throws IllegalArgumentException If no action has the given name.
*/
protected void updateAction(String name, ItemStack item, String title)
{
updateAction(name, item);
ItemMeta meta = item.getItemMeta();
meta.setDisplayName(title);
item.setItemMeta(meta);
}
/**
* Updates the action represented by the given name.
*
* @param name The name of the action to update.
* @param item The new item to affect to the action.
* @throws IllegalArgumentException If no action has the given name.
*/
protected void updateAction(String name, ItemStack item)
{
getAction(name).item = item;
}
/**
* Retrieves the action represented by the given name.
*
* @param name The name of the action to retreive.
* @return The action represented by the given name.
* @throws IllegalArgumentException If no action has the given name.
*/
private Action getAction(String name) throws IllegalArgumentException
{
for(Action action : actions.values())
{
if(action.name.equals(name)) return action;
}
throw new IllegalArgumentException("Unknown action name : " + name);
}
/**
* Raised when the Gui needs to be updated.
* Use this method to create your actions.
*/
@Override
protected abstract void onUpdate();
/**
* Raised when an action without any event handler has been triggered.
*
* @param name The name of the triggered action.
* @param slot The slot of the action.
* @param item The item of the action.
* @param event The {@link InventoryClickEvent} raised when this action was triggered.
*/
protected void unknown_action(String name, int slot, ItemStack item, InventoryClickEvent event)
{
unknown_action(name, slot, item);
}
/**
* Raised when an action without any event handler has been triggered.
*
* @param name The name of the triggered action.
* @param slot The slot of the action.
* @param item The item of the action.
*/
protected void unknown_action(String name, int slot, ItemStack item) {}
@Override
public void update()
{
actions.clear();
super.update();
}
@Override
protected void populate(Inventory inventory)
{
for(Action action : actions.values())
{
inventory.setItem(action.slot, action.item);
}
}
@Override
protected void onClick(InventoryClickEvent event)
{
if(event.getRawSlot() >= event.getInventory().getSize()) //The user clicked in its own inventory
{
if(!event.getAction().equals(InventoryAction.MOVE_TO_OTHER_INVENTORY))
return;
}
event.setCancelled(true);
callAction(actions.get(event.getRawSlot()));
}
/**
* Triggers the given action's event handler.
* @param action The action to trigger.
*/
private void callAction(Action action)
{
if(action == null) return;
if(action.callback == null)
{
unknown_action(action.name, action.slot, action.item);
return;
}
try
{
action.callback.invoke(this);
}
catch (IllegalAccessException | IllegalArgumentException ex)
{
PluginLogger.error("Could not invoke GUI action handler", ex);
}
catch (InvocationTargetException ex)
{
PluginLogger.error("Error while invoking action handler {0} of GUI {1}",
ex.getCause(), action.name, guiClass.getName());
}
}
/**
* Retrieves the event handler matching the given name from a class (or any of its parents).
*
* @param klass The class to retrieve the event handler from.
* @param name The name of the action.
* @return The event handler matching the action name, or null if none was found.
*/
private Method getActionHandler(Class<?> klass, String name)
{
Method callback;
do
{
try
{
try
{
callback = klass.getDeclaredMethod(ACTION_HANDLER_NAME + name, InventoryClickEvent.class);
}
catch(NoSuchMethodException e)
{
callback = klass.getDeclaredMethod(ACTION_HANDLER_NAME + name);
}
callback.setAccessible(true);
break;
}
catch (Throwable ex)
{
callback = null;
klass = klass.getSuperclass();
}
} while (klass != null);
return callback;
}
/**
* @return if this GUI has any actions defined.
*/
protected boolean hasActions()
{
return !actions.isEmpty();
}
/**
* This structure represents an action.
*/
static private class Action
{
/**
* The name of the action.
*/
public String name;
/**
* The slot the action will be put in.
*/
public int slot;
/**
* The item this action will be represented by.
*/
public ItemStack item;
/**
* The callback this action will call when triggered.
*/
public Method callback;
public Action(String name, int slot, ItemStack item, Method callback)
{
this.name = name;
this.slot = slot;
this.item = item;
this.callback = callback;
}
}
}

View File

@ -1,708 +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.guiproko.core;
import org.bukkit.*;
import org.bukkit.event.inventory.ClickType;
import org.bukkit.event.inventory.InventoryAction;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryDragEvent;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.*;
import java.util.*;
/**
* This class implements an exploration GUI, allowing users to see a set of data
* in a paginated view, and to manipulate it or get it (if the
* {@link fr.moribus.imageonmap.guiproko.core.ExplorerGui.Mode#CREATIVE Creative} mode
* is enabled for this GUI enabled by default).
*
* This GUI supports both one- and two-dimensional contents; two-dimensional content is
* represented by a one-dimension list and a width, or by {@link #getViewItem(int, int)}
* if you override it (in this case you need to call {@link #setDataShape(int, int)} in the
* {@link #onUpdate()} method).
*
* @param <T> The type of data this GUI will display.
*
* @author ProkopyL (main) and Amaury Carrade
*/
abstract public class ExplorerGui<T> extends ActionGui
{
/**
* The explorer GUI's reading mode.
*
* In creative mode (the default mode), the players are able to manipulate the content, get it
* inside their own inventory (without consumption, just like the creative mode), and whatever
* you want if you override some methods.
*
* In read-only mode, they are only able to browse the content.
*/
protected enum Mode {READONLY, CREATIVE}
private T[] data;
private boolean isData2D = false;
private int viewSize;
private int viewHeight;
private int viewWidth;
private boolean keepHorizontalScrollingSpace = false;
private boolean keepVerticalScrollingSpace = false;
private int currentPageX = 0;
private int currentPageY = 0;
private int dataHeight = 0;
private int dataWidth = 0;
private int pageCountX;
private int pageCountY;
private Mode mode = Mode.CREATIVE;
public ExplorerGui()
{
// Defined early to be able to use getSize() in the onUpdate method.
setSize(MAX_INVENTORY_SIZE);
}
/**
* Sets the displayed data.
*
* @param data The data.
* @param dataWidth The data's width, if this data is in two dimensions.
* In this case the data array will be read like a matrix, with
* lines stored in a consecutive way; the height is automatically
* calculated.
*/
protected void setData(T[] data, int dataWidth)
{
this.data = data;
if(dataWidth > 0)
setDataShape(dataWidth, (int) Math.ceil((double) data.length / (double) dataWidth));
}
/**
* Sets the data's shape. Use this if you're providing data through
* {@link #getViewItem(int, int)}, as example.
*
* @param dataWidth The data's width.
* @param dataHeight The data's height.
*/
protected void setDataShape(int dataWidth, int dataHeight)
{
this.dataWidth = dataWidth;
this.dataHeight = dataHeight;
this.isData2D = dataWidth > 0;
}
/**
* Sets the displayed data, assuming this data is in one dimension.
*
* @param data The data.
*/
protected void setData(T[] data)
{
setData(data, 0);
}
/**
* Checks if this GUI contains data, either with the data entry or through
* {@link #getViewItem(int, int)} and {@link #setDataShape(int, int)}.
*
* @return {@code true} if this GUI contains some data.
*/
protected boolean hasData()
{
return (data == null || data.length == 0) && (dataWidth == 0 && dataHeight == 0);
}
@Override
protected void populate(Inventory inventory)
{
if(pageCountX > 1)
{
updateAction("next", getPageItem("next", canGoNext()));
updateAction("previous", getPageItem("previous", canGoPrevious()));
}
if(pageCountY > 1)
{
updateAction("up", getPageItem("up", canGoUp()));
updateAction("down", getPageItem("down", canGoDown()));
}
if(hasData())
{
if (!isData2D)
{
int start = currentPageX * viewSize;
int max = Math.min(viewSize, data.length - start);
for (int i = 0; i < max; i++)
{
inventory.setItem(i, getViewItem(i + start));
}
}
else
{
int startX = currentPageX * viewWidth;
int startY = currentPageY * viewHeight;
int maxX = Math.min(viewWidth, dataWidth - startX);
int maxY = Math.min(viewHeight, dataHeight - startY);
for (int i = maxY; i-- > 0; )
{
for (int j = maxX; j-- > 0; )
{
inventory.setItem(i * INVENTORY_ROW_SIZE + j, getViewItem(j + startX, i + startY));
}
}
}
}
else
{
ItemStack emptyIndicator = getEmptyViewItem();
if(emptyIndicator != null)
{
action("", 22, emptyIndicator);
}
}
if(hasActions()) super.populate(inventory);
}
@Override
protected void onClick(InventoryClickEvent event)
{
int slot = event.getRawSlot();
// Clicked in the action bar
if(hasActions() &&
slot >= MAX_INVENTORY_SIZE - INVENTORY_ROW_SIZE
&& slot < MAX_INVENTORY_SIZE)
{
super.onClick(event);
return;
}
if(isData2D && pageCountY > 1 &&
slot % INVENTORY_ROW_SIZE == INVENTORY_ROW_SIZE - 1)
{
super.onClick(event);
return;
}
// The user clicked in the GUI
if(affectsGui(event))
{
if(hasData())
{
switch (event.getAction())
{
case PICKUP_ALL:
case PICKUP_HALF:
case PICKUP_ONE:
case PICKUP_SOME:
case HOTBAR_MOVE_AND_READD:
case HOTBAR_SWAP:
case MOVE_TO_OTHER_INVENTORY:
onActionPickup(event);
break;
case PLACE_ALL:
case PLACE_ONE:
case PLACE_SOME:
case SWAP_WITH_CURSOR:
onActionPut(event);
break;
case DROP_ALL_CURSOR:
case DROP_ONE_CURSOR:
break;
default:
event.setCancelled(true);
}
}
else
{
event.setCancelled(true);
}
}
else
{
if(event.getAction().equals(InventoryAction.MOVE_TO_OTHER_INVENTORY))
onActionMove(event);
}
}
@Override
protected void onDrag(InventoryDragEvent event)
{
if(!affectsGui(event)) return;
for(int slot : event.getRawSlots())
{
// Clicked in the action bar
if(hasActions() &&
slot >= MAX_INVENTORY_SIZE - INVENTORY_ROW_SIZE
&& slot < MAX_INVENTORY_SIZE)
{
super.onDrag(event);
return;
}
if(isData2D && pageCountY > 1 &&
slot % INVENTORY_ROW_SIZE == INVENTORY_ROW_SIZE - 1)
{
super.onDrag(event);
return;
}
}
event.setCancelled(true);
if(mode.equals(Mode.READONLY)) return;
if(!onPutItem(event.getOldCursor())) return;
event.setCursor(new ItemStack(Material.AIR));
}
/**
* Triggered when a player clicks on the GUI to get an item.
*
* @param event The triggered event.
*/
private void onActionPickup(InventoryClickEvent event)
{
int dataIndex = getDataIndex(event.getSlot());
if(event.getClick().equals(ClickType.RIGHT))
{
onRightClick(getData(dataIndex));
event.setCancelled(true);
return;
}
ItemStack pickedUpItem = getPickedUpItem(dataIndex);
if(pickedUpItem == null || mode.equals(Mode.READONLY))
{
event.setCancelled(true);
return;
}
event.setCurrentItem(pickedUpItem);
GuiUtils.setItemLater(this, event.getSlot(), getViewItem(dataIndex));
}
/**
* Triggered when a player clicks on the GUI to place an item.
*
* @param event The triggered event.
*/
private void onActionPut(InventoryClickEvent event)
{
event.setCancelled(true);
if(mode.equals(Mode.READONLY)) return;
if(!onPutItem(event.getCursor())) return;
event.setCursor(new ItemStack(Material.AIR));
}
/**
* Triggered when a player moves an item on the GUI.
*
* @param event The triggered event.
*/
private void onActionMove(InventoryClickEvent event)
{
event.setCancelled(true);
if(mode.equals(Mode.READONLY)) return;
if(!onPutItem(event.getCurrentItem())) return;
event.setCurrentItem(new ItemStack(Material.AIR));
}
@Override
protected void onAfterUpdate()
{
//Calculating page count
if(data != null && data.length <= 0)
{
viewWidth = INVENTORY_ROW_SIZE;
viewHeight = 1;
viewSize = viewWidth;
pageCountX = 1;
pageCountY = 1;
}
else if(!isData2D)
{
int dataLength = (data == null) ? 0 : data.length;
viewWidth = INVENTORY_ROW_SIZE;
viewHeight = Math.min((int)Math.ceil((double)dataLength / (double)viewWidth),
MAX_INVENTORY_COLUMN_SIZE);
if(hasActions() || dataLength > MAX_INVENTORY_SIZE || keepHorizontalScrollingSpace)
viewHeight--;
viewSize = viewWidth * viewHeight;
pageCountX = (int)Math.ceil((double)dataLength / (double)viewSize);
pageCountY = 1;
}
else
{
viewWidth = Math.min(dataWidth, INVENTORY_ROW_SIZE);
viewHeight = Math.min(dataHeight, MAX_INVENTORY_COLUMN_SIZE);
pageCountX = (int)Math.ceil((double)dataWidth / (double)viewWidth);
pageCountY = (int)Math.ceil((double)dataHeight / (double)viewHeight);
if((pageCountY > 1 && viewWidth == INVENTORY_ROW_SIZE) || keepVerticalScrollingSpace)
viewWidth--;
if((pageCountX > 1 && viewHeight == MAX_INVENTORY_COLUMN_SIZE) || keepHorizontalScrollingSpace)
viewHeight--;
pageCountX = (int)Math.ceil((double)dataWidth / (double)viewWidth);
pageCountY = (int)Math.ceil((double)dataHeight / (double)viewHeight);
}
if(pageCountX > 1)
{
action("previous", MAX_INVENTORY_SIZE - INVENTORY_ROW_SIZE);
action("next", MAX_INVENTORY_SIZE - 1 - (pageCountY > 1 ? 1 : 0));
}
if(pageCountY > 1)
{
action("up", INVENTORY_ROW_SIZE - 1);
action("down", MAX_INVENTORY_SIZE - 1);
}
}
private void action_next()
{
next();
}
private void action_previous()
{
previous();
}
private void action_up()
{
up();
}
private void action_down()
{
down();
}
private int getDataIndex(int inventorySlot)
{
if(isData2D)
{
int column = currentPageX * viewWidth + inventorySlot % INVENTORY_ROW_SIZE;
int row = currentPageY * viewHeight + inventorySlot / INVENTORY_ROW_SIZE;
return row * dataWidth + column;
}
else
{
return currentPageX * viewSize + inventorySlot;
}
}
private T getData(int i)
{
if(i < 0 || i >= data.length)
return null;
return data[i];
}
/**
* Returns the stack to display at the given index.
*
* @param i The index.
* @return The stack.
*/
protected ItemStack getViewItem(int i)
{
return getViewItem(getData(i));
}
/**
* Returns the stack to display at the given coordinates.
*
* @param x The x-coordinate (left to right).
* @param y The y-coordinate (top to bottom).
* @return The stack.
*/
protected ItemStack getViewItem(int x, int y)
{
return getViewItem(y * dataWidth + x);
}
/**
* Returns the ItemStack representation of the given piece of data.
*
* @param data The piece of data.
* @return The piece's representation.
*/
protected ItemStack getViewItem(T data) { return null; }
/**
* Returns the stack displayed in the center of the GUI if it is empty.
*
* @return The stack.
*/
protected ItemStack getEmptyViewItem() { return null; }
private ItemStack getPickedUpItem(int dataIndex)
{
if(dataIndex < 0 || dataIndex >= data.length)
return null;
return getPickedUpItem(getData(dataIndex));
}
/**
* Returns the stack the players will get when they try to take an item from
* the GUI, in {@link fr.moribus.imageonmap.guiproko.core.ExplorerGui.Mode#CREATIVE}
* mode.
*
* @param data The picked-up piece of data.
* @return The stack to pick-up ({@code null} to cancel the pick-up).
*/
protected ItemStack getPickedUpItem(T data) { return getViewItem(data); }
/**
* Returns the item to use to display the pagination buttons.
*
* @param paginationButtonType The type of button (either "next", "previous", "up", "down").
* @param canUse {@code true} if the button is usable (i.e. not in the last or first page of
* its kind.
* @return The item.
*/
protected ItemStack getPageItem(String paginationButtonType, boolean canUse)
{
ItemStack icon = new ItemStack(canUse ? Material.ARROW : Material.STICK);
ItemMeta meta = icon.getItemMeta();
String title;
Integer newPage;
Integer lastPage;
switch (paginationButtonType)
{
case "next":
title = canUse ? "Next page" : "No next page";
newPage = currentPageX + 1;
lastPage = getPageCount();
break;
case "previous":
title = canUse ? "Previous page" : "No previous page";
newPage = currentPageX - 1;
lastPage = getPageCount();
break;
case "up":
title = canUse ? "Go up" : "Top page";
newPage = currentPageY + 1;
lastPage = getVerticalPageCount();
break;
case "down":
title = canUse ? "Go down" : "Bottom page";
newPage = currentPageY - 1;
lastPage = getVerticalPageCount();
break;
default:
return null; // invalid page type
}
meta.setDisplayName((canUse ? ChatColor.WHITE : ChatColor.GRAY) + title);
if(canUse)
{
meta.setLore(Collections.singletonList(
ChatColor.GRAY + "Go to page " + ChatColor.WHITE + (newPage + 1) + ChatColor.GRAY + " of " + ChatColor.WHITE + lastPage
));
}
icon.setItemMeta(meta);
return icon;
}
/**
* Triggered when the player right-clicks an item on the GUI.
*
* @param data The right-clicked piece of data.
*/
protected void onRightClick(T data) {}
/**
* Triggered when the player try to place an item inside the GUI.
*
* This will not place the item in the GUI, it's up to you to update the data and refresh
* the GUI if you need so.
*
* @param item The {@link ItemStack} the player is trying to put.
* @return {@code false} to cancel the placement; {@code true} to accept it.
*/
protected boolean onPutItem(ItemStack item) { return true; }
/**
* Displays the next horizontal page, if possible.
*/
public void next()
{
if(!canGoNext()) return;
currentPageX++;
refresh();
}
/**
* Displays the previous horizontal page, if possible.
*/
public void previous()
{
if(!canGoPrevious()) return;
currentPageX--;
refresh();
}
/**
* Displays the previous vertical page, if possible.
*/
public void up()
{
if(!canGoUp()) return;
currentPageY--;
refresh();
}
/**
* Displays the next vertical page, if possible.
*/
public void down()
{
if(!canGoDown()) return;
currentPageY++;
refresh();
}
public boolean canGoNext()
{
return currentPageX < pageCountX - 1;
}
public boolean canGoPrevious()
{
return currentPageX > 0;
}
public boolean canGoUp()
{
return currentPageY > 0;
}
public boolean canGoDown()
{
return currentPageY < pageCountY - 1;
}
/**
* Returns the amount of horizontal pages.
*
* @return The pages' amount.
*/
public int getPageCount()
{
return pageCountX;
}
/**
* Returns the amount of vertical pages.
* This will always be 1 if the GUI is representing a one-dimensional data set.
*
* @return The pages' amount.
*/
public int getVerticalPageCount()
{
return pageCountY;
}
/** @return The GUI's manipulation mode. */
protected Mode getMode()
{
return mode;
}
/**
* Sets the GUI's manipulation mode.
* @param mode The mode.
*/
protected void setMode(Mode mode)
{
this.mode = mode;
}
/**
* If set to {@code true}, the horizontal scrolling line will remain empty even without
* scrolls (with one page typically), so you can place buttons or things like that in this
* area.
*
* Else, with one page, the place will be used to display an additional row of data.
*
* @param keepHorizontalScrollingSpace {@code true} if enabled.
*/
public void setKeepHorizontalScrollingSpace(boolean keepHorizontalScrollingSpace)
{
this.keepHorizontalScrollingSpace = keepHorizontalScrollingSpace;
}
/**
* If set to {@code true}, the vertical scrolling line will remain empty even without
* scrolls (with one page typically), so you can place buttons or things like that in this
* area.
*
* Else, with one page, the place will be used to display an additional column of data.
*
* @param keepVerticalScrollingSpace {@code true} if enabled.
*/
public void setKeepVerticalScrollingSpace(boolean keepVerticalScrollingSpace)
{
this.keepVerticalScrollingSpace = keepVerticalScrollingSpace;
}
}

View File

@ -1,345 +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.guiproko.core;
import org.bukkit.*;
import org.bukkit.entity.*;
import org.bukkit.event.*;
import org.bukkit.event.inventory.*;
import org.bukkit.inventory.*;
import org.bukkit.plugin.*;
import java.util.*;
/**
* This class provides the basic needs for chest-type GUIs.
* It allows you to create custom GUIs by simply providing an inventory
* to fill, as well as rerouting basic events to it.
*/
abstract public class Gui
{
static protected final int INVENTORY_ROW_SIZE = 9;
static protected final int MAX_INVENTORY_COLUMN_SIZE = 6;
static protected final int MAX_INVENTORY_SIZE = INVENTORY_ROW_SIZE * MAX_INVENTORY_COLUMN_SIZE;
/**
* The player this Gui instance is associated to.
*/
private Player player;
/**
* The size of the inventory.
*/
private int size = 0;
/**
* The title of the inventory.
*/
private String title;
/**
* The current Bukkit inventory.
*/
private Inventory inventory;
/**
* If the inventory is currently open.
*/
private boolean open = false;
private void open(Player player)
{
this.player = player;
openGuis.put(player, this);
this.update();
player.openInventory(inventory);
this.open = true;
}
/* ===== Public API ===== */
/**
* Asks the GUI to update its data, and refresh its view accordingly.
* The inventory may be regenerated when calling this method.
*/
public void update()
{
onUpdate();
onAfterUpdate();
//If inventory does not need to be regenerated
if(inventory != null && inventory.getTitle().equals(title) && inventory.getSize() == size)
{
refresh();
}
else
{
inventory = Bukkit.createInventory(player, size, title);
populate(inventory);
if(isOpen()) // Reopening the inventory
{
player.closeInventory();
player.openInventory(inventory);
}
}
}
/**
* Asks the GUI to recreate its view.
* The inventory is cleared, but never regenerated when calling this method.
*/
public void refresh()
{
inventory.clear();
populate(inventory);
}
/**
* Closes this inventory.
*/
public void close()
{
this.open = false;
player.closeInventory();
openGuis.remove(player);
}
/* ===== Protected API ===== */
/**
* Raised when the {@link Gui#update()} method is called.
* Use this method to update your internal data.
*/
protected void onUpdate() {}
/**
* Raised when the {@link Gui#update()} method is called, but before the inventory is populated.
* Use this method in a Gui subclass to analyze given data and set other parameters accordingly.
*/
protected void onAfterUpdate() {}
/**
* Called when the inventory needs to be (re)populated.
*
* @param inventory The inventory to populate
*/
abstract protected void populate(Inventory inventory);
/**
* Raised when an action is performed on an item in the inventory.
*
* @param event The click event data.
*/
abstract protected void onClick(InventoryClickEvent event);
/**
* Raised when an drag is performed on the inventory.
* The default behaviour is to cancel any event that affects the GUI.
*
* @param event The drag event data.
*/
protected void onDrag(InventoryDragEvent event)
{
if(affectsGui(event)) event.setCancelled(true);
}
/**
* Returns if the given event affects the GUI's inventory.
*
* @param event The event to test
* @return {@code true} if the event's slot is in the GUI's inventory,
* {@code false} otherwise.
*/
static protected boolean affectsGui(InventoryClickEvent event)
{
return event.getRawSlot() < event.getInventory().getSize();
}
/**
* Returns if the given event affects the GUI's inventory.
*
* @param event The event to test
* @return true if any of the event's slots is in the GUI's inventory,
* false otherwise.
*/
static protected boolean affectsGui(InventoryDragEvent event)
{
for(int slot : event.getRawSlots())
{
if(slot < event.getInventory().getSize())
{
return true;
}
}
return false;
}
/**
* Raised when the GUI is being closed.
* Use this method to cleanup data.
*/
protected void onClose() {}
/* ===== Getters & Setters ===== */
/** @return If the GUI is currently open or not. */
public boolean isOpen() { return open; }
/** @return The player this Gui instance is associated to. */
protected Player getPlayer() { return player; }
/** @return The size of the inventory. */
protected int getSize() { return size; }
/**
* Sets the new size of the inventory.
* The given value is raised to be a multiple of the size of an inventory's
* row, and is capped to the maximal size of an inventory.
* It will be applied on the next GUI update.
* @param size The new size of the inventory.
*/
protected void setSize(int size)
{
this.size = Math.min(((int)(Math.ceil((double) size / INVENTORY_ROW_SIZE))) * INVENTORY_ROW_SIZE, MAX_INVENTORY_SIZE);
}
/** @return The title of the inventory. */
protected String getTitle() { return title; }
/**
* Sets the new title of the inventory.
* It will be applied on the next GUI update.
* @param title The new title of the inventory
*/
protected void setTitle(String title){this.title = title;}
/** @return The underlying inventory, or null if the Gui has not been opened yet. */
public Inventory getInventory() { return inventory; }
/* ===== Static API ===== */
/**
* A map of all the currently open GUIs, associated to the HumanEntity
* that requested it.
*/
static private HashMap<Player, Gui> openGuis = null;
/**
* The Bukkit listener for all GUI-related events.
*/
static private GuiListener listener = null;
/**
* Initializes the GUI listeners.
* This method must be called on plugin enabling.
* @param plugin The plugin the GUI listeners will be registered on
*/
static public void init(Plugin plugin)
{
openGuis = new HashMap<>();
listener = new GuiListener();
plugin.getServer().getPluginManager().registerEvents(listener, plugin);
GuiUtils.init();
}
/**
* Cleans up the GUI states.
* This method must be called on plugin disabling.
*/
static public void exit()
{
openGuis.clear();
openGuis = null;
listener = null;
}
/**
* Opens a GUI for a player.
* @param <T> A GUI type.
* @param owner The player the GUI will be shown to.
* @param gui The GUI.
* @return The opened GUI.
*/
static public <T extends Gui> T open(Player owner, T gui)
{
close(owner);
((Gui)gui).open(owner);/* JAVA GENERICS Y U NO WORK */
return gui;
}
/**
* Closes any open GUI for a given player.
* @param owner The player.
*/
static public void close(Player owner)
{
Gui openGui = openGuis.get(owner);
if(openGui != null) openGui.close();
}
/**
* Implements a Bukkit listener for all GUI-related events.
*/
static private class GuiListener implements Listener
{
@EventHandler
public void onInventoryDrag(InventoryDragEvent event)
{
if(!(event.getWhoClicked() instanceof Player)) return;
Player owner = (Player) event.getWhoClicked();
Gui openGui = openGuis.get(owner);
if(openGui == null) return;
openGui.onDrag(event);
}
@EventHandler
public void onInventoryClick(InventoryClickEvent event)
{
if(!(event.getWhoClicked() instanceof Player)) return;
Player owner = (Player) event.getWhoClicked();
Gui openGui = openGuis.get(owner);
if(openGui == null) return;
openGui.onClick(event);
}
@EventHandler
public void onInventoryClose(InventoryCloseEvent event)
{
if(!(event.getPlayer() instanceof Player)) return;
Player owner = (Player) event.getPlayer();
Gui openGui = openGuis.get(owner);
if(openGui == null) return;
if(!openGui.isOpen()) return;
openGui.onClose();
openGuis.remove(owner);
}
}
}

View File

@ -1,199 +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.guiproko.core;
import fr.moribus.imageonmap.*;
import org.bukkit.*;
import org.bukkit.entity.*;
import org.bukkit.inventory.*;
import org.bukkit.inventory.meta.*;
import java.lang.reflect.*;
import java.util.*;
/**
* Various utility methods for GUIs.
*/
abstract public class GuiUtils
{
static private Method addItemFlagsMethod = null;
static private Object[] itemFlagValues = null;
/**
* Initializes the GUI utilities. This method must be called on plugin enabling.
*/
static public void init()
{
try
{
Class<?> itemFlagClass = Class.forName("org.bukkit.inventory.ItemFlag");
Method valuesMethod = itemFlagClass.getDeclaredMethod("values");
itemFlagValues = (Object[]) valuesMethod.invoke(null);
addItemFlagsMethod = ItemMeta.class.getMethod("addItemFlags", itemFlagClass);
addItemFlagsMethod.setAccessible(true);
}
catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException e)
{
// Not supported :c
}
catch (InvocationTargetException e)
{
PluginLogger.error("Exception occurred while looking for the ItemFlag API.", e);
}
}
/**
* Hides all the item attributes of the given {@link ItemMeta}.
*
* @param meta The {@link ItemMeta} to hide attributes from.
*/
static public void hideItemAttributes(ItemMeta meta)
{
if (addItemFlagsMethod == null) return;
try
{
addItemFlagsMethod.invoke(meta, itemFlagValues);
}
catch (IllegalAccessException | InvocationTargetException ex)
{
PluginLogger.error("Exception occurred while invoking the ItemMeta.addItemFlags method.", ex);
}
}
/**
* Stores the ItemStack at the given index of a GUI's inventory. The inventory is only updated
* the next time the Bukkit Scheduler runs (i.e. next server tick).
*
* @param gui The GUI to update
* @param slot The slot where to put the ItemStack
* @param item The ItemStack to set
*/
static public void setItemLater(Gui gui, int slot, ItemStack item)
{
Bukkit.getScheduler().scheduleSyncDelayedTask(ImageOnMap.getPlugin(),
new CreateDisplayItemTask(gui.getInventory(), item, slot));
}
/**
* One-liner to construct an {@link ItemStack}.
*
* @param material The stack's material.
*
* @return The constructed {@link ItemStack}.
*/
static public ItemStack makeItem(Material material)
{
return makeItem(material, null, (List<String>) null);
}
/**
* One-liner to construct an {@link ItemStack}.
*
* @param material The stack's material.
* @param title The stack's title.
*
* @return The constructed {@link ItemStack}.
*/
static public ItemStack makeItem(Material material, String title)
{
return makeItem(material, title, (List<String>) null);
}
/**
* One-liner to construct an {@link ItemStack}.
*
* @param material The stack's material.
* @param title The stack's title.
* @param loreLines The stack's lore lines.
*
* @return The constructed {@link ItemStack}.
*/
static public ItemStack makeItem(Material material, String title, String... loreLines)
{
return makeItem(material, title, Arrays.asList(loreLines));
}
/**
* One-liner to construct an {@link ItemStack}.
*
* @param material The stack's material.
* @param title The stack's title.
* @param loreLines The stack's lore lines.
*
* @return The constructed {@link ItemStack}.
*/
static public ItemStack makeItem(Material material, String title, List<String> loreLines)
{
return makeItem(new ItemStack(material), title, loreLines);
}
/**
* One-liner to update an {@link ItemStack}'s {@link ItemMeta}.
*
* If the stack is a map, it's attributes will be hidden.
*
* @param itemStack The original {@link ItemStack}. This stack will be directly modified.
* @param title The stack's title.
* @param loreLines A list containing the stack's lines.
*
* @return The same {@link ItemStack}, but with an updated {@link ItemMeta}.
*/
static public ItemStack makeItem(ItemStack itemStack, String title, List<String> loreLines)
{
ItemMeta meta = itemStack.getItemMeta();
meta.setDisplayName(title);
meta.setLore(loreLines);
if (itemStack.getType().equals(Material.MAP))
hideItemAttributes(meta);
itemStack.setItemMeta(meta);
return itemStack;
}
/**
* Implements a bukkit runnable that updates an inventory slot later.
*/
static private class CreateDisplayItemTask implements Runnable
{
private final Inventory inventory;
private final ItemStack item;
private final int slot;
public CreateDisplayItemTask(Inventory inventory, ItemStack item, int slot)
{
this.inventory = inventory;
this.item = item;
this.slot = slot;
}
@Override
public void run()
{
inventory.setItem(slot, item);
for (HumanEntity player : inventory.getViewers())
{
((Player) player).updateInventory();
}
}
}
}

View File

@ -1,187 +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.guiproko.list;
import fr.moribus.imageonmap.*;
import fr.moribus.imageonmap.guiproko.core.*;
import fr.moribus.imageonmap.map.*;
import fr.moribus.imageonmap.ui.*;
import org.bukkit.*;
import org.bukkit.inventory.*;
import org.bukkit.inventory.meta.*;
import java.text.*;
import java.util.*;
public class MapListGui extends ExplorerGui<ImageMap>
{
private final NumberFormat bigNumbersFormatter = new DecimalFormat("###,###,###,###", DecimalFormatSymbols.getInstance(Locale.ENGLISH));
@Override
protected ItemStack getViewItem(ImageMap map)
{
ItemStack icon = new ItemStack(Material.MAP);
ItemMeta meta = icon.getItemMeta();
meta.setDisplayName(ChatColor.GREEN + "" + ChatColor.BOLD + map.getName());
String mapDescription;
if(map instanceof SingleMap)
mapDescription = "Single map";
else
mapDescription = "Poster map (" + ((PosterMap) map).getColumnCount() + "×" + ((PosterMap) map).getRowCount() + ")";
meta.setLore(Arrays.asList(
ChatColor.WHITE + mapDescription,
"",
ChatColor.GRAY + "Map ID: " + map.getId(),
"",
ChatColor.GRAY + "» Left-click to get this map",
ChatColor.GRAY + "» Right-click for details and options"
));
GuiUtils.hideItemAttributes(meta);
icon.setItemMeta(meta);
return icon;
}
@Override
protected ItemStack getEmptyViewItem()
{
ItemStack empty = new ItemStack(Material.BARRIER);
ItemMeta meta = empty.getItemMeta();
meta.setDisplayName(ChatColor.RED + "You don't have any map.");
meta.setLore(Arrays.asList(
ChatColor.GRAY + "Get started by creating a new one",
ChatColor.GRAY + "using " + ChatColor.WHITE + "/tomap <URL> [resize]" + ChatColor.GRAY + "!"
));
empty.setItemMeta(meta);
return empty;
}
@Override
protected void onRightClick(ImageMap data)
{
Gui.open(getPlayer(), new MapDetailGui(data));
}
@Override
protected ItemStack getPickedUpItem(ImageMap map)
{
if(map instanceof SingleMap)
{
return MapItemManager.createMapItem(map.getMapsIDs()[0], map.getName());
}
MapItemManager.give(getPlayer(), map);
return null;
}
@Override
protected void onUpdate()
{
ImageMap[] maps = MapManager.getMaps(getPlayer().getUniqueId());
setData(maps);
setTitle(ChatColor.BLACK + "Your maps " + ChatColor.RESET + "(" + maps.length + ")");
setKeepHorizontalScrollingSpace(true);
/* ** Statistics ** */
int imagesCount = MapManager.getMapList(getPlayer().getUniqueId()).size();
int mapPartCount = MapManager.getMapPartCount(getPlayer().getUniqueId());
int mapGlobalLimit = PluginConfiguration.MAP_GLOBAL_LIMIT.getInteger();
int mapPersonalLimit = PluginConfiguration.MAP_PLAYER_LIMIT.getInteger();
int mapPartGloballyLeft = mapGlobalLimit - MapManager.getMapCount();
int mapPartPersonallyLeft = mapPersonalLimit - mapPartCount;
int mapPartLeft;
if(mapGlobalLimit <= 0 && mapPersonalLimit <= 0)
mapPartLeft = -1;
else if(mapGlobalLimit <= 0)
mapPartLeft = mapPartPersonallyLeft;
else if(mapPersonalLimit <= 0)
mapPartLeft = mapPartGloballyLeft;
else
mapPartLeft = Math.min(mapPartGloballyLeft, mapPartPersonallyLeft);
double percentageUsed = mapPartLeft < 0 ? 0 : ((double) mapPartCount) / ((double) (mapPartCount + mapPartLeft)) * 100;
ItemStack statistics = new ItemStack(Material.ENCHANTED_BOOK);
ItemMeta meta = statistics.getItemMeta();
meta.setDisplayName(ChatColor.BLUE + "Usage statistics");
meta.setLore(Arrays.asList(
"",
getStatisticText("Images rendered", imagesCount),
getStatisticText("Minecraft maps used", mapPartCount)
));
if(mapPartLeft >= 0)
{
List<String> lore = meta.getLore();
lore.add("");
lore.add(ChatColor.BLUE + "Minecraft maps limits");
lore.add("");
lore.add(getStatisticText("Server-wide limit", mapGlobalLimit, true));
lore.add(getStatisticText("Per-player limit", mapPersonalLimit, true));
lore.add("");
lore.add(getStatisticText("Current consumption", ((int) Math.rint(percentageUsed)) + " %"));
lore.add(getStatisticText("Maps left", mapPartLeft));
meta.setLore(lore);
}
GuiUtils.hideItemAttributes(meta);
statistics.setItemMeta(meta);
action("", getSize() - 5, statistics);
}
private String getStatisticText(String title, Integer value)
{
return getStatisticText(title, value, false);
}
private String getStatisticText(String title, Integer value, boolean zeroIsUnlimited)
{
return getStatisticText(title, zeroIsUnlimited && value <= 0 ? "unlimited" : bigNumbersFormatter.format(value));
}
private String getStatisticText(String title, String value)
{
return ChatColor.GRAY + title + ": " + ChatColor.WHITE + value;
}
}

View File

@ -1,47 +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.guiproko.list;
import fr.moribus.imageonmap.guiproko.core.ExplorerGui;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
public class MaterialGui extends ExplorerGui<Material>
{
@Override
protected ItemStack getViewItem(Material data)
{
return new ItemStack(data);
}
@Override
protected void onRightClick(Material data)
{
getPlayer().sendMessage("You clicked : " + data.toString());
}
@Override
protected void onUpdate()
{
setTitle("All da materials");
setData(Material.values());
}
}

View File

@ -20,53 +20,36 @@ 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 fr.zcraft.zlib.components.worker.Worker;
import fr.zcraft.zlib.components.worker.WorkerAttributes;
import fr.zcraft.zlib.components.worker.WorkerRunnable;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.nio.file.Files;
import javax.imageio.ImageIO;
@WorkerAttributes (name = "Image IO")
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>()
submitQuery(new WorkerRunnable<Void>()
{
@Override
public Void run() throws Exception
{
@Override
public Void run() throws Exception
{
BufferedImage image = ImageIO.read(file);
mapRenderer.setImage(image);
return null;
}
});
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>()
submitQuery(new WorkerRunnable<Void>()
{
@Override
public Void run() throws Throwable
@ -101,7 +84,7 @@ public class ImageIOExecutor extends Worker
static public void deleteImage(final File file)
{
instance.submitQuery(new WorkerRunnable<Void>()
submitQuery(new WorkerRunnable<Void>()
{
@Override
public Void run() throws Throwable
@ -109,7 +92,6 @@ public class ImageIOExecutor extends Worker
Files.delete(file.toPath());
return null;
}
});
}
}

View File

@ -18,46 +18,29 @@
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 fr.zcraft.zlib.components.worker.Worker;
import fr.zcraft.zlib.components.worker.WorkerAttributes;
import fr.zcraft.zlib.components.worker.WorkerCallback;
import fr.zcraft.zlib.components.worker.WorkerRunnable;
import javax.imageio.ImageIO;
import java.awt.*;
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;
@WorkerAttributes (name = "Image IO", queriesMainThread = true)
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>()
submitQuery(new WorkerRunnable<Void>()
{
@Override
public Void run() throws Throwable
@ -70,15 +53,15 @@ public class ImageRendererExecutor extends Worker
static public void Render(final URL url, final boolean scaling, final UUID playerUUID, WorkerCallback<ImageMap> callback)
{
instance.submitQuery(new WorkerRunnable<ImageMap>()
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);
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);
@ -87,7 +70,7 @@ public class ImageRendererExecutor extends Worker
static private ImageMap RenderSingle(final BufferedImage image, final UUID playerUUID) throws Throwable
{
MapManager.checkMapLimit(1, playerUUID);
Future<Short> futureMapID = instance.submitToMainThread(new Callable<Short>()
Future<Short> futureMapID = submitToMainThread(new Callable<Short>()
{
@Override
public Short call() throws Exception
@ -101,7 +84,7 @@ public class ImageRendererExecutor extends Worker
final short mapID = futureMapID.get();
ImageIOExecutor.saveImage(mapID, finalImage);
instance.submitToMainThread(new Callable<Void>()
submitToMainThread(new Callable<Void>()
{
@Override
public Void call() throws Exception
@ -121,7 +104,7 @@ public class ImageRendererExecutor extends Worker
final int mapCount = poster.getImagesCount();
MapManager.checkMapLimit(mapCount, playerUUID);
final Future<short[]> futureMapsIds = instance.submitToMainThread(new Callable<short[]>()
final Future<short[]> futureMapsIds = submitToMainThread(new Callable<short[]>()
{
@Override
public short[] call() throws Exception
@ -136,7 +119,7 @@ public class ImageRendererExecutor extends Worker
ImageIOExecutor.saveImage(mapsIDs, poster);
instance.submitToMainThread(new Callable<Void>()
submitToMainThread(new Callable<Void>()
{
@Override
public Void call() throws Exception
@ -178,5 +161,4 @@ public class ImageRendererExecutor extends Worker
graphics.dispose();
return newImage;
}
}

View File

@ -20,8 +20,15 @@ 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 fr.zcraft.zlib.tools.PluginLogger;
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;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
@ -29,12 +36,6 @@ 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
{

View File

@ -19,7 +19,8 @@
package fr.moribus.imageonmap.migration;
import fr.moribus.imageonmap.ImageOnMap;
import fr.moribus.imageonmap.PluginLogger;
import fr.zcraft.zlib.tools.PluginLogger;
public class MigratorExecutor
{

View File

@ -1,222 +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 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

@ -19,8 +19,14 @@
package fr.moribus.imageonmap.migration;
import fr.moribus.imageonmap.ImageOnMap;
import fr.moribus.imageonmap.PluginLogger;
import fr.moribus.imageonmap.map.MapManager;
import fr.zcraft.zlib.tools.PluginLogger;
import fr.zcraft.zlib.tools.mojang.UUIDFetcher;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.plugin.Plugin;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
@ -37,10 +43,6 @@ 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
@ -350,7 +352,7 @@ public class V3Migrator implements Runnable
}
catch(IOException ex)
{
PluginLogger.error("An error occured while fetching the UUIDs from Mojang", ex);
PluginLogger.error("An error occurred while fetching the UUIDs from Mojang", ex);
throw ex;
}
catch(InterruptedException ex)
@ -358,12 +360,12 @@ public class V3Migrator implements Runnable
PluginLogger.error("The migration worker has been interrupted", ex);
throw ex;
}
PluginLogger.info("Fetching done. {0} UUIDs have been retreived.", usersUUIDs.size());
PluginLogger.info("Fetching done. {0} UUIDs have been retrieved.", 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
* Fetches the UUIDs that could not be retrieved via Mojang's standard API
* @return true if at least one UUID has been retrieved, false otherwise
*/
private boolean fetchMissingUUIDs() throws IOException, InterruptedException
{
@ -378,7 +380,7 @@ public class V3Migrator implements Runnable
}
catch(IOException ex)
{
PluginLogger.error("An error occured while fetching the UUIDs from Mojang");
PluginLogger.error("An error occurred while fetching the UUIDs from Mojang");
throw ex;
}
catch(InterruptedException ex)

View File

@ -18,10 +18,10 @@
package fr.moribus.imageonmap.ui;
import fr.moribus.imageonmap.guiproko.core.GuiUtils;
import fr.moribus.imageonmap.map.ImageMap;
import fr.moribus.imageonmap.map.PosterMap;
import fr.moribus.imageonmap.map.SingleMap;
import fr.zcraft.zlib.components.gui.GuiUtils;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.entity.Player;

View File

@ -1,132 +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.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

@ -1,24 +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.worker;
public interface WorkerCallback<T>
{
public void finished(T result);
public void errored(Throwable exception);
}

View File

@ -1,152 +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.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

@ -1,199 +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.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

@ -1,23 +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.worker;
public interface WorkerRunnable<T>
{
public T run() throws Throwable;
}