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:
parent
38473dcffd
commit
454e1c8c11
|
@ -47,7 +47,7 @@
|
|||
<dependency>
|
||||
<groupId>com.mineplex</groupId>
|
||||
<artifactId>anticheat</artifactId>
|
||||
<version>1.1</version>
|
||||
<version>1.2</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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()
|
||||
);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -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()));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue