package me.desht.chesscraft;
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import me.desht.chesscraft.chess.*;
import me.desht.chesscraft.chess.ai.AIFactory;
import me.desht.chesscraft.citizens.CitizensUtil;
import me.desht.chesscraft.commands.*;
import me.desht.chesscraft.listeners.*;
import me.desht.chesscraft.results.Results;
import me.desht.chesscraft.util.EconomyUtil;
import me.desht.dhutils.*;
import me.desht.dhutils.block.MaterialWithData;
import me.desht.dhutils.commands.CommandManager;
import me.desht.dhutils.nms.NMSHelper;
import me.desht.dhutils.responsehandler.ResponseHandler;
import me.desht.scrollingmenusign.ScrollingMenuSign;
import net.milkbowl.vault.economy.Economy;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.configuration.serialization.ConfigurationSerialization;
import org.bukkit.entity.Entity;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.RegisteredServiceProvider;
import org.bukkit.plugin.java.JavaPlugin;
import org.dynmap.DynmapAPI;
import org.mcstats.Metrics;
import org.mcstats.Metrics.Plotter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class ChessCraft extends JavaPlugin implements ConfigurationListener, PluginVersionListener {
private static ChessCraft instance;
private WorldEditPlugin worldEditPlugin;
private ChessPersistence persistence;
public final ResponseHandler responseHandler = new ResponseHandler(this);
private final CommandManager cmds = new CommandManager(this);
private final PlayerTracker tracker = new PlayerTracker();
private ConfigurationManager configManager;
private ChessFlightListener flightListener;
private SMSIntegration sms;
private ChessTickTask tickTask;
private SpecialFX fx;
private boolean startupFailed = false;
private DynmapIntegration dynmapIntegration;
private boolean protocolLibEnabled;
private boolean citizensEnabled;
private LandslideIntegration landslideIntegration;
@Override
public void onLoad() {
ConfigurationSerialization.registerClass(BoardView.class);
ConfigurationSerialization.registerClass(ChessGame.class);
ConfigurationSerialization.registerClass(TimeControl.class);
ConfigurationSerialization.registerClass(PersistableLocation.class);
}
@Override
public void onEnable() {
setInstance(this);
LogUtils.init(this);
configManager = new ConfigurationManager(this, this);
Debugger.getInstance().setPrefix("[ChessCraft] ");
Debugger.getInstance().setLevel(getConfig().getInt("debug_level"));
Debugger.getInstance().setTarget(getServer().getConsoleSender());
try {
NMSHelper.init(this);
} catch (Exception e) {
e.printStackTrace();
String url = getDescription().getWebsite();
LogUtils.severe("ChessCraft version " + getDescription().getVersion() + " is not compatible with this CraftBukkit version.");
LogUtils.severe("Check " + url + " for information on updated builds.");
LogUtils.severe("Plugin disabled.");
startupFailed = true;
setEnabled(false);
return;
}
MiscUtil.init(this);
MiscUtil.setColouredConsole(getConfig().getBoolean("coloured_console"));
new PluginVersionChecker(this, this);
DirectoryStructure.setup(this);
Messages.init(getConfig().getString("locale", "default"));
persistence = new ChessPersistence();
// This is just here so the results DB stuff gets loaded at startup
// time - easier to test that way. Remove it for production.
// Results.getResultsHandler().addTestData();
// this will cause saved results data to start being pulled in (async)
Results.getResultsHandler();
PluginManager pm = getServer().getPluginManager();
setupVault(pm);
setupSMS(pm);
setupWorldEdit(pm);
setupDynmap(pm);
setupCitizens2(pm);
setupProtocolLib(pm);
setupLandslide(pm);
new ChessPlayerListener(this);
new ChessBlockListener(this);
new ChessEntityListener(this);
new ChessWorldListener(this);
flightListener = new ChessFlightListener(this);
flightListener.setEnabled(getConfig().getBoolean("flying.allowed"));
registerCommands();
MessagePager.setPageCmd("/chess page [#|n|p]");
MessagePager.setDefaultPageSize(getConfig().getInt("pager.lines", 0));
fx = new SpecialFX(getConfig().getConfigurationSection("effects"));
persistence.reload();
if (sms != null) {
sms.setAutosave(true);
}
if (dynmapIntegration != null && dynmapIntegration.isEnabled()) {
dynmapIntegration.setActive(true);
}
if (isProtocolLibEnabled()) {
ProtocolLibIntegration.registerPacketHandler(this);
ProtocolLibIntegration.setEntityVolume(getConfig().getDouble("entity_volume"));
}
tickTask = new ChessTickTask();
tickTask.runTaskTimer(this, 20L, 20L);
setupMetrics();
Debugger.getInstance().debug("Version " + getDescription().getVersion() + " enable complete");
}
@Override
public void onDisable() {
// nothing to shut down if we couldn't even start up
if (startupFailed) return;
tickTask.cancel();
flightListener.restoreSpeeds();
ChessGameManager gm = ChessGameManager.getManager();
AIFactory.getInstance().clearDown();
for (ChessGame game : gm.listGames()) {
game.tick();
}
getServer().getScheduler().cancelTasks(this);
persistence.save();
List<BoardView> views = new ArrayList<BoardView>(BoardViewManager.getManager().listBoardViews());
for (BoardView view : views) {
// this will also do a temporary delete on the board's game, if any
BoardViewManager.getManager().deleteBoardView(view.getName(), false);
}
Results.shutdown();
instance = null;
Debugger.getInstance().debug("disable complete");
}
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
return cmds.dispatch(sender, command, label, args);
}
@Override
public List<String> onTabComplete(CommandSender sender, Command command, String label, String[] args) {
return cmds.onTabComplete(sender, command, label, args);
}
private void setupMetrics() {
if (!getConfig().getBoolean("mcstats")) {
return;
}
try {
Metrics metrics = new Metrics(this);
metrics.createGraph("Boards Created").addPlotter(new Plotter() {
@Override
public int getValue() { return BoardViewManager.getManager().listBoardViews().size(); }
});
metrics.createGraph("Games in Progress").addPlotter(new Plotter() {
@Override
public int getValue() { return ChessGameManager.getManager().listGames().size(); }
});
metrics.start();
} catch (IOException e) {
LogUtils.warning("Can't submit metrics data: " + e.getMessage());
}
}
private void setupVault(PluginManager pm) {
Plugin vault = pm.getPlugin("Vault");
if (vault != null && vault.isEnabled()) {
int ver = PluginVersionChecker.getRelease(vault.getDescription().getVersion());
boolean legacyMode = ver < 1003000; // 1.3.0
Debugger.getInstance().debug("Detected Vault v" + vault.getDescription().getVersion());
if (legacyMode) {
LogUtils.warning("Detected an older version of Vault. Correct UUID functionality requires Vault 1.4.1 or later.");
}
Economy econ = setupEconomy();
if (econ != null) {
EconomyUtil.init(econ, legacyMode);
} else {
LogUtils.warning("No economy plugin detected - game stakes not available");
}
} else {
LogUtils.warning("Vault not loaded: game stakes not available");
}
}
private void setupSMS(PluginManager pm) {
Plugin p = pm.getPlugin("ScrollingMenuSign");
if (p != null && p.isEnabled()) {
sms = new SMSIntegration((ScrollingMenuSign) p);
Debugger.getInstance().debug("ScrollingMenuSign plugin detected: ChessCraft menus created.");
} else {
Debugger.getInstance().debug("ScrollingMenuSign plugin not detected.");
}
}
private void setupLandslide(PluginManager pm) {
Plugin lsl = pm.getPlugin("Landslide");
if (lsl != null && lsl.isEnabled()) {
landslideIntegration = new LandslideIntegration(this);
}
}
private void setupWorldEdit(PluginManager pm) {
Plugin p = pm.getPlugin("WorldEdit");
if (p != null && p.isEnabled()) {
worldEditPlugin = (WorldEditPlugin) p;
Debugger.getInstance().debug("WorldEdit plugin detected: chess board terrain saving enabled.");
} else {
LogUtils.warning("WorldEdit plugin not detected: chess board terrain saving disabled.");
}
}
private void setupDynmap(PluginManager pm) {
Plugin p = pm.getPlugin("dynmap");
if (p != null && p.isEnabled()) {
dynmapIntegration = new DynmapIntegration(this, (DynmapAPI) p);
Debugger.getInstance().debug("dynmap plugin detected. Boards and games will be labelled.");
} else {
Debugger.getInstance().debug("dynmap plugin not detected.");
}
}
private void setupProtocolLib(PluginManager pm) {
Plugin pLib = pm.getPlugin("ProtocolLib");
if (pLib != null && pLib.isEnabled()) {
protocolLibEnabled = true;
Debugger.getInstance().debug("Detected ProtocolLib v" + pLib.getDescription().getVersion());
}
}
private void setupCitizens2(PluginManager pm) {
Plugin citizens = pm.getPlugin("Citizens");
if (citizens != null && citizens.isEnabled()) {
citizensEnabled = true;
Debugger.getInstance().debug("Detected Citizens2 v" + citizens.getDescription().getVersion());
CitizensUtil.initCitizens();
} else {
LogUtils.warning("Citizens plugin not detected: entity-based chess sets will not be available");
}
}
private Economy setupEconomy() {
RegisteredServiceProvider<Economy> economyProvider = getServer().getServicesManager().getRegistration(net.milkbowl.vault.economy.Economy.class);
if (economyProvider != null) {
return economyProvider.getProvider();
} else {
return null;
}
}
public ChessPersistence getPersistenceHandler() {
return persistence;
}
public WorldEditPlugin getWorldEdit() {
return worldEditPlugin;
}
private void setInstance(ChessCraft chessCraft) {
instance = chessCraft;
}
public static ChessCraft getInstance() {
return instance;
}
public DynmapIntegration getDynmapIntegration() {
return dynmapIntegration;
}
public LandslideIntegration getLandslideIntegration() {
return landslideIntegration;
}
public boolean isProtocolLibEnabled() {
return protocolLibEnabled;
}
public boolean isCitizensEnabled() {
return citizensEnabled;
}
public PlayerTracker getPlayerTracker() {
return tracker;
}
public SpecialFX getFX() {
return fx;
}
private void registerCommands() {
cmds.registerCommand(new ArchiveCommand());
cmds.registerCommand(new BoardCreationCommand());
cmds.registerCommand(new BoardDeletionCommand());
cmds.registerCommand(new BoardStyleSaveCommand());
cmds.registerCommand(new BoardStyleSetCommand());
cmds.registerCommand(new ClaimVictoryCommand());
cmds.registerCommand(new CreateGameCommand());
cmds.registerCommand(new DeleteGameCommand());
cmds.registerCommand(new DesignCommand());
cmds.registerCommand(new FenCommand());
cmds.registerCommand(new GameCommand());
cmds.registerCommand(new GetcfgCommand());
cmds.registerCommand(new InvitePlayerCommand());
cmds.registerCommand(new JoinCommand());
cmds.registerCommand(new ListAICommand());
cmds.registerCommand(new ListGameCommand());
cmds.registerCommand(new ListStylesCommand());
cmds.registerCommand(new ListBoardCommand());
cmds.registerCommand(new ListTopCommand());
cmds.registerCommand(new MoveCommand());
cmds.registerCommand(new NoCommand());
cmds.registerCommand(new OfferDrawCommand());
cmds.registerCommand(new OfferSwapCommand());
cmds.registerCommand(new PageCommand());
cmds.registerCommand(new PromoteCommand());
cmds.registerCommand(new RedrawCommand());
cmds.registerCommand(new ReloadCommand());
cmds.registerCommand(new ResignCommand());
cmds.registerCommand(new SaveCommand());
cmds.registerCommand(new SetcfgCommand());
cmds.registerCommand(new StakeCommand());
cmds.registerCommand(new StartCommand());
cmds.registerCommand(new TeleportCommand());
cmds.registerCommand(new TimeControlCommand());
cmds.registerCommand(new UndoCommand());
cmds.registerCommand(new YesCommand());
}
public ConfigurationManager getConfigManager() {
return configManager;
}
/* ConfigurationListener */
@Override
public Object onConfigurationValidate(ConfigurationManager configurationManager, String key, Object oldVal, Object newVal) {
if (key.startsWith("auto_delete.") || key.startsWith("timeout")) {
String dur = newVal.toString();
try {
new Duration(dur);
} catch (NumberFormatException e) {
throw new DHUtilsException("Invalid duration: " + dur);
}
} else if (key.startsWith("effects.") && getConfig().get(key) instanceof String) {
// this will throw an IllegalArgumentException if the value is no good
SpecialFX.SpecialEffect e = fx.new SpecialEffect(newVal.toString(), 1.0f);
e.play(null);
} else if (key.equals("version")) {
throw new DHUtilsException("'version' config item may not be changed");
} else if (key.equals("database.table_prefix") && newVal.toString().isEmpty()) {
throw new DHUtilsException("'database.table_prefix' may not be empty");
} else if (key.equals("wand_item")) {
String s = newVal.toString();
if (!s.isEmpty() && !s.equals("*")) {
try {
MaterialWithData.get(s);
} catch (IllegalArgumentException e) {
throw new DHUtilsException("Invalid wand material: " + s);
}
}
}
return newVal;
}
@Override
public void onConfigurationChanged(ConfigurationManager configurationManager, String key, Object oldVal, Object newVal) {
if (key.equalsIgnoreCase("locale")) {
Messages.setMessageLocale(newVal.toString());
// redraw control panel signs in the right language
updateAllControlPanels();
} else if (key.equalsIgnoreCase("debug_level")) {
Debugger.getInstance().setLevel((Integer) newVal);
} else if (key.equalsIgnoreCase("teleporting")) {
updateAllControlPanels();
} else if (key.equalsIgnoreCase("flying.allowed")) {
flightListener.setEnabled((Boolean) newVal);
} else if (key.equalsIgnoreCase("flying.captive")) {
flightListener.setCaptive((Boolean) newVal);
} else if (key.equalsIgnoreCase("flying.upper_limit") || key.equalsIgnoreCase("flying.outer_limit")) {
BoardViewManager.getManager().recalculateFlightRegions();
} else if (key.equalsIgnoreCase("flying.fly_speed") || key.equalsIgnoreCase("flying.walk_speed")) {
flightListener.updateSpeeds();
} else if (key.equalsIgnoreCase("pager.enabled")) {
if ((Boolean) newVal) {
MessagePager.setDefaultPageSize();
} else {
MessagePager.setDefaultPageSize(Integer.MAX_VALUE);
}
} else if (key.startsWith("effects.")) {
fx = new SpecialFX(getConfig().getConfigurationSection("effects"));
} else if (key.startsWith("database.")) {
Results.shutdown();
if (Results.getResultsHandler() == null) {
LogUtils.warning("DB connection cannot be re-established. Check your settings.");
}
} else if (key.equals("coloured_console")) {
MiscUtil.setColouredConsole((Boolean)newVal);
} else if (key.startsWith("dynmap.") && dynmapIntegration != null) {
dynmapIntegration.processConfig();
dynmapIntegration.setActive(dynmapIntegration.isEnabled());
} else if (key.equals("time_control.default")) {
// force any board which doesn't have a specific time control to update
// its time control button to the new global default
for (BoardView bv : BoardViewManager.getManager().listBoardViews()) {
bv.defaultTimeControlChanged();
}
} else if (key.equals("entity_volume") && isProtocolLibEnabled()) {
ProtocolLibIntegration.setEntityVolume((Double) newVal);
}
}
private void updateAllControlPanels() {
for (BoardView bv : BoardViewManager.getManager().listBoardViews()) {
bv.getControlPanel().repaintControls();
bv.getControlPanel().repaintClocks();
}
}
/* PluginVersionListener */
@Override
public String getPreviousVersion() {
return getConfig().getString("version");
}
@Override
public void setPreviousVersion(String currentVersion) {
getConfig().set("version", getDescription().getVersion());
saveConfig();
}
@Override
public void onVersionChanged(int oldVersion, int newVersion) {
boolean changed = false;
for (String k : getConfig().getConfigurationSection("effects").getKeys(false)) {
if (getConfig().getString("effects." + k).contains("rawname=")) {
String newEffect = getConfig().getDefaults().getString("effects." + k);
LogUtils.info("migrating config setting 'effects." + k + "' => " + newEffect);
getConfig().set("effects." + k, newEffect);
changed = true;
}
}
if (changed) {
saveConfig();
}
}
public boolean isChessNPC(Entity entity) {
return entity != null && entity.hasMetadata("NPC");
}
}