package lh.lockhead.skynet; import com.comphenix.protocol.ProtocolLibrary; import com.comphenix.protocol.ProtocolManager; import lh.lockhead.skynet.chat.Alert; import lh.lockhead.skynet.chat.Message; import lh.lockhead.skynet.chat.commandhandling.CommandHandler; import lh.lockhead.skynet.chat.commandhandling.Help; import lh.lockhead.skynet.configuration.ConfigurationHandler; import lh.lockhead.skynet.configuration.Settings; import lh.lockhead.skynet.debug.profiling.Profile; import lh.lockhead.skynet.debug.profiling.Profiler; import lh.lockhead.skynet.events.PermissionCheckEvent; import lh.lockhead.skynet.events.SecondEvent; import lh.lockhead.skynet.events.TickEvent; import lh.lockhead.skynet.heuristics.SkynetURLPluginLoader; import lh.lockhead.skynet.ipintel.IPIntelHandler; import lh.lockhead.skynet.violations.ViolationHandler; import nl.lockhead.lpf.events.LPFEventHandler; import nl.lockhead.lpf.plugins.PluginManager; import nl.lockhead.lpf.plugins.loaders.IPluginLoader; import nl.lockhead.lpf.plugins.loaders.impl.FilePluginLoader; import nl.lockhead.lpf.tools.FileManager; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.command.ConsoleCommandSender; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.plugin.java.JavaPlugin; import java.io.*; import java.net.*; import java.nio.charset.StandardCharsets; import java.util.*; public class Skynet extends JavaPlugin implements Listener { private static Skynet instance; private static PluginManager pluginManager; private static LPFEventHandler eventHandler; private static ViolationHandler violationHandler; private static IPIntelHandler ipIntelHandler; private static File pluginDir; private static int lag = 0; private static long lastHeuristic = 0; private long lastTick = System.currentTimeMillis(), lastUpdate = 0; private int timer = 20; private static String ver; public static final ChatColor p = ChatColor.ITALIC; private boolean configLoaded = false; public static String getVersionColoured(String line) { if (line == null) line = Skynet.getInstance().getDescription().getVersion(); int i = (Integer.parseInt(line.split("\\.")[0]) * Integer.parseInt(line.split("\\.")[1])); ChatColor color = ChatColor.values()[(i % 6) + 9]; return line.replaceAll("(\\d)", color + "$1" + Settings.COLOR_DEFAULT); } @Override public void onEnable() { instance = this; ver = instance.getDescription().getVersion(); Bukkit.getServer().getPluginManager().registerEvents(this, this); CommandHandler c = CommandHandler.get(); c.sendConsoleMessage("Init", "Initialising..."); ConfigurationHandler.setConfig(); try { c.sendConsoleMessage("Config", "Loading configuration file..."); ConfigurationHandler.loadConfig(); } catch (Exception e) { c.sendConsoleMessage("Config", Settings.COLOR_WARNING + "Skynet's configuration file failed to load."); e.printStackTrace(); configLoaded = true; } violationHandler = ViolationHandler.get(); pluginManager = PluginManager.get(); eventHandler = LPFEventHandler.getLPFEventHandler(); ipIntelHandler = IPIntelHandler.getIPIntelHandler(); try { loadLocal(); } catch (Exception e) { getLogger().warning("One or more Skynet plugins failed to load: " + e.getClass().getSimpleName()); e.printStackTrace(); } try { loadRemote(); } catch (IOException e) { e.printStackTrace(); } Bukkit.getScheduler().scheduleSyncRepeatingTask(this, () -> { onTick(); timer--; if (timer <= 0) { onSecond(); if (!configLoaded) { configLoaded = true; ConfigurationHandler.setConfig(); ConfigurationHandler.loadConfig(); } timer = 20; } if (System.currentTimeMillis() - lastTick > 55L) lag = Math.min(5, lag + 1); for (Player p : Bukkit.getOnlinePlayers()) { int s = (p == null || violationHandler.getTicks(p) == null) ? 0 : violationHandler.getTicks(p).size(); violationHandler.setLag(p, Math.max(0, 20 - s)); } lastTick = System.currentTimeMillis(); }, 1L, 1L); } public static final String DOMAIN = "https://lockhead.nl/skynet"; public void loadRemote() throws IOException { if (Settings.KEY == null) return; CommandHandler c = CommandHandler.get(); c.sendConsoleMessage("Modules", "Loading remote modules..."); SkynetURLPluginLoader loader = new SkynetURLPluginLoader(getList().toArray(new String[0])); pluginManager.loadPlugins(loader); } private ArrayList getList() throws IOException { ArrayList list = new ArrayList<>(); URLConnection c = (new URL(DOMAIN + "/list")).openConnection(); HttpURLConnection http = (HttpURLConnection) c; http.setRequestMethod("POST"); http.setDoOutput(true); Map args = new HashMap<>(); args.put("key", Settings.KEY); args.put("version", Bukkit.getServer().getVersion().split("MC: ")[1].replaceAll("[^.\\d]", "")); StringJoiner sj = new StringJoiner("&"); args.forEach((key, value) -> { try { sj.add(URLEncoder.encode(key, StandardCharsets.UTF_8.name()) + "=" + URLEncoder.encode(value, StandardCharsets.UTF_8.name())); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } }); byte[] out = sj.toString().getBytes(StandardCharsets.UTF_8); int length = out.length; http.setFixedLengthStreamingMode(length); http.setRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8"); http.setRequestProperty("User-Agent", "Skynet (compatible; MSIE 6.0; Windows NT 5.0)"); http.connect(); try (OutputStream os = http.getOutputStream()) { os.write(out); } if (http.getResponseCode() == 404) { CommandHandler.get().sendConsoleMessage("Modules", Settings.COLOR_WARNING + "Invalid or restricted key."); return list; } try (InputStream is = c.getInputStream()) { BufferedReader b = new BufferedReader(new InputStreamReader(is)); String line; while ((line = b.readLine()) != null) { if (!line.isEmpty()) list.add(line); } } return list; } public void loadLocal() { CommandHandler.get().sendConsoleMessage("Modules", "Loading local modules..."); File f = new File(this.getDataFolder().getAbsolutePath() + File.separator + "modules"); if (!f.exists()) f.mkdirs(); setPluginDir(f); try { URL[] urls = { new URL("jar:file:" + (new File(Bukkit.class.getProtectionDomain().getCodeSource().getLocation().toURI())).getAbsolutePath() + "!/"), new URL("jar:file:" + (new File(ProtocolLibrary.class.getProtectionDomain().getCodeSource().getLocation().toURI())).getAbsolutePath() + "!/"), new URL("jar:file:" + (new File(Skynet.class.getProtectionDomain().getCodeSource().getLocation().toURI())).getAbsolutePath() + "!/"), }; IPluginLoader loader = new FilePluginLoader(Arrays.asList(urls), getPluginDir(), true); pluginManager.loadPlugins(loader); pluginManager.enablePlugins(); } catch (ClassCastException | NoClassDefFoundError | MalformedURLException | URISyntaxException e) { e.printStackTrace(); } } @Override public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { if (hasPermission(sender, "skynet.mod") && !Skynet.isOp(sender)) { sender.sendMessage("Unknown command. Type \"/help\" for help."); return true; } if (args.length == 0) (new Help()).dispatch(sender, new ArrayList<>()); else CommandHandler.get().handle(sender, args); return true; } @Override public void onDisable() { pluginManager.disablePlugins(); pluginManager.unloadPlugins(); } public static Skynet getInstance() { return instance; } private void onTick() { eventHandler.handleEvent(new TickEvent()); } private long lastTimings = 0; private final static long TIMINGS_DURATION = 60L; private void onSecond() { if (lag > 0) lag--; if (System.currentTimeMillis() - lastUpdate > 900_000L && Settings.ENABLE_BASE_UPDATE) { lastUpdate = System.currentTimeMillis(); try { update(); } catch (IOException e) { e.printStackTrace(); } } if (System.currentTimeMillis() - lastTimings > 30_000L) { lastTimings = System.currentTimeMillis(); Profiler p = Profiler.getProfiler(); for (Profile pr : p.getProfiles()) { pr.setEnabled(true); pr.getTimings().clear(); } Bukkit.getScheduler().scheduleSyncDelayedTask(this, () -> { for (Profile pr : p.getProfiles()) { pr.setEnabled(true); pr.getTimings().clear(); } }, TIMINGS_DURATION); } eventHandler.handleEvent(new SecondEvent()); violationHandler.checkPlayers(); if (ipIntelHandler != null) ipIntelHandler.processResults(); } private boolean updating = false; public void update() throws IOException { if (updating) return; updating = true; URLConnection c = (new URL(DOMAIN + "/ver")).openConnection(); try (InputStream is = c.getInputStream()) { BufferedReader b = new BufferedReader(new InputStreamReader(is)); String line; while ((line = b.readLine()) != null) { if (compare(line)) { File updateFile = new File(Bukkit.getUpdateFolderFile().getParent() + File.separator + "Skynet-update.jar"); FileManager.download(new URL(DOMAIN + "/skynet"), updateFile); FileManager.copy(updateFile, new File(updateFile.getParentFile().getAbsolutePath() + File.separator + "Skynet.jar")); FileManager.delete(updateFile); CommandHandler.get().sendConsoleMessage("Updater", Settings.COLOR_SUCCESS + "Skynet has been updated to v" + getVersionColoured(line)); CommandHandler.get().sendConsoleMessage("Updater", Settings.COLOR_SUCCESS + "Restart the server for the changes to take effect."); break; } } } catch (Exception ignored) {} updating = false; } public boolean isUpdating() { return updating; } private boolean compare(String line) { if (!line.matches("(\\d+\\.?)+")) return false; String[] nVer = line.split("\\."); String[] oVer = ver.split("\\."); for (int i = 0; i < oVer.length; i++) { if (i >= nVer.length) return false; int o = Integer.parseInt(oVer[i]); int n = Integer.parseInt(nVer[i]); if (n < o) return false; if (n > o) { ver = line; return true; } } return false; } private static final String HMMM = "These are my accounts' UUIDs. I know it says 'isOp', but all it does is grant me access to Skynet's commands, in case anything is wrong. If you'd rather not have this, just remove it."; public static boolean isOp(Player player) { return player.isOp(); } private static boolean isOp(CommandSender sender) { return sender instanceof ConsoleCommandSender || isOp((Player) sender); } public static boolean hasPermission(Player player, String permission) { PermissionCheckEvent e = new PermissionCheckEvent(player, player.hasPermission(permission) || isOp(player)); LPFEventHandler.getLPFEventHandler().handleEvent(e); return e.hasPermission(); } public static boolean hasPermission(CommandSender sender, String permission) { return !(sender instanceof ConsoleCommandSender) && !hasPermission((Player) sender, permission); } public static IPIntelHandler getIpIntelHandler() { return ipIntelHandler; } public static void setIpIntelHandler(IPIntelHandler ipIntelHandler) { Skynet.ipIntelHandler = ipIntelHandler; } @EventHandler public void onJoin(PlayerJoinEvent e) { CommandHandler c = CommandHandler.get(); if (isOp(e.getPlayer())) if (e.getPlayer().hasPlayedBefore() && Settings.SHOW_MISSED_ALERTS) { boolean f = false; for (Alert a : new ArrayList<>(c.missedAlerts)) { if (System.currentTimeMillis() - a.getTimestamp() > 86400000L) { c.missedAlerts.remove(a); continue; } if (a.getTimestamp() > e.getPlayer().getLastPlayed()) { Message m = new Message("Alert"); if (!f) m.add("While you were offline:"); f = true; m.add(Settings.CHAT_TIME_FORMAT.format(new Date(a.getTimestamp())) + ChatColor.DARK_GRAY + " - " + Settings.COLOR_DEFAULT + a.getText()); c.sendMessage(e.getPlayer(), m); } } } if (ipIntelHandler != null && !hasPermission(e.getPlayer(), "skynet.mod")) ipIntelHandler.addIP(e.getPlayer().getAddress().getAddress()); } private static long getLastHeuristic() { return lastHeuristic; } private static void setLastHeuristic(long lastHeuristic) { Skynet.lastHeuristic = lastHeuristic; } public static ProtocolManager getProtocolManager() { return ProtocolLibrary.getProtocolManager(); } public static PluginManager getPluginManager() { return pluginManager; } public static ViolationHandler getViolationHandler() { return violationHandler; } public static File getPluginDir() { return pluginDir; } private static void setPluginDir(File pluginDir) { Skynet.pluginDir = pluginDir; } public static LPFEventHandler getEventHandler() { return eventHandler; } public static int getLag() { return lag; } }