/*
* ExperienceMod - Bukkit server plugin for modifying the experience system in Minecraft.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is 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, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.xp;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Collection;
import java.util.List;
import java.util.MissingResourceException;
import java.util.logging.Logger;
import net.milkbowl.vault.chat.Chat;
import net.milkbowl.vault.economy.Economy;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.World;
import org.bukkit.command.CommandSender;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.RegisteredServiceProvider;
import org.bukkit.plugin.ServicesManager;
import org.bukkit.plugin.java.JavaPlugin;
import com.comphenix.xp.commands.CommandExperienceMod;
import com.comphenix.xp.commands.CommandSpawnExp;
import com.comphenix.xp.expressions.ParameterProviderSet;
import com.comphenix.xp.expressions.StandardPlayerService;
import com.comphenix.xp.extra.Service;
import com.comphenix.xp.extra.ServiceProvider;
import com.comphenix.xp.extra.PermissionSystem.CustomPermission;
import com.comphenix.xp.history.HawkeyeService;
import com.comphenix.xp.history.HistoryProviders;
import com.comphenix.xp.history.LogBlockService;
import com.comphenix.xp.history.MemoryService;
import com.comphenix.xp.listeners.*;
import com.comphenix.xp.lookup.*;
import com.comphenix.xp.messages.ChannelChatService;
import com.comphenix.xp.messages.ChannelProvider;
import com.comphenix.xp.messages.HeroService;
import com.comphenix.xp.messages.MessageFormatter;
import com.comphenix.xp.messages.StandardService;
import com.comphenix.xp.metrics.AutoUpdate;
import com.comphenix.xp.metrics.DataCollector;
import com.comphenix.xp.mods.BlockResponse;
import com.comphenix.xp.mods.CustomBlockProviders;
import com.comphenix.xp.mods.StandardBlockService;
import com.comphenix.xp.parser.ParsingException;
import com.comphenix.xp.parser.Utility;
import com.comphenix.xp.rewards.*;
import com.comphenix.xp.rewards.items.RewardDrops;
import com.comphenix.xp.rewards.xp.RewardEconomy;
import com.comphenix.xp.rewards.xp.RewardExperience;
import com.comphenix.xp.rewards.xp.RewardVirtual;
import com.google.common.base.Function;
import com.google.common.base.Objects;
import com.google.common.collect.Lists;
public class ExperienceMod extends JavaPlugin implements Debugger {
private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
private Logger currentLogger;
private PluginManager manager;
// The current debugger
private static Debugger DEBUGGER;
// Scheduling
private PlayerScheduler playerScheduler;
// VAULT only
private Object economy;
private Object chat;
private PlayerGroupMembership playerGroups;
private ExperienceBlockListener xpBlockListener;
private ExperienceItemListener xpItemListener;
private ExperienceMobListener xpMobListener;
private ExperienceEnhancementsListener xpEnchancer;
private ExperienceCleanupListener xpCleanup;
private ExperienceLevelListener xpLevel;
// Optional components
private ReflectionSlotModifier xpReflectionModifier;
private ProtocolSlotModifier xpProtocolModifier;
private ExperienceInformerListener informer;
private ItemRewardListener itemListener;
private PlayerInteractionListener interactionListener;
// Allows for plugin injection
private RewardProvider rewardProvider;
private ChannelProvider channelProvider;
private CustomBlockProviders customProvider;
private HistoryProviders historyProviders;
private ParameterProviderSet parameterProviders;
private StandardPlayerService standardPlayerService;
private RewardEconomy rewardEconomy;
private GlobalSettings globalSettings;
private ConfigurationLoader configLoader;
private Presets presets;
// Metrics!
private DataCollector dataCollector;
private AutoUpdate autoUpdate;
// Repeating task
private static final int TICK_DELAY = 4; // 50 ms * 4 = 200 ms
private int serverTickTask;
// Error reporter
private ErrorReporting report = ErrorReporting.DEFAULT;
// Commands
private CommandExperienceMod commandExperienceMod;
private CommandSpawnExp commandSpawn;
private boolean debugEnabled;
/**
* Retrieve the default debugger.
* @return Default debugger.
*/
public static Debugger getDefaultDebugger() {
return DEBUGGER;
}
@Override
public void onLoad() {
// Save this as the debugger
DEBUGGER = this;
try {
currentLogger = this.getLogger();
// Initialize scheduler
playerScheduler = new PlayerScheduler(Bukkit.getScheduler(), this);
manager = getServer().getPluginManager();
// Informs about negative events
informer = new ExperienceInformerListener(this, getServer());
// Initialize rewards
rewardProvider = new RewardProvider();
// Load history
historyProviders = new HistoryProviders();
// Load reward types
rewardProvider.register(new RewardExperience());
rewardProvider.register(new RewardVirtual());
rewardProvider.register(new RewardDrops());
rewardProvider.setDefaultReward(RewardTypes.EXPERIENCE);
// Initialize channel providers
channelProvider = new ChannelProvider();
channelProvider.setMessageFormatter(new MessageFormatter());
// Detect the existence of ProtocolLib
if (manager.getPlugin("ProtocolLib") != null) {
xpProtocolModifier = new ProtocolSlotModifier(this, this, presets);
currentLogger.info("Using ProtocolLib for experience slot modification.");
} else {
xpReflectionModifier = new ReflectionSlotModifier(this, presets);
currentLogger.info("Using NMS for experience slot modification.");
}
// Load channel providers if we can
if (HeroService.exists()) {
channelProvider.register(new HeroService());
channelProvider.setDefaultName(HeroService.NAME);
currentLogger.info("Using HeroChat for channels.");
} else if (ChannelChatService.exists()) {
channelProvider.register(new ChannelChatService());
channelProvider.setDefaultName(ChannelChatService.NAME);
currentLogger.info("Using ChannelChat for channels.");
} else {
channelProvider.register(new StandardService( getServer() ));
channelProvider.setDefaultName(StandardService.NAME);
currentLogger.info("Using standard chat.");
}
// Initialize block providers
customProvider = new CustomBlockProviders();
customProvider.register(new StandardBlockService());
// Initialize parameter providers
parameterProviders = new ParameterProviderSet();
standardPlayerService = new StandardPlayerService(this);
parameterProviders.registerPlayer(standardPlayerService);
// Initialize configuration loader
configLoader = new ConfigurationLoader(getDataFolder(), this,
rewardProvider, channelProvider, parameterProviders);
// Initialize error reporter
report.setErrorCount(0);
report.clearGlobalParameters();
report.addGlobalParameter("rewardProvider", rewardProvider);
report.addGlobalParameter("historyProvider", historyProviders);
report.addGlobalParameter("channelProvider", channelProvider);
report.addGlobalParameter("customProvider", customProvider);
report.addGlobalParameter("playerProviders", parameterProviders.getPlayerParameters());
report.addGlobalParameter("entityProviders", parameterProviders.getEntityParameters());
report.addGlobalParameter("blockProviders", parameterProviders.getBlockParameters());
report.addGlobalParameter("itemProviders", parameterProviders.getItemParameters());
} catch (Exception e) {
// Well, this is bad.
report.reportError(this, this, e);
throw new IllegalStateException("An exception occored.", e);
}
}
@Override
public void onEnable() {
try {
interactionListener = new PlayerInteractionListener(this);
// Commands
commandExperienceMod = new CommandExperienceMod(this);
commandSpawn = new CommandSpawnExp(this);
// Block provider
customProvider.setLastInteraction(interactionListener);
// Load economy, if it exists
try {
if (!hasEconomy())
economy = getRegistration(Economy.class, null);
if (!hasChat())
chat = getRegistration(Chat.class, new Function<Chat, Boolean>() {
public Boolean apply(Chat element) {
// Try not to use mChatSuite if we don't have to
return element.getName().equalsIgnoreCase("mChatSuite");
}
});
} catch (NoClassDefFoundError e) {
} catch (NullPointerException e) {
e.printStackTrace();
}
playerGroups = new PlayerGroupMembership(getChat());
// Don't register economy rewards unless we can
if (hasEconomy()) {
itemListener = new ItemRewardListener(this);
rewardEconomy = new RewardEconomy(getEconomy(), this, itemListener);
// Associate everything
rewardProvider.register(rewardEconomy);
itemListener.setReward(rewardEconomy);
standardPlayerService.setEconomy(rewardEconomy);
// Inform the player
currentLogger.info("Economy enabled. Using " + getEconomy().getName() + ".");
// Register listener
manager.registerEvents(itemListener, this);
} else {
// Damn it
currentLogger.info("Economy not registered.");
}
// Display chat hook
if (hasChat()) {
currentLogger.info("Hooked " + getChat().getName() + " for chat options.");
}
try {
// Initialize configuration
loadDefaults(false);
// Register listeners
if (xpReflectionModifier != null) {
manager.registerEvents(xpReflectionModifier, this);
}
if (xpProtocolModifier != null) {
xpProtocolModifier.register();
manager.registerEvents(xpProtocolModifier, this);
}
manager.registerEvents(interactionListener, this);
manager.registerEvents(xpBlockListener, this);
manager.registerEvents(xpItemListener, this);
manager.registerEvents(xpMobListener, this);
manager.registerEvents(xpEnchancer, this);
manager.registerEvents(xpCleanup, this);
manager.registerEvents(xpLevel, this);
manager.registerEvents(informer, this);
} catch (IOException e) {
currentLogger.severe("IO error when loading configurations: " + e.getMessage());
}
xpMobListener.setEconomy(rewardEconomy);
// Create memory history
historyProviders.register(new MemoryService(
globalSettings.getMaxBlocksInHistory(),
globalSettings.getMaxAgeInHistory()
));
registerHistoryServices();
// Collect data, if enabled
if (globalSettings.isUseMetrics()) {
dataCollector = new DataCollector(this);
}
// Register commands
getCommand(CommandExperienceMod.COMMAND_RELOAD).setExecutor(commandExperienceMod);
getCommand(CommandSpawnExp.COMMAND_SPAWN_XP).setExecutor(commandSpawn);
// Begin server tick
serverTickTask = getServer().getScheduler().scheduleSyncRepeatingTask(this, new Runnable() {
public void run() {
onServerTick();
}
}, TICK_DELAY, TICK_DELAY);
// Inform of this problem
if (serverTickTask < 0)
printWarning(this, "Could not start repeating task for sending messages.");
} catch (Exception e) {
report.reportError(this, this, e);
// Let Bukkit know about this too.
throw new IllegalStateException("An exception occored.", e);
}
}
private void registerHistoryServices() {
// Register log block if exists
if (LogBlockService.exists(manager)) {
if (!historyProviders.containsService(LogBlockService.NAME)) {
historyProviders.register(LogBlockService.create(manager));
}
currentLogger.info("Connected to LogBlock.");
} else {
currentLogger.info("Cannot connect to LogBlock.");
}
// Register Hawkeye if it exists
if (manager.getPlugin("HawkEye") != null) {
try {
if (!historyProviders.containsService(HawkeyeService.NAME)) {
historyProviders.register(new HawkeyeService(this));
}
currentLogger.info("Connected to Hawkeye.");
} catch (NoClassDefFoundError e) {
// Occurs if HawkEye disables itself, usually because of database problems.
currentLogger.info("Cannot connect to Hawkeye. Database connection not found.");
}
} else {
currentLogger.info("Cannot connect to Hawkeye.");
}
}
/**
* Disable every service in the given list of services.
* @param provider - registry of services.
* @param serviceNames - services to disable.
*/
private <TService extends Service> void disableServices(ServiceProvider<TService> provider, List<String> serviceNames) {
provider.enableAll();
// Disable all such services
for (String name : serviceNames) {
String enumName = Utility.getEnumName(name);
if (provider.containsService(enumName)) {
provider.setEnabled(enumName, false);
} else {
// We should really complain about this
printWarning(this, "Cannot disable %s: Service doesn't exist.", name);
}
}
}
@Override
public void onDisable() {
// Cancel server tick
if (serverTickTask >= 0)
getServer().getScheduler().cancelTask(serverTickTask);
if (itemListener != null)
itemListener.cleanupItems();
}
public YamlConfiguration loadConfig(String name, String createMessage) throws IOException {
File savedFile = new File(getDataFolder(), name);
File directory = savedFile.getParentFile();
// Reload the saved configuration
if (!savedFile.exists()) {
InputStream input = null;
OutputStream output = null;
try {
// Get the default file
input = ExperienceMod.class.getResourceAsStream("/" + name);
// See if we found it
if (input == null) {
throw new MissingResourceException(
"Cannot find built in resource file.", "ExperienceMod", name);
}
// Make sure the directory exists
if (!directory.exists()) {
directory.mkdirs();
if (!directory.exists())
throw new IOException("Could not create the directory " + directory.getAbsolutePath());
}
// Copy content
output = new FileOutputStream(savedFile);
copyLarge(input, output);
} catch (IOException e) {
throw e;
} finally {
// Clean up
if (input != null)
input.close();
if (output != null)
output.close();
}
}
// Retrieve the saved file
return YamlConfiguration.loadConfiguration(savedFile);
}
/**
* Reloads (if reload is TRUE) configurations. There's no need to call this after adding reward providers.
* @param reload - if TRUE; reload configuration.
* @throws IOException An I/O error occurred.
*/
public void loadDefaults(boolean reload) throws IOException {
// Read from disk again
if (reload || presets == null) {
if (reload) {
// Reset warnings if this is the second time around
informer.clearMessages();
}
// Remove previously loaded files
configLoader.clearCache();
// Initialize parser
if (rewardProvider.containsReward(RewardTypes.DROPS)) {
RewardDrops drops = (RewardDrops) rewardProvider.getByEnum(RewardTypes.DROPS);
drops.setItemNameParser(configLoader.getNameParser());
}
// Load globals
YamlConfiguration globalConfig = loadConfig("global.yml", "Creating default global settings.");
globalSettings = new GlobalSettings(this);
globalSettings.loadFromConfig(globalConfig);
if (autoUpdate == null) {
try {
autoUpdate = new AutoUpdate(this, globalConfig);
} catch (Exception e) {
throw new FileNotFoundException(e.getMessage());
}
} else {
// Update updater
autoUpdate.setConfig(globalConfig);
}
// Disable stuff
disableServices(historyProviders, globalSettings.getDisabledServices());
disableServices(channelProvider, globalSettings.getDisabledServices());
disableServices(rewardProvider, globalSettings.getDisabledServices());
disableServices(customProvider, globalSettings.getDisabledServices());
// Load parts of the configuration
YamlConfiguration presetList = loadConfig("presets.yml", "Creating default preset list.");
loadConfig("config.yml", "Creating default configuration.");
// Load it
presets = new Presets(presetList, configLoader, globalSettings.getPresetCacheTimeout(),
this, getChat());
setPresets(presets);
// Vault is required here
if (chat == null && presets.usesPresetParameters()) {
printWarning(this, "Cannot use presets. VAULT plugin was not found");
} else {
// Show potentially more warnings
checkIllegalPresets();
}
}
}
/**
* Invoked every server tick.
*/
public void onServerTick() {
try {
// Send messages
if (presets != null)
presets.onTick();
} catch (Exception e) {
report.reportError(this, this, e, presets);
}
}
// Check for illegal presets
private void checkIllegalPresets() {
// With no Vault this is impossible
if (chat == null)
return;
String possibleOption = "";
for (String group : getChat().getGroups()) {
for (World world : getServer().getWorlds()) {
String worldName = world.getName();
try {
// We have to be careful here - the plugin might throw an error
possibleOption = getChat().getGroupInfoString(worldName,
group, Presets.OPTION_PRESET_SETTING, null);
if (!Utility.isNullOrIgnoreable(possibleOption) &&
!presets.containsPreset(possibleOption, worldName)) {
// Complain about this too. Is likely an error.
printWarning(this,
"Could not find preset %s. Please check spelling.", possibleOption);
}
} catch (ParsingException e) {
printWarning(this, "Preset '%s' causes error: %s", possibleOption, e.getMessage());
} catch (Exception e) {
// mChat seems to throw this a lot
if (!presets.ignorableException(e))
printWarning(this, "Preset '%s' threw exception: %s", possibleOption, e.toString());
}
}
}
}
private <TClass> TClass getRegistration(Class<TClass> type, Function<TClass, Boolean> isLowPriority)
{
ServicesManager manager = getServer().getServicesManager();
Collection<RegisteredServiceProvider<TClass>> registry = manager.getRegistrations(type);
List<TClass> lower = Lists.newArrayList();
List<TClass> higher = Lists.newArrayList();
if (registry != null) {
// Sort by priority
for (RegisteredServiceProvider<TClass> provider : registry) {
if (provider.getPlugin() != null) {
TClass element = provider.getProvider();
// Use our "lambda"
if (isLowPriority != null && isLowPriority.apply(element)) {
lower.add(provider.getProvider());
} else {
higher.add(provider.getProvider());
}
}
}
// Take the first highest, if possible
if (higher.size() > 0)
return higher.get(0);
else if (lower.size() > 0)
return lower.get(0);
else
// Nothing found!
return null;
} else {
return null;
}
}
private boolean hasEconomy() {
return economy != null;
}
private boolean hasChat() {
return chat != null;
}
@Override
public boolean isDebugEnabled() {
return debugEnabled;
}
/**
* Toggles debug messages.
*/
public void toggleDebug() {
debugEnabled = !debugEnabled;
}
public Chat getChat() {
return (Chat)chat;
}
public Economy getEconomy() {
return (Economy)economy;
}
public ExperienceInformerListener getInformer() {
return informer;
}
public RewardProvider getRewardProvider() {
return rewardProvider;
}
public ChannelProvider getChannelProvider() {
return channelProvider;
}
public CustomBlockProviders getCustomBlockProvider() {
return customProvider;
}
/**
* Retrieves the object responsible for parsing and loading configuration files.
* @return The current configuration loader.
*/
public ConfigurationLoader getConfigLoader() {
return configLoader;
}
/**
* Sets the object responsible for parsing and loading configuration files.
* @param configLoader - the new configuration loader.
*/
public void setConfigLoader(ConfigurationLoader configLoader) {
this.configLoader = configLoader;
}
/**
* Gets the registry of history plugins.
* @return Registry of history plugins.
*/
public HistoryProviders getHistoryProviders() {
return historyProviders;
}
/**
* Sets the registry of history plugins.
* @param historyProviders - new registry of history plugins.
*/
public void setHistoryProviders(HistoryProviders historyProviders) {
this.historyProviders = historyProviders;
}
/**
* Gets the registry of parameter providers.
* @return Registry of parameter providers.
*/
public ParameterProviderSet getParameterProviders() {
return parameterProviders;
}
/**
* Sets the registry of parameter providers.
* @param parameterProviders - new registry of the parameter providers.
*/
public void setParameterProviders(ParameterProviderSet parameterProviders) {
this.parameterProviders = parameterProviders;
}
public ItemRewardListener getItemListener() {
return itemListener;
}
public Presets getPresets() {
return presets;
}
public PlayerScheduler getPlayerScheduler() {
return playerScheduler;
}
/**
* Retrieves the global plugin settings.
* @return Global settings.
*/
public GlobalSettings getGlobalSettings() {
return globalSettings;
}
/**
* Retrieves the current registered action types.
* @return Registry of action types.
*/
public ActionTypes getActionTypes() {
return configLoader.getActionTypes();
}
public DataCollector getDataCollector() {
return dataCollector;
}
/**
* Retrieves the object responsible for notifying of new updates.
* @return The object keeping track of new updates on BukkitDev.
*/
public AutoUpdate getAutoUpdate() {
return autoUpdate;
}
/**
* Retrieves a list of action rewards that applies when a mob is killed, either by the environment (when KILLER is NULL),
* or by a player.
* <p>
* Note that the returned list contains every possible reward that matches the given mob. In reality, only the
* first item will be awarded.
*
* @param killer - the player that killed this mob, or NULL if the mob died naturally.
* @param query - query representing the mob that was killed.
* @return A list of possible rewards. Only the first item will be chosen when rewards are actually awarded.
* @throws ParsingException If the stored preset option associated with the killer is malformed.
*/
public List<Action> getMobReward(Player killer, MobQuery query) throws ParsingException {
Configuration config = getPresets().getConfiguration(killer);
// Mirror the function below
return config.getExperienceDrop().getAllRanked(query);
}
/**
* Retrieves a list of action rewards that applies when a player performs a given action to the item or block
* specified by the query.
* <p>
* The query must be a ItemQuery for every trigger except brewing, where it also can be a PotionQuery.
* <p>
* Also note that this list contains every possible reward that matches the given parameters. In reality, only the
* first item will be awarded.
*
* @param player - player performing the given action, or NULL if the default configuration file should be used.
* @param trigger - action the player performs.
* @param query - query representing the item or block that was the target of the action.
* @return A list of possible rewards. Only the first item will be chosen when rewards are actually awarded.
* @throws ParsingException If the stored preset option associated with this player is malformed.
*/
public List<Action> getPlayerReward(Player player, Integer trigger, Query query) throws ParsingException {
Configuration config = getPresets().getConfiguration(player);
Integer brewing = getActionTypes().getType(ActionTypes.BREWING);
ItemTree current = config.getActionReward(trigger);
// Special brewing type
if (Objects.equal(trigger, brewing) && query instanceof PotionQuery) {
// Use the complex brewing reward rules
return config.getComplexBrewingReward().getAllRanked((PotionQuery) query);
} else {
// Check for incorrect action types/triggers
if (current == null) {
throw new IllegalArgumentException(String.format("Unknown trigger ID: %s", trigger));
}
// Standard item reward rules
return current.getAllRanked((ItemQuery) query);
}
}
/**
* Handles the given inventory event using the default behavior for the given inventory type.
* @param event - inventory click event.
* @param response - block response detailing how to process the inventory.
*/
public void processInventoryClick(InventoryClickEvent event, BlockResponse response) {
if (xpItemListener == null)
throw new RuntimeException("ExperienceMod isn't loaded yet.");
xpItemListener.processInventory(event, response);
}
private void setPresets(Presets presets) {
// Create a new listener if necessary
if (xpBlockListener == null || xpItemListener == null || xpMobListener == null) {
xpItemListener = new ExperienceItemListener(this, playerScheduler, customProvider, presets);
xpBlockListener = new ExperienceBlockListener(this, presets, historyProviders);
xpMobListener = new ExperienceMobListener(this, playerGroups, presets);
xpEnchancer = new ExperienceEnhancementsListener(this, presets,
xpReflectionModifier != null ? xpReflectionModifier : xpProtocolModifier);
xpLevel = new ExperienceLevelListener(this, presets);
xpCleanup = new ExperienceCleanupListener(presets, interactionListener, playerScheduler);
} else {
xpEnchancer.setPresets(presets);
xpItemListener.setPresets(presets);
xpBlockListener.setPresets(presets);
xpMobListener.setPresets(presets);
xpEnchancer.setPresets(presets);
xpLevel.setPresets(presets);
xpCleanup.setPlayerCleanupListeners(presets, interactionListener, playerScheduler);
}
// Set presets
if (xpReflectionModifier != null)
xpReflectionModifier.setPresets(presets);
if (xpProtocolModifier != null)
xpProtocolModifier.setPresets(presets);
}
@Override
public void printDebug(Object sender, String message, Object... params) {
if (debugEnabled) {
String formattedMessage = String.format("[ExperienceMod] " + message, params);
// Every player with the info permission will also see this message
getServer().broadcast(formattedMessage, CustomPermission.INFO.getBukkitPerm());
}
}
public void respond(CommandSender sender, String message) {
if (sender == null) // Sent by the console
currentLogger.info(message);
else
sender.sendMessage(message);
}
@Override
public void printWarning(Object sender, String message, Object... params) {
String formatted = String.format(message, params);
String warningMessage = ChatColor.RED + "Warning: " + formatted;
if (debugEnabled) {
currentLogger.warning(String.format("Warning sent from %s.", sender));
}
// Print immediately
if (currentLogger == null)
System.err.println(warningMessage);
else
currentLogger.warning(warningMessage);
// Add to list of warnings
if (informer != null) {
informer.addWarningMessage(formatted);
informer.broadcastWarning(formatted);
}
}
// Taken from Apache Commons-IO
private static long copyLarge(InputStream input, OutputStream output) throws IOException {
byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
long count = 0;
int n = 0;
while (-1 != (n = input.read(buffer))) {
output.write(buffer, 0, n);
count += n;
}
return count;
}
}