From fd8c45bd7b10d15d1f40ea421d29f9260d9b552e Mon Sep 17 00:00:00 2001 From: Stefatorus Date: Mon, 5 Apr 2021 18:42:13 +0300 Subject: [PATCH] Initial Commit --- .classpath | 20 ++ .gitignore | 2 + .project | 23 ++ .settings/org.eclipse.core.resources.prefs | 3 + .settings/org.eclipse.jdt.core.prefs | 14 ++ .settings/org.eclipse.m2e.core.prefs | 4 + pom.xml | 88 +++++++ resources/plugin.yml | 11 + resources/server.yml | 190 +++++++++++++++ src/com/entryrise/afkguard/Main.java | 133 +++++++++++ .../afkguard/antiafk/AFKManager.java | 74 ++++++ .../entryrise/afkguard/antiafk/AFKPlayer.java | 68 ++++++ .../antiafk/captcha/CaptchaCategory.java | 90 ++++++++ .../afkguard/antiafk/captcha/CaptchaGUI.java | 216 ++++++++++++++++++ .../afkguard/cmd/CommandListener.java | 59 +++++ .../afkguard/cmd/CommandTabListener.java | 19 ++ src/com/entryrise/afkguard/gui/GUI.java | 154 +++++++++++++ .../entryrise/afkguard/gui/GUIInventory.java | 45 ++++ src/com/entryrise/afkguard/gui/GUIItem.java | 45 ++++ .../entryrise/afkguard/gui/GUIManager.java | 20 ++ .../afkguard/gui/inputs/GUIBookInput.java | 82 +++++++ .../afkguard/gui/inputs/GUIChatInput.java | 83 +++++++ .../afkguard/gui/inputs/GUIInput.java | 11 + .../afkguard/listeners/AFKListener.java | 119 ++++++++++ .../afkguard/listeners/ListenersMain.java | 20 ++ .../afkguard/mineutils/HeadUtils.java | 62 +++++ .../afkguard/mineutils/InventoryUtils.java | 95 ++++++++ .../afkguard/mineutils/ItemBuilder.java | 190 +++++++++++++++ .../afkguard/mineutils/ValidationUtils.java | 9 + src/com/entryrise/afkguard/utils/Cache.java | 72 ++++++ src/com/entryrise/afkguard/utils/Chat.java | 30 +++ .../entryrise/afkguard/utils/MathUtils.java | 19 ++ src/com/entryrise/afkguard/utils/Others.java | 75 ++++++ .../entryrise/afkguard/utils/VersionMgr.java | 111 +++++++++ 34 files changed, 2256 insertions(+) create mode 100644 .classpath create mode 100644 .gitignore create mode 100644 .project create mode 100644 .settings/org.eclipse.core.resources.prefs create mode 100644 .settings/org.eclipse.jdt.core.prefs create mode 100644 .settings/org.eclipse.m2e.core.prefs create mode 100644 pom.xml create mode 100644 resources/plugin.yml create mode 100644 resources/server.yml create mode 100644 src/com/entryrise/afkguard/Main.java create mode 100644 src/com/entryrise/afkguard/antiafk/AFKManager.java create mode 100644 src/com/entryrise/afkguard/antiafk/AFKPlayer.java create mode 100644 src/com/entryrise/afkguard/antiafk/captcha/CaptchaCategory.java create mode 100644 src/com/entryrise/afkguard/antiafk/captcha/CaptchaGUI.java create mode 100644 src/com/entryrise/afkguard/cmd/CommandListener.java create mode 100644 src/com/entryrise/afkguard/cmd/CommandTabListener.java create mode 100644 src/com/entryrise/afkguard/gui/GUI.java create mode 100644 src/com/entryrise/afkguard/gui/GUIInventory.java create mode 100644 src/com/entryrise/afkguard/gui/GUIItem.java create mode 100644 src/com/entryrise/afkguard/gui/GUIManager.java create mode 100644 src/com/entryrise/afkguard/gui/inputs/GUIBookInput.java create mode 100644 src/com/entryrise/afkguard/gui/inputs/GUIChatInput.java create mode 100644 src/com/entryrise/afkguard/gui/inputs/GUIInput.java create mode 100644 src/com/entryrise/afkguard/listeners/AFKListener.java create mode 100644 src/com/entryrise/afkguard/listeners/ListenersMain.java create mode 100644 src/com/entryrise/afkguard/mineutils/HeadUtils.java create mode 100644 src/com/entryrise/afkguard/mineutils/InventoryUtils.java create mode 100644 src/com/entryrise/afkguard/mineutils/ItemBuilder.java create mode 100644 src/com/entryrise/afkguard/mineutils/ValidationUtils.java create mode 100644 src/com/entryrise/afkguard/utils/Cache.java create mode 100644 src/com/entryrise/afkguard/utils/Chat.java create mode 100644 src/com/entryrise/afkguard/utils/MathUtils.java create mode 100644 src/com/entryrise/afkguard/utils/Others.java create mode 100644 src/com/entryrise/afkguard/utils/VersionMgr.java diff --git a/.classpath b/.classpath new file mode 100644 index 0000000..fffca7b --- /dev/null +++ b/.classpath @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2461756 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +target/ +bin/ \ No newline at end of file diff --git a/.project b/.project new file mode 100644 index 0000000..d7eb3ed --- /dev/null +++ b/.project @@ -0,0 +1,23 @@ + + + AFKGuard + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.m2e.core.maven2Nature + org.eclipse.jdt.core.javanature + + diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 0000000..933ba18 --- /dev/null +++ b/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,3 @@ +eclipse.preferences.version=1 +encoding/=UTF-8 +encoding/resources=UTF-8 diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..91ca62e --- /dev/null +++ b/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,14 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning +org.eclipse.jdt.core.compiler.release=disabled +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/.settings/org.eclipse.m2e.core.prefs b/.settings/org.eclipse.m2e.core.prefs new file mode 100644 index 0000000..f897a7f --- /dev/null +++ b/.settings/org.eclipse.m2e.core.prefs @@ -0,0 +1,4 @@ +activeProfiles= +eclipse.preferences.version=1 +resolveWorkspaceProjects=true +version=1 diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..4ff07e5 --- /dev/null +++ b/pom.xml @@ -0,0 +1,88 @@ + + 4.0.0 + + com.entryrise + afkguard + 0.2 + jar + + AFKGuard + http://maven.apache.org + + + UTF-8 + Guard against AFKers + + + + ${project.name} + target + src + + + resources + true + + * + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 2.3.2 + + 1.8 + 1.8 + + + + + + + + spigot-repo + https://hub.spigotmc.org/nexus/content/repositories/snapshots/ + + + mikeprimm-repo + http://repo.mikeprimm.com/ + + + jitpack.io + https://jitpack.io + + + + + + org.spigotmc + spigot-api + + 1.8.8-R0.1-SNAPSHOT + provided + + + com.mojang + authlib + 1.5.13 + provided + + + org.projectlombok + lombok + 1.18.20 + provided + + + com.github.MilkBowl + VaultAPI + 1.7 + provided + + + diff --git a/resources/plugin.yml b/resources/plugin.yml new file mode 100644 index 0000000..79305ba --- /dev/null +++ b/resources/plugin.yml @@ -0,0 +1,11 @@ +name: ${project.name} +author: Stefatorus +version: ${project.version} +api-version: 1.13 +description: ${plugin.description} +main: ${project.groupId}.${project.artifactId}.Main +depend: [Vault] +commands: + ${project.name}: + description: command reserved for plugin + \ No newline at end of file diff --git a/resources/server.yml b/resources/server.yml new file mode 100644 index 0000000..0eb18f9 --- /dev/null +++ b/resources/server.yml @@ -0,0 +1,190 @@ +# Plugin config +# ______ _ _______ _ +# /\ | ____| |/ / ____| | | +# / \ | |__ | ' / | __ _ _ __ _ _ __ __| | +# / /\ \ | __| | <| | |_ | | | |/ _` | '__/ _` | +# / ____ \| | | . \ |__| | |_| | (_| | | | (_| | +# /_/ \_\_| |_|\_\_____|\__,_|\__,_|_| \__,_| +# +# By EntryRise S.R.L - https://www.entryrise.com +# +# Support at: +# - https://www.entryrise.com/discord +# - Stefatorus#9382 +# + +settings: + # The AFK detection system of AFKGuard uses a score system + # to allow you to have complete control on the way AFKGuard + # checks if players are actually AFK or just less active. + # + # (!) We recommend testing once you change settings here, + # as not to affect normal players. + afk-detection: + # The punishment table allows you to run actions when a player is found to be afk-ing. + # + # This allows you to discourage AFK-ing without outright kicking players. + # + # Current actions: + # cmd:: - Run a command + # payment:: - Force player to pay money to stay AFK. If he pays, the punishment value will not increment. Useful for charging for chunk loading bots. + # captcha: - Send the player a captcha he will need to complete to reset his punishment value. + # + # If there are multiple counters, it will reset to the last counter in the action chain. + punishment-table: + 5: + # The player will have to run a captcha. If he does, the counter will reset to 3. If not, the counter will keep ticking. + - "captcha:3" + 6: + # Force player to pay 3000 if he wants the counter not to increment. Useful for regulating AFK farms. + - "payment:3000:5" + # If the player fails to complete the captcha or pay, kick the player out, and set his counter to 0. + 10: + - "cmd:kick %player% AFK-ing too much!:0" + # + # The counter is based on scores to allow better management of what is considered AFK or just low activity from the player. + # The calculation is done the following way: + # + # Each counter cycle (configurable timer), the score will reset to the starting score you configure. Each action they do will + # slowly reduce the score, until it reaches 0. If the score is above 0, the player is considered to be AFK at that minute. + # If the player is AFK, we increase counter by one. Otherwise, we decrease by one. + counter: + # 1200 ticks = 1 minute + timer: 1200 + # What should be the starting score. Larger = easier to get flagged as AFK. + start-score: 100 + # How much should we decrease the afk score when player proves not to be AFK? + # + # (!) Setting it to a smaller amount (eg: 1) will allow for activity checkup, meaning + # even small amounts of inactivity will get punished. + decrease-amount: 5 + # What actions should reduce the score. Larger values here = harder to get flagged as AFK. + actions: + # What should we reduce the score by each chat message + chat: 15 + # People can afk and block break, but for sanity purposes, we gave block breaking a minimal value. + # This should allow people to mine normally, but not AFK at generators. + block-break: 1 + # Placing blocks is harder to do while AFK, so we give it a higher value. + block-place: 5 + # The movement check multiplies blocks moved by the value underlined here. I recommend as smaller value, as + # people can AFK in minecarts to move larger distances. + # + # (!) This also accounts for teleportation, which will almost never be done by AFK bots. + # Please note this uses the movement*movement, so for 10 blocks moved, the value will actually be 100 blocks * location-movement. + location-movement: 0.07 + # Movement riding something. This should be set low to account for minecart afk machines. + riding-movement: 0.01 + # Mouse movement multiplies by the angle difference between player's current looking angle, and his previous + # angle. + # + # (!) Non-hacking afk farms will have this always equal to 0, as the mouse is not moved. + # + # Example: Looking the opposite way, your movement would be 180 * 0.2 = 36 (YAW) + 30 * 0.2 = 6 (PITCH) + mouse-movement: 0.01 + # World change is rarely done by AFK actors, meaning that we can assign a high value to it. + world-movement: 75 + # Picking up items can be done by bots, so we will actually reduce the score by a slight amount on item pickup. + # + # Example: Picking 5 stacks = -5 + pickup-item: -1.0 + # Generally, players will sprint when they need to move around. We can reduce the score when player toggles sprinting on. + toggle-sprint: 5.0 + # Flight is rarely implemented by cheats, so it can be a good check for creative servers. + toggle-flight: 15.0 + # Bots are often used for fishing. Penalize players when they are fishing. + fishing: -5.0 + # + # The captcha tool is used to verify if people are AFK-ing or if they are properly in game. It is intrusive, so players will definetly see + # it and have time to react. + captcha: + # What should we do on failed attempt. + fail-command: "kick %player% You failed the AFK Captcha" + # What should we run on success attempt. + success-command: "bc %player% You sucessfully finished the afk captcha" + # The categories for validating if player is afk. + # + # (!) THESE ARE VERSION DEPENDANT. USING INVALID VALUES WILL LEAD TO ERRORS. + gui: + title: "&0&lCaptcha: &8&l%category%" + glass-pane: + material: "LIME_STAINED_GLASS_PANE" + name: "&7" + lore: + - "" + shiny: false + example: + # Material will get replaced during execution. + material: "PAPER" + name: "&e&lAnswer Example" + lore: + - "" + - "&7This is an example of a" + - "&7correct answer" + - "" + shiny: false + item: + # Material will get replaced during execution. + material: "PAPER" + name: "&c&lChoose Answer" + lore: + - "" + - "&7Click here if you feel" + - "&7this is the correct answer" + - "" + shiny: false + + categories: + Stairs: + - "PURPUR_STAIRS" + - "OAK_STAIRS" + - "COBBLESTONE_STAIRS" + - "BRICK_STAIRS" + - "STONE_BRICK_STAIRS" + - "SANDSTONE_STAIRS" + Fence: + - "OAK_FENCE" + - "SPRUCE_FENCE" + - "BIRCH_FENCE" + - "JUNGLE_FENCE" + - "ACACIA_FENCE" + - "DARK_OAK_FENCE" + Log: + - "OAK_LOG" + - "SPRUCE_LOG" + - "BIRCH_LOG" + - "JUNGLE_LOG" + - "ACACIA_LOG" + - "DARK_OAK_LOG" + Sapling: + - "OAK_SAPLING" + - "SPRUCE_SAPLING" + - "BIRCH_SAPLING" + - "JUNGLE_SAPLING" + - "ACACIA_SAPLING" + - "DARK_OAK_SAPLING" + Wool: + - "WHITE_WOOL" + - "ORANGE_WOOL" + - "MAGENTA_WOOL" + - "LIGHT_BLUE_WOOL" + - "YELLOW_WOOL" + - "LIME_WOOL" + - "PINK_WOOL" + - "GRAY_WOOL" + - "LIGHT_GRAY_WOOL" + - "CYAN_WOOL" + Spawnegg: + - "BAT_SPAWN_EGG" + - "BLAZE_SPAWN_EGG" + - "CHICKEN_SPAWN_EGG" + - "COD_SPAWN_EGG" + - "COW_SPAWN_EGG" + - "CREEPER_SPAWN_EGG" + - "DOLPHIN_SPAWN_EGG" + - "DONKEY_SPAWN_EGG" + - "DROWNED_SPAWN_EGG" + + +# Version is used for future updates +version: 1 diff --git a/src/com/entryrise/afkguard/Main.java b/src/com/entryrise/afkguard/Main.java new file mode 100644 index 0000000..0128fd9 --- /dev/null +++ b/src/com/entryrise/afkguard/Main.java @@ -0,0 +1,133 @@ +package com.entryrise.afkguard; + +import java.io.File; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.OfflinePlayer; +import org.bukkit.command.CommandSender; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.Player; +import org.bukkit.event.Listener; +import org.bukkit.plugin.RegisteredServiceProvider; +import org.bukkit.plugin.java.JavaPlugin; + +import com.entryrise.afkguard.antiafk.AFKManager; +import com.entryrise.afkguard.antiafk.captcha.CaptchaCategory; +import com.entryrise.afkguard.antiafk.captcha.CaptchaGUI; +import com.entryrise.afkguard.antiafk.captcha.CaptchaGUI.CaptchaData; +import com.entryrise.afkguard.cmd.CommandListener; +import com.entryrise.afkguard.cmd.CommandTabListener; +import com.entryrise.afkguard.listeners.ListenersMain; +import com.entryrise.afkguard.utils.Others; +import com.entryrise.afkguard.utils.VersionMgr; + +import net.milkbowl.vault.economy.Economy; + +public class Main extends JavaPlugin implements Listener { + + public static String USER = "%%__USER__%%"; + public static final String PREFIX = "§2§lAFK§f§lGuard §e» §f"; + + public static JavaPlugin p; + public static boolean paper = false; + + private static File file; + public static FileConfiguration config = new YamlConfiguration(); + + public static Economy econ; + + public void onEnable() { + p = this; + + file = new File(getDataFolder(), "server.yml"); + config = Others.getConfig(file, 1); + + paper = VersionMgr.isPaper(); + + Bukkit.getLogger().info(PREFIX + "Enabling Systems:"); + + setupEconomy(); + EnableClasses(false); + + getServer().getPluginManager().registerEvents(this, this); + getCommand("afkguard").setExecutor(new CommandListener()); + getCommand("afkguard").setTabCompleter(new CommandTabListener()); + } + + private boolean setupEconomy() { + if (getServer().getPluginManager().getPlugin("Vault") == null) { + return false; + } + RegisteredServiceProvider rsp = getServer().getServicesManager().getRegistration(Economy.class); + if (rsp == null) { + return false; + } + econ = rsp.getProvider(); + return econ != null; + } + + + private static void EnableClasses(boolean reload) { + ListenersMain.Enabler(reload); + CaptchaCategory.Enabler(reload); + AFKManager.Enabler(reload); + } + + public static void ReloadPlugin(CommandSender s) { + config = Others.getConfig(file, 1); + + Bukkit.getLogger().info(PREFIX + "Reloading Systems:"); + EnableClasses(true); + + s.sendMessage(PREFIX + "Reloaded the config successfully."); + } + + @Override + public void onDisable() { + // PacketInjector.Disabler(); <- SEEMS UNIMPORTANT AND GLITCHY + } + + public static void failedAttempt(Player p) { + CaptchaGUI.invs.remove(p.getUniqueId()); + p.closeInventory(); +// p.kickPlayer(getKickMsg()); + Bukkit.dispatchCommand(Bukkit.getConsoleSender(), Main.config.getString("settings.captcha.fail-command").replace("%player%", p.getName())); + } + + public static void goodAttempt(Player p, int target) { + CaptchaGUI.invs.remove(p.getUniqueId()); + p.closeInventory(); +// p.sendMessage(getSuccessfulsg()); + Bukkit.dispatchCommand(Bukkit.getConsoleSender(), Main.config.getString("settings.captcha.success-command").replace("%player%", p.getName())); + AFKManager.setScore(p, target); + } + + public static void captchaResult(Player p, Material mat) { + CaptchaData cd = CaptchaGUI.invs.get(p.getUniqueId()); + if (!cd.category.getAllowed().contains(mat)) { + failedAttempt(p); + return; + } + + goodAttempt(p, cd.counter); + } + + public static void openCapcha(Player p, int resetcounter) { + Bukkit.getScheduler().runTaskLater(Main.p, () -> p.openInventory(CaptchaGUI.getInventory(p, resetcounter)), 20); + } + + + public static void penalizePlayer(Player p, double payment, int reset) { + OfflinePlayer op = Bukkit.getOfflinePlayer(p.getUniqueId()); + + if (!econ.has(op, payment)) { + return; + } + + econ.withdrawPlayer(op, payment); + AFKManager.setScore(p, reset); + } + +} diff --git a/src/com/entryrise/afkguard/antiafk/AFKManager.java b/src/com/entryrise/afkguard/antiafk/AFKManager.java new file mode 100644 index 0000000..56054ef --- /dev/null +++ b/src/com/entryrise/afkguard/antiafk/AFKManager.java @@ -0,0 +1,74 @@ +package com.entryrise.afkguard.antiafk; + +import java.util.Map; +import java.util.WeakHashMap; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +import com.entryrise.afkguard.Main; + +import lombok.Getter; + +public class AFKManager { + + /* + * Using a weak hashmap allows the values to disappear on player quit. May be + * changed to UUID if we notice bots use this to bypass checks by constantly + * rejoining. + */ + @Getter + private static Map players = new WeakHashMap<>(); + + /* + * Increment player score in the hashmap. + */ + + public static void Enabler(boolean reload) { + if (!reload) { + runTask(); + } + + Bukkit.getLogger().info(" §e[§a✔§e] §fAFK Manager"); + } + + private static void runTask() { + int timer = Main.config.getInt("settings.afk-detection.counter.timer"); + Bukkit.getScheduler().runTaskTimer(Main.p, () -> { + for(Player p : Bukkit.getOnlinePlayers()) { + + if (p.hasPermission("afkguard.bypass.admin")) { + continue; + } + + players.getOrDefault(p, new AFKPlayer()).isAfk(p); + } + }, timer, timer); + } + + public static void incrementPlayer(Player p, double d) { + players.compute(p, (k, v) -> { + v = v == null ? new AFKPlayer() : v; + v.addScore(d); + return v; + }); + } + + public static void setScore(Player p, int score) { + if (score < 0) { + return; + } + + players.compute(p, (k, v) -> { + v = v == null ? new AFKPlayer() : v; + + v.setAfkcycles(score); + + return v; + }); + } + + public static int getScore(Player p) { + return players.getOrDefault(p, new AFKPlayer()).getAfkcycles(); + } +} diff --git a/src/com/entryrise/afkguard/antiafk/AFKPlayer.java b/src/com/entryrise/afkguard/antiafk/AFKPlayer.java new file mode 100644 index 0000000..ed9c099 --- /dev/null +++ b/src/com/entryrise/afkguard/antiafk/AFKPlayer.java @@ -0,0 +1,68 @@ +package com.entryrise.afkguard.antiafk; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +import com.entryrise.afkguard.Main; +import com.entryrise.afkguard.utils.MathUtils; + +import lombok.Getter; +import lombok.Setter; + +public class AFKPlayer { + + private double score; + + @Getter + @Setter + private int afkcycles; + + public AFKPlayer() { + score = Main.config.getInt("settings.afk-detection.counter.start-score"); + afkcycles = 0; + } + + /* + * Returns -1 = Decrease Returns +1 = Increase Returns 0 = Stays still. (No + * usage yet) + * + * It also sets the afkcycles and makes sure it doesn't go under 0. + */ + private int calculateCycles(Player p) { + afkcycles = score > 0 ? afkcycles + 1 : Math.max(0, afkcycles - Main.config.getInt("settings.afk-detection.counter.decrease-amount")); + return score > 0 ? 1 : -1; + } + + public void addScore(double increment) { + score -= increment; + } + + public boolean isAfk(Player p) { + + // If the player was found to be AFK, it means there was a change in cyclecount. + // + // This means we should check for actions to run. + if (calculateCycles(p) == 1) { + for (String stg : Main.config.getStringList("settings.afk-detection.punishment-table." + afkcycles)) { + String[] result = stg.split(":"); + + int reset = MathUtils.isInt(result[result.length - 1]) ? Integer.valueOf(result[result.length - 1]) + : -1; + + if (result[0].equalsIgnoreCase("cmd") && result.length >= 2 && !p.hasPermission("afkguard.bypass.cmd")) { + Bukkit.dispatchCommand(Bukkit.getConsoleSender(), result[1].replace("%player%", p.getName())); + AFKManager.setScore(p, reset); + } else if (result[0].equalsIgnoreCase("payment") && !p.hasPermission("afkguard.bypass.payment")) { + double payment = MathUtils.isInt(result[1]) ? Integer.valueOf(result[1]) : 0; + Main.penalizePlayer(p, payment, reset); + } else if (result[0].equalsIgnoreCase("captcha") && !p.hasPermission("afkguard.bypass.captcha")) { + Main.openCapcha(p, reset); + } + } + } + + score = Main.config.getDouble("settings.afk-detection.counter.start-score"); + + return false; + } +} diff --git a/src/com/entryrise/afkguard/antiafk/captcha/CaptchaCategory.java b/src/com/entryrise/afkguard/antiafk/captcha/CaptchaCategory.java new file mode 100644 index 0000000..4b8c92d --- /dev/null +++ b/src/com/entryrise/afkguard/antiafk/captcha/CaptchaCategory.java @@ -0,0 +1,90 @@ +package com.entryrise.afkguard.antiafk.captcha; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.SplittableRandom; + +import org.bukkit.Bukkit; +import org.bukkit.Material; + +import com.entryrise.afkguard.Main; + +public class CaptchaCategory { + + private static SplittableRandom sr = new SplittableRandom(); + private static List categories = new ArrayList(); + + public static void Enabler(boolean reload) { + if (!reload) { + Main.p.getServer().getPluginManager().registerEvents(new CaptchaGUI(), Main.p); + } + for (String stg : Main.config.getConfigurationSection("settings.captcha.categories").getKeys(false)) { + categories.add(new CaptchaCategory(stg)); + + } + Bukkit.getLogger().info(" §e[§a✔§e] §fCategories Added"); + } + + public static CaptchaCategory getCategory(CaptchaCategory exception) { + // For safety if the random randomly breaks out of some reason or another. + // Will render plugin kind of unsafe but still usable and won't crash server. + int i = 50; + CaptchaCategory categ; + do { + categ = categories.get(sr.nextInt(categories.size())); + } while (categ.equals(exception) && --i > 0); + + return categ; + } + + public List getShuffledFiller() { + List raw = new ArrayList(); + for (CaptchaCategory cat : categories) { + if (cat.equals(this)) { + continue; + } + for (int i = 0; i < 5; i++) { + raw.addAll(cat.members); + } + } + + Collections.shuffle(raw); + return raw; + } + + // This is where objective stuff starts and no more statics roam the land. + + private List members = new ArrayList(); + private String name; + + private CaptchaCategory(String name) { + String loc = "settings.captcha.categories." + name; + + this.name = name; + for (String stg : Main.config.getStringList(loc)) { + members.add(Material.getMaterial(stg)); + } + } + + public Material getRandom(Material exception) { + // For safety if the random randomly breaks out of some reason or another. + // Will render plugin kind of unsafe but still usable and won't crash server. + int i = 50; + Material mat; + do { + mat = members.get(sr.nextInt(members.size())); + } while (mat.equals(exception) && --i > 0); + + return mat; + } + + public String getName() { + return name; + } + + public List getAllowed() { + return members; + } + +} diff --git a/src/com/entryrise/afkguard/antiafk/captcha/CaptchaGUI.java b/src/com/entryrise/afkguard/antiafk/captcha/CaptchaGUI.java new file mode 100644 index 0000000..19ecaa4 --- /dev/null +++ b/src/com/entryrise/afkguard/antiafk/captcha/CaptchaGUI.java @@ -0,0 +1,216 @@ +package com.entryrise.afkguard.antiafk.captcha; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.SplittableRandom; +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.entity.HumanEntity; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryCloseEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +import com.entryrise.afkguard.Main; +import com.entryrise.afkguard.mineutils.ItemBuilder; + + +public class CaptchaGUI implements Listener { + + public static List filler = new ArrayList(); + private static SplittableRandom sr = new SplittableRandom(); + + // Fillers where selector items can stay; + static { + for (int i = 19; i < 26; i++) { + filler.add(i); + } + for (int i = 28; i < 35; i++) { + filler.add(i); + } + for (int i = 37; i < 44; i++) { + filler.add(i); + } + + filler.add(10); + filler.add(11); + filler.add(15); + filler.add(16); + } + + public static class CaptchaData { + + public CaptchaCategory category; + public Material example; + public Inventory inv; + + public int counter; + + public CaptchaData() { + this.category = CaptchaCategory.getCategory(null); + this.example = category.getRandom(null); + } + + } + + public static Map invs = new HashMap(); + + public static Inventory getInventory(Player p, int counter) { + return getInventory(p.getUniqueId(), counter); + } + + public static boolean existsAlready(Player p) { + return existsAlready(p.getUniqueId()); + } + + public static boolean existsAlready(UUID u) { + + return invs.containsKey(u); + } + + public static Inventory getInventory(UUID u, int counter) { + if (existsAlready(u)) { + return invs.get(u).inv; + } else if (counter != -1) { + return createInventory(u, counter); + } + + return null; + } + + private static Inventory createInventory(UUID u, int counter) { + CaptchaData cd = new CaptchaData(); + + Inventory inv = Bukkit.createInventory(null, 54, getTitle(cd.category.getName())); + + setBorders(inv); + setRandomExample(inv, cd); + setRandomAnswers(inv, cd); + + cd.inv = inv; + cd.counter = counter; + + invs.put(u, cd); + + return inv; + } + + public static String getTitle(String categoryname) { + return ChatColor.translateAlternateColorCodes('&', Main.config.getString("settings.captcha.gui.title")) + .replace("%category%", categoryname); + } + + @EventHandler + public void onInventoryClick(InventoryClickEvent e) { + + HumanEntity hm = e.getWhoClicked(); + + if (!(hm instanceof Player)) { + return; + } + + Player p = (Player) hm; + + Inventory invent = e.getInventory(); + + ItemStack itm = e.getCurrentItem(); + + if (itm == null) { + return; + } + + if (!invs.containsKey(p.getUniqueId())) { + return; + } + + if (!invent.equals(invs.get(p.getUniqueId()).inv)) { + return; + } + + // Done checks to make sure it's a verify gui. We can now do the work required. + int slot = e.getSlot(); + + // Make sure you're not clicking anywhere except verify items. + e.setCancelled(true); + + if (!filler.contains(slot)) { + return; + } + + Main.captchaResult(p, itm.getType()); + + } + + @EventHandler + public void onInventoryClose(InventoryCloseEvent e) { + HumanEntity hm = e.getPlayer(); + + if (!(hm instanceof Player)) { + return; + } + + Player p = (Player) hm; + + Inventory invent = e.getInventory(); + + if (!invs.containsKey(p.getUniqueId())) { + return; + } + + if (!invent.equals(invs.get(p.getUniqueId()).inv)) { + return; + } + + Bukkit.getScheduler().runTaskLater(Main.p, () -> p.openInventory(CaptchaGUI.getInventory(p, -1)), 5); + } + + public static void setBorders(Inventory inv) { + ItemStack pane = new ItemBuilder(Main.config, "settings.captcha.gui.glass-pane").build(); + for (int i = 4; i <= 49; i += 9) { + inv.setItem(i - 4, pane); + inv.setItem(i + 4, pane); + } + + for (int i = 1; i <= 7; i++) { + inv.setItem(i, pane); + inv.setItem(i + 45, pane); + } + + inv.setItem(12, pane); + inv.setItem(13, pane); + inv.setItem(14, pane); + } + + public static void setRandomExample(Inventory inv, CaptchaData cd) { + ItemStack itm = new ItemBuilder(Main.config, "settings.captcha.gui.example").setType(cd.example.name()).build(); + inv.setItem(4, itm); + } + + public static void setRandomAnswers(Inventory inv, CaptchaData cd) { + List mats = cd.category.getShuffledFiller(); + + for (int i = 0; i < filler.size(); i++) { + Material mat = mats.get(i); + int slot = filler.get(i); + + ItemStack itm = prepareItemStack(mat); + inv.setItem(slot, itm); + } + + inv.setItem(filler.get(sr.nextInt(filler.size())), prepareItemStack(cd.category.getRandom(cd.example))); + } + + private static ItemStack prepareItemStack(Material mat) { + ItemStack itm = new ItemBuilder(Main.config, "settings.captcha.gui.item").setType(mat.name()).build(); + return itm; + } + +} diff --git a/src/com/entryrise/afkguard/cmd/CommandListener.java b/src/com/entryrise/afkguard/cmd/CommandListener.java new file mode 100644 index 0000000..98f1ac6 --- /dev/null +++ b/src/com/entryrise/afkguard/cmd/CommandListener.java @@ -0,0 +1,59 @@ +package com.entryrise.afkguard.cmd; + +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import com.entryrise.afkguard.Main; +import com.entryrise.afkguard.antiafk.AFKManager; +import com.entryrise.afkguard.utils.MathUtils; + +public class CommandListener implements CommandExecutor { + + private static Set debug + + public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { + Player p = sender instanceof Player ? (Player) sender : null; + + if (p == null) { + sender.sendMessage(Main.PREFIX + "Can't run from console"); + return true; + } + + if(args.length >= 2 && args[0].equalsIgnoreCase("captcha")) { + Player pl = Bukkit.getPlayer(args[1]); + + if (pl == null) { + p.sendMessage(Main.PREFIX + "That player doesn't exist"); + return true; + } + + Main.openCapcha(pl, args.length >= 3 && MathUtils.isInt(args[2]) ? Integer.valueOf(args[2]) : 0); + } else if (args.length >= 3 && args[0].equalsIgnoreCase("setscore")) { + Player pl = Bukkit.getPlayer(args[1]); + + if (pl == null) { + p.sendMessage(Main.PREFIX + "That player doesn't exist"); + return true; + } + + AFKManager.setScore(pl, MathUtils.isInt(args[2]) ? Integer.valueOf(args[2]) : 0); + } else if (args.length >= 2 && args[0].equalsIgnoreCase("getscore")) { + Player pl = Bukkit.getPlayer(args[1]); + + if (pl == null) { + p.sendMessage(Main.PREFIX + "That player doesn't exist"); + return true; + } + + p.sendMessage(Main.PREFIX + "Player " + pl.getName() + " has score " + AFKManager.getScore(pl)); + } else + + sender.sendMessage(Main.PREFIX + "Valid args: captcha [Player] [Reset Counter], setscore [Player] [Score]"); + return true; + + } + +} diff --git a/src/com/entryrise/afkguard/cmd/CommandTabListener.java b/src/com/entryrise/afkguard/cmd/CommandTabListener.java new file mode 100644 index 0000000..6b6d65c --- /dev/null +++ b/src/com/entryrise/afkguard/cmd/CommandTabListener.java @@ -0,0 +1,19 @@ +package com.entryrise.afkguard.cmd; + +import java.util.ArrayList; +import java.util.List; + +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabCompleter; + +public class CommandTabListener implements TabCompleter { + + + public List onTabComplete(CommandSender sender, Command cmd, String alias, String[] args) { + return new ArrayList(); + + + } + +} diff --git a/src/com/entryrise/afkguard/gui/GUI.java b/src/com/entryrise/afkguard/gui/GUI.java new file mode 100644 index 0000000..0ef1bea --- /dev/null +++ b/src/com/entryrise/afkguard/gui/GUI.java @@ -0,0 +1,154 @@ +package com.entryrise.afkguard.gui; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryCloseEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +import com.entryrise.afkguard.Main; + + +public abstract class GUI implements Listener { + + private int size; + private String title; + private boolean persistent; + + public Map invs = null; + + // Decorate inventory (Borders, blabla) + public abstract void decorateInventory(Inventory inv); + + public abstract void setGUIItems(GUIInventory ginv, UUID u); + + protected boolean isPersistent() { + return persistent; + } + + public GUI getCurrentInstance() { + return this; + } + + public GUI(String title, int size, boolean persistent) { + this.title = title; + this.size = size; + this.persistent = persistent; + + invs = new HashMap(); + + Bukkit.getPluginManager().registerEvents(this, Main.p); + } + + public void openInventory(Player p) { + p.openInventory(getInventory(p)); + } + + public Inventory getInventory(Player p) { + UUID u = p.getUniqueId(); + + if (invs.containsKey(u)) { + return invs.get(u).inv; + } + + return createInventory(u); + } + + private Inventory createInventory(UUID u) { + Inventory inv = Bukkit.createInventory(null, size, title); + + decorateInventory(inv); + + GUIInventory ginv = new GUIInventory(inv); + + invs.put(u, ginv); + + setGUIItems(ginv, u); + + return inv; + } + + public void refreshInventory(UUID u) { + GUIInventory ginv = invs.get(u); + + if (ginv == null) { + return; + } + + ginv.reset(); + + decorateInventory(ginv.inv); + setGUIItems(ginv, u); + } + + @EventHandler + public void onClick(InventoryClickEvent e) { + if (e.isCancelled()) { + return; + } + + Player p = (Player) e.getWhoClicked(); + Inventory inv = e.getClickedInventory(); + ItemStack hand = e.getCursor(); + + UUID u = p.getUniqueId(); + + if (!invs.containsKey(u)) { + return; + } + + GUIInventory ginv = invs.get(u); + + if (!ginv.inv.equals(inv)) { + return; + } + + // CANCEL ANYTHING + e.setCancelled(true); + + GUIItem gitem = ginv.getGUIItem(e.getSlot()); + + if (gitem == null) { + return; + } + + if (e.isRightClick()) { + gitem.onRightClick(p, inv, hand); + } + if (e.isLeftClick()) { + gitem.onLeftClick(p, inv, hand); + } + } + + @EventHandler + public void onClose(InventoryCloseEvent e) { + Player p = (Player) e.getPlayer(); + Inventory inv = e.getInventory(); + + UUID u = p.getUniqueId(); + + if (!invs.containsKey(u)) { + return; + } + + GUIInventory ginv = invs.get(u); + + if (!ginv.inv.equals(inv)) { + return; + } + + if (isPersistent()) { + return; + } + + invs.remove(u); + } + +} diff --git a/src/com/entryrise/afkguard/gui/GUIInventory.java b/src/com/entryrise/afkguard/gui/GUIInventory.java new file mode 100644 index 0000000..dbc7e24 --- /dev/null +++ b/src/com/entryrise/afkguard/gui/GUIInventory.java @@ -0,0 +1,45 @@ +package com.entryrise.afkguard.gui; + +import java.util.ArrayList; +import java.util.List; + +import org.bukkit.inventory.Inventory; + +public class GUIInventory { + + public Inventory inv; + public List items = new ArrayList(); + + public GUIItem getGUIItem(int slot) { + for (GUIItem itm : items) { + if (itm.getSlot() == slot) { + return itm; + } + } + return null; + } + + public void addGUIItem(GUIItem itm) { + items.add(itm); + + inv.setItem(itm.getSlot(), itm.getItm().clone()); + } + + public void reset() { + items.clear(); + } + + public GUIInventory(Inventory inv) { + this.inv = inv; + + } + + public GUIInventory(Inventory inv, List items) { + this.inv = inv; + for (GUIItem itm : items) { + addGUIItem(itm); + } + } + + +} diff --git a/src/com/entryrise/afkguard/gui/GUIItem.java b/src/com/entryrise/afkguard/gui/GUIItem.java new file mode 100644 index 0000000..3c2af3b --- /dev/null +++ b/src/com/entryrise/afkguard/gui/GUIItem.java @@ -0,0 +1,45 @@ +package com.entryrise.afkguard.gui; + +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +public abstract class GUIItem { + + private ItemStack itm; + private int slot; + + public ItemStack getItm() { + return itm; + } + + public void setItm(ItemStack itm) { + this.itm = itm; + } + + public int getSlot() { + return slot; + } + + public void setSlot(int slot) { + this.slot = slot; + } + + public GUIItem(ItemStack itm, int slot) { + this.setSlot(slot); + this.setItm(itm); + } + + public void onLeftClick(Player p, Inventory inv, ItemStack hand) { + + } + + public void onRightClick(Player p, Inventory inv, ItemStack hand) { + + } + + public void onMiddleClick(Player p, Inventory inv, ItemStack hand) { + + } + +} diff --git a/src/com/entryrise/afkguard/gui/GUIManager.java b/src/com/entryrise/afkguard/gui/GUIManager.java new file mode 100644 index 0000000..5cc62fa --- /dev/null +++ b/src/com/entryrise/afkguard/gui/GUIManager.java @@ -0,0 +1,20 @@ +package com.entryrise.afkguard.gui; + +import org.bukkit.Bukkit; + + +public class GUIManager { + + // public static TestGUI TestGUI; + + public static void Enabler(boolean reload) { + if (!reload) { + registerGUIs(); + } + Bukkit.getLogger().info(" §e[§a✔§e] §fGUIs loaded"); + } + + private static void registerGUIs() { + } + +} diff --git a/src/com/entryrise/afkguard/gui/inputs/GUIBookInput.java b/src/com/entryrise/afkguard/gui/inputs/GUIBookInput.java new file mode 100644 index 0000000..9fc12b7 --- /dev/null +++ b/src/com/entryrise/afkguard/gui/inputs/GUIBookInput.java @@ -0,0 +1,82 @@ +package com.entryrise.afkguard.gui.inputs; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.bukkit.Bukkit; +import org.bukkit.entity.HumanEntity; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; +import org.bukkit.event.player.PlayerEditBookEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.BookMeta; + +import com.entryrise.afkguard.Main; +import com.entryrise.afkguard.utils.VersionMgr; + +public abstract class GUIBookInput extends GUIInput> { + + private static ItemStack editbook; + + private Inventory inv; + private Set ents = new HashSet(); + + static { + editbook = new ItemStack(VersionMgr.getWritableBook()); + BookMeta bmeta = (BookMeta) editbook.getItemMeta(); + bmeta.setDisplayName("§d§4EdItB00k"); + } + + public Inventory getInv() { + return inv; + } + + public void setInv(Inventory inv) { + this.inv = inv; + } + + @EventHandler(priority = EventPriority.LOW) + public void onChat(PlayerEditBookEvent e) { + if (e.isCancelled()) { + return; + } + + Player p = e.getPlayer(); + + if (!ents.contains(p)) { + return; + } + + e.setCancelled(true); + + for (HumanEntity ent : ents) { + ent.openInventory(inv); + } + + HandlerList.unregisterAll(this); + + onResult(Arrays.asList(e.getNewBookMeta().getPage(0).split("\n"))); + + } + + public GUIBookInput(Inventory inv) { + this.setInv(inv); + + for (HumanEntity ent : inv.getViewers()) { + ents.add(ent); + ent.closeInventory(); + + Player p = (Player) ent; + + VersionMgr.openBook(p, editbook); + } + + Bukkit.getPluginManager().registerEvents(this, Main.p); + } + +} diff --git a/src/com/entryrise/afkguard/gui/inputs/GUIChatInput.java b/src/com/entryrise/afkguard/gui/inputs/GUIChatInput.java new file mode 100644 index 0000000..0768c1d --- /dev/null +++ b/src/com/entryrise/afkguard/gui/inputs/GUIChatInput.java @@ -0,0 +1,83 @@ +package com.entryrise.afkguard.gui.inputs; + +import java.util.HashSet; +import java.util.Set; + +import org.bukkit.Bukkit; +import org.bukkit.entity.HumanEntity; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; +import org.bukkit.event.player.AsyncPlayerChatEvent; +import org.bukkit.event.player.PlayerCommandPreprocessEvent; +import org.bukkit.inventory.Inventory; + +import com.entryrise.afkguard.Main; + +public abstract class GUIChatInput extends GUIInput { + + private Inventory inv; + private Set ents = new HashSet(); + + public Inventory getInv() { + return inv; + } + + public void setInv(Inventory inv) { + this.inv = inv; + } + + @EventHandler(priority = EventPriority.LOW) + public void onCommand(PlayerCommandPreprocessEvent e) { + if (e.isCancelled()) { + return; + } + + Player p = e.getPlayer(); + + if (!ents.contains(p)) { + return; + } + + e.setCancelled(true); + p.sendMessage("You can't execute commands while inputting in a GUI."); + } + + @EventHandler(priority = EventPriority.LOW) + public void onChat(AsyncPlayerChatEvent e) { + if (e.isCancelled()) { + return; + } + + Player p = e.getPlayer(); + + if (!ents.contains(p)) { + return; + } + + String chat = e.getMessage(); + e.setCancelled(true); + + for (HumanEntity ent : ents) { + ent.openInventory(inv); + } + + HandlerList.unregisterAll(this); + + onResult(chat); + + } + + public GUIChatInput(Inventory inv) { + this.setInv(inv); + + for (HumanEntity ent : inv.getViewers()) { + ents.add(ent); + ent.closeInventory(); + } + + Bukkit.getPluginManager().registerEvents(this, Main.p); + } + +} diff --git a/src/com/entryrise/afkguard/gui/inputs/GUIInput.java b/src/com/entryrise/afkguard/gui/inputs/GUIInput.java new file mode 100644 index 0000000..cfc333f --- /dev/null +++ b/src/com/entryrise/afkguard/gui/inputs/GUIInput.java @@ -0,0 +1,11 @@ +package com.entryrise.afkguard.gui.inputs; + +import org.bukkit.event.Listener; + +public abstract class GUIInput implements Listener { + + public abstract void onResult(E result); + + + +} diff --git a/src/com/entryrise/afkguard/listeners/AFKListener.java b/src/com/entryrise/afkguard/listeners/AFKListener.java new file mode 100644 index 0000000..936b546 --- /dev/null +++ b/src/com/entryrise/afkguard/listeners/AFKListener.java @@ -0,0 +1,119 @@ +package com.entryrise.afkguard.listeners; + +import java.util.Map; +import java.util.WeakHashMap; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.event.entity.EntityPickupItemEvent; +import org.bukkit.event.player.AsyncPlayerChatEvent; +import org.bukkit.event.player.PlayerFishEvent; +import org.bukkit.event.player.PlayerToggleFlightEvent; +import org.bukkit.event.player.PlayerToggleSprintEvent; + +import com.entryrise.afkguard.Main; +import com.entryrise.afkguard.antiafk.AFKManager; + +public class AFKListener implements Listener { + + private Map oldlocations = new WeakHashMap<>(); + + public AFKListener() { + Bukkit.getScheduler().runTaskTimer(Main.p, () -> { + + for (Player p : Bukkit.getOnlinePlayers()) { + oldlocations.compute(p, (k, oldloc) -> { + + Location newloc = p.getLocation(); + + if (oldloc == null) { + return newloc; + } + + double multiplier = Main.config + .getDouble("settings.afk-detection.counter.actions.location-movement"); + + double value = 0; + + if (!p.isInsideVehicle()) { + if (oldloc.getWorld() == newloc.getWorld()) { + value += multiplier * oldloc.distanceSquared(newloc); + } else { + value += Main.config.getDouble("settings.afk-detection.counter.actions.world-movement"); + } + } else { + value += Main.config.getDouble("settings.afk-detection.counter.actions.riding-movement"); + } + + value += (Math.abs(newloc.getYaw() - oldloc.getYaw()) + + Math.abs((newloc.getPitch() - oldloc.getPitch())) + * Main.config.getDouble("settings.afk-detection.counter.actions.mouse-movement")); + + AFKManager.incrementPlayer(p, value); + + return newloc; + }); + } + + }, 100, 100); + } + + /* + * The plugin uses monitor as it's the intended priority for monitoring tasks. + * + * Since afkguard doesn't do anything directly in the event, it is the ideal + * choice for this usecase. + */ + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onChat(AsyncPlayerChatEvent e) { + AFKManager.incrementPlayer(e.getPlayer(), Main.config.getDouble("settings.afk-detection.counter.actions.chat")); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onBlockBreak(BlockBreakEvent e) { + AFKManager.incrementPlayer(e.getPlayer(), + Main.config.getDouble("settings.afk-detection.counter.actions.block-break")); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onBlockPlace(BlockPlaceEvent e) { + AFKManager.incrementPlayer(e.getPlayer(), + Main.config.getDouble("settings.afk-detection.counter.actions.block-place")); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onToggleSprint(PlayerToggleSprintEvent e) { + AFKManager.incrementPlayer(e.getPlayer(), + Main.config.getDouble("settings.afk-detection.counter.actions.toggle-sprint")); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onToggleSprint(PlayerToggleFlightEvent e) { + AFKManager.incrementPlayer(e.getPlayer(), + Main.config.getDouble("settings.afk-detection.counter.actions.toggle-flight")); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onBlockPlace(PlayerFishEvent e) { + AFKManager.incrementPlayer(e.getPlayer(), + Main.config.getDouble("settings.afk-detection.counter.actions.fishing")); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onItemPickup(EntityPickupItemEvent e) { + Player p = e.getEntity() instanceof Player ? (Player) e.getEntity() : null; + + if (p == null) { + return; + } + AFKManager.incrementPlayer(p, Main.config.getDouble("settings.afk-detection.counter.actions.pickup-item")); + } + +} diff --git a/src/com/entryrise/afkguard/listeners/ListenersMain.java b/src/com/entryrise/afkguard/listeners/ListenersMain.java new file mode 100644 index 0000000..03f524f --- /dev/null +++ b/src/com/entryrise/afkguard/listeners/ListenersMain.java @@ -0,0 +1,20 @@ +package com.entryrise.afkguard.listeners; + +import org.bukkit.Bukkit; + +import com.entryrise.afkguard.Main; + +public class ListenersMain { + + public static void Enabler(boolean reload) { + if (!reload) { + registerEvents(); + } + Bukkit.getLogger().info(" §e[§a✔§e] §fEvent-Listeners loaded"); + } + + private static void registerEvents() { + Bukkit.getServer().getPluginManager().registerEvents(new AFKListener(), Main.p); + } + +} diff --git a/src/com/entryrise/afkguard/mineutils/HeadUtils.java b/src/com/entryrise/afkguard/mineutils/HeadUtils.java new file mode 100644 index 0000000..f38ee54 --- /dev/null +++ b/src/com/entryrise/afkguard/mineutils/HeadUtils.java @@ -0,0 +1,62 @@ +package com.entryrise.afkguard.mineutils; + +import java.lang.reflect.Field; +import java.util.Base64; +import java.util.UUID; + +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.inventory.meta.SkullMeta; + +import com.mojang.authlib.GameProfile; +import com.mojang.authlib.properties.Property; + +public class HeadUtils { + + public static ItemStack getSkull(String skinURL) { + ItemStack head = getSkullItem(); + if(skinURL.isEmpty())return head; + + + ItemMeta headMeta = head.getItemMeta(); + GameProfile profile = new GameProfile(UUID.randomUUID(), null); + byte[] encodedData = Base64.getEncoder().encode(String.format("{textures:{SKIN:{url:\"%s\"}}}", skinURL).getBytes()); + profile.getProperties().put("textures", new Property("textures", new String(encodedData))); + Field profileField = null; + try { + profileField = headMeta.getClass().getDeclaredField("profile"); + } catch (NoSuchFieldException | SecurityException e) { + e.printStackTrace(); + } + profileField.setAccessible(true); + try { + profileField.set(headMeta, profile); + } catch (IllegalArgumentException | IllegalAccessException e) { + e.printStackTrace(); + } + head.setItemMeta(headMeta); + return head; + } + + public static ItemStack getPlayerSkull(String username) { + ItemStack head = getSkullItem(); + ItemMeta imeta = head.getItemMeta(); + + SkullMeta smeta = (SkullMeta) imeta; + + smeta.setOwner(username); + head.setItemMeta(smeta); + return head; + } + + public static Material getSkull() { + Material mat = Material.getMaterial("SKULL_ITEM"); + + return (mat != null) ? mat : Material.getMaterial("PLAYER_HEAD"); + } + + public static ItemStack getSkullItem() { + return new ItemStack(getSkull(), 1, (short) 3); + } +} diff --git a/src/com/entryrise/afkguard/mineutils/InventoryUtils.java b/src/com/entryrise/afkguard/mineutils/InventoryUtils.java new file mode 100644 index 0000000..86c5a4a --- /dev/null +++ b/src/com/entryrise/afkguard/mineutils/InventoryUtils.java @@ -0,0 +1,95 @@ +package com.entryrise.afkguard.mineutils; + +import java.util.ArrayList; +import java.util.List; + +import org.bukkit.enchantments.Enchantment; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemFlag; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +public class InventoryUtils { + + public static List filledSquare(Inventory inv, ItemStack filler, int from, int to) { + int row1 = from%9; + int line1 = from/9; + + int row2 = to%9; + int line2 = to/9; + + List filled = new ArrayList(); + + for (int i = line1; i<=line2; i++) { + for (int j = row1; j<=row2; j++) { + int slot = i*9+j; + filled.add(slot); + inv.setItem(slot, filler); + } + } + + return filled; + } + + public static List outlineSquare(Inventory inv, ItemStack filler, int from, int to) { + int row1 = from%9; + int line1 = from/9; + + int row2 = to%9; + int line2 = to/9; + + List filled = new ArrayList(); + + for (int i=row1; i<=row2; i++) { + filled.add(line1*9+i); + filled.add(line2*9+i); + } + + for (int i=line1; i<=line2; i++) { + filled.add(i*9+row1); + filled.add(i*9+row2); + } + + for (int slot : filled) { + inv.setItem(slot, filler); + } + + return filled; + } + + public static List centeredCross(Inventory inv, ItemStack filler, int center, int size) { + int row = center%9; + int line = center/9; + + List filled = new ArrayList(); + + for (int i = Math.max(0, line-size); i<=Math.min(8, line+size); i++) { + int slot = i*9+row; + filled.add(slot); + inv.setItem(slot, filler); + } + + for (int i = Math.max(0, row-size); i<=Math.min(8, row+size); i++) { + int slot = line*9+i; + filled.add(slot); + inv.setItem(slot, filler); + } + + return filled; + } + + public static ItemStack setName(ItemStack itm, String name) { + ItemMeta imeta = itm.getItemMeta(); + imeta.setDisplayName(name); + itm.setItemMeta(imeta); + return itm; + } + + public static ItemStack setShiny(ItemStack itm) { + ItemMeta imeta = itm.getItemMeta(); + imeta.addEnchant(Enchantment.DURABILITY, 10, true); + imeta.addItemFlags(ItemFlag.HIDE_ENCHANTS); + itm.setItemMeta(imeta); + return itm; + } +} diff --git a/src/com/entryrise/afkguard/mineutils/ItemBuilder.java b/src/com/entryrise/afkguard/mineutils/ItemBuilder.java new file mode 100644 index 0000000..997ac93 --- /dev/null +++ b/src/com/entryrise/afkguard/mineutils/ItemBuilder.java @@ -0,0 +1,190 @@ +package com.entryrise.afkguard.mineutils; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.inventory.ItemFlag; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +public class ItemBuilder { + + private ItemStack itm; + + public ItemBuilder(Material mat) { + this.itm = new ItemStack(mat); + } + + public ItemBuilder(ItemStack itm) { + this.itm = itm; + } + + public ItemBuilder(FileConfiguration config, String location, String... placeholders) { + if (!config.contains(location)) { + return; + } + + this.setType(config.getString(location + ".material")); + this.setName(config.getString(location + ".name")); + + List stgs = config.getStringList(location + ".lore"); + + if (stgs != null && !stgs.isEmpty()) { + this.setLore(stgs.toArray(new String[stgs.size()])); + setLorePlaceholders(placeholders); + } + + if (config.getBoolean(location + ".shiny")) { + this.setShiny(); + } + + setNamePlaceholders(placeholders); + + } + + public ItemBuilder setType(String material) { + if (material == null) { + material = "BARRIER"; + } + ItemMeta imeta = (itm == null) ? null : itm.getItemMeta().clone(); + + String displayname = null; + List lore = null; + Set flags = null; + Map enchants = null; + + if (imeta != null) { + displayname = imeta.hasDisplayName() ? imeta.getDisplayName() : null; + lore = imeta.hasLore() ? imeta.getLore() : null; + enchants = imeta.hasEnchants() ? imeta.getEnchants() : null; + flags = imeta.getItemFlags(); + } + +// Bukkit.getPlayer("Stefytorus").getInventory().addItem(itm); + + String[] split = material.split(":"); + + if (material.startsWith("head:")) { + itm = HeadUtils.getPlayerSkull(material.replace("head:", "")); + } else if (material.startsWith("urlhead:")) { + itm = HeadUtils.getSkull(material.replace("urlhead:", "")); + } else if (split.length == 2) { + Material mat = Material.getMaterial(split[0]); + if (mat == null) { + mat = Material.getMaterial("BARRIER"); + } + itm = new ItemStack(mat, 1, Short.valueOf(split[1])); + } else { + Material mat = Material.getMaterial(material); + if (mat == null) { + mat = Material.getMaterial("BARRIER"); + } + itm = new ItemStack(mat); + } + + imeta = itm.getItemMeta(); + if (lore != null) { + imeta.setLore(lore); + } + if (displayname != null) { + imeta.setDisplayName(displayname); + } + if (enchants != null) { + for (Enchantment ench : enchants.keySet()) { + imeta.addEnchant(ench, enchants.get(ench), true); + } + } + if (flags != null) { + imeta.addItemFlags(flags.toArray(new ItemFlag[flags.size()])); + } + itm.setItemMeta(imeta); + + return this; + } + + public ItemBuilder setName(String name) { + ItemMeta imeta = itm.getItemMeta(); + imeta.setDisplayName(ChatColor.translateAlternateColorCodes('&', name)); + itm.setItemMeta(imeta); + return this; + } + + public ItemBuilder setShiny() { + ItemMeta imeta = itm.getItemMeta(); + imeta.addEnchant(Enchantment.DURABILITY, 10, true); + imeta.addItemFlags(ItemFlag.HIDE_ENCHANTS); + itm.setItemMeta(imeta); + return this; + } + + public ItemBuilder setLore(String... lore) { + List solved = new ArrayList(); + for (String stg : lore) { + solved.add(ChatColor.translateAlternateColorCodes('&', stg)); + } + ItemMeta imeta = itm.getItemMeta(); + imeta.setLore(solved); + itm.setItemMeta(imeta); + return this; + } + + public ItemBuilder setLorePlaceholders(String... placeholders) { + ItemMeta imeta = itm.getItemMeta(); + List lore = imeta.getLore(); + + for (int i = 0; i < lore.size(); i++) { + String val = lore.get(i); + for (int j = 0; j < placeholders.length; j += 2) { + val = val.replace(placeholders[j], placeholders[j + 1]); + } + lore.set(i, val); + } + imeta.setLore(lore); + itm.setItemMeta(imeta); + return this; + } + + public ItemBuilder setLorePlaceholders(String placeholder, List placement) { + ItemMeta imeta = itm.getItemMeta(); + List lore = imeta.getLore(); + List newlore = new ArrayList(); + for (int i = 0; i < lore.size(); i++) { + String val = lore.get(i); + if (val.contains(placeholder)) { + val = val.replace(placeholder, ""); + newlore.add(val); + for (String stg : placement) { + newlore.add(stg); + } + continue; + } + newlore.add(val); + } + imeta.setLore(newlore); + itm.setItemMeta(imeta); + return this; + } + + public ItemBuilder setNamePlaceholders(String... placeholders) { + ItemMeta imeta = itm.getItemMeta(); + String name = imeta.getDisplayName(); + + for (int j = 0; j < placeholders.length; j += 2) { + name = name.replace(placeholders[j], placeholders[j + 1]); + } + imeta.setDisplayName(name); + itm.setItemMeta(imeta); + return this; + } + + public ItemStack build() { + return itm; + } + +} diff --git a/src/com/entryrise/afkguard/mineutils/ValidationUtils.java b/src/com/entryrise/afkguard/mineutils/ValidationUtils.java new file mode 100644 index 0000000..2176276 --- /dev/null +++ b/src/com/entryrise/afkguard/mineutils/ValidationUtils.java @@ -0,0 +1,9 @@ +package com.entryrise.afkguard.mineutils; + +public class ValidationUtils { + + public static boolean isAlphanumeric(String stg) { + return stg.matches("^([A-Za-z]|[0-9]|_)+$"); + } + +} diff --git a/src/com/entryrise/afkguard/utils/Cache.java b/src/com/entryrise/afkguard/utils/Cache.java new file mode 100644 index 0000000..f5e78ec --- /dev/null +++ b/src/com/entryrise/afkguard/utils/Cache.java @@ -0,0 +1,72 @@ +package com.entryrise.afkguard.utils; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +public class Cache { + + protected class Cachebox { + + protected T cached; + protected int time; + + protected Cachebox(T cached, int time) { + this.cached = cached; + this.time = time; + } + + protected boolean tick() { + time--; + return time <= 0; + } + } + + private int cachetime; + private Map cachestorage = new HashMap(); + + public Cache(int cachetime) { + this.cachetime = cachetime; + } + + public void putCached(S key, T cached) { + cachestorage.put(key, new Cachebox(cached, cachetime)); + } + + public boolean isCached(S key) { + return cachestorage.containsKey(key); + } + + public T getCached(S key) { + if (isCached(key)) { + return cachestorage.get(key).cached; + } + return null; + } + + public void tick() { + Set removables = new HashSet(); + + for (S key : cachestorage.keySet()) { + Cachebox box = cachestorage.get(key); + + if (!box.tick()) { + continue; + } + + removables.add(key); + } + + cachestorage.keySet().removeAll(removables); + } + + public void clear() { + cachestorage.clear(); + } + + public boolean remove(S key) { + return cachestorage.remove(key) != null; + } + +} diff --git a/src/com/entryrise/afkguard/utils/Chat.java b/src/com/entryrise/afkguard/utils/Chat.java new file mode 100644 index 0000000..287447a --- /dev/null +++ b/src/com/entryrise/afkguard/utils/Chat.java @@ -0,0 +1,30 @@ +package com.entryrise.afkguard.utils; + +import net.md_5.bungee.api.chat.ClickEvent; +import net.md_5.bungee.api.chat.ComponentBuilder; +import net.md_5.bungee.api.chat.HoverEvent; +import net.md_5.bungee.api.chat.TextComponent; + +public class Chat { + + public static TextComponent genHoverAndSuggestTextComponent(String show, String hover, String click) { + TextComponent msg = new TextComponent(show); + msg.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder(hover).create())); + msg.setClickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, "/" + click)); + return msg; + } + + public static TextComponent genHoverAndRunCommandTextComponent(String show, String hover, String click) { + TextComponent msg = new TextComponent(show); + msg.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder(hover).create())); + msg.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/" + click)); + return msg; + } + + public static TextComponent genHoverTextComponent(String show, String hover) { + TextComponent msg = new TextComponent(show); + msg.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder(hover).create())); + return msg; + } + +} diff --git a/src/com/entryrise/afkguard/utils/MathUtils.java b/src/com/entryrise/afkguard/utils/MathUtils.java new file mode 100644 index 0000000..339c244 --- /dev/null +++ b/src/com/entryrise/afkguard/utils/MathUtils.java @@ -0,0 +1,19 @@ +package com.entryrise.afkguard.utils; + +public class MathUtils { + + public static int toMegaByte(long bytes) { + return (int) (bytes / 1048576); + } + + public static boolean isInt(String str) { + try { + int d = Integer.parseInt(str); + d = d + 1; + } catch (NumberFormatException nfe) { + return false; + } + return true; + } + +} diff --git a/src/com/entryrise/afkguard/utils/Others.java b/src/com/entryrise/afkguard/utils/Others.java new file mode 100644 index 0000000..06a3c26 --- /dev/null +++ b/src/com/entryrise/afkguard/utils/Others.java @@ -0,0 +1,75 @@ +package com.entryrise.afkguard.utils; + +import java.io.File; + +import org.apache.commons.lang.RandomStringUtils; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.WorldBorder; +import org.bukkit.configuration.file.YamlConfiguration; + +import com.entryrise.afkguard.Main; + + +public class Others { + + public static boolean isInsideBorder(Location loc) { + WorldBorder border = loc.getWorld().getWorldBorder(); + double radius = border.getSize() / 2; + Location location = loc, center = border.getCenter(); + + return center.distanceSquared(location) < (radius * radius); + } + + public static YamlConfiguration getConfig(File f, int version) { + YamlConfiguration fc = new YamlConfiguration(); + + if (!f.exists()) { + f.getParentFile().mkdirs(); + Main.p.saveResource(f.getName(), false); + } + + try { + fc.load(f); + + if (fc.contains("version")) { + if (fc.getInt("version") != version) { + Bukkit.getLogger().info(Main.PREFIX + "Updating " + f.getName() + " file!"); + f.renameTo(getOldFile(f)); + Main.p.saveResource(f.getName(), false); + fc.load(f); + + } + } else { + Bukkit.getLogger().info(Main.PREFIX + "Updating " + f.getName() + " file!"); + f.renameTo(getOldFile(f)); + Main.p.saveResource(f.getName(), false); + fc.load(f); + } + + } catch (Exception e) { + e.printStackTrace(); + } + + return fc; + + } + + private static File getOldFile(File f) { + return new File(f.getParentFile(), f.getName() + "." + RandomStringUtils.randomAlphanumeric(3) + ".old"); + } + + public static String serializeLocation(Location loc) { + if (loc.getWorld() == null) { + System.out.println("world doesn't exist man!"); + } + return loc.getWorld().getName() + ";" + loc.getX() + ";" + loc.getY() + ";" + loc.getZ() + ";" + loc.getYaw() + ";" + + loc.getPitch(); + } + + public static Location deserializeLocation(String loc) { + String[] rawloc = loc.split(";"); + return new Location(Bukkit.getWorld(rawloc[0]), Double.valueOf(rawloc[1]), Double.valueOf(rawloc[2]), + Double.valueOf(rawloc[3]), Float.valueOf(rawloc[4]), Float.valueOf(rawloc[5])); + } +} diff --git a/src/com/entryrise/afkguard/utils/VersionMgr.java b/src/com/entryrise/afkguard/utils/VersionMgr.java new file mode 100644 index 0000000..960485e --- /dev/null +++ b/src/com/entryrise/afkguard/utils/VersionMgr.java @@ -0,0 +1,111 @@ +package com.entryrise.afkguard.utils; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import com.entryrise.afkguard.Main; + +public class VersionMgr { + + public static boolean isV1_8() { + return Bukkit.getVersion().contains("1.8"); + } + + public static boolean isV1_9() { + return Bukkit.getVersion().contains("1.9"); + } + + public static boolean isV1_10() { + return Bukkit.getVersion().contains("1.10"); + } + + public static boolean isV1_13() { + return Bukkit.getVersion().contains("1.13"); + } + + public static boolean isV1_11() { + if (Bukkit.getVersion().contains("1.8")) { + return false; + } + if (Bukkit.getVersion().contains("1.9")) { + return false; + } + if (Bukkit.getVersion().contains("1.10")) { + return false; + } + return true; + } + + public static boolean isV1_12() { + if (Bukkit.getVersion().contains("1.8")) { + return false; + } + if (Bukkit.getVersion().contains("1.9")) { + return false; + } + if (Bukkit.getVersion().contains("1.10")) { + return false; + } + if (Bukkit.getVersion().contains("1.11")) { + return false; + } + return true; + } + + public static boolean isNewMaterials() { + if (isV1_8()) { + return false; + } + + if (isV1_9()) { + return false; + } + + if (isV1_10()) { + return false; + } + + if (isV1_11()) { + return false; + } + + if (isV1_12()) { + return false; + } + + return true; + + } + + public static boolean isPaper() { + try { + Class.forName("com.destroystokyo.paper.PaperConfig"); + } catch (ClassNotFoundException e) { + return false; + } + return true; + } + + public static String ChunkExistsName() { + return isV1_13() ? "f" : "e"; + } + + public static Material getWritableBook() { + return Material.getMaterial(isNewMaterials() ? "WRITABLE_BOOK" : "BOOK_AND_QUILL"); + } + + public static void openBook(Player p, ItemStack book) { + ItemStack itm = p.getInventory().getItemInHand(); + p.getInventory().setItemInHand(book); + + String channel = isNewMaterials() ? "minecraft:book_open" : "MC|BOpen"; + + // Main hand = 0 | Off hand = 1 + p.sendPluginMessage(Main.p, channel, new byte[0]); + + p.getInventory().setItemInHand(itm); + } + +}