From cae9d2f67e25daf0d79037a0b56c8d2d20f38d50 Mon Sep 17 00:00:00 2001 From: cnr Date: Thu, 1 Dec 2016 01:24:05 -0600 Subject: [PATCH] Anticheat Logging #299 Remove MAC Add AntiHack Logging Change thresholds, add notifs, move stuff around Add warning if check does not exist Handle multiple bans when ranked TWITCH+ Prevent punishment abuse --- Plugins/Mineplex.Core.Common/pom.xml | 5 + .../mineplex/core/common/util/PlayerMap.java | 3 +- .../mineplex/core/common/util/UtilServer.java | 71 +- Plugins/Mineplex.Core/pom.xml | 5 + .../src/mineplex/core/antihack/AntiHack.java | 1301 ++++------------- .../core/antihack/AntiHackRepository.java | 71 - .../core/antihack/CheckThresholds.java | 19 +- .../src/mineplex/core/antihack/Detector.java | 8 - .../core/antihack/MineplexLinkImpl.java | 50 + .../core/antihack/ViolationLevels.java | 87 ++ .../core/antihack/actions/AntiHackAction.java | 30 +- .../core/antihack/actions/BanwaveAction.java | 6 +- .../antihack/actions/ImmediateBanAction.java | 11 +- .../core/antihack/actions/MixedAction.java | 49 - .../core/antihack/actions/NoopAction.java | 2 +- .../antihack/animations/BanwaveAnimation.java | 9 + .../animations/BanwaveAnimationSpin.java | 123 ++ .../core/antihack/banwave/BanWaveInfo.java | 15 + .../core/antihack/banwave/BanWaveManager.java | 37 +- .../antihack/banwave/BanWaveRepository.java | 18 +- .../commands/AnticheatOffCommand.java | 24 + .../antihack/commands/AnticheatOnCommand.java | 24 + .../commands/DetailedMessagesCommand.java | 30 + .../core/antihack/commands/GetVlsCommand.java | 47 + .../antihack/commands/TestBanCommand.java | 51 + .../{ => guardians}/AntiHackGuardian.java | 2 +- .../antihack/guardians/GuardianManager.java | 129 ++ .../antihack/logging/AnticheatDatabase.java | 196 +++ .../antihack/logging/AnticheatMetadata.java | 23 + .../core/antihack/logging/AntihackLogger.java | 214 +++ .../logging/builtin/PartyInfoMetadata.java | 55 + .../logging/builtin/PlayerInfoMetadata.java | 51 + .../logging/builtin/ServerInfoMetadata.java | 38 + .../builtin/ViolationInfoMetadata.java | 154 ++ .../GwenBanNotification.java | 46 + .../GwenBanwaveNotification.java | 53 + .../src/mineplex/core/antihack/types/Fly.java | 172 --- .../mineplex/core/antihack/types/Idle.java | 78 - .../mineplex/core/antihack/types/Reach.java | 123 -- .../mineplex/core/antihack/types/Speed.java | 105 -- .../src/mineplex/core/punish/Punish.java | 22 +- .../mineplex/core/punish/UI/PunishPage.java | 9 - .../src/mineplex/game/clans/Clans.java | 18 +- .../src/mineplex/clanshub/ClansHub.java | 6 +- .../Mineplex.Hub/src/mineplex/hub/Hub.java | 6 +- .../src/nautilus/game/arcade/Arcade.java | 10 +- .../anticheatmetadata/GameInfoMetadata.java | 207 +++ .../src/nautilus/game/arcade/game/Game.java | 10 +- .../game/arcade/managers/GameFlagManager.java | 12 +- 49 files changed, 2091 insertions(+), 1744 deletions(-) delete mode 100644 Plugins/Mineplex.Core/src/mineplex/core/antihack/AntiHackRepository.java delete mode 100644 Plugins/Mineplex.Core/src/mineplex/core/antihack/Detector.java create mode 100644 Plugins/Mineplex.Core/src/mineplex/core/antihack/MineplexLinkImpl.java create mode 100644 Plugins/Mineplex.Core/src/mineplex/core/antihack/ViolationLevels.java delete mode 100644 Plugins/Mineplex.Core/src/mineplex/core/antihack/actions/MixedAction.java create mode 100644 Plugins/Mineplex.Core/src/mineplex/core/antihack/animations/BanwaveAnimation.java create mode 100644 Plugins/Mineplex.Core/src/mineplex/core/antihack/animations/BanwaveAnimationSpin.java create mode 100644 Plugins/Mineplex.Core/src/mineplex/core/antihack/commands/AnticheatOffCommand.java create mode 100644 Plugins/Mineplex.Core/src/mineplex/core/antihack/commands/AnticheatOnCommand.java create mode 100644 Plugins/Mineplex.Core/src/mineplex/core/antihack/commands/DetailedMessagesCommand.java create mode 100644 Plugins/Mineplex.Core/src/mineplex/core/antihack/commands/GetVlsCommand.java create mode 100644 Plugins/Mineplex.Core/src/mineplex/core/antihack/commands/TestBanCommand.java rename Plugins/Mineplex.Core/src/mineplex/core/antihack/{ => guardians}/AntiHackGuardian.java (99%) create mode 100644 Plugins/Mineplex.Core/src/mineplex/core/antihack/guardians/GuardianManager.java create mode 100644 Plugins/Mineplex.Core/src/mineplex/core/antihack/logging/AnticheatDatabase.java create mode 100644 Plugins/Mineplex.Core/src/mineplex/core/antihack/logging/AnticheatMetadata.java create mode 100644 Plugins/Mineplex.Core/src/mineplex/core/antihack/logging/AntihackLogger.java create mode 100644 Plugins/Mineplex.Core/src/mineplex/core/antihack/logging/builtin/PartyInfoMetadata.java create mode 100644 Plugins/Mineplex.Core/src/mineplex/core/antihack/logging/builtin/PlayerInfoMetadata.java create mode 100644 Plugins/Mineplex.Core/src/mineplex/core/antihack/logging/builtin/ServerInfoMetadata.java create mode 100644 Plugins/Mineplex.Core/src/mineplex/core/antihack/logging/builtin/ViolationInfoMetadata.java create mode 100644 Plugins/Mineplex.Core/src/mineplex/core/antihack/redisnotifications/GwenBanNotification.java create mode 100644 Plugins/Mineplex.Core/src/mineplex/core/antihack/redisnotifications/GwenBanwaveNotification.java delete mode 100644 Plugins/Mineplex.Core/src/mineplex/core/antihack/types/Fly.java delete mode 100644 Plugins/Mineplex.Core/src/mineplex/core/antihack/types/Idle.java delete mode 100644 Plugins/Mineplex.Core/src/mineplex/core/antihack/types/Reach.java delete mode 100644 Plugins/Mineplex.Core/src/mineplex/core/antihack/types/Speed.java create mode 100644 Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/anticheatmetadata/GameInfoMetadata.java diff --git a/Plugins/Mineplex.Core.Common/pom.xml b/Plugins/Mineplex.Core.Common/pom.xml index 3e9fed6c9..92c1a7ff4 100644 --- a/Plugins/Mineplex.Core.Common/pom.xml +++ b/Plugins/Mineplex.Core.Common/pom.xml @@ -20,6 +20,11 @@ org.apache.httpcomponents httpclient + + com.mineplex + mineplex-serverdata + dev-SNAPSHOT + diff --git a/Plugins/Mineplex.Core.Common/src/mineplex/core/common/util/PlayerMap.java b/Plugins/Mineplex.Core.Common/src/mineplex/core/common/util/PlayerMap.java index 426d2fd20..5cb6e7758 100644 --- a/Plugins/Mineplex.Core.Common/src/mineplex/core/common/util/PlayerMap.java +++ b/Plugins/Mineplex.Core.Common/src/mineplex/core/common/util/PlayerMap.java @@ -4,6 +4,7 @@ import com.google.common.collect.Sets; import org.apache.commons.lang3.Validate; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerQuitEvent; @@ -218,7 +219,7 @@ public class PlayerMap implements Map private static class RemovalListener implements Listener { - @EventHandler + @EventHandler (priority = EventPriority.MONITOR) public void onQuit(PlayerQuitEvent event) { synchronized (LOCK) diff --git a/Plugins/Mineplex.Core.Common/src/mineplex/core/common/util/UtilServer.java b/Plugins/Mineplex.Core.Common/src/mineplex/core/common/util/UtilServer.java index 34b6eb12d..86b0717ec 100644 --- a/Plugins/Mineplex.Core.Common/src/mineplex/core/common/util/UtilServer.java +++ b/Plugins/Mineplex.Core.Common/src/mineplex/core/common/util/UtilServer.java @@ -1,7 +1,10 @@ package mineplex.core.common.util; import com.google.common.collect.Lists; + import mineplex.core.common.events.PlayerRecieveBroadcastEvent; +import mineplex.serverdata.Region; + import org.bukkit.Bukkit; import org.bukkit.Server; import org.bukkit.Sound; @@ -13,6 +16,7 @@ import org.bukkit.plugin.Plugin; import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.scheduler.BukkitRunnable; +import org.bukkit.scheduler.BukkitTask; import java.lang.reflect.Field; import java.util.*; @@ -52,7 +56,7 @@ public class UtilServer { return Bukkit.getServer(); } - + public static void broadcast(String message) { for (Player cur : getPlayers()) @@ -61,13 +65,13 @@ public class UtilServer UtilPlayer.message(cur, message); } } - + public static void broadcast(LinkedList messages) { for (Player cur : getPlayers()) UtilPlayer.message(cur, messages); } - + public static void broadcastSpecial(String event, String message) { for (Player cur : getPlayers()) @@ -78,32 +82,32 @@ public class UtilServer cur.playSound(cur.getLocation(), Sound.ORB_PICKUP, 2f, 0f); } } - + public static void broadcast(String sender, String message) { broadcast("§f§l" + sender + " " + "§b" + message); } - + public static void broadcastMagic(String sender, String message) { broadcast("§2§k" + message); } - - public static double getFilledPercent() + + public static double getFilledPercent() { - return (double)getPlayers().length / (double)UtilServer.getServer().getMaxPlayers(); + return (double) getPlayers().length / (double) UtilServer.getServer().getMaxPlayers(); } - + public static void RegisterEvents(Listener listener) { getPluginManager().registerEvents(listener, getPlugin()); } - + public static void Unregister(Listener listener) { HandlerList.unregisterAll(listener); } - + public static JavaPlugin getPlugin() { return JavaPlugin.getProvidingPlugin(UtilServer.class); @@ -135,6 +139,16 @@ public class UtilServer return getPlugin().getConfig().getString("serverstatus.name"); } + public static Region getRegion() + { + return getPlugin().getConfig().getBoolean("serverstatus.us") ? Region.US : Region.EU; + } + + public static String getGroup() + { + return getPlugin().getConfig().getString("serverstatus.group"); + } + public static boolean isTestServer() { return getPlugin().getConfig().getString("serverstatus.group").equalsIgnoreCase("Testing"); @@ -180,4 +194,39 @@ public class UtilServer throwable.printStackTrace(System.out); } } + + public static BukkitTask runAsync(Runnable runnable) + { + return getPlugin().getServer().getScheduler().runTaskAsynchronously(getPlugin(), runnable); + } + + public static BukkitTask runAsync(Runnable runnable, long time) + { + return getPlugin().getServer().getScheduler().runTaskLaterAsynchronously(getPlugin(), runnable, time); + } + + public static BukkitTask runAsyncTimer(Runnable runnable, long time, long period) + { + return getPlugin().getServer().getScheduler().runTaskTimerAsynchronously(getPlugin(), runnable, time, period); + } + + public static BukkitTask runSync(Runnable runnable) + { + return getPlugin().getServer().getScheduler().runTask(getPlugin(), runnable); + } + + public static BukkitTask runSyncLater(Runnable runnable, long delay) + { + return getPlugin().getServer().getScheduler().runTaskLater(getPlugin(), runnable, delay); + } + + public static BukkitTask runSyncTimer(Runnable runnable, long delay, long period) + { + return getPlugin().getServer().getScheduler().runTaskTimer(getPlugin(), runnable, delay, period); + } + + public static BukkitTask runSyncTimer(BukkitRunnable runnable, long delay, long period) + { + return runnable.runTaskTimer(getPlugin(), delay, period); + } } diff --git a/Plugins/Mineplex.Core/pom.xml b/Plugins/Mineplex.Core/pom.xml index c4dbb202a..65a90f2a7 100644 --- a/Plugins/Mineplex.Core/pom.xml +++ b/Plugins/Mineplex.Core/pom.xml @@ -45,6 +45,11 @@ anticheat 1.2 + + org.tukaani + xz + 1.5 + diff --git a/Plugins/Mineplex.Core/src/mineplex/core/antihack/AntiHack.java b/Plugins/Mineplex.Core/src/mineplex/core/antihack/AntiHack.java index b5f56ca2e..0023bb1e6 100644 --- a/Plugins/Mineplex.Core/src/mineplex/core/antihack/AntiHack.java +++ b/Plugins/Mineplex.Core/src/mineplex/core/antihack/AntiHack.java @@ -1,605 +1,270 @@ package mineplex.core.antihack; -import com.google.common.cache.Cache; -import com.google.common.cache.CacheBuilder; -import com.google.common.collect.ImmutableMap; -import com.google.common.util.concurrent.AtomicDouble; -import com.mineplex.anticheat.api.GameEndEvent; -import com.mineplex.anticheat.api.GameStartEvent; -import com.mineplex.anticheat.api.MineplexLink; -import com.mineplex.anticheat.api.PlayerViolationEvent; -import com.mineplex.anticheat.checks.Check; -import mineplex.core.Managers; -import mineplex.core.MiniPlugin; -import mineplex.core.PlayerSelector; -import mineplex.core.ReflectivelyCreateMiniPlugin; -import mineplex.core.account.CoreClient; -import mineplex.core.account.CoreClientManager; -import mineplex.core.antihack.actions.AntiHackAction; -import mineplex.core.antihack.banwave.BanWaveManager; -import mineplex.core.antihack.types.Fly; -import mineplex.core.antihack.types.Idle; -import mineplex.core.antihack.types.Reach; -import mineplex.core.antihack.types.Speed; -import mineplex.core.command.CommandBase; -import mineplex.core.common.Rank; -import mineplex.core.common.util.*; -import mineplex.core.disguise.DisguiseManager; -import mineplex.core.disguise.disguises.DisguiseBase; -import mineplex.core.portal.Portal; -import mineplex.core.preferences.Preference; -import mineplex.core.preferences.PreferencesManager; -import mineplex.core.punish.Category; -import mineplex.core.punish.Punish; -import mineplex.core.punish.PunishClient; -import mineplex.core.punish.Punishment; -import mineplex.core.updater.UpdateType; -import mineplex.core.updater.event.UpdateEvent; -import mineplex.serverdata.commands.ServerCommandManager; -import net.minecraft.server.v1_8_R3.*; -import org.bukkit.*; -import org.bukkit.Material; +import javax.xml.bind.DatatypeConverter; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; + +import net.md_5.bungee.api.ChatColor; +import net.md_5.bungee.api.chat.BaseComponent; +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.minecraft.server.v1_8_R3.MinecraftServer; + +import org.bukkit.Bukkit; import org.bukkit.craftbukkit.v1_8_R3.entity.CraftPlayer; import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; -import org.bukkit.event.player.*; -import org.bukkit.event.server.ServerListPingEvent; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.entity.EntityDamageEvent; +import org.bukkit.event.player.PlayerCommandPreprocessEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.event.player.PlayerMoveEvent; +import org.bukkit.event.player.PlayerToggleFlightEvent; import org.bukkit.plugin.ServicePriority; -import org.bukkit.potion.PotionEffect; -import org.bukkit.potion.PotionEffectType; -import org.bukkit.scheduler.BukkitTask; -import java.util.*; -import java.util.Map.Entry; -import java.util.concurrent.ThreadLocalRandom; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicReference; -import java.util.function.Function; -import java.util.function.Predicate; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.common.collect.ImmutableMap; +import com.google.gson.JsonObject; +import com.mineplex.anticheat.MineplexAnticheat; +import com.mineplex.anticheat.api.GameEndEvent; +import com.mineplex.anticheat.api.GameStartEvent; +import com.mineplex.anticheat.api.MineplexLink; +import com.mineplex.anticheat.api.PlayerViolationEvent; +import com.mineplex.anticheat.checks.Check; +import com.mineplex.anticheat.checks.CheckManager; +import com.mineplex.anticheat.checks.combat.KillauraTypeA; +import com.mineplex.anticheat.checks.combat.KillauraTypeB; +import com.mineplex.anticheat.checks.combat.KillauraTypeC; +import com.mineplex.anticheat.checks.combat.KillauraTypeD; +import com.mineplex.anticheat.checks.combat.KillauraTypeE; +import com.mineplex.anticheat.checks.combat.KillauraTypeF; +import com.mineplex.anticheat.checks.move.Glide; +import com.mineplex.anticheat.checks.move.HeadRoll; +import com.mineplex.anticheat.checks.move.Speed; +import com.mineplex.anticheat.checks.player.BadPackets; + +import mineplex.core.Managers; +import mineplex.core.MiniPlugin; +import mineplex.core.ReflectivelyCreateMiniPlugin; +import mineplex.core.account.CoreClient; +import mineplex.core.account.CoreClientManager; +import mineplex.core.antihack.actions.AntiHackAction; +import mineplex.core.antihack.actions.BanwaveAction; +import mineplex.core.antihack.actions.ImmediateBanAction; +import mineplex.core.antihack.actions.NoopAction; +import mineplex.core.antihack.animations.BanwaveAnimationSpin; +import mineplex.core.antihack.banwave.BanWaveInfo; +import mineplex.core.antihack.banwave.BanWaveManager; +import mineplex.core.antihack.commands.AnticheatOffCommand; +import mineplex.core.antihack.commands.AnticheatOnCommand; +import mineplex.core.antihack.commands.DetailedMessagesCommand; +import mineplex.core.antihack.commands.GetVlsCommand; +import mineplex.core.antihack.commands.TestBanCommand; +import mineplex.core.antihack.guardians.GuardianManager; +import mineplex.core.antihack.logging.AntihackLogger; +import mineplex.core.antihack.redisnotifications.GwenBanNotification; +import mineplex.core.antihack.redisnotifications.GwenBanwaveNotification; +import mineplex.core.command.CommandBase; +import mineplex.core.common.Rank; +import mineplex.core.common.util.C; +import mineplex.core.common.util.F; +import mineplex.core.common.util.UtilMath; +import mineplex.core.common.util.UtilPlayer; +import mineplex.core.common.util.UtilServer; +import mineplex.core.disguise.DisguiseManager; +import mineplex.core.disguise.disguises.DisguiseBase; +import mineplex.core.preferences.Preference; +import mineplex.core.preferences.PreferencesManager; +import mineplex.core.punish.Category; +import mineplex.core.punish.Punish; +import mineplex.core.punish.PunishmentResponse; +import mineplex.serverdata.commands.ServerCommandManager; @ReflectivelyCreateMiniPlugin public class AntiHack extends MiniPlugin { - public static final Map CHECKS = ImmutableMap.builder() - .put("Killaura (Type A)", new CheckThresholds("Kill Aura", 0, 25, 50)) - .put("Killaura (Type B)", new CheckThresholds("High CPS", 0, 0, Integer.MAX_VALUE)) - .put("Killaura (Type C)", new CheckThresholds("Reach", 0, Integer.MAX_VALUE, Integer.MAX_VALUE)) - .put("Killaura (Type D)", new CheckThresholds("Kill Aura", 500, 1000, 1500)) - .put("Killaura (Type E)", new CheckThresholds("Kill Aura", 300, 700, 2000)) - .put("Killaura (Type F)", new CheckThresholds("Kill Aura", 150, 250, 350)) - .put("BadPackets", new CheckThresholds("Regen", 500, 1000, 2000)) - .put("Glide", new CheckThresholds("Flying", 50, 100, 200)) // TODO: specific VL levels - .put("Speed", new CheckThresholds("Speed", 50, 100, 200)) // TODO: specific VL levels - .put("HeadRoll", new CheckThresholds("Illegal Movement", 0, 0, 0)) + private static final Map, CheckThresholds> CHECKS = ImmutableMap., CheckThresholds>builder() + .put(KillauraTypeA.class, new CheckThresholds("Kill Aura", 0, 25, 50)) + .put(KillauraTypeB.class, new CheckThresholds("High CPS", 0, 0, Integer.MAX_VALUE)) + .put(KillauraTypeC.class, new CheckThresholds("Reach", 0, Integer.MAX_VALUE, Integer.MAX_VALUE)) + .put(KillauraTypeD.class, new CheckThresholds("Kill Aura", 500, 1000, 1500)) + .put(KillauraTypeE.class, new CheckThresholds("Kill Aura", 300, 700, 2000)) + .put(KillauraTypeF.class, new CheckThresholds("Kill Aura", 150, 250, 350)) + .put(BadPackets.class, new CheckThresholds("Regen", 500, 1000, 2000)) + .put(Glide.class, new CheckThresholds("Flying", 150, 250, 500)) + .put(Speed.class, new CheckThresholds("Speed", 150, 250, 500)) + .put(HeadRoll.class, new CheckThresholds("Illegal Movement", 0, 0, 0)) .build(); - public static final String NAME = "Chiss"; - public static final String USER_HAS_BEEN_BANNED = F.main("GWEN", "%s has been banned. I am always watching."); - public static final String USER_HAS_BEEN_BANNED_BANWAVE = USER_HAS_BEEN_BANNED; + private static final CheckThresholds NOOP_THRESHOLD = new CheckThresholds("Unknown", 0, Integer.MAX_VALUE, Integer.MAX_VALUE); - private static final int VL_DIFF_BEFORE_RENOTIFY = 999999; - private static final int MAX_STALKED_PLAYERS = 3; - private static final int STALK_COOLDOWN_TIME_SECONDS = 5; - private static final int MIN_STALK_TIME = 10 * 20; - private static final int MAX_STALK_TIME = 20 * 20; - private static final int MAX_MIN_DIFF = MAX_STALK_TIME - MIN_STALK_TIME; - private static final Function STALK_END_PROBABILITY_EQUATION = x -> - { - return 1.0/ MAX_MIN_DIFF * x; // linear equation with points (0, 0) and (diff, 1) - }; + private static final Map, AntiHackAction> ACTIONS = ImmutableMap., AntiHackAction>builder() + .put(KillauraTypeA.class, new ImmediateBanAction(200)) + .put(KillauraTypeD.class, new BanwaveAction(1500)) + .put(Glide.class, new ImmediateBanAction(10000)) + .put(Speed.class, new ImmediateBanAction(10000)) +// .put(HeadRoll.class, new ImmediateBanAction(200)) + .build(); + + private static final AntiHackAction NOOP_ACTION = new NoopAction(); + + private static final String NAME = "Chiss"; + private static final String USER_HAS_BEEN_BANNED = F.main("GWEN", "%s has been banned. I am always watching."); + private static final String USER_HAS_BEEN_BANNED_BANWAVE = USER_HAS_BEEN_BANNED; + + public static final int ID_LENGTH = 5; private final Cache _cooldown = CacheBuilder.newBuilder() .concurrencyLevel(1) .expireAfterWrite(30, TimeUnit.SECONDS) .build(); - private final Cache _stalkingCooldown = CacheBuilder.newBuilder() - .concurrencyLevel(1) - .expireAfterWrite(STALK_COOLDOWN_TIME_SECONDS, TimeUnit.SECONDS) - .build(); - private final List _stalking = new ArrayList<>(); - private final String _thisServer; + private final String _thisServer = UtilServer.getServerName(); - private boolean _enabled = true; - private boolean _strict = false; - private boolean _kick = true; + private final CoreClientManager _clientManager = require(CoreClientManager.class); + private final AntihackLogger _logger = require(AntihackLogger.class); + private final PreferencesManager _preferences = require(PreferencesManager.class); + private final Punish _punish = require(Punish.class); - public Portal Portal = require(Portal.class); - private PreferencesManager _preferences = require(PreferencesManager.class); - private CoreClientManager _clientManager = require(CoreClientManager.class); - - //Record Offenses - private HashMap>> _offense = new HashMap>>(); - - //Ignore Player - private HashMap _ignore = new HashMap(); - - //Player Info - private HashSet _velocityEvent = new HashSet(); - private HashMap _lastMoveEvent = new HashMap(); - - private HashSet _hubAttempted = new HashSet(); - - //Hack Requirements - public int FloatHackTicks = 10; - public int HoverHackTicks = 4; - public int RiseHackTicks = 6; - public int SpeedHackTicks = 6; - public int IdleTime = 20000; - - public int KeepOffensesFor = 30000; - - //Other Times - public int FlightTriggerCancel = 2000; - - public ArrayList _movementDetectors; - public ArrayList _combatDetectors; - - private AntiHackRepository _repository; - - private List _guardians = new ArrayList<>(); + private final Set _detailedMessages = new HashSet<>(); private Set _pendingBan = new HashSet<>(); // These are the GWEN checks to ignore when handling PlayerViolationEvent private HashSet> _ignoredChecks = new HashSet<>(); - @SuppressWarnings("Convert2streamapi") private AntiHack() { super("AntiHack"); - DisguiseManager disguiseManager = require(DisguiseManager.class); + _detailedMessages.add("Spoobncoobr"); - this._thisServer = UtilServer.getServerName(); + require(GuardianManager.class); + require(BanWaveManager.class); - _repository = new AntiHackRepository(this._thisServer); - _repository.initialize(); - - _movementDetectors = new ArrayList(); - _combatDetectors = new ArrayList(); - - _movementDetectors.add(new Fly(this)); - _movementDetectors.add(new Idle(this)); - _movementDetectors.add(new Speed(this)); - - _combatDetectors.add(new Reach(this)); - - Bukkit.getServicesManager().register(MineplexLink.class, new MineplexLink() - { - @Override - public EntityType getActiveDisguise(Player player) - { - DisguiseBase disguise = disguiseManager.getActiveDisguise(player); - return disguise != null ? disguise.getDisguiseType() : null; - } - - @Override - public boolean isSpectator(Player player) - { - return UtilPlayer.isSpectator(player); - } - - @Override - public double getTPS() - { - return MinecraftServer.getServer().recentTps[0]; // Return the average TPS from the last minute - } - - @Override - public int getPing(Player player) - { - return Math.min(((CraftPlayer)player).getHandle().ping, 1000); - } - - @Override - public boolean isUsingItem(Player player) - { - return ((CraftPlayer)player).getHandle().bS(); // See Anticheat javadoc - } - }, this._plugin, ServicePriority.Normal); + Bukkit.getServicesManager().register(MineplexLink.class, new MineplexLinkImpl(), this._plugin, ServicePriority.Normal); ServerCommandManager.getInstance().registerCommandType(MajorViolationCommand.class, violation -> { - IChatBaseComponent component = getMinimalMessage(violation); - + BaseComponent[] minimal = getMinimalMessage(violation); + BaseComponent[] detailed = getDetailedMessage(violation); for (Player player : Bukkit.getOnlinePlayers()) { - if (player.getName().equals("Spoobncoobr")) - { - ((CraftPlayer) player).getHandle().sendMessage(getDetailedMessage(violation)); + if (_detailedMessages.contains(player.getName())) + player.spigot().sendMessage(detailed); + else if (_clientManager.Get(player).GetRank().has(Rank.HELPER) && (violation.getOriginatingServer().equals(_thisServer) || _preferences.get(player).isActive(Preference.GLOBAL_GWEN_REPORTS))) + player.spigot().sendMessage(minimal); + } + }); + } - } else if (_clientManager.Get(player).GetRank().has(Rank.HELPER) && (violation.getOriginatingServer().equals(_thisServer) || Managers.get(PreferencesManager.class).get(player).isActive(Preference.GLOBAL_GWEN_REPORTS))) + @Override + public void addCommands() + { + if (UtilServer.isTestServer()) + { + addCommand(new AnticheatOnCommand(this)); + addCommand(new AnticheatOffCommand(this)); + addCommand(new TestBanCommand(this)); + } + addCommand(new GetVlsCommand(this)); + addCommand(new DetailedMessagesCommand(this)); + } + + private void runBanAnimation(Player player, Runnable after) + { + new BanwaveAnimationSpin().run(player, after); + } + + public void doBan(Player player, Class cause) + { + runSync(() -> + { + if (_pendingBan.add(player)) + { + CoreClient coreClient = _clientManager.Get(player); + + Consumer> doPunish = after -> { - ((CraftPlayer) player).getHandle().sendMessage(component); + JsonObject custom = new JsonObject(); + custom.addProperty("ban-reason", CheckManager.getCheckSimpleName(cause)); + + String id = generateId(); + String finalMessage = "[GWEN] " + id; + _logger.saveMetadata(player, id, () -> + { + _logger.resetViolations(player, () -> + { + runAsync(() -> + { + GwenBanNotification notification = new GwenBanNotification(_thisServer, player.getName(), player.getUniqueId().toString(), CheckManager.getCheckSimpleName(cause), id); + ServerCommandManager.getInstance().publishCommand(notification); + }); + + _punish.AddPunishment(coreClient.getName(), Category.Hacking, finalMessage, AntiHack.NAME, 3, true, -1, true, after); + }); + }, custom); + }; + + if (coreClient.GetRank().has(Rank.TWITCH)) + { + doPunish.accept(result -> _pendingBan.remove(player)); + } + else + { + runBanAnimation(player, () -> + doPunish.accept(result -> + { + if (result == PunishmentResponse.Punished) + { + announceBan(player); + } + _pendingBan.remove(player); + }) + ); } } }); + } - this._plugin.getServer().getScheduler().runTaskTimer(this._plugin, () -> + public void doBanWave(Player player, BanWaveInfo info) + { + runSync(() -> { - for (AntiHackGuardian guardian : this._guardians) + CoreClient coreClient = _clientManager.Get(player); + + Consumer> doPunish = after -> { - if (guardian.getTarget() != null && !guardian.getTarget().isOnline()) - { - this._stalking.remove(guardian.getTarget().getUniqueId()); - guardian.stopTargeting(); - } - else if (guardian.getTargetingTime() > MIN_STALK_TIME) - { - double threshold = STALK_END_PROBABILITY_EQUATION.apply(guardian.getTargetingTime() - MIN_STALK_TIME); - if (Math.random() <= threshold) - { - this._stalking.remove(guardian.getTarget().getUniqueId()); - _stalkingCooldown.put(guardian.getTarget().getUniqueId(), true); - guardian.stopTargeting(); - } - } - guardian.tick(); - } - }, 0L, 1L); - - this._plugin.getServer().getScheduler().runTaskTimer(this._plugin, () -> - { - if (_stalking.size() >= MAX_STALKED_PLAYERS) - { - return; - } - - if (_guardians.size() == 0) - { - return; - } - - List targets = PlayerSelector.selectPlayers( - UtilLambda.and( - PlayerSelector.NOT_VANISHED, - PlayerSelector.hasAnyRank(false, - Rank.ALL, - Rank.ULTRA, - Rank.HERO, - Rank.LEGEND, - Rank.TITAN, - Rank.TWITCH, - Rank.YOUTUBE_SMALL, - Rank.YOUTUBE, - Rank.MEDIA, - Rank.ADMIN, - Rank.DEVELOPER, - Rank.OWNER, - Rank.LT - ), - player -> !_stalking.contains(player.getUniqueId()), - player -> _stalkingCooldown.getIfPresent(player.getUniqueId()) == null - )); - - while (_stalking.size() < MAX_STALKED_PLAYERS && targets.size() > 0) - { - Player target = targets.remove(ThreadLocalRandom.current().nextInt(targets.size())); - - int start = ThreadLocalRandom.current().nextInt(_guardians.size()); - - for (int i = start, j = 0; j < _guardians.size(); i++, j++) - { - if (i >= _guardians.size()) - { - i -= _guardians.size(); - } - AntiHackGuardian guardian = _guardians.get(i); - if (!guardian.isTargeting()) - { - guardian.target(target); - _stalking.add(target.getUniqueId()); - break; - } - } - } - }, 0L, 20L); - - require(BanWaveManager.class); - } - - private IChatBaseComponent getDetailedMessage(MajorViolationCommand violation) - { - return new ChatComponentText("") - .addSibling( - new ChatComponentText("A") - .setChatModifier( - new ChatModifier() - .setColor(EnumChatFormat.AQUA) - .setRandom(true) - ) - ) - .addSibling( - new ChatComponentText(" GWEN > ") - .setChatModifier( - new ChatModifier() - .setColor(EnumChatFormat.RED) - .setBold(true) - ) - ) - .addSibling( - new ChatComponentText(violation.getPlayerName()) - .setChatModifier( - new ChatModifier() - .setColor(EnumChatFormat.GOLD) - ) - ) - .addSibling( - new ChatComponentText(" failed " + violation.getHackType() + " VL" + violation.getViolations() + " in server ") - .setChatModifier( - new ChatModifier() - .setColor(EnumChatFormat.YELLOW) - ) - ) - .addSibling( - new ChatComponentText( - violation.getOriginatingServer() - ) - .setChatModifier( - new ChatModifier() - .setColor(EnumChatFormat.YELLOW) - .setChatClickable( - new ChatClickable( - ChatClickable.EnumClickAction.RUN_COMMAND, - "/server " + violation.getOriginatingServer() - ) - ) - .setChatHoverable( - new ChatHoverable( - ChatHoverable.EnumHoverAction.SHOW_TEXT, - new ChatComponentText("Teleport to " + violation.getOriginatingServer()) - ) - ) - ) - ) - .addSibling( - new ChatComponentText(": " + violation.getMessage() + ".") - .setChatModifier( - new ChatModifier() - .setColor(EnumChatFormat.YELLOW) - ) - ); - } - - private IChatBaseComponent getMinimalMessage(MajorViolationCommand violation) - { - IChatBaseComponent component = new ChatComponentText("") - .addSibling( - new ChatComponentText("A") - .setChatModifier( - new ChatModifier() - .setColor(EnumChatFormat.AQUA) - .setRandom(true) - ) - ) - .addSibling( - new ChatComponentText(" GWEN > ") - .setChatModifier( - new ChatModifier() - .setColor(EnumChatFormat.RED) - .setBold(true) - ) - ) - .addSibling( - new ChatComponentText(violation.getPlayerName()) - .setChatModifier( - new ChatModifier() - .setColor(EnumChatFormat.GOLD) - ) - ) - .addSibling( - new ChatComponentText(" suspected of ") - .setChatModifier( - new ChatModifier() - .setColor(EnumChatFormat.YELLOW) - ) - ) - .addSibling(CHECKS.get(violation.getHackType()).format(violation.getViolations())); - - if (!violation.getOriginatingServer().equals(this._thisServer)) - { - component - .addSibling( - new ChatComponentText(" in ") - .setChatModifier( - new ChatModifier() - .setColor(EnumChatFormat.YELLOW) - ) - ) - .addSibling( - new ChatComponentText(violation.getOriginatingServer()) - .setChatModifier( - new ChatModifier() - .setColor(EnumChatFormat.AQUA) - .setChatClickable( - new ChatClickable( - ChatClickable.EnumClickAction.RUN_COMMAND, - "/server " + violation.getOriginatingServer() - ) - ) - .setChatHoverable( - new ChatHoverable( - ChatHoverable.EnumHoverAction.SHOW_TEXT, - new ChatComponentText("Teleport to " + violation.getOriginatingServer()) - ) - ) - ) - ); - } - - return component.addSibling( - new ChatComponentText(".") - .setChatModifier( - new ChatModifier() - .setColor(EnumChatFormat.YELLOW) - ) - ); - } - - public void registerGuardian(AntiHackGuardian guardian) - { - this._guardians.add(guardian); - } - - public void clearGuardians() - { - this._guardians.forEach(AntiHackGuardian::remove); - this._guardians.clear(); - this._stalking.clear(); - this._stalkingCooldown.cleanUp(); - } - - public void runBanAnimation(Player player, Runnable after) - { - if (_pendingBan.add(player)) - { - float oldWalkSpeed = player.getWalkSpeed(); - player.setWalkSpeed(0); - player.addPotionEffect(new PotionEffect(PotionEffectType.JUMP, 999999, -10)); - - double radius = 4; - double heightAdj = 8; - - double baseDeg = 18; - - Location center = player.getLocation().add(0, heightAdj, 0); - AntiHackGuardian north = new AntiHackGuardian(center.clone().add(0, 0, -radius), 0, 0, 0, 0, 0, 0, false); - AntiHackGuardian east = new AntiHackGuardian(center.clone().add(radius, 0, 0), 0, 0, 0, 0, 0, 0, false); - AntiHackGuardian south = new AntiHackGuardian(center.clone().add(0, 0, radius), 0, 0, 0, 0, 0, 0, false); - AntiHackGuardian west = new AntiHackGuardian(center.clone().add(-radius, 0, 0), 0, 0, 0, 0, 0, 0, false); - - UtilEnt.CreatureLook(east.getEntity(), player); - UtilEnt.CreatureLook(west.getEntity(), player); - UtilEnt.CreatureLook(south.getEntity(), player); - UtilEnt.CreatureLook(north.getEntity(), player); - - Function magic = seconds -> - { - return Math.pow(2, seconds - 5); + _punish.AddPunishment(coreClient.getName(), Category.Hacking, info.getMessage(), AntiHack.NAME, 3, true, -1, true, after); }; - runSyncLater(() -> - { - north.shoot(player); - east.shoot(player); - south.shoot(player); - west.shoot(player); - - // We get 5 seconds, or 100 ticks - AtomicInteger timer = new AtomicInteger(5); - AtomicReference task = new AtomicReference<>(); - - AtomicDouble cNorth = new AtomicDouble(270); - AtomicDouble cEast = new AtomicDouble(0); - AtomicDouble cSouth = new AtomicDouble(90); - AtomicDouble cWest = new AtomicDouble(180); - - task.set(runSyncTimer(() -> - { - timer.getAndIncrement(); - if (timer.get() > 100) - { - task.get().cancel(); - - player.removePotionEffect(PotionEffectType.JUMP); - player.setWalkSpeed(oldWalkSpeed); - Location location = player.getLocation(); - - UtilParticle.PlayParticle(UtilParticle.ParticleType.HUGE_EXPLOSION, player.getLocation(), 3f, 3f, 3f, 0, 32, UtilParticle.ViewDist.MAX, UtilServer.getPlayers()); - - after.run(); - - _pendingBan.remove(player); - - north.shoot(null); - south.shoot(null); - east.shoot(null); - west.shoot(null); - UtilEnt.CreatureLook(north.getEntity(), location); - UtilEnt.CreatureLook(south.getEntity(), location); - UtilEnt.CreatureLook(east.getEntity(), location); - UtilEnt.CreatureLook(west.getEntity(), location); - runSyncLater(() -> - { - north.remove(); - south.remove(); - east.remove(); - west.remove(); - }, 40L); - return; - } - - double seconds = timer.get() / 20.0; - - double rate = magic.apply(seconds) * 3 * baseDeg; - - player.getLocation(center); - center.add(0, heightAdj, 0); - - { - cNorth.addAndGet(rate); - north.move(center.getX() + radius * MathHelper.cos((float) Math.toRadians(cNorth.get())), center.getY(), center.getZ() + radius * MathHelper.sin((float) Math.toRadians(cNorth.get()))); - } - { - cSouth.addAndGet(rate); - south.move(center.getX() + radius * MathHelper.cos((float) Math.toRadians(cSouth.get())), center.getY(), center.getZ() + radius * MathHelper.sin((float) Math.toRadians(cSouth.get()))); - } - { - cEast.addAndGet(rate); - east.move(center.getX() + radius * MathHelper.cos((float) Math.toRadians(cEast.get())), center.getY(), center.getZ() + radius * MathHelper.sin((float) Math.toRadians(cEast.get()))); - } - { - cWest.addAndGet(rate); - west.move(center.getX() + radius * MathHelper.cos((float) Math.toRadians(cWest.get())), center.getY(), center.getZ() + radius * MathHelper.sin((float) Math.toRadians(cWest.get()))); - } - }, 5, 1)); - }, 20); - } - } - - public void doBan(Player player, String message) - { - runSync(() -> - { - CoreClient coreClient = _clientManager.Get(player); - if (coreClient.GetRank().has(Rank.TWITCH)) { - require(Punish.class).AddPunishment(coreClient.getName(), Category.Hacking, message, AntiHack.NAME, 3, true, -1, true); - } - else - { - runBanAnimation(player, () -> + doPunish.accept(response -> { - require(Punish.class).AddPunishment(coreClient.getName(), Category.Hacking, message, AntiHack.NAME, 3, true, -1, true); - announceBan(player); }); } - }); - } - - public void doBanWave(Player player, String message) - { - runSync(() -> - { - int totalPunishments = getPunishments(player); - int daysBanned = getDaysBanned(player); - CoreClient coreClient = _clientManager.Get(player); - if (coreClient.GetRank().has(Rank.TWITCH)) - { - require(Punish.class).AddPunishment(coreClient.getName(), Category.Hacking, message, AntiHack.NAME, totalPunishments + 1, true, daysBanned == -1 ? -1 : TimeUnit.DAYS.toHours(daysBanned), true); - } else { runBanAnimation(player, () -> { - require(Punish.class).AddPunishment(coreClient.getName(), Category.Hacking, message, AntiHack.NAME, totalPunishments + 1, true, daysBanned == -1 ? -1 : TimeUnit.DAYS.toHours(daysBanned), true); - announceBanwave(player); + doPunish.accept(result -> + { + if (result == PunishmentResponse.Punished) + { + announceBanwave(player); + } + }); }); } }); @@ -642,13 +307,18 @@ public class AntiHack extends MiniPlugin } } - @EventHandler - public void on(PlayerViolationEvent event) + @EventHandler(priority = EventPriority.LOWEST) + public void on(EntityDamageEvent event) { - if (_ignoredChecks.contains(event.getCheckClass())) - return; + if (_pendingBan.contains(event.getEntity())) + event.setCancelled(true); + } - AntiHackAction.getAction(event.getCheckClass()).handle(event); + @EventHandler(priority = EventPriority.LOWEST) + public void on(EntityDamageByEntityEvent event) + { + if (_pendingBan.contains(event.getDamager())) + event.setCancelled(true); } public void announceBan(Player player) @@ -661,326 +331,16 @@ public class AntiHack extends MiniPlugin Bukkit.getServer().broadcastMessage(String.format(USER_HAS_BEEN_BANNED_BANWAVE, player.getName())); } - public int getPunishments(Player player) + public boolean toggleDetailedMessage(Player player) { - PunishClient punishClient = require(Punish.class).GetClient(player.getName()); - - int totalPunishments = 0; - - if (punishClient.GetPunishments().containsKey(Category.Hacking)) - { - for (Punishment punishment : punishClient.GetPunishments().get(Category.Hacking)) - { - if (punishment.GetAdmin().equalsIgnoreCase(NAME)) - { - totalPunishments++; - } - } - } - - return totalPunishments; - } - - public int getDaysBanned(Player player) - { - int totalPunishments = getPunishments(player); - - int daysBanned = 0; - - switch (totalPunishments) - { - case 0: - daysBanned = 7; - break; - case 1: - daysBanned = 30; - break; - case 2: - default: - daysBanned = -1; - } - - return daysBanned; - } - - @Override - public void addCommands() - { - if (UtilServer.isTestServer()) - { - addCommand(new CommandBase(this, Rank.DEVELOPER, "acon") - { - @Override - public void Execute(Player caller, String[] args) - { - if (caller.getUniqueId().toString().equals("b86b54da-93dd-46f9-be33-27bd92aa36d7")) - { - enableNewAnticheat(); - UtilPlayer.message(caller, F.main(getName(), "Enabled new anticheat")); - } - } - }); - addCommand(new CommandBase(this, Rank.DEVELOPER, "acoff") - { - @Override - public void Execute(Player caller, String[] args) - { - if (caller.getUniqueId().toString().equals("b86b54da-93dd-46f9-be33-27bd92aa36d7")) - { - disableNewAnticheat(); - UtilPlayer.message(caller, F.main(getName(), "Disabled new anticheat")); - } - } - }); - addCommand(new CommandBase(this, Rank.DEVELOPER, "testban") - { - @Override - public void Execute(Player caller, String[] args) - { - if (caller.getUniqueId().toString().equals("b86b54da-93dd-46f9-be33-27bd92aa36d7")) - { - if (args.length > 0) - { - Player p = Bukkit.getPlayerExact(args[0]); - if (p != null) - { - runBanAnimation(p, () -> - { - String reason = C.cRed + C.Bold + "You are banned for permanent by " + NAME + - "\n" + C.cWhite + "Seems to be speeding up time. (" + ThreadLocalRandom.current().nextInt(200, 400) + " packets/150 ms)" + - "\n" + C.cDGreen + "Unfairly banned? Appeal at " + C.cGreen + "www.mineplex.com/appeals"; - p.kickPlayer(reason); - - announceBan(p); - }); - } - else - { - UtilPlayer.message(caller, F.main(getName(), "Could not find player")); - } - } - else - { - UtilPlayer.message(caller, F.main(getName(), "No player specified")); - } - } - } - }); - } - } - - @EventHandler - public void playerMove(PlayerMoveEvent event) - { - if (!_enabled) - return; - - _lastMoveEvent.put(event.getPlayer(), System.currentTimeMillis()); - } - - @EventHandler - public void playerTeleport(PlayerTeleportEvent event) - { - if (!_enabled) - return; - - setIgnore(event.getPlayer(), 2000); - } - - @EventHandler - public void playerVelocity(PlayerVelocityEvent event) - { - if (!_enabled) - return; - - _velocityEvent.add(event.getPlayer()); - } - - @EventHandler - public void playerQuit(PlayerQuitEvent event) - { - if (!_enabled) - return; - - resetAll(event.getPlayer()); - } - - @EventHandler - public void startIgnore(PlayerMoveEvent event) - { - if (!_enabled) - return; - - Player player = event.getPlayer(); - - if (_velocityEvent.remove(player)) - { - setIgnore(player, 2000); - } - - //Initial Move (or Lag) Ignore - if (_lastMoveEvent.containsKey(player)) - { - long timeBetweenPackets = System.currentTimeMillis() - _lastMoveEvent.get(player); - - if (timeBetweenPackets > 500) - { - setIgnore(player, Math.min(4000, timeBetweenPackets)); - } - } - } - - public void setIgnore(Player player, long time) - { - //Wipe Detection - for (Detector detector : _movementDetectors) - detector.Reset(player); - - //Already ignoring for a longer period - if (_ignore.containsKey(player) && _ignore.get(player) > System.currentTimeMillis() + time) - return; - - //Add Ignore - _ignore.put(player, System.currentTimeMillis() + time); - } - - public boolean isValid(Player player, boolean groundValid) - { - //Near Other Player - for (Player other : UtilServer.getPlayers()) - { - if (player.equals(other)) - continue; - - if (other.getGameMode() != GameMode.SURVIVAL || UtilPlayer.isSpectator(player)) - continue; - - if (other.getVehicle() != null) - continue; - - if (UtilMath.offset(player, other) < 2) - return true; - } - - if (player.isFlying() || ((CraftPlayer) player).getHandle().isGliding() || player.isInsideVehicle() || player.getGameMode() != GameMode.SURVIVAL || UtilPlayer.isSpectator(player)) + if (_detailedMessages.add(player.getName())) { return true; } - - if (UtilInv.IsItem(player.getInventory().getArmorContents()[2], Material.ELYTRA, (byte) 0)) + else { - return true; - } - - //On Ground - if (groundValid) - { - if (UtilEnt.onBlock(player) || player.getLocation().getBlock().getType() != Material.AIR) - { - return true; - } - } - - if ((_ignore.containsKey(player) && System.currentTimeMillis() < _ignore.get(player))) - { - return true; - } - - return false; - } - - public void addSuspicion(Player player, String type) - { - if (!_enabled) - return; - - System.out.println(C.cRed + C.Bold + player.getName() + " suspected for " + type + "."); - - //Add Offense - if (!_offense.containsKey(player)) - _offense.put(player, new HashMap>()); - - if (!_offense.get(player).containsKey(type)) - _offense.get(player).put(type, new ArrayList()); - - _offense.get(player).get(type).add(System.currentTimeMillis()); - - //Cull & Count - int total = 0; - for (String curType : _offense.get(player).keySet()) - { - //Remove Old Offenses - Iterator offenseIterator = _offense.get(player).get(curType).iterator(); - while (offenseIterator.hasNext()) - { - if (UtilTime.elapsed(offenseIterator.next(), KeepOffensesFor)) - offenseIterator.remove(); - } - - //Count - total += _offense.get(player).get(curType).size(); - } - - // Print (Debug) - System.out.println("[Offense] #" + total + ": " + player.getName() + " received suspicion for " + type + "."); - } - - @EventHandler - public void generateReports(UpdateEvent event) - { - if (!_enabled) - return; - - if (event.getType() != UpdateType.SEC) - return; - - for (Iterator>>> playerIterator = _offense.entrySet().iterator(); playerIterator.hasNext(); ) - { - Entry>> entry = playerIterator.next(); - Player player = entry.getKey(); - - String out = ""; - int total = 0; - - for (String type : entry.getValue().keySet()) - { - //Remove Old Offenses - Iterator offenseIterator = entry.getValue().get(type).iterator(); - while (offenseIterator.hasNext()) - { - long time = offenseIterator.next(); - - if (UtilTime.elapsed(time, KeepOffensesFor)) - offenseIterator.remove(); - } - - //Count - int count = entry.getValue().get(type).size(); - total += count; - - out += count + " " + type + ", "; - } - - if (out.length() > 0) - out = out.substring(0, out.length() - 2); - - String severity = "Low"; - - if (total > (_strict ? 6 : 18)) - severity = "Extreme"; - else if (total > (_strict ? 4 : 12)) - severity = "High"; - else if (total > (_strict ? 2 : 6)) - severity = "Medium"; - - //Send Report - sendReport(player, out, severity); - - if (severity.equalsIgnoreCase("Extreme")) - { - playerIterator.remove(); - resetAll(player, false); - } + _detailedMessages.remove(player.getName()); + return false; } } @@ -990,13 +350,11 @@ public class AntiHack extends MiniPlugin if (_ignoredChecks.contains(event.getCheckClass())) return; + ACTIONS.getOrDefault(event.getCheckClass(), NOOP_ACTION).handle(event); + if (event.shouldTellStaff()) { - CheckThresholds thresholds = CHECKS.get(event.getHackType()); - if (thresholds == null) - { - thresholds = new CheckThresholds(event.getHackType(), 0, Integer.MAX_VALUE, Integer.MAX_VALUE); - } + CheckThresholds thresholds = CHECKS.getOrDefault(event.getCheckClass(), NOOP_THRESHOLD); CheckThresholds.Severity severity = thresholds.getSeverity(event.getViolations()); if (severity == CheckThresholds.Severity.NONE) @@ -1007,18 +365,9 @@ public class AntiHack extends MiniPlugin String key = event.getPlayer().getName() + "." + event.getHackType() + "." + severity.toString(); Integer pastVl = this._cooldown.getIfPresent(key); - if (pastVl != null) + if (pastVl == null) { - if (event.getViolations() - pastVl > VL_DIFF_BEFORE_RENOTIFY) - { - this._cooldown.put(key, event.getViolations()); - MajorViolationCommand command = new MajorViolationCommand(_thisServer, event.getPlayer().getName(), event.getHackType(), event.getViolations(), event.getMessage()); - ServerCommandManager.getInstance().publishCommand(command); - } - } - else - { - MajorViolationCommand command = new MajorViolationCommand(_thisServer, event.getPlayer().getName(), event.getHackType(), event.getViolations(), event.getMessage()); + MajorViolationCommand command = new MajorViolationCommand(_thisServer, event.getPlayer().getName(), CheckManager.getCheckSimpleName(event.getCheckClass()), event.getViolations(), event.getMessage()); ServerCommandManager.getInstance().publishCommand(command); this._cooldown.put(key, event.getViolations()); @@ -1026,138 +375,10 @@ public class AntiHack extends MiniPlugin } } - public void sendReport(Player player, String report, String severity) - { - if (severity.equals("Extreme")) - { - //Staff - boolean handled = false; - for (Player staff : UtilServer.getPlayers()) - { - if (_clientManager.Get(staff).GetRank().has(Rank.MODERATOR)) - { - UtilPlayer.message(staff, C.cAqua + C.Scramble + "A" + ChatColor.RESET + C.cRed + C.Bold + " MAC > " + ChatColor.RESET + C.cYellow + report); - UtilPlayer.message(staff, C.cAqua + C.Scramble + "A" + ChatColor.RESET + C.cRed + C.Bold + " MAC > " + ChatColor.RESET + C.cGold + player.getName() + C.cYellow + " has extreme violation."); - - handled = true; - } - } - - //Record - ServerListPingEvent event = new ServerListPingEvent(null, Bukkit.getServer().getMotd(), Bukkit.getServer().getOnlinePlayers().size(), Bukkit.getServer().getMaxPlayers()); - getPluginManager().callEvent(event); - - String motd = event.getMotd(); - String game = "N/A"; - String map = "N/A"; - - String[] args = motd.split("\\|"); - - if (args.length > 0) - motd = args[0]; - - if (args.length > 2) - game = args[2]; - - if (args.length > 3) - map = args[3]; - - _repository.saveOffense(player, motd, game, map, report); - } - } - - private void reset() - { - for (Player player : UtilServer.getPlayers()) - resetAll(player); - } - - private void resetAll(Player player) - { - resetAll(player, true); - } - - private void resetAll(Player player, boolean removeOffenses) - { - _ignore.remove(player); - _velocityEvent.remove(player); - _lastMoveEvent.remove(player); - - if (removeOffenses) - _offense.remove(player); - - for (Detector detector : _movementDetectors) - detector.Reset(player); - - for (Detector detector : _combatDetectors) - detector.Reset(player); - } - - @EventHandler - public void cleanupPlayers(UpdateEvent event) - { - if (!_enabled) - return; - - if (event.getType() != UpdateType.SLOW) - return; - - for (Iterator> playerIterator = _ignore.entrySet().iterator(); playerIterator.hasNext(); ) - { - Player player = playerIterator.next().getKey(); - - if (!player.isOnline() || player.isDead() || !player.isValid()) - { - playerIterator.remove(); - - _velocityEvent.remove(player); - _lastMoveEvent.remove(player); - - _offense.remove(player); - - for (Detector detector : _movementDetectors) - detector.Reset(player); - - for (Detector detector : _combatDetectors) - detector.Reset(player); - } - } - - - for (Iterator playerIterator = _hubAttempted.iterator(); playerIterator.hasNext(); ) - { - Player player = playerIterator.next(); - - if (!player.isOnline() || !player.isValid()) - { - playerIterator.remove(); - } - } - } - - public void setEnabled(boolean b) - { - _enabled = b; - System.out.println("MAC Enabled: " + b); - } - - public boolean isEnabled() - { - return _enabled; - } - - public void setStrict(boolean strict) - { - _strict = strict; - - reset(); - - System.out.println("MAC Strict: " + strict); - } - /** * Add a GWEN Anticheat class to the ignored checks. * All violation events for these checks will be ignored + * * @param check The class of the check to ignore */ public void addIgnoredCheck(Class check) @@ -1174,27 +395,63 @@ public class AntiHack extends MiniPlugin _ignoredChecks.clear(); } - public boolean isStrict() - { - return _strict; - } - - public void setKick(boolean kick) - { - _kick = kick; - - System.out.println("MAC Kick: " + kick); - } - - public void enableNewAnticheat() + public void enableAnticheat() { UtilServer.CallEvent(new GameStartEvent()); - System.out.println("Enabled new anticheat"); } - public void disableNewAnticheat() + public void disableAnticheat() { UtilServer.CallEvent(new GameEndEvent()); - System.out.println("Disabled new anticheat"); + } + + private BaseComponent[] getDetailedMessage(MajorViolationCommand violation) + { + return new ComponentBuilder("") + .append("A").color(ChatColor.AQUA).obfuscated(true) + .append(" GWEN > ", ComponentBuilder.FormatRetention.NONE).color(ChatColor.RED).bold(true) + .append(violation.getPlayerName(), ComponentBuilder.FormatRetention.NONE).color(ChatColor.GOLD) + .append(" failed " + violation.getHackType() + " VL" + violation.getViolations() + " in server", ComponentBuilder.FormatRetention.NONE).color(ChatColor.YELLOW) + .append(violation.getOriginatingServer(), ComponentBuilder.FormatRetention.NONE).color(ChatColor.YELLOW) + .event(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/server " + violation.getOriginatingServer())) + .event(new HoverEvent(HoverEvent.Action.SHOW_TEXT, + new ComponentBuilder("Teleport to " + violation.getOriginatingServer()).create())) + .append(": " + violation.getMessage() + ".", ComponentBuilder.FormatRetention.NONE).color(ChatColor.YELLOW) + .create(); + } + + private BaseComponent[] getMinimalMessage(MajorViolationCommand violation) + { + Class checkType = CheckManager.getCheckBySimpleName(violation.getHackType()); + if (!CHECKS.containsKey(checkType)) + { + System.out.println("Warning: Unknown check type '" + violation.getHackType() + "' " + checkType); + } + ComponentBuilder componentBuilder = new ComponentBuilder("") + .append("A").color(ChatColor.AQUA).obfuscated(true) + .append(" GWEN > ", ComponentBuilder.FormatRetention.NONE).color(ChatColor.RED).bold(true) + .append(violation.getPlayerName(), ComponentBuilder.FormatRetention.NONE).color(ChatColor.GOLD) + .append(" suspected of ", ComponentBuilder.FormatRetention.NONE).color(ChatColor.YELLOW); + CHECKS.getOrDefault(checkType, NOOP_THRESHOLD).format(componentBuilder, violation.getViolations()); + + if (!violation.getOriginatingServer().equals(this._thisServer)) + { + componentBuilder.append(" in ", ComponentBuilder.FormatRetention.NONE).color(ChatColor.YELLOW) + .append(violation.getOriginatingServer()).color(ChatColor.AQUA) + .event(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/server " + violation.getOriginatingServer())) + .event(new HoverEvent(HoverEvent.Action.SHOW_TEXT, + new ComponentBuilder("Teleport to " + violation.getOriginatingServer()).create())); + } + + componentBuilder.append(".").color(ChatColor.YELLOW); + + return componentBuilder.create(); + } + + public static String generateId() + { + byte[] holder = new byte[ID_LENGTH]; + ThreadLocalRandom.current().nextBytes(holder); + return DatatypeConverter.printHexBinary(holder); } } diff --git a/Plugins/Mineplex.Core/src/mineplex/core/antihack/AntiHackRepository.java b/Plugins/Mineplex.Core/src/mineplex/core/antihack/AntiHackRepository.java deleted file mode 100644 index eb3b3cd0c..000000000 --- a/Plugins/Mineplex.Core/src/mineplex/core/antihack/AntiHackRepository.java +++ /dev/null @@ -1,71 +0,0 @@ -package mineplex.core.antihack; - -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.SQLException; - -import mineplex.serverdata.database.DBPool; - -import org.bukkit.craftbukkit.v1_8_R3.entity.CraftPlayer; -import org.bukkit.entity.Player; - -public class AntiHackRepository -{ - private String _serverName; - - //private static String CREATE_TABLE = "CREATE TABLE IF NOT EXISTS AntiHack_Kick_Log (id INT NOT NULL AUTO_INCREMENT, updated LONG, playerName VARCHAR(256), motd VARCHAR(56), gameType VARCHAR(56), map VARCHAR(256), serverName VARCHAR(256), report VARCHAR(256), ping VARCHAR(25), PRIMARY KEY (id));"; - private static String UPDATE_PLAYER_OFFENSES = "INSERT INTO AntiHack_Kick_Log (updated, playerName, motd, gameType, map, serverName, report, ping) VALUES (now(), ?, ?, ?, ?, ?, ?, ?);"; - - public AntiHackRepository(String serverName) - { - _serverName = serverName; - } - - public void initialize() - { - } - - public void saveOffense(final Player player, final String motd, final String game, final String map, final String report) - { - new Thread(new Runnable() - { - public void run() - { - PreparedStatement preparedStatement = null; - - try (Connection connection = DBPool.getMineplexStats().getConnection()) - { - preparedStatement = connection.prepareStatement(UPDATE_PLAYER_OFFENSES); - - preparedStatement.setString(1, player.getName()); - preparedStatement.setString(2, motd); - preparedStatement.setString(3, game); - preparedStatement.setString(4, map); - preparedStatement.setString(5, _serverName); - preparedStatement.setString(6, report); - preparedStatement.setString(7, ((CraftPlayer)player).getHandle().ping + "ms"); - - preparedStatement.execute(); - } - catch (Exception exception) - { - exception.printStackTrace(); - } - finally - { - if (preparedStatement != null) - { - try - { - preparedStatement.close(); - } - catch (SQLException e) - { - e.printStackTrace(); - } - } - } - } - }).start(); - } -} diff --git a/Plugins/Mineplex.Core/src/mineplex/core/antihack/CheckThresholds.java b/Plugins/Mineplex.Core/src/mineplex/core/antihack/CheckThresholds.java index f64783e55..590739775 100644 --- a/Plugins/Mineplex.Core/src/mineplex/core/antihack/CheckThresholds.java +++ b/Plugins/Mineplex.Core/src/mineplex/core/antihack/CheckThresholds.java @@ -1,5 +1,7 @@ package mineplex.core.antihack; +import net.md_5.bungee.api.ChatColor; +import net.md_5.bungee.api.chat.ComponentBuilder; import net.minecraft.server.v1_8_R3.ChatComponentText; import net.minecraft.server.v1_8_R3.ChatModifier; import net.minecraft.server.v1_8_R3.EnumChatFormat; @@ -25,10 +27,9 @@ public class CheckThresholds return _friendlyName; } - public IChatBaseComponent format(int violationLevel) + public void format(ComponentBuilder builder, int violationLevel) { - EnumChatFormat color = getSeverity(violationLevel)._color; - return new ChatComponentText(_friendlyName).setChatModifier(new ChatModifier().setColor(color)); + builder.append(_friendlyName, ComponentBuilder.FormatRetention.NONE).color(getSeverity(violationLevel)._color); } public Severity getSeverity(int violationLevel) @@ -51,14 +52,14 @@ public class CheckThresholds public enum Severity { - NONE(EnumChatFormat.GREEN), - LOW(EnumChatFormat.GREEN), - MEDIUM(EnumChatFormat.GOLD), - HIGH(EnumChatFormat.RED), + NONE(ChatColor.GREEN), + LOW(ChatColor.GREEN), + MEDIUM(ChatColor.GOLD), + HIGH(ChatColor.RED), ; - private final EnumChatFormat _color; + private final ChatColor _color; - Severity(EnumChatFormat color) + Severity(ChatColor color) { _color = color; } diff --git a/Plugins/Mineplex.Core/src/mineplex/core/antihack/Detector.java b/Plugins/Mineplex.Core/src/mineplex/core/antihack/Detector.java deleted file mode 100644 index 5cfec165e..000000000 --- a/Plugins/Mineplex.Core/src/mineplex/core/antihack/Detector.java +++ /dev/null @@ -1,8 +0,0 @@ -package mineplex.core.antihack; - -import org.bukkit.entity.Player; - -public interface Detector -{ - public void Reset(Player player); -} diff --git a/Plugins/Mineplex.Core/src/mineplex/core/antihack/MineplexLinkImpl.java b/Plugins/Mineplex.Core/src/mineplex/core/antihack/MineplexLinkImpl.java new file mode 100644 index 000000000..6195a3fd2 --- /dev/null +++ b/Plugins/Mineplex.Core/src/mineplex/core/antihack/MineplexLinkImpl.java @@ -0,0 +1,50 @@ +package mineplex.core.antihack; + +import net.minecraft.server.v1_8_R3.MinecraftServer; + +import org.bukkit.craftbukkit.v1_8_R3.entity.CraftPlayer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; + +import com.mineplex.anticheat.api.MineplexLink; + +import mineplex.core.Managers; +import mineplex.core.common.util.UtilPlayer; +import mineplex.core.disguise.DisguiseManager; +import mineplex.core.disguise.disguises.DisguiseBase; + +public class MineplexLinkImpl implements MineplexLink +{ + private final DisguiseManager _disguiseManager = Managers.require(DisguiseManager.class); + + @Override + public EntityType getActiveDisguise(Player player) + { + DisguiseBase disguise = _disguiseManager.getActiveDisguise(player); + return disguise != null ? disguise.getDisguiseType() : null; + } + + @Override + public boolean isSpectator(Player player) + { + return UtilPlayer.isSpectator(player); + } + + @Override + public double getTPS() + { + return MinecraftServer.getServer().recentTps[0]; // Return the average TPS from the last minute + } + + @Override + public int getPing(Player player) + { + return Math.min(((CraftPlayer) player).getHandle().ping, 1000); + } + + @Override + public boolean isUsingItem(Player player) + { + return ((CraftPlayer) player).getHandle().bS(); // See Anticheat javadoc + } +} diff --git a/Plugins/Mineplex.Core/src/mineplex/core/antihack/ViolationLevels.java b/Plugins/Mineplex.Core/src/mineplex/core/antihack/ViolationLevels.java new file mode 100644 index 000000000..00a800bce --- /dev/null +++ b/Plugins/Mineplex.Core/src/mineplex/core/antihack/ViolationLevels.java @@ -0,0 +1,87 @@ +package mineplex.core.antihack; + +import com.mineplex.anticheat.checks.Check; +import com.mineplex.anticheat.checks.CheckManager; + +import gnu.trove.map.TObjectIntMap; +import gnu.trove.map.hash.TObjectIntHashMap; + +/** + * Locally cached information about a user's max violations and total number of alerts for each + * check type. + *

