AntiCheat 1.2 (#200)

* Implement Anticheat 1.2 hooks

* Add default check thresholds for unknown check types

* Add violation level support for new checks

* Add ban waves for KillAura type D

* Amend DELETE_PENDING now that it's not hardcoded

* Add instant bans for high VL Glide/Speed

* Don't specify hack type in banwave ban message
This commit is contained in:
Connor James 2016-09-10 22:01:13 -05:00 committed by Shaun Bennett
parent 38473dcffd
commit 454e1c8c11
8 changed files with 113 additions and 117 deletions

View File

@ -47,7 +47,7 @@
<dependency>
<groupId>com.mineplex</groupId>
<artifactId>anticheat</artifactId>
<version>1.1</version>
<version>1.2</version>
</dependency>
</dependencies>

View File

@ -62,13 +62,16 @@ import java.util.function.Predicate;
@ReflectivelyCreateMiniPlugin
public class AntiHack extends MiniPlugin
{
public static final Map<String, CheckThresholds> CHECKS = ImmutableMap.of(
"Killaura (Type A)", new CheckThresholds("Kill Aura", 25, 50),
"Killaura (Type B)", new CheckThresholds("High CPS", 0, Integer.MAX_VALUE),
"Killaura (Type C)", new CheckThresholds("Reach", Integer.MAX_VALUE, Integer.MAX_VALUE),
"Killaura (Type D)", new CheckThresholds("Kill Aura", 1000, 1500),
"BadPackets", new CheckThresholds("Regen", 1000, 2000)
);
public static final Map<String, CheckThresholds> CHECKS = ImmutableMap.<String, CheckThresholds>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("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))
.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");
@ -176,6 +179,24 @@ public class AntiHack extends MiniPlugin
{
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);
ServerCommandManager.getInstance().registerCommandType(MajorViolationCommand.class, violation ->
@ -973,7 +994,20 @@ public class AntiHack extends MiniPlugin
{
if (event.shouldTellStaff())
{
String key = event.getPlayer().getName() + "." + event.getHackType() + "." + CHECKS.get(event.getHackType()).getSeverity(event.getViolations()).toString();
CheckThresholds thresholds = CHECKS.get(event.getHackType());
if (thresholds == null)
{
thresholds = new CheckThresholds(event.getHackType(), 0, Integer.MAX_VALUE, Integer.MAX_VALUE);
}
CheckThresholds.Severity severity = thresholds.getSeverity(event.getViolations());
if (severity == CheckThresholds.Severity.NONE)
{
return;
}
String key = event.getPlayer().getName() + "." + event.getHackType() + "." + severity.toString();
Integer pastVl = this._cooldown.getIfPresent(key);
if (pastVl != null)
{

View File

@ -8,12 +8,14 @@ import net.minecraft.server.v1_8_R3.IChatBaseComponent;
public class CheckThresholds
{
private final String _friendlyName;
private final int _low;
private final int _med;
private final int _high;
public CheckThresholds(String friendlyName, int med, int high)
public CheckThresholds(String friendlyName, int low, int med, int high)
{
_friendlyName = friendlyName;
_low = low;
_med = med;
_high = high;
}
@ -39,14 +41,17 @@ public class CheckThresholds
{
return Severity.MEDIUM;
} else
} else if (violationLevel >= _low)
{
return Severity.LOW;
}
return Severity.NONE;
}
public enum Severity
{
NONE(EnumChatFormat.GREEN),
LOW(EnumChatFormat.GREEN),
MEDIUM(EnumChatFormat.GOLD),
HIGH(EnumChatFormat.RED),

View File

@ -2,6 +2,9 @@ package mineplex.core.antihack.actions;
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.Speed;
import mineplex.core.common.util.UtilServer;
import org.bukkit.event.Listener;
@ -20,6 +23,9 @@ public abstract class AntiHackAction implements Listener
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;

View File

@ -3,18 +3,17 @@ 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;
import java.util.Date;
class BanwaveAction extends AntiHackAction
{
private Date nextBanWave;
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(Date nextBanWave, int vl)
BanwaveAction(int vl)
{
super(vl);
this.nextBanWave = nextBanWave;
}
@Override
@ -22,11 +21,13 @@ class BanwaveAction extends AntiHackAction
{
if (event.getViolations() >= this.getMinVl())
{
// Delay bans by 6 hours +/- 2 hours for fuzzing
long banTime = System.currentTimeMillis() + BAN_DELAY_AVERAGE + (UtilMath.r(BAN_DELAY_VARIANCE_SPAN) - (BAN_DELAY_VARIANCE_SPAN / 2));
Managers.get(BanWaveManager.class).insertBanWaveInfo(
event.getPlayer(),
nextBanWave.getTime(),
banTime,
event.getCheckClass(),
event.getMessage(),
"[GWEN] Hacking [BanWave]",
event.getViolations(),
UtilServer.getServerName()
);

View File

@ -1,12 +1,9 @@
package mineplex.core.antihack.banwave;
import java.util.Objects;
public class BanWaveInfo
{
/**
* The unique id for this BanWaveInfo
*/
private int _id;
/**
* The account id for this BanWaveInfo
*/
@ -17,11 +14,6 @@ public class BanWaveInfo
*/
private long _timeToBan;
/**
* Whether this BanWaveInfo has been executed
*/
private boolean _banned;
/**
* The hack type
*/
@ -42,16 +34,6 @@ public class BanWaveInfo
*/
private String _server;
public int getId()
{
return _id;
}
public void setId(int id)
{
_id = id;
}
public int getAccountId()
{
return _accountId;
@ -72,16 +54,6 @@ public class BanWaveInfo
_timeToBan = timeToBan;
}
public boolean isBanned()
{
return _banned;
}
public void setBanned(boolean banned)
{
_banned = banned;
}
public String getHackType()
{
return _hackType;
@ -130,28 +102,18 @@ public class BanWaveInfo
BanWaveInfo that = (BanWaveInfo) o;
if (_id != that._id) return false;
if (_accountId != that._accountId) return false;
if (_timeToBan != that._timeToBan) return false;
if (_banned != that._banned) return false;
if (_vl != that._vl) return false;
if (_hackType != null ? !_hackType.equals(that._hackType) : that._hackType != null) return false;
if (_message != null ? !_message.equals(that._message) : that._message != null) return false;
return _server != null ? _server.equals(that._server) : that._server == null;
return Objects.equals(_accountId, that._accountId)
&& Objects.equals(_timeToBan, that._timeToBan)
&& Objects.equals(_vl, that._vl)
&& Objects.equals(_hackType, that._hackType)
&& Objects.equals(_message, that._message)
&& Objects.equals(_server, that._server);
}
@Override
public int hashCode()
{
int result = _id;
result = 31 * result + _accountId;
result = 31 * result + (int) (_timeToBan ^ (_timeToBan >>> 32));
result = 31 * result + (_banned ? 1 : 0);
result = 31 * result + (_hackType != null ? _hackType.hashCode() : 0);
result = 31 * result + (_message != null ? _message.hashCode() : 0);
result = 31 * result + _vl;
result = 31 * result + (_server != null ? _server.hashCode() : 0);
return result;
return Objects.hash(_accountId, _timeToBan, _hackType, _message, _vl, _server);
}
}

View File

@ -9,8 +9,6 @@ import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.player.PlayerJoinEvent;
import java.util.List;
@ReflectivelyCreateMiniPlugin
public class BanWaveManager extends MiniPlugin
{
@ -28,28 +26,16 @@ public class BanWaveManager extends MiniPlugin
{
CoreClient client = require(CoreClientManager.class).Get(event.getPlayer());
List<BanWaveInfo> infos = _repository.getBanWaveInfo(client.getAccountId());
_repository.getPendingBanWaveInfo(client.getAccountId(), info ->
{
long now = System.currentTimeMillis();
boolean banned = false;
for (BanWaveInfo info : infos)
if (info.getTimeToBan() < now)
{
if (info.getTimeToBan() < now && !info.isBanned())
{
banned = true;
require(AntiHack.class).doBanWave(event.getPlayer(), info.getMessage());
break;
}
}
if (banned)
{
for (BanWaveInfo info : infos)
{
_repository.flagDone(info);
}
}
});
});
}

View File

@ -1,5 +1,6 @@
package mineplex.core.antihack.banwave;
import mineplex.core.common.util.Callback;
import mineplex.core.common.util.UtilServer;
import mineplex.core.database.MinecraftRepository;
import mineplex.serverdata.database.DBPool;
@ -7,27 +8,33 @@ import mineplex.serverdata.database.column.ColumnInt;
import mineplex.serverdata.database.column.ColumnLong;
import mineplex.serverdata.database.column.ColumnVarChar;
import java.util.ArrayList;
import java.util.List;
public class BanWaveRepository extends MinecraftRepository
{
private static final String TABLE_NAME = "banwave";
private static final String INITIALIZE_TABLE = "CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " (" +
"id INT AUTO_INCREMENT, " +
"accountId INT NOT NULL, " +
private static final String INITIALIZE_PENDING_TABLE = "CREATE TABLE IF NOT EXISTS banwavePending (" +
"accountId INT(11) NOT NULL, " +
"timeToBan BIGINT UNSIGNED NOT NULL, " +
"hacktype VARCHAR(64), " +
"message VARCHAR(255), " +
"vl INT, " +
"server VARCHAR(32), " +
"PRIMARY KEY (accountId)," +
"FOREIGN KEY (accountId) REFERENCES accounts(id))";
private static final String INITIALIZED_PROCESSED_TABLE = "CREATE TABLE IF NOT EXISTS banwaveProcessed (" +
"id INT NOT NULL AUTO_INCREMENT, " +
"accountId INT(11) NOT NULL, " +
"timeToBan BIGINT UNSIGNED NOT NULL, " +
"banned TINYINT DEFAULT '0', " +
"hacktype VARCHAR(64), " +
"message VARCHAR(255), " +
"vl INT, " +
"server VARCHAR(32), " +
"PRIMARY KEY (id)," +
"INDEX (accountId))";
private static final String QUERY_BY_ACCOUNT = "SELECT * FROM " + TABLE_NAME + " WHERE accountId = ?";
private static final String INSERT_INTO_TABLE = "INSERT INTO " + TABLE_NAME + " (accountId, timeToBan, hacktype, message, vl, server) VALUES (?, ?, ?, ?, ?, ?)";
private static final String FLAG_DONE = "UPDATE " + TABLE_NAME + " SET banned = 1 WHERE 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 DELETE_PENDING = "DELETE FROM banwavePending WHERE accountId = ?";
BanWaveRepository()
{
@ -37,7 +44,7 @@ public class BanWaveRepository extends MinecraftRepository
@Override
protected void initialize()
{
executeUpdate(INITIALIZE_TABLE);
//executeUpdate(INITIALIZE_TABLE);
}
@Override
@ -46,34 +53,28 @@ public class BanWaveRepository extends MinecraftRepository
}
List<BanWaveInfo> getBanWaveInfo(int accountId)
void getPendingBanWaveInfo(int accountId, Callback<BanWaveInfo> callback)
{
List<BanWaveInfo> banWaveInfo = new ArrayList<>();
executeQuery(QUERY_BY_ACCOUNT, resultSet ->
executeQuery(QUERY_PENDING, resultSet ->
{
while (resultSet.next())
if (resultSet.next())
{
BanWaveInfo info = new BanWaveInfo();
info.setId(resultSet.getInt(1));
info.setAccountId(resultSet.getInt(2));
info.setTimeToBan(resultSet.getLong(3));
info.setBanned(resultSet.getInt(4) == 1);
info.setHackType(resultSet.getString(5));
info.setMessage(resultSet.getString(6));
info.setVl(resultSet.getInt(7));
info.setServer(resultSet.getString(8));
info.setAccountId(resultSet.getInt(1));
info.setTimeToBan(resultSet.getLong(2));
info.setHackType(resultSet.getString(3));
info.setMessage(resultSet.getString(4));
info.setVl(resultSet.getInt(5));
info.setServer(resultSet.getString(6));
banWaveInfo.add(info);
callback.run(info);
}
}, new ColumnInt("accountId", accountId));
return banWaveInfo;
}
void insertBanWaveInfo(int accountId, long timeToBan, String hackType, String message, int vl, String server)
{
executeInsert(INSERT_INTO_TABLE, null,
executeInsert(INSERT_PENDING, null,
new ColumnInt("accountId", accountId),
new ColumnLong("timeToBan", timeToBan),
new ColumnVarChar("hacktype", 64, hackType),
@ -85,6 +86,7 @@ public class BanWaveRepository extends MinecraftRepository
void flagDone(BanWaveInfo info)
{
executeUpdate(FLAG_DONE, new ColumnInt("id", info.getId()));
executeUpdate(PROCESS_WAVE_FOR_ACCOUNT, new ColumnInt("id", info.getAccountId()));
executeUpdate(DELETE_PENDING, new ColumnInt("id", info.getAccountId()));
}
}