package com.sk89q.craftbook.bukkit;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Random;
import java.util.TreeMap;
import java.util.jar.JarFile;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.ZipEntry;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Chunk;
import org.bukkit.Server;
import org.bukkit.World;
import org.bukkit.command.CommandSender;
import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.event.block.SignChangeEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.plugin.java.JavaPlugin;
import com.sk89q.bukkit.util.CommandsManagerRegistration;
import com.sk89q.craftbook.CraftBookMechanic;
import com.sk89q.craftbook.LocalPlayer;
import com.sk89q.craftbook.bukkit.Metrics.Graph;
import com.sk89q.craftbook.bukkit.Metrics.Plotter;
import com.sk89q.craftbook.bukkit.commands.TopLevelCommands;
import com.sk89q.craftbook.bukkit.util.BukkitUtil;
import com.sk89q.craftbook.core.LanguageManager;
import com.sk89q.craftbook.core.st.MechanicClock;
import com.sk89q.craftbook.core.st.SelfTriggeringManager;
import com.sk89q.craftbook.mechanics.AIMechanic;
import com.sk89q.craftbook.mechanics.Ammeter;
import com.sk89q.craftbook.mechanics.BetterLeads;
import com.sk89q.craftbook.mechanics.BetterPhysics;
import com.sk89q.craftbook.mechanics.BetterPistons;
import com.sk89q.craftbook.mechanics.BetterPlants;
import com.sk89q.craftbook.mechanics.Bookcase;
import com.sk89q.craftbook.mechanics.BounceBlocks;
import com.sk89q.craftbook.mechanics.Chair;
import com.sk89q.craftbook.mechanics.ChunkAnchor;
import com.sk89q.craftbook.mechanics.CommandSigns;
import com.sk89q.craftbook.mechanics.CookingPot;
import com.sk89q.craftbook.mechanics.Elevator;
import com.sk89q.craftbook.mechanics.Footprints;
import com.sk89q.craftbook.mechanics.GlowStone;
import com.sk89q.craftbook.mechanics.HeadDrops;
import com.sk89q.craftbook.mechanics.HiddenSwitch;
import com.sk89q.craftbook.mechanics.JackOLantern;
import com.sk89q.craftbook.mechanics.LightStone;
import com.sk89q.craftbook.mechanics.LightSwitch;
import com.sk89q.craftbook.mechanics.MapChanger;
import com.sk89q.craftbook.mechanics.Marquee;
import com.sk89q.craftbook.mechanics.Netherrack;
import com.sk89q.craftbook.mechanics.PaintingSwitch;
import com.sk89q.craftbook.mechanics.Payment;
import com.sk89q.craftbook.mechanics.RedstoneJukebox;
import com.sk89q.craftbook.mechanics.Snow;
import com.sk89q.craftbook.mechanics.Sponge;
import com.sk89q.craftbook.mechanics.Teleporter;
import com.sk89q.craftbook.mechanics.TreeLopper;
import com.sk89q.craftbook.mechanics.XPStorer;
import com.sk89q.craftbook.mechanics.area.Area;
import com.sk89q.craftbook.mechanics.area.simple.Bridge;
import com.sk89q.craftbook.mechanics.area.simple.Door;
import com.sk89q.craftbook.mechanics.area.simple.Gate;
import com.sk89q.craftbook.mechanics.boat.Drops;
import com.sk89q.craftbook.mechanics.boat.LandBoats;
import com.sk89q.craftbook.mechanics.boat.Uncrashable;
import com.sk89q.craftbook.mechanics.boat.WaterPlaceOnly;
import com.sk89q.craftbook.mechanics.cauldron.ImprovedCauldron;
import com.sk89q.craftbook.mechanics.cauldron.legacy.Cauldron;
import com.sk89q.craftbook.mechanics.crafting.CustomCrafting;
import com.sk89q.craftbook.mechanics.dispenser.DispenserRecipes;
import com.sk89q.craftbook.mechanics.drops.CustomDrops;
import com.sk89q.craftbook.mechanics.drops.legacy.LegacyCustomDrops;
import com.sk89q.craftbook.mechanics.ic.ICMechanic;
import com.sk89q.craftbook.mechanics.items.CommandItemDefinition;
import com.sk89q.craftbook.mechanics.items.CommandItems;
import com.sk89q.craftbook.mechanics.minecart.CollisionEntry;
import com.sk89q.craftbook.mechanics.minecart.ConstantSpeed;
import com.sk89q.craftbook.mechanics.minecart.EmptyDecay;
import com.sk89q.craftbook.mechanics.minecart.EmptySlowdown;
import com.sk89q.craftbook.mechanics.minecart.FallModifier;
import com.sk89q.craftbook.mechanics.minecart.ItemPickup;
import com.sk89q.craftbook.mechanics.minecart.MobBlocker;
import com.sk89q.craftbook.mechanics.minecart.MoreRails;
import com.sk89q.craftbook.mechanics.minecart.NoCollide;
import com.sk89q.craftbook.mechanics.minecart.PlaceAnywhere;
import com.sk89q.craftbook.mechanics.minecart.RailPlacer;
import com.sk89q.craftbook.mechanics.minecart.TemporaryCart;
import com.sk89q.craftbook.mechanics.minecart.VisionSteering;
import com.sk89q.craftbook.mechanics.minecart.blocks.CartBooster;
import com.sk89q.craftbook.mechanics.minecart.blocks.CartDeposit;
import com.sk89q.craftbook.mechanics.minecart.blocks.CartDispenser;
import com.sk89q.craftbook.mechanics.minecart.blocks.CartEjector;
import com.sk89q.craftbook.mechanics.minecart.blocks.CartLift;
import com.sk89q.craftbook.mechanics.minecart.blocks.CartMaxSpeed;
import com.sk89q.craftbook.mechanics.minecart.blocks.CartMessenger;
import com.sk89q.craftbook.mechanics.minecart.blocks.CartReverser;
import com.sk89q.craftbook.mechanics.minecart.blocks.CartSorter;
import com.sk89q.craftbook.mechanics.minecart.blocks.CartStation;
import com.sk89q.craftbook.mechanics.minecart.blocks.CartTeleporter;
import com.sk89q.craftbook.mechanics.pipe.Pipes;
import com.sk89q.craftbook.mechanics.signcopier.SignCopier;
import com.sk89q.craftbook.mechanics.variables.VariableManager;
import com.sk89q.craftbook.util.CompatabilityUtil;
import com.sk89q.craftbook.util.ItemSyntax;
import com.sk89q.craftbook.util.RegexUtil;
import com.sk89q.craftbook.util.UUIDMappings;
import com.sk89q.craftbook.util.compat.companion.CompanionPlugins;
import com.sk89q.craftbook.util.persistent.PersistentStorage;
import com.sk89q.minecraft.util.commands.CommandException;
import com.sk89q.minecraft.util.commands.CommandPermissionsException;
import com.sk89q.minecraft.util.commands.CommandUsageException;
import com.sk89q.minecraft.util.commands.CommandsManager;
import com.sk89q.minecraft.util.commands.MissingNestedCommandException;
import com.sk89q.minecraft.util.commands.SimpleInjector;
import com.sk89q.minecraft.util.commands.WrappedCommandException;
import com.sk89q.util.yaml.YAMLFormat;
import com.sk89q.util.yaml.YAMLProcessor;
import com.sk89q.wepif.PermissionsResolverManager;
public class CraftBookPlugin extends JavaPlugin {
/**
* Companion Plugins for CraftBook.
*/
public static CompanionPlugins plugins;
/**
* The instance for CraftBook
*/
private static CraftBookPlugin instance;
/**
* The language manager
*/
private LanguageManager languageManager;
/**
* The random
*/
private Random random;
/**
* Manager for commands. This automatically handles nested commands,
* permissions checking, and a number of other fancy command things.
* We just set it up and register commands against it.
*/
private CommandsManager<CommandSender> commands;
/**
* Handles all configuration.
*/
private BukkitConfiguration config;
/**
* The adapter for events to the manager.
*/
MechanicListenerAdapter managerAdapter;
/**
* The MechanicClock that manages all Self-Triggering Components.
*/
private MechanicClock mechanicClock;
/**
* The persistent storage database of CraftBook.
*/
protected PersistentStorage persistentStorage;
/**
* The UUID Mappings for CraftBook.
*/
protected UUIDMappings uuidMappings;
/**
* List of common mechanics.
*/
private List<CraftBookMechanic> mechanics;
/**
* The manager for SelfTriggering components.
*/
private SelfTriggeringManager selfTriggerManager;
public static final Map<String, Class<? extends CraftBookMechanic>> availableMechanics;
static {
availableMechanics = new TreeMap<String, Class<? extends CraftBookMechanic>>();
availableMechanics.put("Variables", VariableManager.class);
availableMechanics.put("CommandItems", CommandItems.class);
availableMechanics.put("CustomCrafting", CustomCrafting.class);
availableMechanics.put("DispenserRecipes", DispenserRecipes.class);
availableMechanics.put("Snow", Snow.class);
availableMechanics.put("CustomDrops", CustomDrops.class);
availableMechanics.put("LegacyCustomDrops", LegacyCustomDrops.class);
availableMechanics.put("AI", AIMechanic.class);
availableMechanics.put("PaintingSwitcher", PaintingSwitch.class);
availableMechanics.put("BetterPhysics", BetterPhysics.class);
availableMechanics.put("HeadDrops", HeadDrops.class);
availableMechanics.put("BetterLeads", BetterLeads.class);
availableMechanics.put("Marquee", Marquee.class);
availableMechanics.put("TreeLopper", TreeLopper.class);
availableMechanics.put("MapChanger", MapChanger.class);
availableMechanics.put("XPStorer", XPStorer.class);
availableMechanics.put("LightStone", LightStone.class);
availableMechanics.put("CommandSigns", CommandSigns.class);
availableMechanics.put("LightSwitch", LightSwitch.class);
availableMechanics.put("ChunkAnchor", ChunkAnchor.class);
availableMechanics.put("Ammeter", Ammeter.class);
availableMechanics.put("HiddenSwitch", HiddenSwitch.class);
availableMechanics.put("Bookcase", Bookcase.class);
availableMechanics.put("SignCopier", SignCopier.class);
availableMechanics.put("Bridge", Bridge.class);
availableMechanics.put("Door", Door.class);
availableMechanics.put("Elevator", Elevator.class);
availableMechanics.put("Teleporter", Teleporter.class);
availableMechanics.put("ToggleArea", Area.class);
availableMechanics.put("Cauldron", ImprovedCauldron.class);
availableMechanics.put("LegacyCauldron", Cauldron.class);
availableMechanics.put("Gate", Gate.class);
availableMechanics.put("BetterPistons", BetterPistons.class);
availableMechanics.put("CookingPot", CookingPot.class);
availableMechanics.put("Sponge", Sponge.class);
availableMechanics.put("BetterPlants", BetterPlants.class);
availableMechanics.put("Chairs", Chair.class);
availableMechanics.put("Footprints", Footprints.class);
availableMechanics.put("Pay", Payment.class);
availableMechanics.put("Jukebox", RedstoneJukebox.class);
availableMechanics.put("Glowstone", GlowStone.class);
availableMechanics.put("Netherrack", Netherrack.class);
availableMechanics.put("JackOLantern", JackOLantern.class);
availableMechanics.put("Pipes", Pipes.class);
availableMechanics.put("BounceBlocks", BounceBlocks.class);
availableMechanics.put("ICs", ICMechanic.class);
availableMechanics.put("MinecartBooster", CartBooster.class);
availableMechanics.put("MinecartReverser", CartReverser.class);
availableMechanics.put("MinecartSorter", CartSorter.class);
availableMechanics.put("MinecartStation", CartStation.class);
availableMechanics.put("MinecartEjector", CartEjector.class);
availableMechanics.put("MinecartDeposit", CartDeposit.class);
availableMechanics.put("MinecartTeleporter", CartTeleporter.class);
availableMechanics.put("MinecartElevator", CartLift.class);
availableMechanics.put("MinecartDispenser", CartDispenser.class);
availableMechanics.put("MinecartMessenger", CartMessenger.class);
availableMechanics.put("MinecartMaxSpeed", CartMaxSpeed.class);
availableMechanics.put("MinecartMoreRails", MoreRails.class);
availableMechanics.put("MinecartRemoveEntities", com.sk89q.craftbook.mechanics.minecart.RemoveEntities.class);
availableMechanics.put("MinecartVisionSteering", VisionSteering.class);
availableMechanics.put("MinecartDecay", EmptyDecay.class);
availableMechanics.put("MinecartMobBlocker", MobBlocker.class);
availableMechanics.put("MinecartExitRemover", com.sk89q.craftbook.mechanics.minecart.ExitRemover.class);
availableMechanics.put("MinecartCollisionEntry", CollisionEntry.class);
availableMechanics.put("MinecartItemPickup", ItemPickup.class);
availableMechanics.put("MinecartFallModifier", FallModifier.class);
availableMechanics.put("MinecartConstantSpeed", ConstantSpeed.class);
availableMechanics.put("MinecartRailPlacer", RailPlacer.class);
availableMechanics.put("MinecartSpeedModifiers", com.sk89q.craftbook.mechanics.minecart.SpeedModifiers.class);
availableMechanics.put("MinecartEmptySlowdown", EmptySlowdown.class);
availableMechanics.put("MinecartRailPlacer", RailPlacer.class);
availableMechanics.put("MinecartNoCollide", NoCollide.class);
availableMechanics.put("MinecartPlaceAnywhere", PlaceAnywhere.class);
availableMechanics.put("MinecartTemporaryCart", TemporaryCart.class);
availableMechanics.put("BoatRemoveEntities", com.sk89q.craftbook.mechanics.boat.RemoveEntities.class);
availableMechanics.put("BoatUncrashable", Uncrashable.class);
availableMechanics.put("BoatDrops", Drops.class);
availableMechanics.put("BoatDecay", com.sk89q.craftbook.mechanics.boat.EmptyDecay.class);
availableMechanics.put("BoatSpeedModifiers", com.sk89q.craftbook.mechanics.boat.SpeedModifiers.class);
availableMechanics.put("LandBoats", LandBoats.class);
availableMechanics.put("BoatExitRemover", com.sk89q.craftbook.mechanics.boat.ExitRemover.class);
availableMechanics.put("BoatWaterPlaceOnly", WaterPlaceOnly.class);
}
/**
* Construct objects. Actual loading occurs when the plugin is enabled, so
* this merely instantiates the objects.
*/
public CraftBookPlugin() {
super();
// Set the instance
instance = this;
}
public static String getVersion() {
return "3.9b4";
}
/**
* Gets the build equivalent of the last stable version.
*
* @return the build number
*/
public static String getStableBuild() {
return "3895";
}
public static int getUpdaterID() {
return 31055;
}
public List<CraftBookMechanic> getMechanics() {
return mechanics;
}
public boolean isMechanicEnabled(Class<? extends CraftBookMechanic> clazz) {
for(CraftBookMechanic mech : mechanics) {
if(mech.getClass().equals(clazz))
return true;
}
return false;
}
public CraftBookMechanic getMechanic(Class<? extends CraftBookMechanic> clazz) {
for(CraftBookMechanic mech : mechanics) {
if(mech.getClass().equals(clazz))
return mech;
}
return null;
}
/**
* Retrieve the UUID Mappings system of CraftBook.
*
* @return The UUID Mappings System.
*/
public UUIDMappings getUUIDMappings() {
return uuidMappings;
}
/**
* Called on plugin enable.
*/
@Override
public void onEnable() {
ItemSyntax.plugin = this;
plugins = new CompanionPlugins();
plugins.initiate(this);
// Need to create the plugins/CraftBook folder
getDataFolder().mkdirs();
// Setup Config and the Commands Manager
createDefaultConfiguration(new File(getDataFolder(), "config.yml"), "config.yml");
config = new BukkitConfiguration(new YAMLProcessor(new File(getDataFolder(), "config.yml"), true, YAMLFormat.EXTENDED), logger());
// Load the configuration
try {
config.load();
} catch (Throwable e) {
getLogger().severe("Failed to load CraftBook Configuration File! Is it corrupt?");
getLogger().severe(getStackTrace(e));
getLogger().severe("Disabling CraftBook due to invalid Configuration File!");
getServer().getPluginManager().disablePlugin(this);
return;
}
persistentStorage = PersistentStorage.createFromType(getConfiguration().persistentStorageType);
if(persistentStorage != null)
persistentStorage.open();
uuidMappings = new UUIDMappings();
uuidMappings.enable();
logDebugMessage("Initializing Managers!", "startup");
managerAdapter = new MechanicListenerAdapter();
mechanicClock = new MechanicClock();
selfTriggerManager = new SelfTriggeringManager();
logDebugMessage("Initializing Permission!", "startup");
PermissionsResolverManager.initialize(this);
// Register command classes
logDebugMessage("Initializing Commands!", "startup");
commands = new CommandsManager<CommandSender>() {
@Override
public boolean hasPermission(CommandSender player, String perm) {
return CraftBookPlugin.inst().hasPermission(player, perm);
}
};
// Set the proper command injector
commands.setInjector(new SimpleInjector(this));
final CommandsManagerRegistration reg = new CommandsManagerRegistration(this, commands);
reg.register(TopLevelCommands.class);
if(config.realisticRandoms)
try {
random = SecureRandom.getInstance("SHA1PRNG");
} catch (NoSuchAlgorithmException e1) {
getLogger().severe(getStackTrace(e1));
random = new Random();
}
else
random = new Random();
// Let's start the show
setupCraftBook();
registerGlobalEvents();
getServer().getPluginManager().registerEvents(new Listener() {
/* Bukkit Bug Fixes */
@EventHandler(priority = EventPriority.LOWEST)
public void signChange(SignChangeEvent event) {
for(int i = 0; i < event.getLines().length; i++) {
StringBuilder builder = new StringBuilder();
for (char c : event.getLine(i).toCharArray()) {
if (c < 0xF700 || c > 0xF747) {
builder.append(c);
}
}
String fixed = builder.toString();
if(!fixed.equals(event.getLine(i)))
event.setLine(i, fixed);
}
}
/* Alerts */
@EventHandler(priority = EventPriority.HIGH)
public void playerJoin(PlayerJoinEvent event) {
if(!event.getPlayer().isOp()) return;
boolean foundAMech = false;
for(CraftBookMechanic mech : getMechanics())
if(!(mech instanceof VariableManager)) {
foundAMech = true;
break;
}
if(!foundAMech) {
event.getPlayer().sendMessage(ChatColor.RED + "[CraftBook] Warning! You have no mechanics enabled, the plugin will appear to do nothing until a feature is enabled!");
}
}
}, this);
boolean foundAMech = false;
for(CraftBookMechanic mech : getMechanics())
if(!(mech instanceof VariableManager)) {
foundAMech = true;
break;
}
if(!foundAMech) {
Bukkit.getScheduler().runTaskTimer(this, new Runnable() {
@Override
public void run () {
getLogger().warning(ChatColor.RED + "Warning! You have no mechanics enabled, the plugin will appear to do nothing until a feature is enabled!");
}
}, 20L, 20*60*5);
}
}
public boolean updateAvailable = false;
String latestVersion = null;
long updateSize = 0;
public String getLatestVersion() {
return latestVersion;
}
public boolean isUpdateAvailable() {
return updateAvailable;
}
YAMLProcessor mechanismsConfig;
/**
* Register basic things to the plugin. For example, languages.
*/
public void setupCraftBook() {
if(config.debugLogToFile) {
try {
debugLogger = new PrintWriter(new File(getDataFolder(), "debug.log"));
} catch (FileNotFoundException e1) {
e1.printStackTrace();
}
}
// Initialize the language manager.
logDebugMessage("Initializing Languages!", "startup");
languageManager = new LanguageManager();
languageManager.init();
getServer().getScheduler().runTask(this, new Runnable() {
@Override
public void run () {
CompatabilityUtil.init();
}
});
mechanics = new ArrayList<CraftBookMechanic>();
logDebugMessage("Initializing Mechanisms!", "startup");
createDefaultConfiguration(new File(getDataFolder(), "mechanisms.yml"), "mechanisms.yml");
mechanismsConfig = new YAMLProcessor(new File(getDataFolder(), "mechanisms.yml"), true, YAMLFormat.EXTENDED);
try {
mechanismsConfig.load();
} catch (IOException e) {
e.printStackTrace();
}
mechanismsConfig.setWriteDefaults(true);
mechanismsConfig.setHeader(
"# CraftBook Mechanism Configuration. Generated for version: " + (CraftBookPlugin.inst() == null ? CraftBookPlugin.getVersion() : CraftBookPlugin.inst().getDescription().getVersion()),
"# This configuration will automatically add new configuration options for you,",
"# So there is no need to regenerate this configuration unless you need to.",
"# More information about these features are available at...",
"# " + CraftBookPlugin.getWikiDomain() + "/Usage",
"#",
"# NOTE! MAKE SURE TO ENABLE FEATURES IN THE config.yml FILE!",
"");
for(String enabled : config.enabledMechanics) {
Class<? extends CraftBookMechanic> mechClass = availableMechanics.get(enabled);
try {
if(mechClass != null) {
CraftBookMechanic mech = mechClass.newInstance();
mech.loadConfiguration(mechanismsConfig, "mechanics." + enabled + ".");
mechanics.add(mech);
}
} catch (Throwable t) {
getLogger().log(Level.WARNING, "Failed to load mechanic: " + enabled, t);
}
}
mechanismsConfig.save();
Iterator<CraftBookMechanic> iter = mechanics.iterator();
while(iter.hasNext()) {
CraftBookMechanic mech = iter.next();
try {
if(!mech.enable()) {
getLogger().warning("Failed to enable mechanic: " + mech.getClass().getSimpleName());
mech.disable();
iter.remove();
continue;
}
getServer().getPluginManager().registerEvents(mech, this);
} catch(Throwable t) {
getLogger().log(Level.WARNING, "Failed to enable mechanic: " + mech.getClass().getSimpleName(), t);
}
}
setupSelfTriggered();
}
/**
* Enables the mechanic with the specified name.
*
* @param string The name of the mechanic.
* @return If the mechanic could be found and enabled.
*/
public boolean enableMechanic(String mechanic) {
try {
mechanismsConfig.load();
} catch (IOException e) {
e.printStackTrace();
}
mechanismsConfig.setHeader(
"# CraftBook Mechanism Configuration. Generated for version: " + (CraftBookPlugin.inst() == null ? CraftBookPlugin.getVersion() : CraftBookPlugin.inst().getDescription().getVersion()),
"# This configuration will automatically add new configuration options for you,",
"# So there is no need to regenerate this configuration unless you need to.",
"# More information about these features are available at...",
"# " + CraftBookPlugin.getWikiDomain() + "/Usage",
"#",
"# NOTE! MAKE SURE TO ENABLE FEATURES IN THE config.yml FILE!",
"");
Class<? extends CraftBookMechanic> mechClass = availableMechanics.get(mechanic);
try {
if(mechClass != null) {
CraftBookMechanic mech = mechClass.newInstance();
mech.loadConfiguration(mechanismsConfig, "mechanics." + mechanic + ".");
mechanics.add(mech);
if(!mech.enable()) {
getLogger().warning("Failed to enable mechanic: " + mech.getClass().getSimpleName());
mech.disable();
return false;
}
getServer().getPluginManager().registerEvents(mech, this);
} else
return false;
} catch (Throwable t) {
getLogger().log(Level.WARNING, "Failed to load mechanic: " + mechanic, t);
return false;
}
mechanismsConfig.save();
config.save();
return true;
}
/**
* Disables the mechanic with the specified name.
*
* @param string The name of the mechanic.
* @return If the mechanic could be found and disabled.
*/
public boolean disableMechanic(String mechanic) {
Class<? extends CraftBookMechanic> mechClass = availableMechanics.get(mechanic);
if(mechClass == null) return false;
boolean found = false;
for(CraftBookMechanic mech : mechanics) {
if(mech.getClass().equals(mechClass)) {
found = true;
break;
}
}
if(!found) return false;
config.enabledMechanics.remove(mechanic);
config.save();
return true;
}
/**
* Registers events used by the main CraftBook plugin. Also registers PluginMetrics
*/
public void registerGlobalEvents() {
logDebugMessage("Registring managers!", "startup");
getServer().getPluginManager().registerEvents(managerAdapter, inst());
if(getConfiguration().updateNotifier) {
logDebugMessage("Performing update checks!", "startup");
checkForUpdates();
}
if(getConfiguration().easterEggs) {
Bukkit.getScheduler().runTaskLater(this, new Runnable() {
@Override
public void run () {
logDebugMessage("Checking easter eggs!", "startup");
Calendar date = Calendar.getInstance();
if(date.get(Calendar.MONTH) == Calendar.JUNE && date.get(Calendar.DAY_OF_MONTH) == 22) //Me4502 reddit cakeday
getLogger().info("Happy " + formatDate(date.get(Calendar.YEAR) - 2012) + " reddit cakeday me4502!");
else if(date.get(Calendar.MONTH) == Calendar.OCTOBER && date.get(Calendar.DAY_OF_MONTH) == 16) //Me4502 birthday
getLogger().info("Happy birthday me4502!");
else if(date.get(Calendar.MONTH) == Calendar.JANUARY && date.get(Calendar.DAY_OF_MONTH) == 1) //New Years
getLogger().info("Happy new years! Happy " + date.get(Calendar.YEAR) + "!!!");
else if(date.get(Calendar.MONTH) == Calendar.OCTOBER && date.get(Calendar.DAY_OF_MONTH) == 22) //CraftBook birthday
getLogger().info("Happy " + formatDate(date.get(Calendar.YEAR) - 2010) + " birthday CraftBook!");
else if(date.get(Calendar.MONTH) == Calendar.APRIL && date.get(Calendar.DAY_OF_MONTH) == 24) //Me4502ian CraftBook birthday
getLogger().info("CraftBook has been under Me4502's 'harsh dictatorship :P' for " + (date.get(Calendar.YEAR) - 2012) + " year(s) today!");
}
private String formatDate(int date) {
if (String.valueOf(date).endsWith("1"))
return date + "st";
else if (String.valueOf(date).endsWith("2"))
return date + "nd";
else if (String.valueOf(date).endsWith("3"))
return date + "rd";
else
return date + "th";
}
}, 20L);
}
try {
logDebugMessage("Initializing Metrics!", "startup");
Metrics metrics = new Metrics(this);
metrics.start();
Graph languagesGraph = metrics.createGraph("Language");
for (String language : languageManager.getLanguages()) {
languagesGraph.addPlotter(new Plotter(language) {
@Override
public int getValue () {
return 1;
}
});
}
languagesGraph.addPlotter(new Plotter("Total") {
@Override
public int getValue () {
return languageManager.getLanguages().size();
}
});
Graph mechanicsGraph = metrics.createGraph("Enabled Mechanics");
for(CraftBookMechanic mech : getMechanics()) {
mechanicsGraph.addPlotter(new Plotter(mech.getClass().getSimpleName()) {
@Override
public int getValue () {
return 1;
}
});
}
} catch (Throwable e1) {
BukkitUtil.printStacktrace(e1);
}
}
public void checkForUpdates() {
boolean exempt = false;
try {
int ver = Integer.parseInt(getDescription().getVersion().split(":")[1].split("-")[0]);
if (ver < 1541) //Not valid prior to this version.
exempt = true;
}
catch(Exception e) {
exempt = true;
}
if(!exempt) {
final Updater updater = new Updater(this, getUpdaterID(), getFile(), Updater.UpdateType.NO_DOWNLOAD, true); // Start Updater but just do a version check
updateAvailable = updater.getResult() == Updater.UpdateResult.UPDATE_AVAILABLE; // Determine if there is an update ready for us
latestVersion = updater.getLatestName();
getLogger().info(latestVersion + " is the latest version available, and the updatability of it is: " + updater.getResult().name() + ". You currently have version " + getLatestVersion() + " installed.");
if(updateAvailable) {
for (Player player : getServer().getOnlinePlayers()) {
if (hasPermission(player, "craftbook.update")) {
player.sendMessage(ChatColor.YELLOW + "An update is available: " + latestVersion + "(" + updateSize + " bytes)");
player.sendMessage(ChatColor.YELLOW + "Type /cb update if you would like to update.");
}
}
getServer().getPluginManager().registerEvents(new Listener() {
@EventHandler
public void onPlayerJoin (PlayerJoinEvent event) {
Player player = event.getPlayer();
if (hasPermission(player, "craftbook.update")) {
player.sendMessage(ChatColor.YELLOW + "An update is available: " + latestVersion + "(" + updateSize + " bytes)");
player.sendMessage(ChatColor.YELLOW + "Type /cb update if you would like to update.");
}
}
}, CraftBookPlugin.inst());
}
} else {
getLogger().info("The Auto-Updater is disabled for your version!");
}
}
/**
* Called on plugin disable.
*/
@Override
public void onDisable() {
if(languageManager != null)
languageManager.close();
if(mechanics != null) {
for(CraftBookMechanic mech : mechanics)
mech.disable();
mechanics = null;
}
if(hasPersistentStorage())
getPersistentStorage().close();
if(uuidMappings != null)
uuidMappings.disable();
}
/**
* Handle a command.
*/
@Override
public boolean onCommand(CommandSender sender, org.bukkit.command.Command cmd, String label,
String[] args) {
try {
commands.execute(cmd.getName(), args, sender, sender);
} catch (CommandPermissionsException e) {
sender.sendMessage(ChatColor.RED + "You don't have permission.");
} catch (MissingNestedCommandException e) {
sender.sendMessage(ChatColor.RED + e.getUsage());
} catch (CommandUsageException e) {
sender.sendMessage(ChatColor.RED + e.getMessage());
sender.sendMessage(ChatColor.RED + e.getUsage());
} catch (WrappedCommandException e) {
if (e.getCause() instanceof NumberFormatException) {
sender.sendMessage(ChatColor.RED + "Number expected, string received instead.");
} else {
sender.sendMessage(ChatColor.RED + "An error has occurred. See console.");
e.printStackTrace();
}
} catch (CommandException e) {
sender.sendMessage(ChatColor.RED + e.getMessage());
}
return true;
}
/**
* This retrieves the CraftBookPlugin instance for static access.
*
* @return Returns a CraftBookPlugin
*/
public static CraftBookPlugin inst() {
return instance;
}
public static void setInstance(CraftBookPlugin instance) throws IllegalArgumentException {
if(CraftBookPlugin.instance != null)
throw new IllegalArgumentException("Instance already set!");
CraftBookPlugin.instance = instance;
}
/**
* This retrieves the CraftBookPlugin logger.
*
* @return Returns the CraftBookPlugin {@link Logger}
*/
public static Logger logger() {
return inst().getLogger();
}
/**
* This retrieves the CraftBookPlugin server.
*
* @return Returns the CraftBookPlugin {@link Server}
*/
public static Server server() {
return inst().getServer();
}
/**
* Setup the required components of self-triggered Mechanics.
*/
private void setupSelfTriggered() {
getLogger().info("Enumerating chunks for self-triggered components...");
long start = System.currentTimeMillis();
int numWorlds = 0;
int numChunks = 0;
for (World world : getServer().getWorlds()) {
for (Chunk chunk : world.getLoadedChunks()) {
getSelfTriggerManager().registerSelfTrigger(chunk);
numChunks++;
}
numWorlds++;
}
long time = System.currentTimeMillis() - start;
getLogger().info(numChunks + " chunk(s) for " + numWorlds + " world(s) processed " + "(" + time + "ms elapsed)");
// Set up the clock for self-triggered ICs.
getServer().getScheduler().runTaskTimer(this, mechanicClock, 0, getConfiguration().stThinkRate);
}
/**
* This is a method used to register events for a class under CraftBook.
*/
public static void registerEvents(Listener ... listeners) {
for(Listener listener : listeners)
inst().getServer().getPluginManager().registerEvents(listener, inst());
}
/**
* This is a method used to register commands for a class.
*/
public void registerCommands(Class<?> clazz) {
final CommandsManagerRegistration reg = new CommandsManagerRegistration(this, commands);
reg.register(clazz);
}
/**
* Get the global ConfigurationManager.
* Use this to access global configuration values and per-world configuration values.
*
* @return The global ConfigurationManager
*/
public BukkitConfiguration getConfiguration() {
return config;
}
/**
* This method is used to get the CraftBook {@link LanguageManager}.
*
* @return The CraftBook {@link LanguageManager}
*/
public LanguageManager getLanguageManager() {
return languageManager;
}
/**
* This method is used to get CraftBook's {@link Random}.
*
* @return CraftBook's {@link Random}
*/
public Random getRandom() {
if(random == null)
return new Random(); //Use a temporary random whilst CraftBooks random is being set.
return random;
}
/**
* Check whether a player is in a group.
* This calls the corresponding method in PermissionsResolverManager
*
* @param player The player to check
* @param group The group
*
* @return whether {@code player} is in {@code group}
*/
public boolean inGroup(Player player, String group) {
try {
return PermissionsResolverManager.getInstance().inGroup(player, group);
} catch (Throwable t) {
t.printStackTrace();
return false;
}
}
/**
* Get the groups of a player.
* This calls the corresponding method in PermissionsResolverManager.
*
* @param player The player to check
*
* @return The names of each group the playe is in.
*/
public String[] getGroups(Player player) {
try {
return PermissionsResolverManager.getInstance().getGroups(player);
} catch (Throwable t) {
t.printStackTrace();
return new String[0];
}
}
/**
* Gets the name of a command sender. This is a unique name and this
* method should never return a "display name".
*
* @param sender The sender to get the name of
*
* @return The unique name of the sender.
*/
public String toUniqueName(CommandSender sender) {
if (sender instanceof ConsoleCommandSender) {
return "*Console*";
} else {
return sender.getName();
}
}
/**
* Gets the name of a command sender. This play be a display name.
*
* @param sender The CommandSender to get the name of.
*
* @return The name of the given sender
*/
public String toName(CommandSender sender) {
if (sender instanceof ConsoleCommandSender) {
return "*Console*";
} else if (sender instanceof Player) {
return ((Player) sender).getDisplayName();
} else {
return sender.getName();
}
}
/**
* Checks permissions.
*
* @param sender The sender to check the permission on.
* @param perm The permission to check the permission on.
*
* @return whether {@code sender} has {@code perm}
*/
public boolean hasPermission(CommandSender sender, String perm) {
if (sender.isOp()) {
if (sender instanceof Player) {
if (!getConfiguration().noOpPermissions) return true;
} else {
return true;
}
}
// Invoke the permissions resolver
if (sender instanceof Player) {
Player player = (Player) sender;
return PermissionsResolverManager.getInstance().hasPermission(player.getWorld().getName(), player.getName(), perm);
}
return false;
}
/**
* Checks permissions and throws an exception if permission is not met.
*
* @param sender The sender to check the permission on.
* @param perm The permission to check the permission on.
*
* @throws CommandPermissionsException if {@code sender} doesn't have {@code perm}
*/
public void checkPermission(CommandSender sender, String perm)
throws CommandPermissionsException {
if (!hasPermission(sender, perm)) {
throw new CommandPermissionsException();
}
}
/**
* Checks to see if the sender is a player, otherwise throw an exception.
*
* @param sender The {@link CommandSender} to check
*
* @return {@code sender} casted to a player
*
* @throws CommandException if {@code sender} isn't a {@link Player}
*/
public Player checkPlayer(CommandSender sender)
throws CommandException {
if (sender instanceof Player) {
return (Player) sender;
} else {
throw new CommandException("A player is expected.");
}
}
/**
* Wrap a player as a LocalPlayer.
*
* @param player The player to wrap
*
* @return The wrapped player
*/
public LocalPlayer wrapPlayer(Player player) {
return new BukkitPlayer(this, player);
}
/**
* Grabs the manager for self triggered components.
*/
public SelfTriggeringManager getSelfTriggerManager() {
return selfTriggerManager;
}
/**
* Reload configuration
*/
public void reloadConfiguration() throws Throwable {
if(mechanics != null)
for(CraftBookMechanic mech : mechanics)
mech.disable();
mechanics = null;
getServer().getScheduler().cancelTasks(inst());
HandlerList.unregisterAll(inst());
if(config.debugLogToFile) {
debugLogger.close();
debugLogger = null;
}
config.load();
managerAdapter = new MechanicListenerAdapter();
mechanicClock = new MechanicClock();
setupCraftBook();
registerGlobalEvents();
}
/**
* Create a default configuration file from the .jar.
*
* @param actual The destination file
* @param defaultName The name of the file inside the jar's defaults folder
* @param force If it should make the file even if it already exists
*/
public void createDefaultConfiguration(File actual, String defaultName) {
// Make parent directories
File parent = actual.getParentFile();
if (!parent.exists()) {
parent.mkdirs();
}
if (actual.exists()) {
return;
}
InputStream input = null;
JarFile file = null;
try {
file = new JarFile(getFile());
ZipEntry copy = file.getEntry("defaults/" + defaultName);
if (copy == null) {
file.close();
throw new FileNotFoundException();
}
input = file.getInputStream(copy);
} catch (IOException e) {
getLogger().severe("Unable to read default configuration: " + defaultName);
}
if (input != null) {
FileOutputStream output = null;
try {
output = new FileOutputStream(actual);
byte[] buf = new byte[8192];
int length = 0;
while ((length = input.read(buf)) > 0) {
output.write(buf, 0, length);
}
getLogger().info("Default configuration file written: " + actual.getAbsolutePath());
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
file.close();
} catch (IOException ignored) {
}
try {
input.close();
} catch (IOException ignore) {
}
try {
if (output != null) {
output.close();
}
} catch (IOException ignore) {
}
}
} else if (file != null)
try {
file.close();
} catch (IOException ignored) {
}
}
@Override
public File getFile() {
return super.getFile();
}
public static String getStackTrace(Throwable ex) {
Writer out = new StringWriter();
PrintWriter pw = new PrintWriter(out);
ex.printStackTrace(pw);
return out.toString();
}
public static boolean isDebugFlagEnabled(String flag) {
if(inst() == null) return false;
if(!inst().getConfiguration().debugMode || inst().getConfiguration().debugFlags == null || inst().getConfiguration().debugFlags.isEmpty())
return false;
String[] flagBits = RegexUtil.PERIOD_PATTERN.split(flag);
String tempFlag = "";
for(int i = 0; i < flagBits.length; i++) {
if(i == 0)
tempFlag = flagBits[i];
else
tempFlag = tempFlag + "." + flagBits[i];
for(String testflag : inst().getConfiguration().debugFlags) {
if(testflag.toLowerCase(Locale.ENGLISH).equals(tempFlag))
return true;
}
}
return false;
}
private static PrintWriter debugLogger;
public static void logDebugMessage(String message, String code) {
if(!isDebugFlagEnabled(code))
return;
logger().info("[Debug][" + code + "] " + message);
if(CraftBookPlugin.inst().getConfiguration().debugLogToFile)
debugLogger.println("[" + code + "] " + message);
}
public boolean hasPersistentStorage() {
return persistentStorage != null && persistentStorage.isValid();
}
public PersistentStorage getPersistentStorage() {
return persistentStorage;
}
public void setPersistentStorage(PersistentStorage storage) {
persistentStorage = storage;
getConfiguration().persistentStorageType = storage.getType();
getConfiguration().config.setProperty("persistent-storage-type", storage.getType());
getConfiguration().config.save();
}
/**
* Parses more advanced portions of the Item Syntax.
*
* @param item The item to parse
* @return The parsed string. (Can be the same, and should be if nothing found)
*/
public String parseItemSyntax(String item) {
if(CommandItems.INSTANCE != null) {
CommandItemDefinition def = CommandItems.INSTANCE.getDefinitionByName(item);
if(def != null) {
return ItemSyntax.getStringFromItem(def.getItem());
}
}
return item;
}
public static String getWikiDomain() {
return "http://wiki.sk89q.com/wiki/CraftBook";
}
}