AntiHack Logging

This commit is contained in:
samczsun 2016-09-28 23:03:02 -04:00
parent 77da6ac2c2
commit 6970c3205e
12 changed files with 830 additions and 373 deletions

View File

@ -3,12 +3,20 @@ 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 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.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.PlayerSelector;
@ -16,15 +24,15 @@ import mineplex.core.ReflectivelyCreateMiniPlugin;
import mineplex.core.account.CoreClient;
import mineplex.core.account.CoreClientManager;
import mineplex.core.antihack.actions.AntiHackAction;
import mineplex.core.antihack.animations.BanwaveAnimationSpin;
import mineplex.core.antihack.banwave.BanWaveManager;
import mineplex.core.antihack.guardians.GuardianManager;
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.UtilEnt;
import mineplex.core.common.util.UtilLambda;
import mineplex.core.common.util.UtilMath;
import mineplex.core.common.util.UtilParticle;
import mineplex.core.common.util.UtilPlayer;
import mineplex.core.common.util.UtilServer;
import mineplex.core.disguise.DisguiseManager;
@ -43,10 +51,8 @@ import net.minecraft.server.v1_8_R3.ChatHoverable;
import net.minecraft.server.v1_8_R3.ChatModifier;
import net.minecraft.server.v1_8_R3.EnumChatFormat;
import net.minecraft.server.v1_8_R3.IChatBaseComponent;
import net.minecraft.server.v1_8_R3.MathHelper;
import net.minecraft.server.v1_8_R3.MinecraftServer;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.craftbukkit.v1_8_R3.entity.CraftPlayer;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
@ -57,9 +63,6 @@ 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.ArrayList;
import java.util.HashSet;
@ -69,8 +72,6 @@ import java.util.Set;
import java.util.UUID;
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;
@ -90,39 +91,24 @@ public class AntiHack extends MiniPlugin
.put("HeadRoll", new CheckThresholds("Illegal Movement", 0, 0, 0))
.build();
public static final CheckThresholds UNKNOWN_TYPE = new CheckThresholds("Unknown", 0, Integer.MAX_VALUE, Integer.MAX_VALUE);
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 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<Integer, Double> STALK_END_PROBABILITY_EQUATION = x ->
{
return 1.0/ MAX_MIN_DIFF * x; // linear equation with points (0, 0) and (diff, 1)
};
private final Cache<String, Integer> _cooldown = CacheBuilder.newBuilder()
.concurrencyLevel(1)
.expireAfterWrite(30, TimeUnit.SECONDS)
.build();
private final Cache<UUID, Boolean> _stalkingCooldown = CacheBuilder.newBuilder()
.concurrencyLevel(1)
.expireAfterWrite(STALK_COOLDOWN_TIME_SECONDS, TimeUnit.SECONDS)
.build();
private final List<UUID> _stalking = new ArrayList<>();
private final String _thisServer;
public Portal Portal = require(Portal.class);
private CoreClientManager _clientManager = require(CoreClientManager.class);
private List<AntiHackGuardian> _guardians = new ArrayList<>();
private Set<Player> _pendingBan = new HashSet<>();
// These are the GWEN checks to ignore when handling PlayerViolationEvent
@ -133,6 +119,8 @@ public class AntiHack extends MiniPlugin
super("AntiHack");
DisguiseManager disguiseManager = require(DisguiseManager.class);
require(AntihackLogger.class);
require(GuardianManager.class);
this._thisServer = UtilServer.getServerName();
@ -187,337 +175,18 @@ public class AntiHack extends MiniPlugin
}
});
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<Player> 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 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<Double, Double> magic = seconds ->
new BanwaveAnimationSpin().run(this, player, () ->
{
return Math.pow(2, seconds - 5);
};
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<BukkitTask> 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);
after.run();
_pendingBan.remove(player);
});
}
}
@ -674,7 +343,7 @@ public class AntiHack extends MiniPlugin
{
if (caller.getUniqueId().toString().equals("b86b54da-93dd-46f9-be33-27bd92aa36d7"))
{
enableNewAnticheat();
enableAnticheat();
UtilPlayer.message(caller, F.main(getName(), "Enabled new anticheat"));
}
}
@ -686,7 +355,7 @@ public class AntiHack extends MiniPlugin
{
if (caller.getUniqueId().toString().equals("b86b54da-93dd-46f9-be33-27bd92aa36d7"))
{
disableNewAnticheat();
disableAnticheat();
UtilPlayer.message(caller, F.main(getName(), "Disabled new anticheat"));
}
}
@ -736,7 +405,7 @@ public class AntiHack extends MiniPlugin
if (event.shouldTellStaff())
{
CheckThresholds thresholds = CHECKS.get(event.getHackType());
CheckThresholds thresholds = CHECKS.get(event.getCheckClass());
if (thresholds == null)
{
thresholds = new CheckThresholds(event.getHackType(), 0, Integer.MAX_VALUE, Integer.MAX_VALUE);
@ -756,13 +425,13 @@ public class AntiHack extends MiniPlugin
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());
MajorViolationCommand command = new MajorViolationCommand(_thisServer, event.getPlayer().getName(), CheckManager.getCheckSimpleName(event.getCheckClass()), 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());
@ -789,15 +458,152 @@ public class AntiHack extends MiniPlugin
_ignoredChecks.clear();
}
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 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)
{
Class<? extends Check> checkType = CheckManager.getCheckBySimpleName(violation.getHackType());
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.getOrDefault(checkType, UNKNOWN_TYPE).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)
)
);
}
}

