Option to decide between two variants for supporting velocity

(standard and "less precise" for compatibility with other plugins +
cleanup function running at regular intervals + permission to allow
flying
This commit is contained in:
Evenprime 2011-04-10 15:20:41 +02:00
parent 5268c6a11a
commit 3082133e40
5 changed files with 95 additions and 30 deletions

View File

@ -3,7 +3,7 @@ name: NoCheat
author: Evenprime
main: cc.co.evenprime.bukkit.nocheat.NoCheat
version: 0.8.1
version: 0.8.2
commands:
nocheat:

View File

@ -5,6 +5,7 @@ import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
@ -48,6 +49,8 @@ public class NoCheat extends JavaPlugin {
private NoCheatConfiguration config;
private boolean exceptionWithPermissions = false;
private boolean cleanUpTaskSetup = false;
// Permissions 2.x, if available
private PermissionHandler permissions;
@ -100,8 +103,7 @@ public class NoCheat extends JavaPlugin {
*/
public void cleanPlayerDataCollection() {
synchronized(playerData) {
Player[] storedPlayers = (Player[]) playerData.keySet().toArray();
for(Player p : storedPlayers) {
for(Player p : playerData.keySet()) {
if(!p.isOnline()) {
playerData.remove(p);
}
@ -189,6 +191,25 @@ public class NoCheat extends JavaPlugin {
setupIRC();
Logger.getLogger("Minecraft").info( "[NoCheat] version [" + pdfFile.getVersion() + "] is enabled with the following checks: "+getActiveChecksAsString());
setupCleanupTask();
}
private void setupCleanupTask() {
if(cleanUpTaskSetup) return;
cleanUpTaskSetup = true;
Bukkit.getServer().getScheduler().scheduleAsyncRepeatingTask(this, new Runnable() {
@Override
public void run() {
cleanPlayerDataCollection();
}
}, 5000, 5000);
}
/**
@ -322,6 +343,7 @@ public class NoCheat extends JavaPlugin {
private String getPermissionsForPlayerAsString(Player p) {
return (!movingCheck.isActive() ? movingCheck.getName() + "* " : (hasPermission(p, "nocheat.moving") ? movingCheck.getName() + " " : "") +
(!movingCheck.isActive() ? "flying* " : (hasPermission(p, "nocheat.flying") ? "flying " : "")) +
(!speedhackCheck.isActive() ? speedhackCheck.getName() + "* " : (hasPermission(p, "nocheat.speedhack") ? speedhackCheck.getName() + " " : "")) +
(!airbuildCheck.isActive() ? airbuildCheck.getName() + "* " : (hasPermission(p, "nocheat.airbuild") ? airbuildCheck.getName() + " " : "")) +
(!bedteleportCheck.isActive() ? bedteleportCheck.getName() + "* " : (hasPermission(p, "nocheat.bedteleport") ? bedteleportCheck.getName() + " " : "")) +

View File

@ -104,6 +104,8 @@ public class NoCheatConfiguration {
plugin.movingCheck.logMessage = c.getString("moving.logmessage", plugin.movingCheck.logMessage);
plugin.movingCheck.summaryMessage = c.getString("moving.summarymessage", plugin.movingCheck.summaryMessage);
plugin.movingCheck.preciseVelocity = c.getBoolean("moving.precisevelocity", plugin.movingCheck.preciseVelocity);
plugin.speedhackCheck.actions[0] = stringToActions(c.getString("speedhack.action.low"), plugin.speedhackCheck.actions[0]);
plugin.speedhackCheck.actions[1] = stringToActions(c.getString("speedhack.action.med"), plugin.speedhackCheck.actions[1]);
plugin.speedhackCheck.actions[2] = stringToActions(c.getString("speedhack.action.high"), plugin.speedhackCheck.actions[2]);
@ -243,6 +245,8 @@ public class NoCheatConfiguration {
w.write("moving:"); w.newLine();
w.write(" logmessage: \"" + plugin.movingCheck.logMessage+"\""); w.newLine();
w.write(" summarymessage: \"" + plugin.movingCheck.summaryMessage+"\""); w.newLine();
w.write("# If you get problems with plugins that accellerate players movement, try setting this to false"); w.newLine();
w.write(" precisevelocity: \"" + plugin.movingCheck.preciseVelocity+"\""); w.newLine();
w.write("# Moving Action, one or more of 'loglow logmed loghigh cancel'"); w.newLine();
w.write(" action:"); w.newLine();
w.write(" low: "+actionsToString(plugin.movingCheck.actions[0])); w.newLine();

View File

@ -19,6 +19,7 @@ public class NoCheatData {
public int movingJumpPhase = 0;
public int movingViolationsInARow[] = { 0, 0, 0 };
public double movingHorizFreedom = 0.0D;
public int movingHorizFreedomCounter = 0;
public double movingVertFreedom = 0.0D;
public int movingVertFreedomCounter = 0;
public Location movingSetBackPoint = null;

View File

@ -42,18 +42,22 @@ public class MovingCheck extends Check {
// Limits
public final double moveLimits[] = { 0.0D, 0.5D, 2.0D };
public final double heightLimits[] = { 0.0D, 0.5D, 2.0D };
public int ticksBeforeSummary = 100;
public int ticksDelayForVelocity = 40;
// How should moving violations be treated?
public final Action actions[][] = {
{ LogAction.loglow, CancelAction.cancel },
{ LogAction.logmed, CancelAction.cancel },
{ LogAction.loghigh, CancelAction.cancel } };
public String logMessage = "Moving violation: %1$s from %2$s (%4$.5f, %5$.5f, %6$.5f) to %3$s (%7$.5f, %8$.5f, %9$.5f)";
public String summaryMessage = "Moving summary of last ~%2$d seconds: %1$s total Violations: (%3$d,%4$d,%5$d)";
public boolean preciseVelocity = true;
private static final double magic = 0.30000001192092896D;
private static final double magic2 = 0.69999998807907103D;
@ -158,10 +162,16 @@ public class MovingCheck extends Check {
public void check(final PlayerMoveEvent event) {
// Should we check at all
if(plugin.hasPermission(event.getPlayer(), "nocheat.moving"))
return;
boolean flyingAllowed = false;
if(plugin.hasPermission(event.getPlayer(), "nocheat.flying"))
flyingAllowed = true;
// Get the player-specific data
final NoCheatData data = plugin.getPlayerData(event.getPlayer());
@ -172,7 +182,7 @@ public class MovingCheck extends Check {
final Location from = data.movingTeleportTo == null ? event.getFrom() : data.movingTeleportTo;
data.movingTeleportTo = null;
// vehicles are a special case
// vehicles are a special case, I ignore them because the server controls them
if(event.getPlayer().isInsideVehicle()) {
resetData(data, event.getTo());
return;
@ -187,6 +197,7 @@ public class MovingCheck extends Check {
double combined = Math.sqrt((xDistance*xDistance + zDistance*zDistance));
// If the target is a bed and distance not too big, allow it
// Bukkit prevents using blocks behind walls already, so I don't have to check for that
if(to.getWorld().getBlockTypeIdAt(to) == Material.BED_BLOCK.getId() && combined < 8.0D) {
return; // players are allowed to "teleport" into a bed over "short" distances
}
@ -194,13 +205,35 @@ public class MovingCheck extends Check {
// Give additional movement based on velocity (not too precise, but still better than false positives)
Vector v = event.getPlayer().getVelocity();
// Compare the velocity vector to the existing movement freedom that we've from previous events
double tmp = Math.abs(v.getX()*5D) + Math.abs(v.getZ()*5D);
data.movingHorizFreedom = tmp > data.movingHorizFreedom * 0.9D ? tmp : data.movingHorizFreedom * 0.9D;
int vl1 = -1;
// Violation level
int vl1 = limitCheck(combined - data.movingHorizFreedom - 0.6D, moveLimits);
if(preciseVelocity) {
// Compare the velocity vector to the existing movement freedom that we've from previous events
double tmp = Math.abs(v.getX()*5D) + Math.abs(v.getZ()*5D);
if(tmp > data.movingHorizFreedom)
data.movingHorizFreedom = tmp;
else
data.movingHorizFreedom *= 0.9;
// Violation level
vl1 = limitCheck(combined - (data.movingHorizFreedom + 0.6D), moveLimits);
}
else
{
// Compare the velocity vector to the existing movement freedom that we've from previous events
double tmp = Math.abs(v.getX()) + Math.abs(v.getZ());
if(tmp > data.movingHorizFreedom || tmp > 1.0D)
data.movingHorizFreedomCounter = 20;
else if(data.movingHorizFreedomCounter > 0 )
data.movingHorizFreedomCounter--;
data.movingHorizFreedom = tmp;
// Violation level
vl1 = limitCheck(combined - ((data.movingHorizFreedomCounter > 0 ? 15.0D : 0.6D)), moveLimits);
}
// pre-calculate boundary values that are needed multiple times in the following checks
// the array each contains [lowerX, higherX, Y, lowerZ, higherZ]
int fromValues[] = {lowerBorder(from.getX()), upperBorder(from.getX()), (int)Math.floor(from.getY()+0.5D), lowerBorder(from.getZ()),upperBorder(from.getZ()) };
@ -210,7 +243,6 @@ public class MovingCheck extends Check {
final boolean onGroundFrom = playerIsOnGround(from.getWorld(), fromValues, from);
final boolean onGroundTo = playerIsOnGround(to.getWorld(), toValues, to);
// Handle 4 distinct cases: Walk, Jump, Land, Fly
int vl2 = -1;
// A halfway lag-resistant method of allowing vertical acceleration without allowing blatant cheating
@ -222,32 +254,33 @@ public class MovingCheck extends Check {
// SOLUTION: Give the client at least 10 events after sending "velocity" to actually use the velocity for
// its movement, plus additional events if the "velocity" was big and can cause longer flights
// The server sent the player a "velocity" a short time ago
// The server sent the player a "velocity" packet a short time ago
if(v.getY() > 0.0D) {
if(data.movingVertFreedomCounter < 10)
data.movingVertFreedomCounter = 10;
data.movingVertFreedomCounter = ticksDelayForVelocity;
// Be generous with the height limit for the client
data.movingVertFreedom += v.getY()*3D;
// Set a top limit for how many events a client has to "consume" the added freedom
if(data.movingVertFreedomCounter < 40)
data.movingVertFreedomCounter++;
}
// If the server no longer has a positive velocity, start consuming the event counter for this client
else if(data.movingVertFreedomCounter > 0) {
data.movingVertFreedomCounter--;
}
// If the event counter has been consumed, remove the vertical movement limit increase
if(data.movingVertFreedomCounter <= 0) {
double limit = data.movingVertFreedom;
// If the event counter has been consumed, remove the vertical movement limit increase when landing the next time
if(data.movingVertFreedomCounter <= 0 && (onGroundFrom || onGroundTo)) {
data.movingVertFreedom = 0.0D;
}
double limit = data.movingVertFreedom;
// The location we'd use as a new setback if there are no violations
Location newSetBack = null;
// there's no use for counting this
if(flyingAllowed) data.movingJumpPhase = 0;
// Handle 4 distinct cases: Walk, Jump, Land, Fly
// Walk or start Jump
if(onGroundFrom)
{
@ -270,9 +303,14 @@ public class MovingCheck extends Check {
// Land or Fly/Fall
else
{
Location l = data.movingSetBackPoint == null ? from : data.movingSetBackPoint;
Location l = null;
if(data.movingSetBackPoint == null || flyingAllowed)
l = from;
else
l = data.movingSetBackPoint;
if(data.movingJumpPhase > jumpingLimit)
if(!flyingAllowed && data.movingJumpPhase > jumpingLimit)
limit += jumpHeight - (data.movingJumpPhase-jumpingLimit) * 0.2D;
else limit += jumpHeight;
@ -300,13 +338,13 @@ public class MovingCheck extends Check {
int vl = vl1 > vl2 ? vl1 : vl2;
if(vl < 0) {
data.movingSetBackPoint = newSetBack == null ? data.movingSetBackPoint : newSetBack;
if(vl < 0 && newSetBack != null) {
data.movingSetBackPoint = newSetBack;
}
// If we haven't already got a setback point by now, make this location the new setback point
if(data.movingSetBackPoint == null) {
data.movingSetBackPoint = event.getFrom().clone();
data.movingSetBackPoint = from.clone();
}
if(vl >= 0) {
@ -332,7 +370,7 @@ public class MovingCheck extends Check {
}
// deleting its own reference
data.movingSummaryTask = null;
data.movingViolationsInARow[0] = 0;
data.movingViolationsInARow[1] = 0;
data.movingViolationsInARow[2] = 0;
@ -430,7 +468,7 @@ public class MovingCheck extends Check {
// Set a flag that gets used while handling teleport events
data.reset = true;
resetData(data, data.movingSetBackPoint);
// Put the player back to the chosen location