diff --git a/src/main/java/fr/moribus/imageonmap/ImageOnMap.java b/src/main/java/fr/moribus/imageonmap/ImageOnMap.java index de5828a..62ef341 100644 --- a/src/main/java/fr/moribus/imageonmap/ImageOnMap.java +++ b/src/main/java/fr/moribus/imageonmap/ImageOnMap.java @@ -80,13 +80,13 @@ public final class ImageOnMap extends JavaPlugin } //Init all the things ! + PluginConfiguration.init(this); MetricsLite.startMetrics(); ImageIOExecutor.start(); ImageRendererExecutor.start(); MapManager.init(); Commands.init(this); - getServer().getPluginManager().registerEvents(new MapInitEvent(), this); - MapInitEvent.init(); + MapInitEvent.init(this); MapItemManager.init(); } diff --git a/src/main/java/fr/moribus/imageonmap/PluginConfiguration.java b/src/main/java/fr/moribus/imageonmap/PluginConfiguration.java index 3b2f2d3..c0a69be 100644 --- a/src/main/java/fr/moribus/imageonmap/PluginConfiguration.java +++ b/src/main/java/fr/moribus/imageonmap/PluginConfiguration.java @@ -19,21 +19,24 @@ package fr.moribus.imageonmap; import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.plugin.Plugin; public enum PluginConfiguration { //Configuration field Names, with default values COLLECT_DATA("collect-data", true), - MAP_GLOBAL_LIMIT("map-global-limit", 0), - MAP_PLAYER_LIMIT("map-player-limit", 0); + MAP_GLOBAL_LIMIT("map-global-limit", 0, "Limit-map-by-server"), + MAP_PLAYER_LIMIT("map-player-limit", 0, "Limit-map-by-player"); private final String fieldName; private final Object defaultValue; + private final String[] deprecatedNames; - private PluginConfiguration(String fieldName, Object defaultValue) + private PluginConfiguration(String fieldName, Object defaultValue, String ... deprecatedNames) { this.fieldName = fieldName; this.defaultValue = defaultValue; + this.deprecatedNames = deprecatedNames; } public Object get() @@ -46,9 +49,9 @@ public enum PluginConfiguration return defaultValue; } - public boolean isDefaultValue() + public boolean isDefined() { - return get().equals(defaultValue); + return getConfig().contains(fieldName); } @Override @@ -72,8 +75,51 @@ public enum PluginConfiguration return getConfig().getBoolean(fieldName, (Boolean)defaultValue); } + private boolean init() + { + boolean affected = false; + + if(!isDefined()) + { + getConfig().set(fieldName, defaultValue); + affected = true; + } + + for(String deprecatedName : deprecatedNames) + { + if(getConfig().contains(deprecatedName)) + { + getConfig().set(fieldName, getConfig().get(deprecatedName)); + getConfig().set(deprecatedName, null); + affected = true; + } + } + return affected; + } + + /* ===== Static API ===== */ + + static private Plugin plugin; static public FileConfiguration getConfig() { - return ImageOnMap.getPlugin().getConfig(); + return plugin.getConfig(); + } + + static public void init(Plugin plugin) + { + PluginConfiguration.plugin = plugin; + loadDefaultValues(); + } + + static private void loadDefaultValues() + { + boolean affected = false; + + for(PluginConfiguration configField : PluginConfiguration.values()) + { + if(configField.init()) affected = true; + } + + if(affected) plugin.saveConfig(); } } diff --git a/src/main/java/fr/moribus/imageonmap/commands/CommandException.java b/src/main/java/fr/moribus/imageonmap/commands/CommandException.java index 3fe20c8..2be9800 100644 --- a/src/main/java/fr/moribus/imageonmap/commands/CommandException.java +++ b/src/main/java/fr/moribus/imageonmap/commands/CommandException.java @@ -18,6 +18,8 @@ package fr.moribus.imageonmap.commands; +import fr.moribus.imageonmap.PluginLogger; + public class CommandException extends Exception { public enum Reason @@ -63,6 +65,7 @@ public class CommandException extends Exception case SENDER_NOT_AUTHORIZED: return "You do not have the permission to use this command."; default: + PluginLogger.LogWarning("Unknown CommandException caught", this); return "An unknown error suddenly happened."; } } diff --git a/src/main/java/fr/moribus/imageonmap/commands/maptool/DeleteNoConfirmCommand.java b/src/main/java/fr/moribus/imageonmap/commands/maptool/DeleteNoConfirmCommand.java index f61160d..3a2cc6e 100644 --- a/src/main/java/fr/moribus/imageonmap/commands/maptool/DeleteNoConfirmCommand.java +++ b/src/main/java/fr/moribus/imageonmap/commands/maptool/DeleteNoConfirmCommand.java @@ -18,9 +18,11 @@ package fr.moribus.imageonmap.commands.maptool; +import fr.moribus.imageonmap.PluginLogger; import fr.moribus.imageonmap.commands.*; import fr.moribus.imageonmap.map.ImageMap; import fr.moribus.imageonmap.map.MapManager; +import fr.moribus.imageonmap.map.MapManagerException; import java.util.List; import org.bukkit.entity.Player; @@ -39,8 +41,16 @@ public class DeleteNoConfirmCommand extends Command Player player = playerSender(); ImageMap map = getMapFromArgs(); MapManager.clear(player.getInventory(), map); - MapManager.deleteMap(map); - info("Map successfully deleted."); + try + { + MapManager.deleteMap(map); + info("Map successfully deleted."); + } + catch (MapManagerException ex) + { + PluginLogger.LogWarning("A non-existent map was requested to be deleted", ex); + warning("This map does not exist."); + } } @Override diff --git a/src/main/java/fr/moribus/imageonmap/image/ImageRendererExecutor.java b/src/main/java/fr/moribus/imageonmap/image/ImageRendererExecutor.java index 8a5fec9..7245c7b 100644 --- a/src/main/java/fr/moribus/imageonmap/image/ImageRendererExecutor.java +++ b/src/main/java/fr/moribus/imageonmap/image/ImageRendererExecutor.java @@ -86,6 +86,7 @@ public class ImageRendererExecutor extends Worker static private ImageMap RenderSingle(final BufferedImage image, final UUID playerUUID) throws Throwable { + MapManager.checkMapLimit(1, playerUUID); Future futureMapID = instance.submitToMainThread(new Callable() { @Override @@ -118,7 +119,8 @@ public class ImageRendererExecutor extends Worker { final PosterImage poster = new PosterImage(image); final int mapCount = poster.getImagesCount(); - + + MapManager.checkMapLimit(mapCount, playerUUID); final Future futureMapsIds = instance.submitToMainThread(new Callable() { @Override diff --git a/src/main/java/fr/moribus/imageonmap/image/MapInitEvent.java b/src/main/java/fr/moribus/imageonmap/image/MapInitEvent.java index fcad286..ce3a1c0 100644 --- a/src/main/java/fr/moribus/imageonmap/image/MapInitEvent.java +++ b/src/main/java/fr/moribus/imageonmap/image/MapInitEvent.java @@ -32,11 +32,14 @@ import org.bukkit.event.player.PlayerItemHeldEvent; import org.bukkit.event.world.ChunkLoadEvent; import org.bukkit.inventory.ItemStack; import org.bukkit.map.MapView; +import org.bukkit.plugin.Plugin; public class MapInitEvent implements Listener { - static public void init() + static public void init(Plugin plugin) { + plugin.getServer().getPluginManager().registerEvents(new MapInitEvent(), plugin); + for(World world : Bukkit.getWorlds()) { for(ItemFrame frame : world.getEntitiesByClass(ItemFrame.class)) diff --git a/src/main/java/fr/moribus/imageonmap/map/ImageMap.java b/src/main/java/fr/moribus/imageonmap/map/ImageMap.java index e91750e..390a4a8 100644 --- a/src/main/java/fr/moribus/imageonmap/map/ImageMap.java +++ b/src/main/java/fr/moribus/imageonmap/map/ImageMap.java @@ -66,6 +66,7 @@ public abstract class ImageMap implements ConfigurationSerializable public abstract short[] getMapsIDs(); public abstract boolean managesMap(short mapID); + public abstract int getMapCount(); public boolean managesMap(ItemStack item) { diff --git a/src/main/java/fr/moribus/imageonmap/map/MapManager.java b/src/main/java/fr/moribus/imageonmap/map/MapManager.java index d0aaa40..75320c1 100644 --- a/src/main/java/fr/moribus/imageonmap/map/MapManager.java +++ b/src/main/java/fr/moribus/imageonmap/map/MapManager.java @@ -19,8 +19,10 @@ package fr.moribus.imageonmap.map; import fr.moribus.imageonmap.ImageOnMap; +import fr.moribus.imageonmap.PluginConfiguration; import fr.moribus.imageonmap.image.ImageIOExecutor; import fr.moribus.imageonmap.image.PosterImage; +import fr.moribus.imageonmap.map.MapManagerException.Reason; import java.util.ArrayList; import java.util.List; import java.util.UUID; @@ -72,14 +74,14 @@ abstract public class MapManager return false; } - static public ImageMap createMap(UUID playerUUID, short mapID) + static public ImageMap createMap(UUID playerUUID, short mapID) throws MapManagerException { ImageMap newMap = new SingleMap(playerUUID, mapID); addMap(newMap); return newMap; } - static public ImageMap createMap(PosterImage image, UUID playerUUID, short[] mapsIDs) + static public ImageMap createMap(PosterImage image, UUID playerUUID, short[] mapsIDs) throws MapManagerException { ImageMap newMap; if(image.getImagesCount() == 1) @@ -104,15 +106,15 @@ abstract public class MapManager return mapsIds; } - static public void addMap(ImageMap map) + static public void addMap(ImageMap map) throws MapManagerException { getPlayerMapStore(map.getUserUUID()).addMap(map); } - static public void deleteMap(ImageMap map) + static public void deleteMap(ImageMap map) throws MapManagerException { - ImageIOExecutor.deleteImage(map); getPlayerMapStore(map.getUserUUID()).deleteMap(map); + ImageIOExecutor.deleteImage(map); } static public void notifyModification(UUID playerUUID) @@ -170,14 +172,48 @@ abstract public class MapManager } } + static public void checkMapLimit(ImageMap map) throws MapManagerException + { + checkMapLimit(map.getMapCount(), map.getUserUUID()); + } + + static public void checkMapLimit(int newMapsCount, UUID userUUID) throws MapManagerException + { + int limit = PluginConfiguration.MAP_GLOBAL_LIMIT.getInteger(); + if(limit > 0) + { + if(getMapCount() + newMapsCount > limit) + throw new MapManagerException(Reason.MAXIMUM_SERVER_MAPS_EXCEEDED); + } + getPlayerMapStore(userUUID).checkMapLimit(newMapsCount); + } + + static public int getMapCount() + { + int mapCount = 0; + synchronized(playerMaps) + { + for(PlayerMapStore tStore : playerMaps) + { + mapCount += tStore.getMapCount(); + } + } + return mapCount; + } + static private PlayerMapStore getPlayerMapStore(UUID playerUUID) { - PlayerMapStore store = getExistingPlayerMapStore(playerUUID); - if(store == null) + PlayerMapStore store; + synchronized(playerMaps) { - store = new PlayerMapStore(playerUUID); - synchronized(playerMaps){playerMaps.add(store);} - store.load(); + store = getExistingPlayerMapStore(playerUUID); + if(store == null) + { + store = new PlayerMapStore(playerUUID); + + playerMaps.add(store); + store.load(); + } } return store; } diff --git a/src/main/java/fr/moribus/imageonmap/map/MapManagerException.java b/src/main/java/fr/moribus/imageonmap/map/MapManagerException.java new file mode 100644 index 0000000..b7cf91a --- /dev/null +++ b/src/main/java/fr/moribus/imageonmap/map/MapManagerException.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2013 Moribus + * Copyright (C) 2015 ProkopyL + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package fr.moribus.imageonmap.map; + +import java.text.MessageFormat; + +public class MapManagerException extends Exception +{ + public enum Reason + { + MAXIMUM_PLAYER_MAPS_EXCEEDED("You have too many maps (maximum : {0})."), + MAXIMUM_SERVER_MAPS_EXCEEDED("The server ImageOnMap limit has been reached."), + IMAGEMAP_DOES_NOT_EXIST("The given map does not exist."); + + private final String reasonString; + private Reason(String reasonString) + { + this.reasonString = reasonString; + } + + public String getReasonString(Object ...arguments) + { + return MessageFormat.format(reasonString, arguments); + } + } + + private final Reason reason; + + public MapManagerException(Reason reason, Object ...arguments) + { + super(reason.getReasonString(arguments)); + this.reason = reason; + } + + public Reason getReason() { return reason; } + +} diff --git a/src/main/java/fr/moribus/imageonmap/map/PlayerMapStore.java b/src/main/java/fr/moribus/imageonmap/map/PlayerMapStore.java index ab56c52..0a34084 100644 --- a/src/main/java/fr/moribus/imageonmap/map/PlayerMapStore.java +++ b/src/main/java/fr/moribus/imageonmap/map/PlayerMapStore.java @@ -19,7 +19,9 @@ package fr.moribus.imageonmap.map; import fr.moribus.imageonmap.ImageOnMap; +import fr.moribus.imageonmap.PluginConfiguration; import fr.moribus.imageonmap.PluginLogger; +import fr.moribus.imageonmap.map.MapManagerException.Reason; import java.io.File; import java.io.IOException; import java.util.ArrayList; @@ -39,6 +41,7 @@ public class PlayerMapStore implements ConfigurationSerializable private final UUID playerUUID; private final ArrayList mapList = new ArrayList(); private boolean modified = false; + private int mapCount = 0; public PlayerMapStore(UUID playerUUID) { @@ -63,18 +66,34 @@ public class PlayerMapStore implements ConfigurationSerializable return false; } - public synchronized void addMap(ImageMap map) + public synchronized void addMap(ImageMap map) throws MapManagerException { - mapList.add(map); + checkMapLimit(map); + _addMap(map); notifyModification(); } - public synchronized void deleteMap(ImageMap map) + private void _addMap(ImageMap map) { - mapList.remove(map); + mapList.add(map); + mapCount += map.getMapCount(); + } + + public synchronized void deleteMap(ImageMap map) throws MapManagerException + { + _removeMap(map); notifyModification(); } + private void _removeMap(ImageMap map) throws MapManagerException + { + if(!mapList.remove(map)) + { + throw new MapManagerException(Reason.IMAGEMAP_DOES_NOT_EXIST); + } + mapCount -= map.getMapCount(); + } + public synchronized boolean mapExists(String id) { for(ImageMap map : mapList) @@ -113,6 +132,20 @@ public class PlayerMapStore implements ConfigurationSerializable return null; } + public void checkMapLimit(ImageMap map) throws MapManagerException + { + checkMapLimit(map.getMapCount()); + } + + public void checkMapLimit(int newMapsCount) throws MapManagerException + { + int limit = PluginConfiguration.MAP_PLAYER_LIMIT.getInteger(); + if(limit <= 0) return; + + if(getMapCount() + newMapsCount > limit) + throw new MapManagerException(Reason.MAXIMUM_PLAYER_MAPS_EXCEEDED, limit); + } + /* ===== Getters & Setters ===== */ public UUID getUUID() @@ -130,6 +163,11 @@ public class PlayerMapStore implements ConfigurationSerializable this.modified = true; } + public synchronized int getMapCount() + { + return this.mapCount; + } + /* ****** Serializing ***** */ @Override @@ -159,13 +197,20 @@ public class PlayerMapStore implements ConfigurationSerializable try { ImageMap newMap = ImageMap.fromConfig(tMap, playerUUID); - synchronized(this) {mapList.add(newMap);} + synchronized(this) {_addMap(newMap);} } catch(InvalidConfigurationException ex) { PluginLogger.LogWarning("Could not load map data : " + ex.getMessage()); } } + + try { checkMapLimit(0); } + catch(MapManagerException ex) + { + PluginLogger.LogWarning("Map limit exceeded for player " + playerUUID.toString() + + " (" + mapList.size() + " maps loaded)."); + } } /* ****** Configuration Files management ***** */ diff --git a/src/main/java/fr/moribus/imageonmap/map/PosterMap.java b/src/main/java/fr/moribus/imageonmap/map/PosterMap.java index 1520f62..b4b6e86 100644 --- a/src/main/java/fr/moribus/imageonmap/map/PosterMap.java +++ b/src/main/java/fr/moribus/imageonmap/map/PosterMap.java @@ -101,4 +101,10 @@ public class PosterMap extends ImageMap return (i / columnCount) + 1; } + @Override + public int getMapCount() + { + return mapsIDs.length; + } + } diff --git a/src/main/java/fr/moribus/imageonmap/map/SingleMap.java b/src/main/java/fr/moribus/imageonmap/map/SingleMap.java index f384a51..2385ca1 100644 --- a/src/main/java/fr/moribus/imageonmap/map/SingleMap.java +++ b/src/main/java/fr/moribus/imageonmap/map/SingleMap.java @@ -44,6 +44,12 @@ public class SingleMap extends ImageMap return this.mapID == mapID; } + @Override + public int getMapCount() + { + return 1; + } + /* ====== Serialization methods ====== */ public SingleMap(Map map, UUID userUUID) throws InvalidConfigurationException