View File

@ -0,0 +1,141 @@
package mineplex.core.antihack;
import com.mineplex.anticheat.checks.Check;
import com.mineplex.anticheat.checks.CheckManager;
import gnu.trove.map.TIntObjectMap;
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.Optional;
public class AnticheatDatabase extends MinecraftRepository
{
//CREATE TABLE IF NOT EXISTS user_anticheat_vls (accountId INT, checkId INT, maxViolations INT, totalAlerts INT, PRIMARY KEY(accountId, checkId));
public static final String UPDATE_VIOLATIONS = "INSERT INTO user_anticheat_vls (accountId, checkId, "
+ "maxViolations, totalAlerts) VALUES (?, ?, ?, ?) ON DUPLICATE KEY"
+ " UPDATE maxViolations = VALUES(maxViolations), totalAlerts = VALUES(totalAlerts);";
private static final String GET_VLS = "SELECT checkId, maxViolations, totalAlerts FROM user_anticheat_vls";
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(TIntObjectMap<ViolationLevels> uploadQueue)
{
try(Connection connection = getConnection())
{
PreparedStatement preparedStatement = connection.prepareStatement(UPDATE_VIOLATIONS);
uploadQueue.forEachEntry((accountId, vls) ->
{
CheckManager.AVAILABLE_CHECKS.forEach(check ->
{
int checkId = CheckManager.getCheckId(check),
maxVls = vls.getMaxViolationsForCheck(check),
totalAlerts = vls.getTotalAlertsForCheck(check);
// if neither value has been set don't store anything
if (maxVls < 0 && totalAlerts < 0)
{
return;
}
maxVls = Math.max(maxVls, 0);
totalAlerts = Math.max(totalAlerts, 0);
try
{
preparedStatement.setInt(1, accountId);
preparedStatement.setInt(2, checkId);
preparedStatement.setInt(3, maxVls);
preparedStatement.setInt(4, totalAlerts);
preparedStatement.addBatch();
}
catch (SQLException e)
{
e.printStackTrace();
}
});
// return true to continue iterating after this entry
return true;
});
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<ViolationLevels> getViolationLevels(int accountId) throws SQLException
{
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<? extends Check> checkType = CheckManager.getCheckById(checkId);
if (checkType == null)
{
System.err.println("Whoops. Unintended refactor?");
continue;
}
levels.updateMaxViolations(checkType, result.getInt("maxViolations"));
levels.setTotalAlerts(checkType, result.getInt("totalAlerts"));
}
}
catch (SQLException ex)
{
ex.printStackTrace();
}
return Optional.ofNullable(levels);
}
@Override
protected void initialize()
{
}
@Override
protected void update()
{
}
}

