package us.icebrg.hungry; import java.io.File; import java.io.IOException; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Timer; import java.util.logging.Logger; import org.bukkit.block.Block; import org.bukkit.entity.Player; import org.bukkit.event.Event; import org.bukkit.event.Event.Priority; import org.bukkit.inventory.ItemStack; import org.bukkit.plugin.PluginDescriptionFile; import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.java.JavaPlugin; import us.icebrg.hungry.commands.HungryFoodCommand; import us.icebrg.hungry.commands.HungryHungerCommand; import us.icebrg.hungry.commands.HungryListFoodsCommand; import us.icebrg.hungry.commands.HungryReloadCommand; import us.icebrg.hungry.commands.HungrySaveCommand; import us.icebrg.hungry.commands.HungrySetHungerCommand; import us.icebrg.hungry.commands.HungryToggleCommand; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonParseException; public class Hungry extends JavaPlugin { public static String pluginDir = "plugins/Hungry/"; public static HungryPermissions permissions; protected Logger log = Logger.getLogger("Minecraft"); protected volatile HungryConfiguration config; protected HungryBlockListener blockListener; protected HungryPlayerListener playerListener; protected Timer timer = new Timer(); protected Gson gson; public HungryConfiguration getConfig() { return this.config; } /** * To be called if a player has clicked on a block (a food block, i.e. cake, * or a default-non-edible block) to check for and take appropriate action * if the block is registered as edible with Hungry. * * @todo fix cake * @param player * the player in question * @param block * the block that was clicked on * @return true if the block was registered as eaten by Hungry, false * otherwise */ public boolean handlePlayerEat(Player player, Block block) { if (!Hungry.permissions.has(player, "hungry.player.hunger")) { return true; } String blockMatName = block.getType().name(); // Check to see if the material name of the block was specified in the // HungryConfiguration if (!this.config.foodBlocks.containsKey(blockMatName)) { // If not, pass the event and just return. return false; } // Otherwise, if it's another block, just destroy it... (NOTE: To be // implemented in the future. // For now, the only supported block food is cake.) this.restorePlayerHunger(player, this.config.foodBlocks.get(blockMatName)); return true; } /** * To be called if a player has used an item (a food item or * default-non-edible item) to check for and take appropriate action if the * item is registered as edible with Hungry. * * @todo reduce duplication in here! * @param player * the player in question * @param item * the item the player is posibbly eating * @return true if the item was registered as eaten by Hungry, false * otherwise */ public boolean handlePlayerEat(Player player, ItemStack item) { if (!Hungry.permissions.has(player, "hungry.player.hunger")) { return true; } // Get the block the player was looking at, for comparison with the // HungryConfiguration.ignoreClickBlocks (all blocks except air, within // 100 blocks) Block block = player.getTargetBlock(null, 100); String blockMatName = block.getType().name(); List<String> ignoreClickBlocks = Arrays .asList(this.config.ignoreClickBlocks); // If the player selected a block... if (block != null) { // ... and if the Material enum name is on the ignoreClickBlocks // list... if (ignoreClickBlocks.contains(blockMatName)) { // Return false, as the player clicked on an ignoreClickBlock (a // door, etc.) return false; } } String itemMatName = item.getType().name(); // First off, get the name of the Material enum key (used in // HungryConfiguration) that // identifies this item. This would, for example, be BREAD or // MUSHROOM_SOUP. // Then, check if HungryConfiguration.foods contains this key. If it // doesn't, this isn't // an edible item, so just return. if (!this.config.foods.containsKey(itemMatName)) { return false; } this.restorePlayerHunger(player, this.config.foods.get(itemMatName)); return true; } @Override public void onDisable() { // Cancel all HungryTimerTasks this.timer.cancel(); this.saveConfiguration(); this.log.info("[Hungry] Hungry disabled!"); } @Override public void onEnable() { PluginDescriptionFile pdfFile = this.getDescription(); PluginManager pm = this.getServer().getPluginManager(); this.blockListener = new HungryBlockListener(this); this.playerListener = new HungryPlayerListener(this); this.gson = new GsonBuilder().setPrettyPrinting().create(); // Setup configuration, initializing if it isn't already there... if (!this.setupConfiguration()) { // If setupConfiguration() returned false, an error was occurred and // configuration // could not be setup. Disable the plugin. log.severe("[Hungry] Failed to setup and or load configuration files - aborting!"); this.getServer().getPluginManager().disablePlugin(this); return; } // Register events pm.registerEvent(Event.Type.PLAYER_INTERACT, this.playerListener, Priority.Monitor, this); pm.registerEvent(Event.Type.PLAYER_RESPAWN, this.playerListener, Priority.Monitor, this); // Register commands try { // Administrative commands... this.getCommand("hungryreload").setExecutor( new HungryReloadCommand(this)); this.getCommand("hungrysave").setExecutor( new HungrySaveCommand(this)); this.getCommand("hungrytoggle").setExecutor( new HungryToggleCommand(this)); this.getCommand("sethunger").setExecutor( new HungrySetHungerCommand(this)); // Player commands... this.getCommand("hunger") .setExecutor(new HungryHungerCommand(this)); this.getCommand("listfoods").setExecutor( new HungryListFoodsCommand(this)); this.getCommand("food").setExecutor(new HungryFoodCommand(this)); } catch (NullPointerException e) { this.log.severe("[Hungry] Failed to register one or more commands!" + "Please disable conflicting plugins - aborting!"); this.getServer().getPluginManager().disablePlugin(this); return; } Hungry.permissions = new HungryPermissions(this); if (!Hungry.permissions.init()) { this.log.severe("[Hungry] Failed to load Permissions plugin - aborting!"); this.getServer().getPluginManager().disablePlugin(this); return; } this.log.info("[Hungry] Initializing hunger loop..."); this.timer.scheduleAtFixedRate( new HungryTimerTask(this), this.config.checkInterval * 1000, this.config.checkInterval * 1000); // Multiply by 1000 because Timer accepts its arguments in milliseconds... this.log.info("[Hungry] Hungry version " + pdfFile.getVersion() + " loaded!"); } /** * Restores the given player's hunger by the given amount, respecting * configuration options such as minHunger and canStockUpInfinitely... * * @param player * the player whose hunger to restore * @param amount * the amount by which to restore the player's hunger */ public void restorePlayerHunger(Player player, Integer amount) { HashMap<String, Integer> playerHungers = this.config.playerHungers; Integer currentPlayerHunger = playerHungers.get(player.getName()); Integer newPlayerHunger = currentPlayerHunger - amount; // Check if the canStockUpInfinitely option is true and, if so, // if the new player hunger is below the minimum... if (!this.config.canStockUpInfinitely && newPlayerHunger < this.config.minHunger) { // ... floor it at the minimum newPlayerHunger = this.config.minHunger; } playerHungers.put(player.getName(), newPlayerHunger); } /** * Attempts to save the configuration to disk. * * @return true if success, false if failure */ public boolean saveConfiguration() { try { // Try to write the configuration from this.config... this.log.info("[Hungry] Attempting to write configuration file to disk..."); this.config.save(new File(Hungry.pluginDir + "config.json")); } catch (IOException e) { // If we failed to save, display a message but // a) don't disable the plugin (this is called in onDisable) // b) don't delete/reset the file (let the user check it out) this.log.severe("[Hungry] Failed to write configuration file to disk!"); return false; } this.log.info("[Hungry] Succesfully saved configuration to disk."); return true; } /** * Attempts to load configuration. If configuration is not present, sets up * default configuration. * * @return true if success, false if failure in any operation */ public boolean setupConfiguration() { File pluginDirH = new File(Hungry.pluginDir); File configFileH; this.log.info("[Hungry] Loading configuration..."); if (!pluginDirH.exists()) { this.log.info("[Hungry] Configuration directory doesn't exist! Creating..."); if (!pluginDirH.mkdir()) { this.log.severe("[Hungry] Failed to create configuration directory !"); return false; } } configFileH = new File(Hungry.pluginDir + "config.json"); if (!configFileH.exists()) { this.log.info("[Hungry] Configuration file doesn't exist! Creating..."); this.config = new HungryConfiguration(); this.config.setDefaults(); try { this.config.save(configFileH); } catch (IOException e) { this.log.severe("[Hungry] Failed to serialize configuration file!"); return false; } this.log.info("[Hungry] Configuration file succesfully serialized..."); return true; } // If the configuration file didn't have to be created, just load it! this.log.info("[Hungry] Loading configuration file..."); try { this.config = HungryConfiguration.load(configFileH); } catch (JsonParseException e) { this.log.severe("[Hungry] Configuration file corrupt or invalid!"); this.log.severe("[Hungry] Moving old configuration file to config.json.bak..."); // "Rename" the config.json file to config.json.bak (new File(Hungry.pluginDir + "config.json")).renameTo(new File( Hungry.pluginDir + "config.json.bak")); this.log.severe("[Hungry] 'Loading' a new, default configuration file..."); this.config = new HungryConfiguration(); this.config.setDefaults(); return false; } catch (IOException e) { this.log.severe("[Hungry] Failed to load configuration file!"); return false; } this.log.info("[Hungry] Succesfully loaded configuration file!"); return true; } }