+ * Instances of this have no concept of identity i.e. account id is not tracked by this. + */ +public class ViolationLevels +{ + private final TObjectIntMap> _maxViolations; + + private final TObjectIntMap> _totalAlerts; + + private final TObjectIntMap> _lastBan; + + public ViolationLevels() + { + _maxViolations = new TObjectIntHashMap<>(CheckManager.AVAILABLE_CHECKS.size()); + _totalAlerts = new TObjectIntHashMap<>(CheckManager.AVAILABLE_CHECKS.size()); + _lastBan = new TObjectIntHashMap<>(CheckManager.AVAILABLE_CHECKS.size()); + } + + public void updateMaxViolations(Class check, int violationLevel) + { + if (violationLevel > _maxViolations.get(check)) + { + _maxViolations.put(check, violationLevel); + } + } + + public void updateMaxViolationsSinceLastBan(Class check, int violationLevel) + { + if (violationLevel > _lastBan.get(check)) + { + _lastBan.put(check, violationLevel); + } + } + + public void incrementAlerts(Class check) + { + int cur = _totalAlerts.get(check); + + setTotalAlerts(check, cur + 1); + } + + public void setTotalAlerts(Class check, int totalAlerts) + { + _totalAlerts.put(check, totalAlerts); + } + + public int getTotalAlertsForCheck(Class check) + { + if (_totalAlerts.containsKey(check)) + { + return _totalAlerts.get(check); + } + + return -1; + } + + public int getMaxViolationsForCheck(Class check) + { + if (_maxViolations.containsKey(check)) + { + return _maxViolations.get(check); + } + + return -1; + } + + public int getLastBanViolationsForCheck(Class check) + { + if (_lastBan.containsKey(check)) + { + return _lastBan.get(check); + } + + return -1; + } +} diff --git a/Plugins/Mineplex.Core/src/mineplex/core/antihack/actions/AntiHackAction.java b/Plugins/Mineplex.Core/src/mineplex/core/antihack/actions/AntiHackAction.java index ddcc5e909..c919bba58 100644 --- a/Plugins/Mineplex.Core/src/mineplex/core/antihack/actions/AntiHackAction.java +++ b/Plugins/Mineplex.Core/src/mineplex/core/antihack/actions/AntiHackAction.java @@ -4,6 +4,7 @@ import com.mineplex.anticheat.api.PlayerViolationEvent; import com.mineplex.anticheat.checks.combat.KillauraTypeA; import com.mineplex.anticheat.checks.combat.KillauraTypeD; import com.mineplex.anticheat.checks.move.Glide; +import com.mineplex.anticheat.checks.move.HeadRoll; import com.mineplex.anticheat.checks.move.Speed; import mineplex.core.common.util.UtilServer; import org.bukkit.event.Listener; @@ -13,40 +14,19 @@ import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; -public abstract class AntiHackAction implements Listener +public abstract class AntiHackAction { - private static final Map, AntiHackAction> ACTIONS = new HashMap<>(); - private static final AntiHackAction NOOP_ACTION = new NoopAction(); - - private static final Date NEXT_BAN_WAVE = new Date(System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(5)); - - static - { - ACTIONS.put(KillauraTypeA.class, new ImmediateBanAction(200)); - ACTIONS.put(KillauraTypeD.class, new BanwaveAction(2000)); - ACTIONS.put(Glide.class, new ImmediateBanAction(10000)); - ACTIONS.put(Speed.class, new ImmediateBanAction(10000)); - } - - private int _vl; + private final int _vl; AntiHackAction(int vl) { this._vl = vl; - - UtilServer.RegisterEvents(this); } - public abstract void handle(PlayerViolationEvent event); - - public int getMinVl() + public final int getMinVl() { return this._vl; } - public static AntiHackAction getAction(Class checkClass) - { - AntiHackAction action = ACTIONS.getOrDefault(checkClass, NOOP_ACTION); - return action; - } + public abstract void handle(PlayerViolationEvent event); } diff --git a/Plugins/Mineplex.Core/src/mineplex/core/antihack/actions/BanwaveAction.java b/Plugins/Mineplex.Core/src/mineplex/core/antihack/actions/BanwaveAction.java index 876671029..24408afe9 100644 --- a/Plugins/Mineplex.Core/src/mineplex/core/antihack/actions/BanwaveAction.java +++ b/Plugins/Mineplex.Core/src/mineplex/core/antihack/actions/BanwaveAction.java @@ -1,17 +1,18 @@ package mineplex.core.antihack.actions; import com.mineplex.anticheat.api.PlayerViolationEvent; + import mineplex.core.Managers; import mineplex.core.antihack.banwave.BanWaveManager; import mineplex.core.common.util.UtilMath; import mineplex.core.common.util.UtilServer; -class BanwaveAction extends AntiHackAction +public class BanwaveAction extends AntiHackAction { private static final int BAN_DELAY_AVERAGE = 6 * 60 * 60 * 1000; // 6 hours private static final int BAN_DELAY_VARIANCE_SPAN = 4 * 60 * 60 * 1000; // 4 hours total; 2 on either side - BanwaveAction(int vl) + public BanwaveAction(int vl) { super(vl); } @@ -27,7 +28,6 @@ class BanwaveAction extends AntiHackAction event.getPlayer(), banTime, event.getCheckClass(), - "[GWEN] Hacking [BanWave]", event.getViolations(), UtilServer.getServerName() ); diff --git a/Plugins/Mineplex.Core/src/mineplex/core/antihack/actions/ImmediateBanAction.java b/Plugins/Mineplex.Core/src/mineplex/core/antihack/actions/ImmediateBanAction.java index ce6eeaf08..a06b38daf 100644 --- a/Plugins/Mineplex.Core/src/mineplex/core/antihack/actions/ImmediateBanAction.java +++ b/Plugins/Mineplex.Core/src/mineplex/core/antihack/actions/ImmediateBanAction.java @@ -5,9 +5,9 @@ import mineplex.core.Managers; import mineplex.core.antihack.AntiHack; import mineplex.core.common.util.UtilServer; -class ImmediateBanAction extends AntiHackAction +public class ImmediateBanAction extends AntiHackAction { - ImmediateBanAction(int vl) + public ImmediateBanAction(int vl) { super(vl); } @@ -17,12 +17,7 @@ class ImmediateBanAction extends AntiHackAction { if (event.getViolations() >= this.getMinVl()) { - String server = UtilServer.getServerName(); - if (server.contains("-")) - { - server = server.substring(0, server.indexOf('-')); - } - Managers.get(AntiHack.class).doBan(event.getPlayer(), "[GWEN] Hacking [" + server + "]"); + Managers.get(AntiHack.class).doBan(event.getPlayer(), event.getCheckClass()); } } } diff --git a/Plugins/Mineplex.Core/src/mineplex/core/antihack/actions/MixedAction.java b/Plugins/Mineplex.Core/src/mineplex/core/antihack/actions/MixedAction.java deleted file mode 100644 index 14066a4d7..000000000 --- a/Plugins/Mineplex.Core/src/mineplex/core/antihack/actions/MixedAction.java +++ /dev/null @@ -1,49 +0,0 @@ -package mineplex.core.antihack.actions; - -import com.mineplex.anticheat.api.PlayerViolationEvent; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.UUID; - -public class MixedAction extends AntiHackAction -{ - private List _actions = new ArrayList<>(); - private Map> _punished = new HashMap<>(); - - public MixedAction(AntiHackAction firstAction, AntiHackAction... actions) - { - super(firstAction.getMinVl()); - this._actions.add(firstAction); - this._actions.addAll(Arrays.asList(actions)); - } - - - @Override - public void handle(PlayerViolationEvent event) - { - for (int i = this._actions.size() - 1; i >= 0; i--) - { - AntiHackAction action = this._actions.get(i); - if (action.getMinVl() <= event.getViolations()) - { - if (_punished.computeIfAbsent(event.getPlayer().getUniqueId(), key -> new HashSet<>()).add(action)) - { - action.handle(event); - } - break; - } - } - } - - public int getMinVl() - { - return this._actions.get(0).getMinVl(); - } -} diff --git a/Plugins/Mineplex.Core/src/mineplex/core/antihack/actions/NoopAction.java b/Plugins/Mineplex.Core/src/mineplex/core/antihack/actions/NoopAction.java index 38794c630..848322ac3 100644 --- a/Plugins/Mineplex.Core/src/mineplex/core/antihack/actions/NoopAction.java +++ b/Plugins/Mineplex.Core/src/mineplex/core/antihack/actions/NoopAction.java @@ -4,7 +4,7 @@ import com.mineplex.anticheat.api.PlayerViolationEvent; public class NoopAction extends AntiHackAction { - NoopAction() + public NoopAction() { super(Integer.MAX_VALUE); } diff --git a/Plugins/Mineplex.Core/src/mineplex/core/antihack/animations/BanwaveAnimation.java b/Plugins/Mineplex.Core/src/mineplex/core/antihack/animations/BanwaveAnimation.java new file mode 100644 index 000000000..e3af1b117 --- /dev/null +++ b/Plugins/Mineplex.Core/src/mineplex/core/antihack/animations/BanwaveAnimation.java @@ -0,0 +1,9 @@ +package mineplex.core.antihack.animations; + +import mineplex.core.antihack.AntiHack; +import org.bukkit.entity.Player; + +public interface BanwaveAnimation +{ + void run(Player player, Runnable after); +} diff --git a/Plugins/Mineplex.Core/src/mineplex/core/antihack/animations/BanwaveAnimationSpin.java b/Plugins/Mineplex.Core/src/mineplex/core/antihack/animations/BanwaveAnimationSpin.java new file mode 100644 index 000000000..3370f28f2 --- /dev/null +++ b/Plugins/Mineplex.Core/src/mineplex/core/antihack/animations/BanwaveAnimationSpin.java @@ -0,0 +1,123 @@ +package mineplex.core.antihack.animations; + +import com.google.common.util.concurrent.AtomicDouble; +import mineplex.core.antihack.AntiHack; +import mineplex.core.antihack.guardians.AntiHackGuardian; +import mineplex.core.common.util.UtilEnt; +import mineplex.core.common.util.UtilParticle; +import mineplex.core.common.util.UtilServer; +import net.minecraft.server.v1_8_R3.MathHelper; +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; +import org.bukkit.scheduler.BukkitRunnable; + +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Function; + +public class BanwaveAnimationSpin implements BanwaveAnimation +{ + @Override + public void run(Player player, Runnable after) + { + float oldWalkSpeed = player.getWalkSpeed(); + player.setWalkSpeed(0); + player.addPotionEffect(new PotionEffect(PotionEffectType.JUMP, 999999, -10)); + + double radius = 4; + double heightAdj = 8; + + double baseDeg = 18; + + Location center = player.getLocation().add(0, heightAdj, 0); + AntiHackGuardian north = new AntiHackGuardian(center.clone().add(0, 0, -radius), 0, 0, 0, 0, 0, 0, false); + AntiHackGuardian east = new AntiHackGuardian(center.clone().add(radius, 0, 0), 0, 0, 0, 0, 0, 0, false); + AntiHackGuardian south = new AntiHackGuardian(center.clone().add(0, 0, radius), 0, 0, 0, 0, 0, 0, false); + AntiHackGuardian west = new AntiHackGuardian(center.clone().add(-radius, 0, 0), 0, 0, 0, 0, 0, 0, false); + + UtilEnt.CreatureLook(east.getEntity(), player); + UtilEnt.CreatureLook(west.getEntity(), player); + UtilEnt.CreatureLook(south.getEntity(), player); + UtilEnt.CreatureLook(north.getEntity(), player); + + Function magic = seconds -> Math.pow(2, seconds - 5); + + UtilServer.runSyncLater(() -> + { + north.shoot(player); + east.shoot(player); + south.shoot(player); + west.shoot(player); + + // We get 5 seconds, or 100 ticks + AtomicInteger timer = new AtomicInteger(5); + + AtomicDouble cNorth = new AtomicDouble(270); + AtomicDouble cEast = new AtomicDouble(0); + AtomicDouble cSouth = new AtomicDouble(90); + AtomicDouble cWest = new AtomicDouble(180); + + UtilServer.runSyncTimer(new BukkitRunnable() + { + public void run() + { + timer.getAndIncrement(); + if (timer.get() > 100) + { + cancel(); + + player.removePotionEffect(PotionEffectType.JUMP); + player.setWalkSpeed(oldWalkSpeed); + Location location = player.getLocation(); + + UtilParticle.PlayParticle(UtilParticle.ParticleType.HUGE_EXPLOSION, player.getLocation(), 3f, 3f, 3f, 0, 32, UtilParticle.ViewDist.MAX, UtilServer.getPlayers()); + + after.run(); + + north.shoot(null); + south.shoot(null); + east.shoot(null); + west.shoot(null); + UtilEnt.CreatureLook(north.getEntity(), location); + UtilEnt.CreatureLook(south.getEntity(), location); + UtilEnt.CreatureLook(east.getEntity(), location); + UtilEnt.CreatureLook(west.getEntity(), location); + UtilServer.runSyncLater(() -> + { + north.remove(); + south.remove(); + east.remove(); + west.remove(); + }, 40L); + return; + } + + double seconds = timer.get() / 20.0; + + double rate = magic.apply(seconds) * 3 * baseDeg; + + player.getLocation(center); + center.add(0, heightAdj, 0); + + { + cNorth.addAndGet(rate); + north.move(center.getX() + radius * MathHelper.cos((float) Math.toRadians(cNorth.get())), center.getY(), center.getZ() + radius * MathHelper.sin((float) Math.toRadians(cNorth.get()))); + } + { + cSouth.addAndGet(rate); + south.move(center.getX() + radius * MathHelper.cos((float) Math.toRadians(cSouth.get())), center.getY(), center.getZ() + radius * MathHelper.sin((float) Math.toRadians(cSouth.get()))); + } + { + cEast.addAndGet(rate); + east.move(center.getX() + radius * MathHelper.cos((float) Math.toRadians(cEast.get())), center.getY(), center.getZ() + radius * MathHelper.sin((float) Math.toRadians(cEast.get()))); + } + { + cWest.addAndGet(rate); + west.move(center.getX() + radius * MathHelper.cos((float) Math.toRadians(cWest.get())), center.getY(), center.getZ() + radius * MathHelper.sin((float) Math.toRadians(cWest.get()))); + } + } + }, 5L, 1L); + }, 20); + } +} diff --git a/Plugins/Mineplex.Core/src/mineplex/core/antihack/banwave/BanWaveInfo.java b/Plugins/Mineplex.Core/src/mineplex/core/antihack/banwave/BanWaveInfo.java index 2c471cd07..821a4018e 100644 --- a/Plugins/Mineplex.Core/src/mineplex/core/antihack/banwave/BanWaveInfo.java +++ b/Plugins/Mineplex.Core/src/mineplex/core/antihack/banwave/BanWaveInfo.java @@ -34,6 +34,11 @@ public class BanWaveInfo */ private String _server; + /** + * The metadata id + */ + private String _metadata; + public int getAccountId() { return _accountId; @@ -94,6 +99,16 @@ public class BanWaveInfo _server = server; } + public String getMetadataId() + { + return this._metadata; + } + + public void setMetadataId(String id) + { + this._metadata = id; + } + @Override public boolean equals(Object o) { diff --git a/Plugins/Mineplex.Core/src/mineplex/core/antihack/banwave/BanWaveManager.java b/Plugins/Mineplex.Core/src/mineplex/core/antihack/banwave/BanWaveManager.java index dd2bebe96..1dd36c4b5 100644 --- a/Plugins/Mineplex.Core/src/mineplex/core/antihack/banwave/BanWaveManager.java +++ b/Plugins/Mineplex.Core/src/mineplex/core/antihack/banwave/BanWaveManager.java @@ -5,14 +5,25 @@ import mineplex.core.ReflectivelyCreateMiniPlugin; import mineplex.core.account.CoreClient; import mineplex.core.account.CoreClientManager; import mineplex.core.antihack.AntiHack; +import mineplex.core.antihack.logging.AntihackLogger; +import mineplex.core.antihack.redisnotifications.GwenBanwaveNotification; +import mineplex.core.common.util.UtilServer; +import mineplex.serverdata.commands.ServerCommandManager; + +import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.player.PlayerJoinEvent; +import com.google.gson.JsonObject; +import com.mineplex.anticheat.checks.Check; +import com.mineplex.anticheat.checks.CheckManager; + @ReflectivelyCreateMiniPlugin public class BanWaveManager extends MiniPlugin { private final BanWaveRepository _repository = new BanWaveRepository(); + private final CoreClientManager _clientManager = require(CoreClientManager.class); private BanWaveManager() { @@ -32,29 +43,39 @@ public class BanWaveManager extends MiniPlugin if (info.getTimeToBan() < now) { - require(AntiHack.class).doBanWave(event.getPlayer(), info.getMessage()); + require(AntiHack.class).doBanWave(event.getPlayer(), info); _repository.flagDone(info); } }); }); } - public void insertBanWaveInfo(Player player, long timeToBan, Class checkClass, String message, int vl, String server) + public void insertBanWaveInfo(Player player, long timeToBan, Class checkClass, int vl, String server) { - insertBanWaveInfo(player, timeToBan, checkClass, message, vl, server, null); + insertBanWaveInfo(player, timeToBan, checkClass, vl, server, null); } - public void insertBanWaveInfo(Player player, long timeToBan, Class checkClass, String message, int vl, String server, Runnable after) + public void insertBanWaveInfo(Player player, long timeToBan, Class checkClass, int vl, String server, Runnable after) { runAsync(() -> { - CoreClient client = require(CoreClientManager.class).Get(player); + String id = AntiHack.generateId(); + String newMessage = "[GWEN] [BanWave] " + id; - this._repository.insertBanWaveInfo(client.getAccountId(), timeToBan, checkClass.getName(), message, vl, server); + CoreClient client = _clientManager.Get(player); - if (after != null) + if (this._repository.insertBanWaveInfo(client.getAccountId(), timeToBan, CheckManager.getCheckSimpleName(checkClass), newMessage, vl, server, id)) { - after.run(); + runAsync(() -> + { + GwenBanwaveNotification notification = new GwenBanwaveNotification(UtilServer.getServerName(), player.getName(), player.getUniqueId().toString(), CheckManager.getCheckSimpleName(checkClass), id, timeToBan); + ServerCommandManager.getInstance().publishCommand(notification); + }); + + JsonObject custom = new JsonObject(); + custom.addProperty("is-banwave", true); + + require(AntihackLogger.class).saveMetadata(player, id, after, custom); } }); } diff --git a/Plugins/Mineplex.Core/src/mineplex/core/antihack/banwave/BanWaveRepository.java b/Plugins/Mineplex.Core/src/mineplex/core/antihack/banwave/BanWaveRepository.java index ff41ab647..723f67620 100644 --- a/Plugins/Mineplex.Core/src/mineplex/core/antihack/banwave/BanWaveRepository.java +++ b/Plugins/Mineplex.Core/src/mineplex/core/antihack/banwave/BanWaveRepository.java @@ -17,6 +17,7 @@ public class BanWaveRepository extends MinecraftRepository "message VARCHAR(255), " + "vl INT, " + "server VARCHAR(32), " + + "metadata VARCHAR(10), " + "PRIMARY KEY (accountId)," + "FOREIGN KEY (accountId) REFERENCES accounts(id))"; @@ -28,12 +29,14 @@ public class BanWaveRepository extends MinecraftRepository "message VARCHAR(255), " + "vl INT, " + "server VARCHAR(32), " + + "metadata VARCHAR(10), " + "PRIMARY KEY (id)," + "FOREIGN KEY (accountId) REFERENCES accounts(id))"; - private static final String QUERY_PENDING = "SELECT * FROM banwavePending WHERE accountId = ?"; - private static final String INSERT_PENDING = "INSERT IGNORE INTO banwavePending (accountId, timeToBan, hacktype, message, vl, server) VALUES (?, ?, ?, ?, ?, ?)"; - private static final String PROCESS_WAVE_FOR_ACCOUNT = "INSERT INTO banwaveProcessed SELECT 0, accountId, timeToBan, hacktype, message, vl, server FROM banwavePending WHERE accountId = ?"; + private static final String QUERY_PENDING = "SELECT * FROM banwavePending WHERE accountId = ?"; + private static final String INSERT_PENDING = "INSERT IGNORE INTO banwavePending (accountId, timeToBan, hacktype, message, vl, server, metadata) VALUES (?, ?, ?, ?, ?, ?, ?)"; + + private static final String PROCESS_WAVE_FOR_ACCOUNT = "INSERT INTO banwaveProcessed SELECT 0, accountId, timeToBan, hacktype, message, vl, server, metadata FROM banwavePending WHERE accountId = ?"; private static final String DELETE_PENDING = "DELETE FROM banwavePending WHERE accountId = ?"; BanWaveRepository() @@ -66,22 +69,25 @@ public class BanWaveRepository extends MinecraftRepository info.setMessage(resultSet.getString(4)); info.setVl(resultSet.getInt(5)); info.setServer(resultSet.getString(6)); + info.setMessage(resultSet.getString(7)); callback.run(info); } }, new ColumnInt("accountId", accountId)); } - void insertBanWaveInfo(int accountId, long timeToBan, String hackType, String message, int vl, String server) + boolean insertBanWaveInfo(int accountId, long timeToBan, String hackType, String message, int vl, String server, String metadata) { - executeInsert(INSERT_PENDING, null, + int affectedRows = executeInsert(INSERT_PENDING, null, new ColumnInt("accountId", accountId), new ColumnLong("timeToBan", timeToBan), new ColumnVarChar("hacktype", 64, hackType), new ColumnVarChar("message", 255, message), new ColumnInt("vl", vl), - new ColumnVarChar("server", 32, server) + new ColumnVarChar("server", 32, server), + new ColumnVarChar("metadata", 10, metadata) ); + return affectedRows > 0; } void flagDone(BanWaveInfo info) diff --git a/Plugins/Mineplex.Core/src/mineplex/core/antihack/commands/AnticheatOffCommand.java b/Plugins/Mineplex.Core/src/mineplex/core/antihack/commands/AnticheatOffCommand.java new file mode 100644 index 000000000..a3d1f4267 --- /dev/null +++ b/Plugins/Mineplex.Core/src/mineplex/core/antihack/commands/AnticheatOffCommand.java @@ -0,0 +1,24 @@ +package mineplex.core.antihack.commands; + +import org.bukkit.entity.Player; + +import mineplex.core.antihack.AntiHack; +import mineplex.core.command.CommandBase; +import mineplex.core.common.Rank; +import mineplex.core.common.util.F; +import mineplex.core.common.util.UtilPlayer; + +public class AnticheatOffCommand extends CommandBase +{ + public AnticheatOffCommand(AntiHack plugin) + { + super(plugin, Rank.DEVELOPER, "acoff"); + } + + @Override + public void Execute(Player caller, String[] args) + { + Plugin.disableAnticheat(); + UtilPlayer.message(caller, F.main(Plugin.getName(), "Disabled anticheat")); + } +} diff --git a/Plugins/Mineplex.Core/src/mineplex/core/antihack/commands/AnticheatOnCommand.java b/Plugins/Mineplex.Core/src/mineplex/core/antihack/commands/AnticheatOnCommand.java new file mode 100644 index 000000000..7c1f3a767 --- /dev/null +++ b/Plugins/Mineplex.Core/src/mineplex/core/antihack/commands/AnticheatOnCommand.java @@ -0,0 +1,24 @@ +package mineplex.core.antihack.commands; + +import org.bukkit.entity.Player; + +import mineplex.core.antihack.AntiHack; +import mineplex.core.command.CommandBase; +import mineplex.core.common.Rank; +import mineplex.core.common.util.F; +import mineplex.core.common.util.UtilPlayer; + +public class AnticheatOnCommand extends CommandBase +{ + public AnticheatOnCommand(AntiHack plugin) + { + super(plugin, Rank.DEVELOPER, "acon"); + } + + @Override + public void Execute(Player caller, String[] args) + { + Plugin.enableAnticheat(); + UtilPlayer.message(caller, F.main(Plugin.getName(), "Enabled anticheat")); + } +} diff --git a/Plugins/Mineplex.Core/src/mineplex/core/antihack/commands/DetailedMessagesCommand.java b/Plugins/Mineplex.Core/src/mineplex/core/antihack/commands/DetailedMessagesCommand.java new file mode 100644 index 000000000..e873d22e8 --- /dev/null +++ b/Plugins/Mineplex.Core/src/mineplex/core/antihack/commands/DetailedMessagesCommand.java @@ -0,0 +1,30 @@ +package mineplex.core.antihack.commands; + +import org.bukkit.entity.Player; + +import mineplex.core.antihack.AntiHack; +import mineplex.core.command.CommandBase; +import mineplex.core.common.Rank; +import mineplex.core.common.util.F; +import mineplex.core.common.util.UtilPlayer; + +public class DetailedMessagesCommand extends CommandBase +{ + public DetailedMessagesCommand(AntiHack plugin) + { + super(plugin, Rank.DEVELOPER, "detailedmessages"); + } + + @Override + public void Execute(Player caller, String[] args) + { + if (Plugin.toggleDetailedMessage(caller)) + { + UtilPlayer.message(caller, F.main(Plugin.getName(), "Detailed messages enabled")); + } + else + { + UtilPlayer.message(caller, F.main(Plugin.getName(), "Detailed messages disabled")); + } + } +} diff --git a/Plugins/Mineplex.Core/src/mineplex/core/antihack/commands/GetVlsCommand.java b/Plugins/Mineplex.Core/src/mineplex/core/antihack/commands/GetVlsCommand.java new file mode 100644 index 000000000..84f32311b --- /dev/null +++ b/Plugins/Mineplex.Core/src/mineplex/core/antihack/commands/GetVlsCommand.java @@ -0,0 +1,47 @@ +package mineplex.core.antihack.commands; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +import com.mineplex.anticheat.MineplexAnticheat; +import com.mineplex.anticheat.checks.Check; +import com.mineplex.anticheat.checks.CheckManager; + +import mineplex.core.antihack.AntiHack; +import mineplex.core.command.CommandBase; +import mineplex.core.common.Rank; +import mineplex.core.common.util.F; +import mineplex.core.common.util.UtilPlayer; + +public class GetVlsCommand extends CommandBase +{ + public GetVlsCommand(AntiHack plugin) + { + super(plugin, Rank.DEVELOPER, "getvls"); + } + + @Override + public void Execute(Player caller, String[] args) + { + if (args.length > 0) + { + Player p = Bukkit.getPlayerExact(args[0]); + if (p != null) + { + CheckManager manager = MineplexAnticheat.getPlugin(MineplexAnticheat.class).getCheckManager(); + for (Check check : manager.getActiveChecks()) + { + UtilPlayer.message(caller, F.desc(check.getName(), String.valueOf(check.getViolationLevel(p)))); + } + } + else + { + UtilPlayer.message(caller, F.main(Plugin.getName(), "Could not find player")); + } + } + else + { + UtilPlayer.message(caller, F.main(Plugin.getName(), "No player specified")); + } + } +} diff --git a/Plugins/Mineplex.Core/src/mineplex/core/antihack/commands/TestBanCommand.java b/Plugins/Mineplex.Core/src/mineplex/core/antihack/commands/TestBanCommand.java new file mode 100644 index 000000000..40ac6c1aa --- /dev/null +++ b/Plugins/Mineplex.Core/src/mineplex/core/antihack/commands/TestBanCommand.java @@ -0,0 +1,51 @@ +package mineplex.core.antihack.commands; + +import java.util.concurrent.ThreadLocalRandom; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +import mineplex.core.antihack.AntiHack; +import mineplex.core.antihack.animations.BanwaveAnimationSpin; +import mineplex.core.command.CommandBase; +import mineplex.core.common.Rank; +import mineplex.core.common.util.C; +import mineplex.core.common.util.F; +import mineplex.core.common.util.UtilPlayer; + +public class TestBanCommand extends CommandBase +{ + public TestBanCommand(AntiHack plugin) + { + super(plugin, Rank.DEVELOPER, "testban"); + } + + @Override + public void Execute(Player caller, String[] args) + { + if (args.length > 0) + { + Player p = Bukkit.getPlayerExact(args[0]); + if (p != null) + { + new BanwaveAnimationSpin().run(p, () -> + { + String reason = C.cRed + C.Bold + "You are banned for permanent by Test" + + "\n" + C.cWhite + "Seems to be speeding up time. (" + ThreadLocalRandom.current().nextInt(200, 400) + " packets/150 ms)" + + "\n" + C.cDGreen + "Unfairly banned? Appeal at " + C.cGreen + "www.mineplex.com/appeals"; + p.kickPlayer(reason); + + Plugin.announceBan(p); + }); + } + else + { + UtilPlayer.message(caller, F.main(Plugin.getName(), "Could not find player")); + } + } + else + { + UtilPlayer.message(caller, F.main(Plugin.getName(), "No player specified")); + } + } +} diff --git a/Plugins/Mineplex.Core/src/mineplex/core/antihack/AntiHackGuardian.java b/Plugins/Mineplex.Core/src/mineplex/core/antihack/guardians/AntiHackGuardian.java similarity index 99% rename from Plugins/Mineplex.Core/src/mineplex/core/antihack/AntiHackGuardian.java rename to Plugins/Mineplex.Core/src/mineplex/core/antihack/guardians/AntiHackGuardian.java index 9cff0ce43..cb9ef2898 100644 --- a/Plugins/Mineplex.Core/src/mineplex/core/antihack/AntiHackGuardian.java +++ b/Plugins/Mineplex.Core/src/mineplex/core/antihack/guardians/AntiHackGuardian.java @@ -1,4 +1,4 @@ -package mineplex.core.antihack; +package mineplex.core.antihack.guardians; import com.mineplex.spigot.ChunkAddEntityEvent; import mineplex.core.Managers; diff --git a/Plugins/Mineplex.Core/src/mineplex/core/antihack/guardians/GuardianManager.java b/Plugins/Mineplex.Core/src/mineplex/core/antihack/guardians/GuardianManager.java new file mode 100644 index 000000000..a5806e753 --- /dev/null +++ b/Plugins/Mineplex.Core/src/mineplex/core/antihack/guardians/GuardianManager.java @@ -0,0 +1,129 @@ +package mineplex.core.antihack.guardians; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import mineplex.core.MiniPlugin; +import mineplex.core.PlayerSelector; +import mineplex.core.ReflectivelyCreateMiniPlugin; +import mineplex.core.common.Rank; +import mineplex.core.common.util.UtilLambda; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.TimeUnit; +import java.util.function.Function; + +@ReflectivelyCreateMiniPlugin +public class GuardianManager extends MiniPlugin +{ + private static final int MAX_STALKED_PLAYERS = 3; + private static final int STALK_COOLDOWN_TIME_SECONDS = 5; + + private static final int MIN_STALK_TIME = 10 * 20; + private static final int MAX_STALK_TIME = 20 * 20; + private static final int MAX_MIN_DIFF = MAX_STALK_TIME - MIN_STALK_TIME; + private static final Function STALK_END_PROBABILITY_EQUATION = x -> + { + return 1.0/ MAX_MIN_DIFF * x; // linear equation with points (0, 0) and (diff, 1) + }; + + private final Cache _stalkingCooldown = CacheBuilder.newBuilder() + .concurrencyLevel(1) + .expireAfterWrite(STALK_COOLDOWN_TIME_SECONDS, TimeUnit.SECONDS) + .build(); + private final List _stalking = new ArrayList<>(); + private List _guardians = new ArrayList<>(); + + private GuardianManager() + { + super("GuardianManager"); + + this._plugin.getServer().getScheduler().runTaskTimer(this._plugin, () -> + { + for (AntiHackGuardian guardian : this._guardians) + { + if (guardian.getTarget() != null && !guardian.getTarget().isOnline()) + { + this._stalking.remove(guardian.getTarget().getUniqueId()); + guardian.stopTargeting(); + } + else if (guardian.getTargetingTime() > MIN_STALK_TIME) + { + double threshold = STALK_END_PROBABILITY_EQUATION.apply(guardian.getTargetingTime() - MIN_STALK_TIME); + if (Math.random() <= threshold) + { + this._stalking.remove(guardian.getTarget().getUniqueId()); + _stalkingCooldown.put(guardian.getTarget().getUniqueId(), true); + guardian.stopTargeting(); + } + } + guardian.tick(); + } + }, 0L, 1L); + + this._plugin.getServer().getScheduler().runTaskTimer(this._plugin, () -> + { + if (_stalking.size() >= MAX_STALKED_PLAYERS) + { + return; + } + + if (_guardians.size() == 0) + { + return; + } + + List targets = PlayerSelector.selectPlayers( + UtilLambda.and( + PlayerSelector.NOT_VANISHED, + PlayerSelector.hasAnyRank(false, + Rank.ALL, + Rank.ULTRA, + Rank.HERO, + Rank.LEGEND, + Rank.TITAN, + Rank.TWITCH, + Rank.YOUTUBE_SMALL, + Rank.YOUTUBE, + Rank.MEDIA, + Rank.ADMIN, + Rank.DEVELOPER, + Rank.OWNER, + Rank.LT + ), + player -> !_stalking.contains(player.getUniqueId()), + player -> _stalkingCooldown.getIfPresent(player.getUniqueId()) == null + )); + + while (_stalking.size() < MAX_STALKED_PLAYERS && targets.size() > 0) + { + Player target = targets.remove(ThreadLocalRandom.current().nextInt(targets.size())); + + int start = ThreadLocalRandom.current().nextInt(_guardians.size()); + + for (int i = start, j = 0; j < _guardians.size(); i++, j++) + { + if (i >= _guardians.size()) + { + i -= _guardians.size(); + } + AntiHackGuardian guardian = _guardians.get(i); + if (!guardian.isTargeting()) + { + guardian.target(target); + _stalking.add(target.getUniqueId()); + break; + } + } + } + }, 0L, 20L); + } + + public void registerGuardian(AntiHackGuardian guardian) + { + this._guardians.add(guardian); + } +} diff --git a/Plugins/Mineplex.Core/src/mineplex/core/antihack/logging/AnticheatDatabase.java b/Plugins/Mineplex.Core/src/mineplex/core/antihack/logging/AnticheatDatabase.java new file mode 100644 index 000000000..723a0f735 --- /dev/null +++ b/Plugins/Mineplex.Core/src/mineplex/core/antihack/logging/AnticheatDatabase.java @@ -0,0 +1,196 @@ +package mineplex.core.antihack.logging; + +import com.mineplex.anticheat.checks.Check; +import com.mineplex.anticheat.checks.CheckManager; + +import gnu.trove.map.TIntObjectMap; + +import mineplex.core.antihack.ViolationLevels; +import mineplex.core.database.MinecraftRepository; +import mineplex.serverdata.database.DBPool; + +import org.bukkit.plugin.java.JavaPlugin; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Map; +import java.util.Optional; + +public class AnticheatDatabase extends MinecraftRepository +{ + /* + CREATE TABLE IF NOT EXISTS anticheat_vl_logs (accountId INT, checkId INT, maxViolations INT, totalAlerts INT, sinceLastBan INT, PRIMARY KEY(accountId, checkId)); + CREATE TABLE IF NOT EXISTS anticheat_ban_metadata (id INT NOT NULL AUTO_INCREMENT, accountId INT, banId CHAR(10) NOT NULL, data MEDIUMTEXT NOT NULL, PRIMARY KEY(id)); + */ + + private static final String INSERT_INTO_METADATA = "INSERT INTO anticheat_ban_metadata (accountId, banId, data) VALUES (?, ?, ?);"; + + private static final String UPDATE_VIOLATIONS = "INSERT INTO anticheat_vl_logs (accountId, checkId, " + + "maxViolations, sinceLastBan, totalAlerts) VALUES (?, ?, ?, ?, ?) ON DUPLICATE KEY" + + " UPDATE maxViolations = VALUES(maxViolations), totalAlerts = VALUES(totalAlerts), sinceLastBan = VALUES(sinceLastBan);"; + + private static final String CLEAR_LAST_BAN_VIOLATIONS = "UPDATE anticheat_vl_logs SET sinceLastBan = 0 WHERE accountId = ?;"; + + private static final String GET_VLS = "SELECT checkId, maxViolations, sinceLastBan, totalAlerts FROM anticheat_vl_logs"; + + private static final String GET_VLS_BY_ACCOUNT_ID = GET_VLS + " WHERE accountId = ?"; + + private static final String GET_VLS_FOR_CHECK = GET_VLS + " WHERE checkId = ? AND accountId = ?"; + + + public AnticheatDatabase(JavaPlugin plugin) + { + super(plugin, DBPool.getAccount()); + } + + /** + * Submit a set of user violation changes batch style. + * + * @param uploadQueue the {@link TIntObjectMap} describing the changes. + */ + public void saveViolationLevels(Map uploadQueue) + { + try (Connection connection = getConnection()) + { + PreparedStatement preparedStatement = connection.prepareStatement(UPDATE_VIOLATIONS); + + uploadQueue.forEach((accountId, vls) -> + { + CheckManager.AVAILABLE_CHECKS.values().forEach(check -> + { + int checkId = CheckManager.getCheckId(check), + maxVls = vls.getMaxViolationsForCheck(check), + maxVlsSinceLastBan = vls.getLastBanViolationsForCheck(check), + totalAlerts = vls.getTotalAlertsForCheck(check); + + // if neither value has been set don't store anything + if (maxVls < 0 && totalAlerts < 0 && maxVlsSinceLastBan < 0) + { + return; + } + + maxVls = Math.max(maxVls, 0); + maxVlsSinceLastBan = Math.max(maxVlsSinceLastBan, 0); + totalAlerts = Math.max(totalAlerts, 0); + + try + { + preparedStatement.setInt(1, accountId); + preparedStatement.setInt(2, checkId); + preparedStatement.setInt(3, maxVls); + preparedStatement.setInt(4, maxVlsSinceLastBan); + preparedStatement.setInt(5, totalAlerts); + preparedStatement.addBatch(); + } + catch (SQLException e) + { + e.printStackTrace(); + } + }); + }); + + preparedStatement.executeBatch(); + } + catch (SQLException ex) + { + ex.printStackTrace(); + } + } + + /** + * Attempts to retrieve violation levels for the given account id. + * + * @param accountId The account id; + * @return an {@link Optional} describing the user's violation levels, or an empty one if none + * are found. + * @throws SQLException On failing to connect to the database. + */ + public Optional getViolationLevels(int accountId) + { + ViolationLevels levels = new ViolationLevels(); + + try (Connection connection = getConnection()) + { + PreparedStatement statement = connection.prepareStatement(GET_VLS_BY_ACCOUNT_ID); + statement.setInt(1, accountId); + + ResultSet result = statement.executeQuery(); + + while (result.next()) + { + int checkId = result.getInt("checkId"); + Class checkType = CheckManager.getCheckById(checkId); + if (checkType == null) + { + System.err.println("Whoops. Unintended refactor?"); + continue; + } + levels.updateMaxViolations(checkType, result.getInt("maxViolations")); + levels.updateMaxViolationsSinceLastBan(checkType, result.getInt("sinceLastBan")); + levels.setTotalAlerts(checkType, result.getInt("totalAlerts")); + } + } + catch (SQLException ex) + { + ex.printStackTrace(); + } + + return Optional.of(levels); + } + + public void clearLastBan(int accountId, Runnable after) + { + try (Connection connection = getConnection()) + { + PreparedStatement statement = connection.prepareStatement(CLEAR_LAST_BAN_VIOLATIONS); + statement.setInt(1, accountId); + + statement.executeUpdate(); + } + catch (SQLException e) + { + e.printStackTrace(); + } + finally + { + if (after != null) + after.run(); + } + } + + public void saveMetadata(int accountId, String id, String base64, Runnable after) + { + try (Connection connection = getConnection()) + { + PreparedStatement statement = connection.prepareStatement(INSERT_INTO_METADATA); + statement.setInt(1, accountId); + statement.setString(2, id); + statement.setString(3, base64); + + statement.executeUpdate(); + } + catch (SQLException ex) + { + ex.printStackTrace(); + } + finally + { + if (after != null) + after.run(); + } + } + + @Override + protected void initialize() + { + + } + + @Override + protected void update() + { + + } +} diff --git a/Plugins/Mineplex.Core/src/mineplex/core/antihack/logging/AnticheatMetadata.java b/Plugins/Mineplex.Core/src/mineplex/core/antihack/logging/AnticheatMetadata.java new file mode 100644 index 000000000..dc015a66a --- /dev/null +++ b/Plugins/Mineplex.Core/src/mineplex/core/antihack/logging/AnticheatMetadata.java @@ -0,0 +1,23 @@ +package mineplex.core.antihack.logging; + +import java.util.UUID; + +import org.bukkit.event.Listener; + +import com.google.gson.JsonElement; + +import mineplex.core.common.util.UtilServer; + +public abstract class AnticheatMetadata implements Listener +{ + public AnticheatMetadata() + { + UtilServer.RegisterEvents(this); + } + + public abstract String getId(); + + public abstract JsonElement build(UUID player); + + public abstract void remove(UUID player); +} diff --git a/Plugins/Mineplex.Core/src/mineplex/core/antihack/logging/AntihackLogger.java b/Plugins/Mineplex.Core/src/mineplex/core/antihack/logging/AntihackLogger.java new file mode 100644 index 000000000..2a9a628c8 --- /dev/null +++ b/Plugins/Mineplex.Core/src/mineplex/core/antihack/logging/AntihackLogger.java @@ -0,0 +1,214 @@ +package mineplex.core.antihack.logging; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.player.PlayerLoginEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.tukaani.xz.LZMA2Options; +import org.tukaani.xz.XZ; +import org.tukaani.xz.XZOutputStream; + +import com.google.gson.Gson; +import com.google.gson.JsonNull; +import com.google.gson.JsonObject; +import com.mineplex.anticheat.api.PlayerViolationEvent; +import com.mineplex.anticheat.checks.Check; + +import mineplex.core.MiniPlugin; +import mineplex.core.ReflectivelyCreateMiniPlugin; +import mineplex.core.account.CoreClientManager; +import mineplex.core.antihack.AntiHack; +import mineplex.core.antihack.ViolationLevels; +import mineplex.core.antihack.logging.builtin.PartyInfoMetadata; +import mineplex.core.antihack.logging.builtin.PlayerInfoMetadata; +import mineplex.core.antihack.logging.builtin.ServerInfoMetadata; +import mineplex.core.antihack.logging.builtin.ViolationInfoMetadata; +import mineplex.core.command.CommandBase; +import mineplex.core.common.Rank; +import mineplex.core.common.util.F; +import mineplex.core.common.util.UtilPlayer; +import mineplex.core.common.util.UtilServer; + +@ReflectivelyCreateMiniPlugin +public class AntihackLogger extends MiniPlugin +{ + public static final Gson GSON = new Gson(); + + private static final int PUSH_QUEUE_TIME_IN_SECONDS = 60; + + private final CoreClientManager _clientManager = require(CoreClientManager.class); + + private final Map _violationLevels = new ConcurrentHashMap<>(); + + private final Map _metadata = new HashMap<>(); + + private final AnticheatDatabase _db; + + private AntihackLogger() + { + super("AnticheatPlugin"); + + _db = new AnticheatDatabase(getPlugin()); + + runSyncTimer(this::pushQueuedViolationChanges, 20, 20 * PUSH_QUEUE_TIME_IN_SECONDS); + + registerMetadata(new ServerInfoMetadata()); + registerMetadata(new ViolationInfoMetadata()); + registerMetadata(new PartyInfoMetadata()); + registerMetadata(new PlayerInfoMetadata()); + } + + @Override + public void disable() + { + pushQueuedViolationChanges(); + } + + @EventHandler + public void addCommands() + { + if (UtilServer.isTestServer()) + { + addCommand(new CommandBase(this, Rank.SNR_MODERATOR, "savemetadata") + { + @Override + public void Execute(Player caller, String[] args) + { + if (args.length == 1) + { + Player player = Bukkit.getPlayer(args[0]); + if (player != null) + { + JsonObject custom = new JsonObject(); + custom.addProperty("is-test-metadata", true); + String id = AntiHack.generateId(); + saveMetadata(player, id, () -> + { + UtilPlayer.message(caller, F.main(getName(), "Saved metadata for " + player.getName() + " with id " + id)); + }, custom); + } + } + } + }); + } + } + + private void pushQueuedViolationChanges() + { + Map clone = new HashMap<>(_violationLevels); + runAsync(() -> _db.saveViolationLevels(clone)); + } + + @EventHandler + public void onCheckFail(PlayerViolationEvent event) + { + ViolationLevels playerVls = _violationLevels.get(_clientManager.getAccountId(event.getPlayer())); + Class check = event.getCheckClass(); + playerVls.updateMaxViolations(check, event.getViolations()); + playerVls.updateMaxViolationsSinceLastBan(check, event.getViolations()); + playerVls.incrementAlerts(check); + } + + @EventHandler + public void onLoad(PlayerLoginEvent event) + { + runAsync(() -> + { + int accountId = _clientManager.getAccountId(event.getPlayer()); + + _db.getViolationLevels(accountId) + .ifPresent(vls -> + { + _violationLevels.put(accountId, vls); + }); + }); + } + + @EventHandler + public void onQuit(PlayerQuitEvent event) + { + Player player = event.getPlayer(); + int accountId =_clientManager.getAccountId(event.getPlayer()); + + ViolationLevels levels = _violationLevels.get(accountId); + + if (levels != null) + { + Map clone = new HashMap<>(); + clone.put(accountId, levels); + + runAsync(() -> _db.saveViolationLevels(clone)); + } + + _metadata.values().forEach(metadata -> metadata.remove(event.getPlayer().getUniqueId())); + } + + public void saveMetadata(Player player, String id, Runnable after, JsonObject custom) + { + runAsync(() -> + { + JsonObject info = new JsonObject(); + + for (AnticheatMetadata anticheatMetadata : _metadata.values()) + { + try + { + info.add(anticheatMetadata.getId(), anticheatMetadata.build(player.getUniqueId())); + } + catch (Throwable t) + { + t.printStackTrace(); + } + } + + info.add("custom", custom == null ? JsonNull.INSTANCE: custom); + + String str = GSON.toJson(info); + byte[] b = str.getBytes(StandardCharsets.UTF_8); + + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + + try + { + XZOutputStream o2 = new XZOutputStream(bout, new LZMA2Options(LZMA2Options.PRESET_MIN), XZ.CHECK_NONE); + o2.write(b); + o2.close(); + } + catch (IOException ex) + { + // Should never happen + ex.printStackTrace(); + } + + String base64 = Base64.getEncoder().encodeToString(bout.toByteArray()); + + _db.saveMetadata(_clientManager.getAccountId(player), id, base64, after); + }); + } + + public void registerMetadata(AnticheatMetadata metadata) + { + if (!this._metadata.containsKey(metadata.getId())) + { + this._metadata.put(metadata.getId(), metadata); + } + else + { + throw new IllegalArgumentException("Attempting to register: " + metadata.getId()); + } + } + + public void resetViolations(Player player, Runnable after) + { + _db.clearLastBan(_clientManager.getAccountId(player), after); + } +} diff --git a/Plugins/Mineplex.Core/src/mineplex/core/antihack/logging/builtin/PartyInfoMetadata.java b/Plugins/Mineplex.Core/src/mineplex/core/antihack/logging/builtin/PartyInfoMetadata.java new file mode 100644 index 000000000..9ffba8361 --- /dev/null +++ b/Plugins/Mineplex.Core/src/mineplex/core/antihack/logging/builtin/PartyInfoMetadata.java @@ -0,0 +1,55 @@ +package mineplex.core.antihack.logging.builtin; + +import java.util.UUID; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonNull; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; + +import mineplex.core.antihack.logging.AnticheatMetadata; +import mineplex.core.party.Party; +import mineplex.core.party.PartyManager; + +import static mineplex.core.Managers.require; + +public class PartyInfoMetadata extends AnticheatMetadata +{ + private static final String KEY_OWNER = "owner"; + private static final String KEY_MEMBERS = "members"; + + @Override + public String getId() + { + return "party-info"; + } + + @Override + public JsonElement build(UUID player) + { + Party party = require(PartyManager.class).getPlayerParties().get(player); + if (party != null) + { + JsonObject partyData = new JsonObject(); + partyData.addProperty(KEY_OWNER, party.getOwner()); + + JsonArray members = new JsonArray(); + party.getMembers().forEach(m -> members.add(new JsonPrimitive(m))); + + partyData.add(KEY_MEMBERS, members); + + return partyData; + } + else + { + return JsonNull.INSTANCE; + } + } + + @Override + public void remove(UUID player) + { + + } +} diff --git a/Plugins/Mineplex.Core/src/mineplex/core/antihack/logging/builtin/PlayerInfoMetadata.java b/Plugins/Mineplex.Core/src/mineplex/core/antihack/logging/builtin/PlayerInfoMetadata.java new file mode 100644 index 000000000..479db70d2 --- /dev/null +++ b/Plugins/Mineplex.Core/src/mineplex/core/antihack/logging/builtin/PlayerInfoMetadata.java @@ -0,0 +1,51 @@ +package mineplex.core.antihack.logging.builtin; + +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + +import mineplex.core.account.CoreClientManager; +import mineplex.core.antihack.logging.AnticheatMetadata; + +import static mineplex.core.Managers.require; + +public class PlayerInfoMetadata extends AnticheatMetadata +{ + private static final String KEY_UUID = "uuid"; + private static final String KEY_ACCOUNT_ID = "accountid"; + private static final String KEY_NAME = "name"; + + private final CoreClientManager _clientManager = require(CoreClientManager.class); + + @Override + public String getId() + { + return "player-info"; + } + + @Override + public JsonElement build(UUID player) + { + JsonObject object = new JsonObject(); + object.addProperty(KEY_UUID, player.toString()); + + Player bPlayer = Bukkit.getPlayer(player); + if (bPlayer != null) + { + object.addProperty(KEY_NAME, bPlayer.getName()); + object.addProperty(KEY_ACCOUNT_ID, _clientManager.getAccountId(bPlayer)); + } + + return object; + } + + @Override + public void remove(UUID player) + { + + } +} diff --git a/Plugins/Mineplex.Core/src/mineplex/core/antihack/logging/builtin/ServerInfoMetadata.java b/Plugins/Mineplex.Core/src/mineplex/core/antihack/logging/builtin/ServerInfoMetadata.java new file mode 100644 index 000000000..7376a6d3e --- /dev/null +++ b/Plugins/Mineplex.Core/src/mineplex/core/antihack/logging/builtin/ServerInfoMetadata.java @@ -0,0 +1,38 @@ +package mineplex.core.antihack.logging.builtin; + +import java.util.UUID; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + +import mineplex.core.antihack.logging.AnticheatMetadata; +import mineplex.core.common.util.UtilServer; + +public class ServerInfoMetadata extends AnticheatMetadata +{ + private static final String KEY_SERVER_NAME = "server-name"; + private static final String KEY_SERVER_REGION = "server-region"; + private static final String KEY_SERVER_GROUP = "server-group"; + + @Override + public String getId() + { + return "server-info"; + } + + @Override + public JsonElement build(UUID player) + { + JsonObject info = new JsonObject(); + info.addProperty(KEY_SERVER_NAME, UtilServer.getServerName()); + info.addProperty(KEY_SERVER_REGION, UtilServer.getRegion().name()); + info.addProperty(KEY_SERVER_GROUP, UtilServer.getGroup()); + return info; + } + + @Override + public void remove(UUID player) + { + + } +} diff --git a/Plugins/Mineplex.Core/src/mineplex/core/antihack/logging/builtin/ViolationInfoMetadata.java b/Plugins/Mineplex.Core/src/mineplex/core/antihack/logging/builtin/ViolationInfoMetadata.java new file mode 100644 index 000000000..da2ee4278 --- /dev/null +++ b/Plugins/Mineplex.Core/src/mineplex/core/antihack/logging/builtin/ViolationInfoMetadata.java @@ -0,0 +1,154 @@ +package mineplex.core.antihack.logging.builtin; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import net.minecraft.server.v1_8_R3.MinecraftServer; + +import org.bukkit.Location; +import org.bukkit.event.EventHandler; +import org.bukkit.event.player.PlayerJoinEvent; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.mineplex.anticheat.api.CheckDisabledEvent; +import com.mineplex.anticheat.api.PlayerViolationEvent; +import com.mineplex.anticheat.checks.Check; + +import mineplex.core.antihack.logging.AnticheatMetadata; + +import gnu.trove.map.TObjectIntMap; +import gnu.trove.map.TObjectLongMap; +import gnu.trove.map.hash.TObjectIntHashMap; +import gnu.trove.map.hash.TObjectLongHashMap; + +public class ViolationInfoMetadata extends AnticheatMetadata +{ + private static final Location MUTABLE_LOCATION = new Location(null, 0, 0, 0); + + private static final String KEY_JOIN_TIME_MS = "join-time-ms"; + private static final String KEY_JOIN_TIME_TICK = "join-time-tick"; + + private static final String KEY_CURRENT_TIME = "current-time"; + private static final String KEY_MS = "ms"; + private static final String KEY_TICK = "tick"; + + private static final String KEY_VIOLATION_INFO = "violation-info"; + private static final String KEY_VL = "current-vl"; + private static final String KEY_MESSAGE = "msg"; + + private static final String KEY_PLAYER_INFO = "player-info"; + private static final String KEY_LOCATION = "loc"; + private static final String KEY_WORLD = "world"; + private static final String KEY_X = "x"; + private static final String KEY_Y = "y"; + private static final String KEY_Z = "z"; + private static final String KEY_YAW = "yaw"; + private static final String KEY_PITCH = "pitch"; + + private static final JsonObject VAL_CHECK_DISABLED; + + static + { + VAL_CHECK_DISABLED = new JsonObject(); + VAL_CHECK_DISABLED.addProperty(KEY_MESSAGE, "disabled"); + } + + private TObjectLongMap _joinTime = new TObjectLongHashMap<>(); + private TObjectIntMap _joinTimeTick = new TObjectIntHashMap<>(); + private Map, List>> _violations = new HashMap<>(); + + @Override + public String getId() + { + return "violation-info"; + } + + @Override + public JsonElement build(UUID player) + { + JsonObject object = new JsonObject(); + object.addProperty(KEY_JOIN_TIME_MS, _joinTime.get(player)); + object.addProperty(KEY_JOIN_TIME_TICK, _joinTimeTick.get(player)); + _violations.get(player).forEach((check, list) -> + { + JsonArray checkElem = new JsonArray(); + list.forEach(checkElem::add); + object.add(check.getName(), checkElem); + }); + + return object; + } + + @Override + public void remove(UUID player) + { + _joinTime.remove(player); + _joinTimeTick.remove(player); + _violations.remove(player); + } + + @EventHandler + public void onJoin(PlayerJoinEvent event) + { + long thisMs = System.currentTimeMillis(); + int thisTick = MinecraftServer.getServer().at(); + _joinTime.put(event.getPlayer().getUniqueId(), thisMs); + _joinTimeTick.put(event.getPlayer().getUniqueId(), thisTick); + _violations.put(event.getPlayer().getUniqueId(), new HashMap<>()); + } + + @EventHandler + public void onDisabledCheck(CheckDisabledEvent event) + { + _violations.values().forEach(map -> + { + List data = map.get(event.getCheck()); + if (data != null) + { + data.add(VAL_CHECK_DISABLED); + } + }); + } + + @EventHandler + public void onViolation(PlayerViolationEvent event) + { + long thisMs = System.currentTimeMillis(); + int thisTick = MinecraftServer.getServer().at(); + + List violations = _violations.get(event.getPlayer().getUniqueId()).computeIfAbsent(event.getCheckClass(), key -> new ArrayList<>()); + + JsonObject currentTime = new JsonObject(); + currentTime.addProperty(KEY_MS, thisMs); + currentTime.addProperty(KEY_TICK, thisTick); + + JsonObject violationInfo = new JsonObject(); + violationInfo.addProperty(KEY_VL, event.getViolations()); + violationInfo.addProperty(KEY_MESSAGE, event.getMessage()); + + event.getPlayer().getLocation(MUTABLE_LOCATION); + + JsonObject playerInfo = new JsonObject(); + JsonObject location = new JsonObject(); + location.addProperty(KEY_WORLD, MUTABLE_LOCATION.getWorld().getName()); + location.addProperty(KEY_X, MUTABLE_LOCATION.getX()); + location.addProperty(KEY_Y, MUTABLE_LOCATION.getY()); + location.addProperty(KEY_Z, MUTABLE_LOCATION.getZ()); + location.addProperty(KEY_YAW, MUTABLE_LOCATION.getYaw()); + location.addProperty(KEY_PITCH, MUTABLE_LOCATION.getPitch()); + + playerInfo.add(KEY_LOCATION, location); + + JsonObject data = new JsonObject(); + data.add(KEY_CURRENT_TIME, currentTime); + data.add(KEY_VIOLATION_INFO, violationInfo); + data.add(KEY_PLAYER_INFO, playerInfo); + + violations.add(data); + } +} diff --git a/Plugins/Mineplex.Core/src/mineplex/core/antihack/redisnotifications/GwenBanNotification.java b/Plugins/Mineplex.Core/src/mineplex/core/antihack/redisnotifications/GwenBanNotification.java new file mode 100644 index 000000000..5be1a474c --- /dev/null +++ b/Plugins/Mineplex.Core/src/mineplex/core/antihack/redisnotifications/GwenBanNotification.java @@ -0,0 +1,46 @@ +package mineplex.core.antihack.redisnotifications; + +import mineplex.serverdata.commands.ServerCommand; + +public class GwenBanNotification extends ServerCommand +{ + private final String _serverName; + private final String _playerName; + private final String _playerUUID; + private final String _hackType; + private final String _metadataId; + + public GwenBanNotification(String serverName, String playerName, String playerUUID, String hackType, String metadataId) + { + _serverName = serverName; + _playerName = playerName; + _playerUUID = playerUUID; + _hackType = hackType; + _metadataId = metadataId; + } + + public String getServerName() + { + return _serverName; + } + + public String getPlayerName() + { + return _playerName; + } + + public String getPlayerUUID() + { + return _playerUUID; + } + + public String getHackType() + { + return _hackType; + } + + public String getMetadataId() + { + return _metadataId; + } +} diff --git a/Plugins/Mineplex.Core/src/mineplex/core/antihack/redisnotifications/GwenBanwaveNotification.java b/Plugins/Mineplex.Core/src/mineplex/core/antihack/redisnotifications/GwenBanwaveNotification.java new file mode 100644 index 000000000..0ca1cf628 --- /dev/null +++ b/Plugins/Mineplex.Core/src/mineplex/core/antihack/redisnotifications/GwenBanwaveNotification.java @@ -0,0 +1,53 @@ +package mineplex.core.antihack.redisnotifications; + +import mineplex.serverdata.commands.ServerCommand; + +public class GwenBanwaveNotification extends ServerCommand +{ + private final String _serverName; + private final String _playerName; + private final String _playerUUID; + private final String _hackType; + private final String _metadataId; + private final long _timeToBan; + + public GwenBanwaveNotification(String serverName, String playerName, String playerUUID, String hackType, String metadataId, long timeToBan) + { + _serverName = serverName; + _playerName = playerName; + _playerUUID = playerUUID; + _hackType = hackType; + _metadataId = metadataId; + _timeToBan = timeToBan; + } + + public String getServerName() + { + return _serverName; + } + + public String getPlayerName() + { + return _playerName; + } + + public String getPlayerUUID() + { + return _playerUUID; + } + + public String getHackType() + { + return _hackType; + } + + public String getMetadataId() + { + return _metadataId; + } + + public long getTimeToBan() + { + return _timeToBan; + } +} diff --git a/Plugins/Mineplex.Core/src/mineplex/core/antihack/types/Fly.java b/Plugins/Mineplex.Core/src/mineplex/core/antihack/types/Fly.java deleted file mode 100644 index 0941d4a2a..000000000 --- a/Plugins/Mineplex.Core/src/mineplex/core/antihack/types/Fly.java +++ /dev/null @@ -1,172 +0,0 @@ -package mineplex.core.antihack.types; - -import java.util.AbstractMap; -import java.util.HashMap; -import java.util.Map.Entry; - -import mineplex.core.MiniPlugin; -import mineplex.core.antihack.AntiHack; -import mineplex.core.antihack.Detector; -import mineplex.core.common.util.UtilBlock; -import mineplex.core.common.util.UtilMath; - -import org.bukkit.Material; -import org.bukkit.block.Block; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.player.PlayerMoveEvent; -import org.bukkit.potion.PotionEffect; -import org.bukkit.potion.PotionEffectType; - -public class Fly extends MiniPlugin implements Detector -{ - private AntiHack Host; - - private HashMap> _floatTicks = new HashMap>(); //Ticks, PrevY - private HashMap> _hoverTicks = new HashMap>(); //Ticks, PrevY - private HashMap> _riseTicks = new HashMap>(); //Ticks, PrevY - - public Fly (AntiHack host) - { - super("Fly Detector", host.getPlugin()); - Host = host; - } - - @EventHandler(priority = EventPriority.MONITOR) - public void updateFlyhack(PlayerMoveEvent event) - { - if (!Host.isEnabled()) - return; - - Player player = event.getPlayer(); - - //100% Valid - if (Host.isValid(player, true)) - Reset(player); - - //Hasn't moved, just looking around - if (UtilMath.offset(event.getFrom(), event.getTo()) <= 0) - { - updateFloat(player); - return; - } - else - { - _floatTicks.remove(player); - } - - updateHover(player); - updateRise(player); - } - - private void updateFloat(Player player) - { - int count = 0; - - if (_floatTicks.containsKey(player)) - { - if (player.getLocation().getY() == _floatTicks.get(player).getValue()) - { - count = _floatTicks.get(player).getKey() + 1; - } - else - { - count = 0; - } - } - - if (count > Host.FloatHackTicks) - { - Host.addSuspicion(player, "Fly (Float)"); - count -= 2; - } - - _floatTicks.put(player, new AbstractMap.SimpleEntry(count, player.getLocation().getY())); - } - - private void updateHover(Player player) - { - int count = 0; - - if (_hoverTicks.containsKey(player)) - { - if (player.getLocation().getY() == _hoverTicks.get(player).getValue()) - { - count = _hoverTicks.get(player).getKey() + 1; - } - else - { - count = 0; - } - - //player.sendMessage(count + " - " + player.getLocation().getY() + " vs " + _hoverTicks.get(player).getValue()); - } - - if (count > Host.HoverHackTicks) - { - Host.addSuspicion(player, "Fly (Hover)"); - count -= 2; - } - - _hoverTicks.put(player, new AbstractMap.SimpleEntry(count, player.getLocation().getY())); - } - - private void updateRise(Player player) - { - int count = 0; - - if (_riseTicks.containsKey(player)) - { - if (player.getLocation().getY() >= _riseTicks.get(player).getValue()) - { - boolean nearBlocks = false; - for (Block block : UtilBlock.getSurrounding(player.getLocation().getBlock(), true)) - { - if (block.getType() != Material.AIR) - { - nearBlocks = true; - break; - } - } - - for (PotionEffect effect : player.getActivePotionEffects()) - if (effect.getType() == PotionEffectType.JUMP || effect.getType().equals(PotionEffectType.JUMP)) - nearBlocks = true; - - if (nearBlocks) - { - count = 0; - } - else - { - count = _riseTicks.get(player).getKey() + 1; - } - - } - else - { - count = 0; - } - } - - if (count > Host.RiseHackTicks) - { - //Only give Offense if actually rising - initial ticks can be trigged via Hover. - if (player.getLocation().getY() > _riseTicks.get(player).getValue()) - Host.addSuspicion(player, "Fly (Rise)"); - - count -= 2; - } - - _riseTicks.put(player, new AbstractMap.SimpleEntry(count, player.getLocation().getY())); - } - - @Override - public void Reset(Player player) - { - _floatTicks.remove(player); - _hoverTicks.remove(player); - _riseTicks.remove(player); - } -} diff --git a/Plugins/Mineplex.Core/src/mineplex/core/antihack/types/Idle.java b/Plugins/Mineplex.Core/src/mineplex/core/antihack/types/Idle.java deleted file mode 100644 index 18889a4c6..000000000 --- a/Plugins/Mineplex.Core/src/mineplex/core/antihack/types/Idle.java +++ /dev/null @@ -1,78 +0,0 @@ -package mineplex.core.antihack.types; - -import java.util.HashMap; - -import mineplex.core.MiniPlugin; -import mineplex.core.antihack.AntiHack; -import mineplex.core.antihack.Detector; -import mineplex.core.common.util.C; -import mineplex.core.common.util.UtilPlayer; -import mineplex.core.common.util.UtilServer; -import mineplex.core.common.util.UtilTime; -import mineplex.core.updater.UpdateType; -import mineplex.core.updater.event.UpdateEvent; - -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.player.PlayerMoveEvent; - -public class Idle extends MiniPlugin implements Detector -{ - private AntiHack Host; - - private HashMap _idleTime = new HashMap(); - - public Idle (AntiHack host) - { - super("Idle Detector", host.getPlugin()); - Host = host; - } - - @EventHandler(priority = EventPriority.MONITOR) - public void updateFlyhack(PlayerMoveEvent event) - { - if (!Host.isEnabled()) - return; - - Player player = event.getPlayer(); - - _idleTime.put(player, System.currentTimeMillis()); - } - - @EventHandler(priority = EventPriority.MONITOR) - public void updateFreeCam(UpdateEvent event) - { - if (!Host.isEnabled()) - return; - - if (event.getType() != UpdateType.FAST) - return; - - for (Player player : UtilServer.getPlayers()) - { - //100% Valid - if (Host.isValid(player, true)) - continue; - - if (!_idleTime.containsKey(player)) - continue; - - if (!UtilTime.elapsed(_idleTime.get(player), Host.IdleTime)) - continue; - - //Host.addSuspicion(player, "Lag / Fly (Idle)"); - //player.kickPlayer(C.cGold + "Mineplex " + C.cRed + "Anti-Cheat " + C.cWhite + "Kicked for Lag / Fly (Idle)"); - - UtilPlayer.message(player, C.cRed + C.Bold + "Mineplex Anti-Cheat detected Lagging / Fly (Idle)"); - UtilPlayer.message(player, C.cRed + C.Bold + "You have been returned to Lobby."); - Host.Portal.sendPlayerToServer(player, "Lobby"); - } - } - - @Override - public void Reset(Player player) - { - _idleTime.remove(player); - } -} diff --git a/Plugins/Mineplex.Core/src/mineplex/core/antihack/types/Reach.java b/Plugins/Mineplex.Core/src/mineplex/core/antihack/types/Reach.java deleted file mode 100644 index 9bf8b4a00..000000000 --- a/Plugins/Mineplex.Core/src/mineplex/core/antihack/types/Reach.java +++ /dev/null @@ -1,123 +0,0 @@ -package mineplex.core.antihack.types; - -import java.util.ArrayList; -import java.util.HashMap; - -import mineplex.core.MiniPlugin; -import mineplex.core.antihack.AntiHack; -import mineplex.core.antihack.Detector; -import mineplex.core.common.util.UtilEvent; -import mineplex.core.common.util.UtilMath; -import mineplex.core.common.util.UtilPlayer; -import mineplex.core.common.util.UtilServer; -import mineplex.core.updater.UpdateType; -import mineplex.core.updater.event.UpdateEvent; - -import org.bukkit.GameMode; -import org.bukkit.Location; -import org.bukkit.entity.LivingEntity; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.entity.EntityDamageEvent; -import org.bukkit.event.entity.EntityDamageEvent.DamageCause; - -public class Reach extends MiniPlugin implements Detector -{ - private AntiHack Host; - - private HashMap> _history = new HashMap>(); - - public Reach (AntiHack host) - { - super("Reach Detector", host.getPlugin()); - Host = host; - } - - @EventHandler(priority = EventPriority.LOWEST) - public void recordMove(UpdateEvent event) - { - if (!Host.isEnabled()) - return; - - if (event.getType() != UpdateType.TICK) - return; - - for (Player player : UtilServer.getPlayers()) - { - if (player.getGameMode() != GameMode.SURVIVAL || UtilPlayer.isSpectator(player)) - continue; - - if (!_history.containsKey(player)) - _history.put(player, new ArrayList()); - - _history.get(player).add(0, player.getLocation()); - - while (_history.get(player).size() > 40) - { - _history.get(player).remove(_history.get(player).size()-1); - } - } - } - - @EventHandler(priority = EventPriority.LOWEST) - public void reachDetect(EntityDamageEvent event) - { - if (event.getCause() != DamageCause.ENTITY_ATTACK) - return; - - if (!(event.getEntity() instanceof Player)) - return; - - LivingEntity damagerEntity = UtilEvent.GetDamagerEntity(event, false); - - if (!(damagerEntity instanceof Player)) - return; - - Player damager = (Player)damagerEntity; - Player damagee = (Player)event.getEntity(); - - if (!isInRange(damager.getLocation(), damagee.getLocation())) - { - ArrayList damageeHistory = _history.get(damagee); - - if (damageeHistory != null) - { - for (Location historyLoc : damageeHistory) - { - if (isInRange(damager.getLocation(), historyLoc)) - { - return; - } - } - } - - //Host.addSuspicion(damager, "Reach"); - } - } - - private boolean isInRange(Location a, Location b) - { - //2d Range - double distFlat = UtilMath.offset2d(a, b); //Limit is 3.40 - if (distFlat > 3.4) - { - return true; - } - - //3d Range - double dist = UtilMath.offset(a, b); //Limit is 6 (highest i saw was 5.67) - if (dist > 6.0) - { - return true; - } - - return false; - } - - @Override - public void Reset(Player player) - { - _history.remove(player); - } -} diff --git a/Plugins/Mineplex.Core/src/mineplex/core/antihack/types/Speed.java b/Plugins/Mineplex.Core/src/mineplex/core/antihack/types/Speed.java deleted file mode 100644 index 9e57c33df..000000000 --- a/Plugins/Mineplex.Core/src/mineplex/core/antihack/types/Speed.java +++ /dev/null @@ -1,105 +0,0 @@ -package mineplex.core.antihack.types; - -import java.util.AbstractMap; -import java.util.HashMap; -import java.util.Map.Entry; - -import mineplex.core.MiniPlugin; -import mineplex.core.antihack.AntiHack; -import mineplex.core.antihack.Detector; -import mineplex.core.common.util.UtilEnt; -import mineplex.core.common.util.UtilMath; -import mineplex.core.common.util.UtilTime; - -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.player.PlayerMoveEvent; -import org.bukkit.potion.PotionEffect; -import org.bukkit.potion.PotionEffectType; - -public class Speed extends MiniPlugin implements Detector -{ - private AntiHack Host; - - private HashMap> _speedTicks = new HashMap>(); //Ticks, PrevY - - public Speed (AntiHack host) - { - super("Speed Detector", host.getPlugin()); - Host = host; - } - - @EventHandler(priority = EventPriority.MONITOR) - public void updateSpeedhack(PlayerMoveEvent event) - { - if (!Host.isEnabled()) - return; - - Player player = event.getPlayer(); - - //100% Valid - if (Host.isValid(player, false)) - return; - - UpdateSpeed(player, event); - } - - private void UpdateSpeed(Player player, PlayerMoveEvent event) - { - int count = 0; - - if (_speedTicks.containsKey(player)) - { - double offset; - if (event.getFrom().getY() > event.getTo().getY()) - { - offset = UtilMath.offset2d(event.getFrom(), event.getTo()); - } - else - { - offset = UtilMath.offset(event.getFrom(), event.getTo()); - } - - //Limit - double limit = 0.74; - if (UtilEnt.isGrounded(player)) - limit = 0.32; - - for (PotionEffect effect : player.getActivePotionEffects()) - { - if (effect.getType().equals(PotionEffectType.SPEED)) - { - if (UtilEnt.isGrounded(player)) - limit += 0.08 * (effect.getAmplifier() + 1); - else - limit += 0.04 * (effect.getAmplifier() + 1); - } - } - - //Check - if (offset > limit && !UtilTime.elapsed(_speedTicks.get(player).getValue(), 100))//Counters Lag - { - count = _speedTicks.get(player).getKey() + 1; - } - else - { - count = 0; - } - } - - if (count > Host.SpeedHackTicks) - { - Host.addSuspicion(player, "Speed (Fly/Move)"); - count -= 2; - } - - _speedTicks.put(player, new AbstractMap.SimpleEntry(count, System.currentTimeMillis())); - } - - @Override - public void Reset(Player player) - { - _speedTicks.remove(player); - } -} diff --git a/Plugins/Mineplex.Core/src/mineplex/core/punish/Punish.java b/Plugins/Mineplex.Core/src/mineplex/core/punish/Punish.java index 4575de030..8f6624ba7 100644 --- a/Plugins/Mineplex.Core/src/mineplex/core/punish/Punish.java +++ b/Plugins/Mineplex.Core/src/mineplex/core/punish/Punish.java @@ -1,6 +1,7 @@ package mineplex.core.punish; import java.util.HashMap; +import java.util.function.Consumer; import java.util.logging.Level; import mineplex.core.account.CoreClient; @@ -167,6 +168,11 @@ public class Punish extends MiniPlugin } public void AddPunishment(String playerName, final Category category, final String reason, String callerName, final int severity, boolean ban, long duration, final boolean silent) + { + AddPunishment(playerName, category, reason, callerName, severity, ban, duration, silent, null); + } + + public void AddPunishment(String playerName, final Category category, final String reason, String callerName, final int severity, boolean ban, long duration, final boolean silent, Consumer callback) { Player player = Bukkit.getPlayerExact(playerName); if (player != null) @@ -250,8 +256,8 @@ public class Punish extends MiniPlugin if (player != null) player.kickPlayer(kickReason); - else - new mineplex.serverdata.commands.PunishCommand(finalPlayerName, true, false, kickReason).publish(); + + new mineplex.serverdata.commands.PunishCommand(finalPlayerName, true, false, kickReason).publish(); } }); @@ -277,8 +283,8 @@ public class Punish extends MiniPlugin UtilPlayer.message(player, F.main("Punish", F.elem(C.cGray + C.Bold + "Reason: ") + reason)); player.playSound(player.getLocation(), Sound.CAT_MEOW, 1f, 1f); } - else - new mineplex.serverdata.commands.PunishCommand(finalPlayerName, false, finalDuration != 0, F.main("Punish", F.elem(C.cGray + C.Bold + "Report Ban Reason: ") + reason)).publish(); + + new mineplex.serverdata.commands.PunishCommand(finalPlayerName, false, finalDuration != 0, F.main("Punish", F.elem(C.cGray + C.Bold + "Report Ban Reason: ") + reason)).publish(); _repository.LoadPunishClient(finalPlayerName, new Callback() { @@ -316,8 +322,8 @@ public class Punish extends MiniPlugin UtilPlayer.message(player, F.main("Punish", F.elem(C.cGray + C.Bold + "Reason: ") + reason)); player.playSound(player.getLocation(), Sound.CAT_MEOW, 1f, 1f); } - else - new mineplex.serverdata.commands.PunishCommand(finalPlayerName, false, finalDuration != 0, F.main("Punish", F.elem(C.cGray + C.Bold + (finalDuration != 0 ? "Mute" : "Warning") + " Reason: ") + reason)).publish(); + + new mineplex.serverdata.commands.PunishCommand(finalPlayerName, false, finalDuration != 0, F.main("Punish", F.elem(C.cGray + C.Bold + (finalDuration != 0 ? "Mute" : "Warning") + " Reason: ") + reason)).publish(); _repository.LoadPunishClient(finalPlayerName, new Callback() { @@ -328,6 +334,10 @@ public class Punish extends MiniPlugin }); } } + if (callback != null) + { + callback.accept(banResult); + } } diff --git a/Plugins/Mineplex.Core/src/mineplex/core/punish/UI/PunishPage.java b/Plugins/Mineplex.Core/src/mineplex/core/punish/UI/PunishPage.java index e28ddbcfd..a4cccea66 100644 --- a/Plugins/Mineplex.Core/src/mineplex/core/punish/UI/PunishPage.java +++ b/Plugins/Mineplex.Core/src/mineplex/core/punish/UI/PunishPage.java @@ -259,15 +259,6 @@ public class PunishPage extends CraftInventoryCustom implements Listener int flightSeverity = 2; - try - { - if (Managers.get(AntiHack.class).isStrict()) - { - flightSeverity = 3; - } - } - catch (Exception e) {} - AddButton(41, new ShopItem(Material.INK_SACK, (byte)1, "Severity 3",new String[] { diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/Clans.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/Clans.java index d907ecb99..5af6d50a0 100644 --- a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/Clans.java +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/Clans.java @@ -6,7 +6,8 @@ import mineplex.core.TimingsFix; import mineplex.core.account.CoreClientManager; import mineplex.core.achievement.AchievementManager; import mineplex.core.antihack.AntiHack; -import mineplex.core.antihack.AntiHackGuardian; +import mineplex.core.antihack.guardians.AntiHackGuardian; +import mineplex.core.antihack.guardians.GuardianManager; import mineplex.core.blockrestore.BlockRestore; import mineplex.core.chat.Chat; import mineplex.core.chatsnap.SnapshotManager; @@ -139,8 +140,9 @@ public class Clans extends JavaPlugin Creature creature = new Creature(this); AntiHack antiHack = require(AntiHack.class); - antiHack.setKick(false); - Bukkit.getScheduler().runTask(this, antiHack::enableNewAnticheat); + GuardianManager guardianManager = require(GuardianManager.class); + + Bukkit.getScheduler().runTask(this, antiHack::enableAnticheat); new EternalGiveawayManager(this, _clientManager, serverStatusManager); @@ -155,7 +157,7 @@ public class Clans extends JavaPlugin Location spawn = new Location(Bukkit.getWorld("world"), -422, 95, 8); for (int i = 0; i < 10; i++) { - antiHack.registerGuardian(new AntiHackGuardian(spawn.clone(), maxX, minX, maxY, minY, maxZ, minZ)); + guardianManager.registerGuardian(new AntiHackGuardian(spawn.clone(), maxX, minX, maxY, minY, maxZ, minZ)); } } @@ -170,7 +172,7 @@ public class Clans extends JavaPlugin Location spawn = new Location(Bukkit.getWorld("world"), 424, 95, -8); for (int i = 0; i < 10; i++) { - antiHack.registerGuardian(new AntiHackGuardian(spawn.clone(), maxX, minX, maxY, minY, maxZ, minZ)); + guardianManager.registerGuardian(new AntiHackGuardian(spawn.clone(), maxX, minX, maxY, minY, maxZ, minZ)); } } @@ -185,7 +187,7 @@ public class Clans extends JavaPlugin Location spawn = new Location(Bukkit.getWorld("world"), 9, 210, -393); for (int i = 0; i < 10; i++) { - antiHack.registerGuardian(new AntiHackGuardian(spawn.clone(), maxX, minX, maxY, minY, maxZ, minZ)); + guardianManager.registerGuardian(new AntiHackGuardian(spawn.clone(), maxX, minX, maxY, minY, maxZ, minZ)); } } @@ -200,7 +202,7 @@ public class Clans extends JavaPlugin Location spawn = new Location(Bukkit.getWorld("world"), 8, 210, 390); for (int i = 0; i < 10; i++) { - antiHack.registerGuardian(new AntiHackGuardian(spawn.clone(), maxX, minX, maxY, minY, maxZ, minZ)); + guardianManager.registerGuardian(new AntiHackGuardian(spawn.clone(), maxX, minX, maxY, minY, maxZ, minZ)); } } @@ -215,7 +217,7 @@ public class Clans extends JavaPlugin Location spawn = new Location(Bukkit.getWorld("world"), 0, 100, 0); for (int i = 0; i < 40; i++) { - antiHack.registerGuardian(new AntiHackGuardian(spawn.clone(), maxX, minX, maxY, minY, maxZ, minZ)); + guardianManager.registerGuardian(new AntiHackGuardian(spawn.clone(), maxX, minX, maxY, minY, maxZ, minZ)); } } diff --git a/Plugins/Mineplex.Hub.Clans/src/mineplex/clanshub/ClansHub.java b/Plugins/Mineplex.Hub.Clans/src/mineplex/clanshub/ClansHub.java index 4c4c12616..f84826dee 100644 --- a/Plugins/Mineplex.Hub.Clans/src/mineplex/clanshub/ClansHub.java +++ b/Plugins/Mineplex.Hub.Clans/src/mineplex/clanshub/ClansHub.java @@ -5,7 +5,8 @@ import mineplex.core.PacketsInteractionFix; import mineplex.core.account.CoreClientManager; import mineplex.core.achievement.AchievementManager; import mineplex.core.antihack.AntiHack; -import mineplex.core.antihack.AntiHackGuardian; +import mineplex.core.antihack.guardians.AntiHackGuardian; +import mineplex.core.antihack.guardians.GuardianManager; import mineplex.core.aprilfools.AprilFoolsManager; import mineplex.core.blockrestore.BlockRestore; import mineplex.core.boosters.BoosterManager; @@ -120,10 +121,11 @@ public class ClansHub extends JavaPlugin Portal portal = new Portal(this, clientManager, serverStatusManager.getCurrentServerName()); AntiHack antiHack = require(AntiHack.class); + GuardianManager guardianManager = require(GuardianManager.class); for (int i = 0; i < 8; i++) { - antiHack.registerGuardian(new AntiHackGuardian(new Location(Bukkit.getWorld("world"), 0, 195, 0), 25, -8, 195, 185, 17, -16)); + guardianManager.registerGuardian(new AntiHackGuardian(new Location(Bukkit.getWorld("world"), 0, 195, 0), 25, -8, 195, 185, 17, -16)); } IgnoreManager ignoreManager = new IgnoreManager(this, clientManager, preferenceManager, portal); diff --git a/Plugins/Mineplex.Hub/src/mineplex/hub/Hub.java b/Plugins/Mineplex.Hub/src/mineplex/hub/Hub.java index e838912dd..a99b013f0 100644 --- a/Plugins/Mineplex.Hub/src/mineplex/hub/Hub.java +++ b/Plugins/Mineplex.Hub/src/mineplex/hub/Hub.java @@ -10,7 +10,8 @@ import mineplex.core.PacketsInteractionFix; import mineplex.core.account.CoreClientManager; import mineplex.core.achievement.AchievementManager; import mineplex.core.antihack.AntiHack; -import mineplex.core.antihack.AntiHackGuardian; +import mineplex.core.antihack.guardians.AntiHackGuardian; +import mineplex.core.antihack.guardians.GuardianManager; import mineplex.core.aprilfools.AprilFoolsManager; import mineplex.core.blockrestore.BlockRestore; import mineplex.core.boosters.BoosterManager; @@ -147,10 +148,11 @@ public class Hub extends JavaPlugin implements IRelation Portal portal = new Portal(this, clientManager, serverStatusManager.getCurrentServerName()); AntiHack antiHack = require(AntiHack.class); + GuardianManager guardianManager = require(GuardianManager.class); for (int i = 0; i < 8; i++) { - antiHack.registerGuardian(new AntiHackGuardian(new Location(Bukkit.getWorld("world"), 0, 100, 0), 50, -50, 105, 95, 50, -50)); + guardianManager.registerGuardian(new AntiHackGuardian(new Location(Bukkit.getWorld("world"), 0, 100, 0), 50, -50, 105, 95, 50, -50)); } IgnoreManager ignoreManager = new IgnoreManager(this, clientManager, preferenceManager, portal); diff --git a/Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/Arcade.java b/Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/Arcade.java index 5baa975df..1348fafc3 100644 --- a/Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/Arcade.java +++ b/Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/Arcade.java @@ -4,6 +4,7 @@ import mineplex.core.CustomTagFix; import mineplex.core.FoodDupeFix; import mineplex.core.PacketsInteractionFix; import mineplex.core.TimingsFix; +import mineplex.core.antihack.logging.AntihackLogger; import mineplex.core.chatsnap.SnapshotRepository; import mineplex.core.customdata.CustomDataManager; import mineplex.core.chatsnap.SnapshotManager; @@ -65,6 +66,8 @@ import mineplex.core.velocity.VelocityFix; import mineplex.core.visibility.VisibilityManager; import mineplex.minecraft.game.core.combat.CombatManager; import mineplex.minecraft.game.core.damage.DamageManager; + +import nautilus.game.arcade.anticheatmetadata.GameInfoMetadata; import nautilus.game.arcade.game.GameServerConfig; import net.minecraft.server.v1_8_R3.BiomeBase; import net.minecraft.server.v1_8_R3.MinecraftServer; @@ -149,8 +152,7 @@ public class Arcade extends JavaPlugin Punish punish = new Punish(this, webServerAddress, _clientManager); - AntiHack antiHack = require(AntiHack.class); - antiHack.setKick(false); + require(AntiHack.class); IgnoreManager ignoreManager = new IgnoreManager(this, _clientManager, preferenceManager, portal); StatsManager statsManager = new StatsManager(this, _clientManager); @@ -186,7 +188,9 @@ public class Arcade extends JavaPlugin //Arcade Manager PollManager pollManager = new PollManager(this, _clientManager, _donationManager); _gameManager = new ArcadeManager(this, serverStatusManager, ReadServerConfig(), _clientManager, _donationManager, _damageManager, statsManager, incognito, achievementManager, disguiseManager, creature, teleport, new Blood(this), chat, portal, preferenceManager, inventoryManager, packetHandler, cosmeticManager, projectileManager, petManager, hologramManager, webServerAddress, pollManager, npcmanager, customDataManager, punish, eloManager, thankManager, boosterManager); - + + require(AntihackLogger.class).registerMetadata(new GameInfoMetadata()); + new GlobalPacketManager(this, _clientManager, serverStatusManager, inventoryManager, _donationManager, petManager, statsManager, _gameManager.getBonusManager().getRewardManager()); //new BroadcastManager(this, _gameManager); diff --git a/Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/anticheatmetadata/GameInfoMetadata.java b/Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/anticheatmetadata/GameInfoMetadata.java new file mode 100644 index 000000000..8cf07119d --- /dev/null +++ b/Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/anticheatmetadata/GameInfoMetadata.java @@ -0,0 +1,207 @@ +package nautilus.game.arcade.anticheatmetadata; + +import java.util.ArrayList; +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.player.PlayerJoinEvent; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + +import mineplex.core.antihack.logging.AnticheatMetadata; +import mineplex.core.common.util.UtilServer; + +import nautilus.game.arcade.ArcadeManager; +import nautilus.game.arcade.events.GameStateChangeEvent; +import nautilus.game.arcade.game.Game; +import nautilus.game.arcade.kit.Kit; +import nautilus.game.arcade.kit.ProgressingKit; +import nautilus.game.arcade.managers.GameHostManager; +import static mineplex.core.Managers.require; + +public class GameInfoMetadata extends AnticheatMetadata +{ + private static final String KEY_GAME_INFO = "game-info"; + private static final String KEY_GAME_MAP = "map"; + private static final String KEY_GAME_TYPE = "type"; + private static final String KEY_GAME_MODE = "mode"; + private static final String KEY_CURRENT_STATE = "current-state"; + private static final String KEY_STATE_START_TIME = "current-state-start-time"; + private static final String KEY_JOIN_GAME_TIME = "join-game-time-ms"; + + private static final String KEY_STATE_TIMES = "state-times"; + + private static final String KEY_KIT_INFO = "kit-info"; + private static final String KEY_KIT_NAME = "name"; + private static final String KEY_KIT_LEVEL = "level"; + + private static final String KEY_MPS = "mps"; + private static final String KEY_OWNER = "owner"; + + private static final String KEY_STATS = "stats"; + + private static final String KEY_WINNER = "winner"; + + private final Map _allGames = new HashMap<>(); + private final Map _currentGame = new HashMap<>(); + + private final ArcadeManager _arcadeManager = require(ArcadeManager.class); + + @Override + public String getId() + { + return "game-info"; + } + + @EventHandler + public void onJoin(PlayerJoinEvent event) + { + _allGames.put(event.getPlayer().getUniqueId(), new JsonArray()); + + JsonObject currentGame = buildCurrentGame(); + + if (currentGame != null) + { + _currentGame.put(event.getPlayer().getUniqueId(), currentGame); + } + } + + private JsonObject buildCurrentGame() + { + Game game = _arcadeManager.GetGame(); + + if (game == null) + return null; + + JsonObject currentGame = new JsonObject(); + + JsonObject gameInfo = new JsonObject(); + gameInfo.addProperty(KEY_GAME_MAP, game.WorldData.File); + gameInfo.addProperty(KEY_GAME_TYPE, game.GetName()); + gameInfo.addProperty(KEY_GAME_MODE, game.GetMode()); + gameInfo.addProperty(KEY_CURRENT_STATE, game.GetState().name()); + gameInfo.addProperty(KEY_STATE_START_TIME, game.GetStateTime()); + gameInfo.addProperty(KEY_JOIN_GAME_TIME, System.currentTimeMillis()); + + if (_arcadeManager.GetGameHostManager() != null && _arcadeManager.GetGameHostManager().isPrivateServer()) + { + GameHostManager gameHostManager = _arcadeManager.GetGameHostManager(); + + JsonObject mpsInfo = new JsonObject(); + mpsInfo.addProperty(KEY_OWNER, _arcadeManager.GetHost()); + + currentGame.add(KEY_MPS, mpsInfo); + } + + currentGame.add(KEY_GAME_INFO, gameInfo); + + JsonObject stateStartTimes = new JsonObject(); + stateStartTimes.addProperty(game.GetState().name(), game.GetStateTime()); + + currentGame.add(KEY_STATE_TIMES, stateStartTimes); + + return currentGame; + } + + @EventHandler + public void onStateChange(GameStateChangeEvent event) + { + if (event.GetState() == Game.GameState.Recruit) + { + for (Player player : UtilServer.getPlayersCollection()) + { + if (!_currentGame.containsKey(player.getUniqueId())) + { + _currentGame.put(player.getUniqueId(), buildCurrentGame()); + } + } + } + + if (event.GetState() == Game.GameState.Live) + { + _currentGame.forEach((id, obj) -> + { + Player player = Bukkit.getPlayer(id); + if (player != null) + { + Kit kit = event.GetGame().GetKit(player); + if (kit != null) + { + JsonObject kitInfo = new JsonObject(); + kitInfo.addProperty(KEY_KIT_NAME, kit.GetName()); + + if (kit instanceof ProgressingKit) + { + ProgressingKit pk = (ProgressingKit) kit; + kitInfo.addProperty(KEY_KIT_LEVEL, pk.getLevel(player.getUniqueId())); + } + + obj.add(KEY_KIT_INFO, kitInfo); + } + } + }); + + } + + _currentGame.values().forEach(obj -> + { + obj.get(KEY_STATE_TIMES).getAsJsonObject().addProperty(event.GetState().name(), System.currentTimeMillis()); + }); + + if (event.GetState() == Game.GameState.Dead) + { + new ArrayList<>(_currentGame.keySet()).forEach(ent -> archivePlayer(event.GetGame(), ent)); + } + } + + private void archivePlayer(Game game, UUID uuid) + { + JsonObject gameObj = _currentGame.remove(uuid); + + if (gameObj == null) + return; + + _allGames.get(uuid).add(gameObj); + + if (game == null) + return; + + Player player = Bukkit.getPlayer(uuid); + + if (player == null) + return; + + Map stats = game.GetStats().get(player); + + if (stats != null) + { + JsonObject statsObject = new JsonObject(); + stats.forEach(statsObject::addProperty); + + gameObj.add(KEY_STATS, statsObject); + } + + gameObj.addProperty(KEY_WINNER, game.Winner); + } + + @Override + public JsonElement build(UUID player) + { + archivePlayer(_arcadeManager.GetGame(), player); + + return _allGames.get(player); + } + + @Override + public void remove(UUID player) + { + _allGames.remove(player); + _currentGame.remove(player); + } +} diff --git a/Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/game/Game.java b/Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/game/Game.java index 94d745ab4..1bbc42461 100644 --- a/Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/game/Game.java +++ b/Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/game/Game.java @@ -82,6 +82,9 @@ import nautilus.game.arcade.events.GameStateChangeEvent; import nautilus.game.arcade.events.PlayerGameRespawnEvent; import nautilus.game.arcade.events.PlayerStateChangeEvent; import nautilus.game.arcade.game.GameTeam.PlayerState; +import nautilus.game.arcade.game.games.build.Build; +import nautilus.game.arcade.game.games.draw.Draw; +import nautilus.game.arcade.game.games.speedbuilders.SpeedBuilders; import nautilus.game.arcade.game.modules.Module; import nautilus.game.arcade.kit.ChampionsKit; import nautilus.game.arcade.kit.Kit; @@ -740,11 +743,14 @@ public abstract class Game implements Listener if (this._gameState == Game.GameState.Prepare) { - Managers.get(AntiHack.class).enableNewAnticheat(); + if (!(this instanceof SpeedBuilders) && !(this instanceof Build) && !(this instanceof Draw)) + { + Managers.get(AntiHack.class).enableAnticheat(); + } } else if (this._gameState == Game.GameState.End) { - Managers.get(AntiHack.class).disableNewAnticheat(); + Managers.get(AntiHack.class).disableAnticheat(); } diff --git a/Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/managers/GameFlagManager.java b/Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/managers/GameFlagManager.java index a76cf81ab..b9c61fddb 100644 --- a/Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/managers/GameFlagManager.java +++ b/Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/managers/GameFlagManager.java @@ -1230,17 +1230,7 @@ public class GameFlagManager implements Listener } } } - - @EventHandler - public void AntiHackStrict(GameStateChangeEvent event) - { - if (event.GetState() == GameState.Prepare || event.GetState() == GameState.Live) - Managers.get(AntiHack.class).setStrict(event.GetGame().StrictAntiHack); - - else - Managers.get(AntiHack.class).setStrict(true); - } - + @EventHandler public void PlayerKillCommandCancel(PlayerCommandPreprocessEvent event) {