View File

@ -0,0 +1,174 @@
package mineplex.core.antihack;
import com.mineplex.anticheat.api.PlayerViolationEvent;
import com.mineplex.anticheat.checks.Check;
import gnu.trove.map.TIntObjectMap;
import gnu.trove.map.hash.TIntObjectHashMap;
import mineplex.core.MiniPlugin;
import mineplex.core.ReflectivelyCreateMiniPlugin;
import mineplex.core.account.CoreClientManager;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.player.PlayerLoginEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import javax.annotation.concurrent.GuardedBy;
import java.sql.SQLException;
import java.util.Optional;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
@ReflectivelyCreateMiniPlugin
public class AntihackLogger extends MiniPlugin
{
private static final int PUSH_QUEUE_TIME_IN_SECONDS = 60;
private final ReadWriteLock _lock = new ReentrantReadWriteLock();
@GuardedBy("_lock")
private final TIntObjectMap<ViolationLevels> _userViolationLevels = new TIntObjectHashMap<>();
private final AnticheatDatabase _db;
private final CoreClientManager _clientManager = require(CoreClientManager.class);
private AntihackLogger()
{
super("AnticheatPlugin");
// create database for storing violation info
_db = new AnticheatDatabase(getPlugin());
// periodically push locally cached authenticated user VLs to db
_plugin.getServer().getScheduler().runTaskTimerAsynchronously(
_plugin,
this::pushQueuedViolationChanges,
20,
20 * PUSH_QUEUE_TIME_IN_SECONDS
);
}
@Override
public void disable()
{
pushQueuedViolationChanges();
}
private void pushQueuedViolationChanges()
{
TIntObjectMap<ViolationLevels> ret;
_lock.readLock().lock();
try
{
ret = new TIntObjectHashMap<>(_userViolationLevels);
}
finally
{
_lock.readLock().unlock();
}
_db.saveViolationLevels(ret);
}
@EventHandler
public void onCheckFail(PlayerViolationEvent event)
{
Player player = event.getPlayer();
ViolationLevels playerVls;
int accountId = _clientManager.getAccountId(player);
playerVls = getVlsForAccount(accountId);
Class<? extends Check> check = event.getCheckClass();
playerVls.updateMaxViolations(check, event.getViolations());
playerVls.incrementAlerts(check);
}
private ViolationLevels getVlsForAccount(int accountId)
{
ViolationLevels playerVls;
_lock.writeLock().lock();
try
{
if (_userViolationLevels.containsKey(accountId))
{
playerVls = _userViolationLevels.get(accountId);
}
else
{
playerVls = new ViolationLevels();
_userViolationLevels.put(accountId, playerVls);
}
}
finally
{
_lock.writeLock().unlock();
}
return playerVls;
}
@EventHandler
public void onLoad(PlayerLoginEvent event)
{
runAsync(() ->
{
int accountId = _clientManager.getAccountId(event.getPlayer());
Optional<ViolationLevels> optional = null;
try
{
optional = _db.getViolationLevels(accountId);
}
catch (SQLException e)
{
e.printStackTrace();
}
optional.ifPresent(vls ->
{
_lock.writeLock().lock();
try
{
_userViolationLevels.put(accountId, vls);
}
finally
{
_lock.writeLock().unlock();
}
});
});
}
@EventHandler
public void onQuit(PlayerQuitEvent event)
{
Player player = event.getPlayer();
int accountId = _clientManager.getAccountId(player);
ViolationLevels vls;
// synchronize state
_lock.writeLock().lock();
try
{
vls = _userViolationLevels.remove(accountId);
}
finally
{
_lock.writeLock().unlock();
}
// push local changes to db
if (vls != null)
{
TIntObjectMap<ViolationLevels> queue = new TIntObjectHashMap<>();
queue.put(accountId, vls);
runAsync(() -> _db.saveViolationLevels(queue));
}
}
}

