package com.nyancraft.reportrts;
import java.io.FileReader;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.logging.Logger;
import com.nyancraft.reportrts.api.ApiServer;
import com.nyancraft.reportrts.command.*;
import com.nyancraft.reportrts.data.Ticket;
import com.nyancraft.reportrts.persistence.DataProvider;
import com.nyancraft.reportrts.persistence.MySQLDataProvider;
import com.nyancraft.reportrts.util.*;
import net.milkbowl.vault.permission.Permission;
import org.bukkit.entity.Player;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.RegisteredServiceProvider;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.plugin.messaging.PluginMessageListener;
public class ReportRTS extends JavaPlugin implements PluginMessageListener {
private static ReportRTS plugin;
private final Logger log = Logger.getLogger("Minecraft");
private static MessageHandler messageHandler = new MessageHandler();
private VersionChecker versionChecker = new VersionChecker();
private DataProvider provider;
public Map<Integer, Ticket> tickets = new TreeMap<>();
public Map<Integer, UUID> notifications = new HashMap<>();
public Map<UUID, Integer> teleportMap = new HashMap<>();
public Map<String, String> commandMap = new HashMap<>();
public ArrayList<UUID> staff = new ArrayList<>();
public boolean notifyStaffOnNewRequest;
public boolean notificationSound;
public boolean hideNotification;
public boolean hideWhenOffline;
public boolean debugMode;
public boolean outdated;
public boolean vanishSupport;
public boolean bungeeCordSupport;
public boolean setupDone = true;
public boolean ticketNagHeld;
public boolean ticketPreventDuplicate;
public boolean apiEnabled;
public boolean legacyCommands;
public boolean fancify;
public int maxTickets;
public int ticketDelay;
public int ticketMinimumWords;
public int ticketsPerPage;
public int storagePort;
public long ticketNagging;
public long storageRefreshTime;
public long bungeeCordSync;
public String storageType;
public String storageHostname;
public String storageDatabase;
public String storageUsername;
public String storagePassword;
public String storagePrefix;
public String versionString;
public String bungeeCordServerPrefix;
public String lineSeparator = System.lineSeparator();
public static Permission permission = null;
private ApiServer apiServer;
private int apiPort;
private String apiPassword;
private List<String> apiAllowedIPs = new ArrayList<>();
private String serverIP;
public void onDisable() {
provider.close();
if(apiEnabled) {
try{
apiServer.getListener().close();
}catch(IOException e) {
e.printStackTrace();
}
}
messageHandler.saveMessageConfig();
}
public void onEnable() {
plugin = this;
reloadSettings();
// Enable BungeeCord support if wanted.
if(bungeeCordSupport) {
if(getConfig().getString("bungeecord.serverName") == null || getConfig().getString("bungeecord.serverName").isEmpty()) {
plugin.getLogger().warning("BungeeCord support enabled, but server name is not set yet. Scheduling a name-update task.");
new BungeeNameTask(plugin).runTaskTimer(plugin, 160L, 480L);
} else {
BungeeCord.setServer(getConfig().getString("bungeecord.serverName"));
}
}
final PluginManager pm = getServer().getPluginManager();
// Register events that ReportRTS listens to.
pm.registerEvents(new RTSListener(plugin), plugin);
// Ensure that storage information is not default as that may not work.
if(assertConfigIsDefault("STORAGE")) {
setupDone = false;
} else {
if(!storageType.equalsIgnoreCase("MYSQL")) {
log.severe("Unsupported STORAGE type specified. Allowed types are: MySQL");
pm.disablePlugin(this);
}
setDataProvider(new MySQLDataProvider(plugin));
if(!provider.load()) {
log.severe("Encountered an error while attempting to connect to the data-provider. Disabling...");
pm.disablePlugin(this);
return;
}
reloadSettings();
RTSFunctions.populateStaffMap();
}
// Check if plugin is up to date. TODO: This has to be updated for Spigot's website.
outdated = !versionChecker.upToDate();
// Enable fancier tickets if enabled and if ProtocolLib is enabled on the server.
if(fancify && pm.getPlugin("ProtocolLib") == null) {
log.warning("Fancy messages are enabled, but ProtocolLib was not found.");
fancify = false;
}
// Register commands.
if(legacyCommands) {
pm.registerEvents(new LegacyCommandListener(commandMap.get("readTicket"), commandMap.get("openTicket"), commandMap.get("closeTicket"), commandMap.get("reopenTicket"),
commandMap.get("claimTicket"), commandMap.get("unclaimTicket"), commandMap.get("holdTicket"), commandMap.get("teleportToTicket"), commandMap.get("broadcastToStaff"),
commandMap.get("listStaff"), commandMap.get("commentTicket")), plugin);
}
getCommand("reportrts").setExecutor(new ReportRTSCommand(plugin));
getCommand("ticket").setExecutor(new TicketCommand(plugin));
getCommand("ticket").setTabCompleter(new TabCompleteHelper(plugin));
// Set up Vault if it exists on the server.
if(pm.getPlugin("Vault") != null) setupPermissions();
// Attempt to set up Metrics.
try {
MetricsLite metrics = new MetricsLite(this);
metrics.start();
} catch(IOException e) {
log.info("Unable to submit stats!");
}
// Enable API. (Not recommended since it is very incomplete!)
apiEnabled = false; // TODO: Remove hard-coded false when this works!
if(apiEnabled) {
try {
Properties props = new Properties();
props.load(new FileReader("server.properties"));
serverIP = props.getProperty("server-ip", "ANY");
if(serverIP.isEmpty()) serverIP = "ANY";
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
apiPassword = apiPassword + "ReportRTS";
md.update(apiPassword.getBytes("UTF-8"));
byte[] hash = md.digest();
StringBuffer sb = new StringBuffer();
for(byte b : hash) {
sb.append(String.format("%02x", b));
}
apiPassword = sb.toString();
} catch(NoSuchAlgorithmException e) {
log.warning("[ReportRTS] Unable to hash password, consider disabling the API!");
e.printStackTrace();
}
apiServer = new ApiServer(plugin, serverIP, apiPort, apiAllowedIPs, apiPassword);
} catch(IOException e) {
log.warning("[ReportRTS] Unable to start API server!");
e.printStackTrace();
}
apiServer.start();
}
// Enable nagging, staff will be reminded of unresolved tickets.
if(ticketNagging > 0){
getServer().getScheduler().scheduleSyncRepeatingTask(plugin, new Runnable(){
public void run(){
int openTickets = tickets.size();
if(ticketNagHeld) {
int heldTickets = getDataProvider().countTickets(2);
if(heldTickets > 0) {
if(openTickets > 0) RTSFunctions.messageStaff(Message.ticketUnresolvedHeld(openTickets, heldTickets, (plugin.legacyCommands ? plugin.commandMap.get("readTicket") : "ticket " + plugin.commandMap.get("readTicket"))), false);
} else {
if(openTickets > 0) RTSFunctions.messageStaff(Message.ticketUnresolved(openTickets, (plugin.legacyCommands ? plugin.commandMap.get("readTicket") : "ticket " + plugin.commandMap.get("readTicket"))), false);
}
} else {
if(openTickets > 0) RTSFunctions.messageStaff(Message.ticketUnresolved(openTickets, (plugin.legacyCommands ? plugin.commandMap.get("readTicket") : "ticket " + plugin.commandMap.get("readTicket"))), false);
}
}
}, 120L, (ticketNagging * 60) * 20);
}
if(bungeeCordSupport) {
// Register BungeeCord channels.
getServer().getMessenger().registerOutgoingPluginChannel(this, "BungeeCord");
getServer().getMessenger().registerIncomingPluginChannel(this, "BungeeCord", this);
// Schedule a offline-sync in case no players are online.
getServer().getScheduler().scheduleSyncRepeatingTask(plugin, new Runnable() {
public void run() {
if(BungeeCord.isServerEmpty()) {
RTSFunctions.sync();
}
}
}, plugin.bungeeCordSync * 20, plugin.bungeeCordSync * 20);
}
}
public void reloadPlugin() {
reloadSettings();
RTSFunctions.sync();
}
public void reloadSettings() {
reloadConfig();
getConfig().options().copyDefaults(true);
assertConfigUpToDate();
messageHandler.reloadMessageConfig();
messageHandler.saveMessageConfig();
messageHandler.reloadMessageMap();
notifyStaffOnNewRequest = getConfig().getBoolean("notifyStaff");
notificationSound = getConfig().getBoolean("notifySound");
hideNotification = getConfig().getBoolean("hideMessageIfEmpty");
hideWhenOffline = getConfig().getBoolean("ticket.hideOffline");
maxTickets = getConfig().getInt("ticket.max");
ticketDelay = getConfig().getInt("ticket.delay");
ticketMinimumWords = getConfig().getInt("ticket.minimumWords");
ticketsPerPage = getConfig().getInt("ticket.perPage");
ticketPreventDuplicate = getConfig().getBoolean("ticket.preventDuplicates", true);
ticketNagging = getConfig().getLong("ticket.nag");
ticketNagHeld = getConfig().getBoolean("ticket.nagHeld", false);
storageRefreshTime = getConfig().getLong("storage.refreshTime");
storageType = getConfig().getString("storage.type", "mysql");
storagePort = getConfig().getInt("storage.port");
storageHostname = getConfig().getString("storage.hostname");
storageDatabase = getConfig().getString("storage.database");
storageUsername = getConfig().getString("storage.username");
storagePassword = getConfig().getString("storage.password");
storagePrefix = getConfig().getString("storage.prefix");
debugMode = getConfig().getBoolean("debug");
vanishSupport = getConfig().getBoolean("VanishSupport", false);
bungeeCordSupport = getConfig().getBoolean("bungeecord.enable", false);
bungeeCordSync = getConfig().getLong("bungeecord.sync", 300L);
bungeeCordServerPrefix = getConfig().getString("bungeecord.serverPrefix");
apiEnabled = false; // TODO: Change to this when it's ready: getConfig().getBoolean("api.enable", false);
apiPort = getConfig().getInt("api.port", 25567);
apiPassword = getConfig().getString("api.password");
apiAllowedIPs = getConfig().getStringList("api.whitelist");
legacyCommands = getConfig().getBoolean("command.legacy", false);
fancify = getConfig().getBoolean("ticket.fancify", true);
commandMap.clear();
// Register all commands/subcommands.
commandMap.put("readTicket",getConfig().getString("command.readTicket"));
commandMap.put("openTicket",getConfig().getString("command.openTicket"));
commandMap.put("closeTicket",getConfig().getString("command.closeTicket"));
commandMap.put("reopenTicket",getConfig().getString("command.reopenTicket"));
commandMap.put("claimTicket",getConfig().getString("command.claimTicket"));
commandMap.put("unclaimTicket",getConfig().getString("command.unclaimTicket"));
commandMap.put("holdTicket",getConfig().getString("command.holdTicket"));
commandMap.put("teleportToTicket",getConfig().getString("command.teleportToTicket"));
commandMap.put("broadcastToStaff",getConfig().getString("command.broadcastToStaff"));
commandMap.put("listStaff",getConfig().getString("command.listStaff"));
commandMap.put("assignTicket",getConfig().getString("command.assignTicket"));
commandMap.put("commentTicket",getConfig().getString("command.commentTicket"));
// Commands registered!
}
public static ReportRTS getPlugin() {
return plugin;
}
public static MessageHandler getMessageHandler() {
return messageHandler;
}
public DataProvider getDataProvider() {
return provider;
}
public void setDataProvider(DataProvider provider) {
if(this.provider != null) this.provider.close();
this.provider = provider;
}
private Boolean setupPermissions() {
RegisteredServiceProvider<Permission> permissionProvider = getServer().getServicesManager().getRegistration(Permission.class);
if(permissionProvider != null) {
permission = permissionProvider.getProvider();
}
return (permission != null);
}
public void onPluginMessageReceived(String pluginChannel, Player player, byte[] bytes) {
if(!pluginChannel.equals("BungeeCord")) return;
BungeeCord.handleNotify(bytes);
}
private void assertConfigUpToDate() {
/**
* What it does:
* - - - - -
* Checks if the mapping "requests" is located in the config
* and replaces it with "ticket".
* - - - - -
* Since version:
* 1.2.3
*/
if(getConfig().getConfigurationSection("request") != null) {
getConfig().createSection("ticket", getConfig().getConfigurationSection("request").getValues(false));
getConfig().set("request", null);
log.info("Updated configuration. 'request' => 'ticket'.");
}
// Save changes.
saveConfig();
}
private boolean assertConfigIsDefault(String path) {
/**
* What it does:
* - - - - -
* Checks if the specified configuration section is default,
* returns a boolean depending on the result.
*/
switch(path.toUpperCase()) {
case "STORAGE":
return (storageHostname.equalsIgnoreCase("localhost") && storagePort == 3306 && storageDatabase.equalsIgnoreCase("minecraft")
&& storageUsername.equalsIgnoreCase("username") && storagePassword.equalsIgnoreCase("password")
&& storagePrefix.equalsIgnoreCase("") && storageRefreshTime == 600);
}
return false;
}
}