View File

@ -0,0 +1,66 @@
package mineplex.core.antihack;
import com.mineplex.anticheat.MineplexAnticheat;
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.
* <p>
* Instances of this have no concept of identity i.e. account id is not tracked by this.
*/
public class ViolationLevels
{
private final TObjectIntMap<Class<? extends Check>> _maxViolations;
private final TObjectIntMap<Class<? extends Check>> _totalAlerts;
public ViolationLevels()
{
_maxViolations = new TObjectIntHashMap<>(CheckManager.AVAILABLE_CHECKS.size());
_totalAlerts = new TObjectIntHashMap<>(CheckManager.AVAILABLE_CHECKS.size());
}
public void updateMaxViolations(Class<? extends Check> check, int violationLevel)
{
if (violationLevel > _maxViolations.get(check))
{
_maxViolations.put(check, violationLevel);
}
}
public void incrementAlerts(Class<? extends Check> check)
{
int cur = _totalAlerts.get(check);
setTotalAlerts(check, cur + 1);
}
public void setTotalAlerts(Class<? extends Check> check, int totalAlerts)
{
_totalAlerts.put(check, totalAlerts);
}
public int getTotalAlertsForCheck(Class<? extends Check> check)
{
if (_totalAlerts.containsKey(check))
{
return _totalAlerts.get(check);
}
return -1;
}
public int getMaxViolationsForCheck(Class<? extends Check> check)
{
if (_maxViolations.containsKey(check))
{
return _maxViolations.get(check);
}
return -1;
}
}

View File

@ -0,0 +1,9 @@
package mineplex.core.antihack.animations;
import mineplex.core.antihack.AntiHack;
import org.bukkit.entity.Player;
public interface BanwaveAnimation
{
void run(AntiHack antiHack, Player player, Runnable after);
}

View File

@ -0,0 +1,126 @@
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(AntiHack antiHack, 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<Double, Double> magic = seconds ->
{
return Math.pow(2, seconds - 5);
};
antiHack.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);
antiHack.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);
antiHack.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);
}
}

View File

@ -1,4 +1,4 @@
package mineplex.core.antihack;
package mineplex.core.antihack.guardians;
import com.mineplex.spigot.ChunkAddEntityEvent;
import mineplex.core.Managers;

View File

@ -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<Integer, Double> STALK_END_PROBABILITY_EQUATION = x ->
{
return 1.0/ MAX_MIN_DIFF * x; // linear equation with points (0, 0) and (diff, 1)
};
private final Cache<UUID, Boolean> _stalkingCooldown = CacheBuilder.newBuilder()
.concurrencyLevel(1)
.expireAfterWrite(STALK_COOLDOWN_TIME_SECONDS, TimeUnit.SECONDS)
.build();
private final List<UUID> _stalking = new ArrayList<>();
private List<AntiHackGuardian> _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<Player> 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);
}
}

View File

@ -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);
GuardianManager guardianManager = require(GuardianManager.class);
Bukkit.getScheduler().runTask(this, antiHack::enableNewAnticheat);
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));
}
}

View File

@ -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;
@ -122,10 +123,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);

View File

@ -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;
@ -148,10 +149,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);

View File

@ -740,11 +740,11 @@ public abstract class Game implements Listener
if (this._gameState == Game.GameState.Prepare)
{
Managers.get(AntiHack.class).enableNewAnticheat();
Managers.get(AntiHack.class).enableAnticheat();
}
else if (this._gameState == Game.GameState.End)
{
Managers.get(AntiHack.class).disableNewAnticheat();
Managers.get(AntiHack.class).disableAnticheat();
}