And even more comments

This commit is contained in:
Evenprime 2012-03-01 21:23:06 +01:00
parent 2aedcd7515
commit f830defb5f
16 changed files with 190 additions and 77 deletions

View File

@ -86,7 +86,7 @@ public class NoCheat extends JavaPlugin implements Listener {
eventManagers = new ArrayList<EventManager>(8); // Big enough eventManagers = new ArrayList<EventManager>(8); // Big enough
// Then set up the event listeners // Then set up the event listeners
eventManagers.add(new MovingCheckListener(this)); eventManagers.add(new MovingCheckListener(this));
eventManagers.add(new WorkaroundsListener(this)); eventManagers.add(new WorkaroundsListener());
eventManagers.add(new ChatCheckListener(this)); eventManagers.add(new ChatCheckListener(this));
eventManagers.add(new BlockBreakCheckListener(this)); eventManagers.add(new BlockBreakCheckListener(this));
eventManagers.add(new BlockPlaceCheckListener(this)); eventManagers.add(new BlockPlaceCheckListener(this));

View File

@ -17,6 +17,10 @@ import cc.co.evenprime.bukkit.nocheat.actions.types.SpecialAction;
import cc.co.evenprime.bukkit.nocheat.config.ConfigurationCacheStore; import cc.co.evenprime.bukkit.nocheat.config.ConfigurationCacheStore;
import cc.co.evenprime.bukkit.nocheat.data.Statistics.Id; import cc.co.evenprime.bukkit.nocheat.data.Statistics.Id;
/**
* The abstract Check class, providing some basic functionality
*
*/
public abstract class Check { public abstract class Check {
private final String name; private final String name;
@ -41,13 +45,18 @@ public abstract class Check {
boolean special = false; boolean special = false;
// Get the to be executed actions
Action[] actions = actionList.getActions(violationLevel); Action[] actions = actionList.getActions(violationLevel);
final long time = System.currentTimeMillis() / 1000L; final long time = System.currentTimeMillis() / 1000L;
// The configuration will be needed too
final ConfigurationCacheStore cc = player.getConfigurationStore(); final ConfigurationCacheStore cc = player.getConfigurationStore();
for(Action ac : actions) { for(Action ac : actions) {
if(player.getExecutionHistory().executeAction(groupId, ac, time)) { if(player.getExecutionHistory().executeAction(groupId, ac, time)) {
// The executionHistory said it really is time to execute the
// action, find out what it is and do what is needed
if(ac instanceof LogAction && !player.hasPermission(actionList.permissionSilent)) { if(ac instanceof LogAction && !player.hasPermission(actionList.permissionSilent)) {
executeLogAction((LogAction) ac, this, player, cc); executeLogAction((LogAction) ac, this, player, cc);
} else if(ac instanceof SpecialAction) { } else if(ac instanceof SpecialAction) {
@ -79,6 +88,7 @@ public abstract class Check {
if(!cc.logging.active) if(!cc.logging.active)
return; return;
// Fire one of our custom "Log" Events
Bukkit.getServer().getPluginManager().callEvent(new NoCheatLogEvent(cc.logging.prefix, l.getLogMessage(player, check), cc.logging.toConsole && l.toConsole(), cc.logging.toChat && l.toChat(), cc.logging.toFile && l.toFile())); Bukkit.getServer().getPluginManager().callEvent(new NoCheatLogEvent(cc.logging.prefix, l.getLogMessage(player, check), cc.logging.toConsole && l.toConsole(), cc.logging.toChat && l.toChat(), cc.logging.toFile && l.toFile()));
} }

View File

@ -12,7 +12,8 @@ import cc.co.evenprime.bukkit.nocheat.NoCheatPlayer;
import cc.co.evenprime.bukkit.nocheat.data.PreciseLocation; import cc.co.evenprime.bukkit.nocheat.data.PreciseLocation;
/** /**
* Some stuff that's used by different checks * Some stuff that's used by different checks or just too complex to keep
* in other places
* *
*/ */
public class CheckUtil { public class CheckUtil {
@ -85,10 +86,11 @@ public class CheckUtil {
// All fences are solid - fences are treated specially due // All fences are solid - fences are treated specially due
// to being 1.5 blocks high // to being 1.5 blocks high
private static final int FENCE = 16 | SOLID | NONSOLID; // 0x00010010 private static final int FENCE = 16 | SOLID | NONSOLID; // 0x00010011
private static final int INGROUND = 128; private static final int INGROUND = 128;
private static final int ONGROUND = 256; private static final int ONGROUND = 256;
// Until I can think of a better way to determine if a block is solid or // Until I can think of a better way to determine if a block is solid or
// not, this is what I'll do // not, this is what I'll do
private static final int types[]; private static final int types[];
@ -168,6 +170,8 @@ public class CheckUtil {
* } * }
* } * }
*/ */
// We need to know what is considered food for the instanteat check
foods.add(Material.APPLE); foods.add(Material.APPLE);
foods.add(Material.BREAD); foods.add(Material.BREAD);
foods.add(Material.COOKED_BEEF); foods.add(Material.COOKED_BEEF);
@ -202,7 +206,7 @@ public class CheckUtil {
final int lowerX = lowerBorder(location.x); final int lowerX = lowerBorder(location.x);
final int upperX = upperBorder(location.x); final int upperX = upperBorder(location.x);
final int Y = (int) Math.floor(location.y); final int Y = (int) location.y;
final int lowerZ = lowerBorder(location.z); final int lowerZ = lowerBorder(location.z);
final int upperZ = upperBorder(location.z); final int upperZ = upperBorder(location.z);

View File

@ -8,7 +8,6 @@ import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerMoveEvent; import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.event.player.PlayerToggleSprintEvent; import org.bukkit.event.player.PlayerToggleSprintEvent;
import cc.co.evenprime.bukkit.nocheat.EventManager; import cc.co.evenprime.bukkit.nocheat.EventManager;
import cc.co.evenprime.bukkit.nocheat.NoCheat;
import cc.co.evenprime.bukkit.nocheat.config.ConfigurationCacheStore; import cc.co.evenprime.bukkit.nocheat.config.ConfigurationCacheStore;
/** /**
@ -18,12 +17,7 @@ import cc.co.evenprime.bukkit.nocheat.config.ConfigurationCacheStore;
*/ */
public class WorkaroundsListener implements Listener, EventManager { public class WorkaroundsListener implements Listener, EventManager {
//private final NoCheat plugin; public WorkaroundsListener() {}
public WorkaroundsListener(NoCheat plugin) {
//this.plugin = plugin;
}
@EventHandler(priority = EventPriority.HIGHEST) @EventHandler(priority = EventPriority.HIGHEST)
public void playerMove(final PlayerMoveEvent event) { public void playerMove(final PlayerMoveEvent event) {
@ -39,12 +33,14 @@ public class WorkaroundsListener implements Listener, EventManager {
@EventHandler(priority = EventPriority.HIGHEST) @EventHandler(priority = EventPriority.HIGHEST)
public void toggleSprint(final PlayerToggleSprintEvent event) { public void toggleSprint(final PlayerToggleSprintEvent event) {
// Some plugins cancel "sprinting", which makes no sense at all because
// it doesn't stop people from sprinting and rewards them by reducing
// their hunger bar as if they were walking instead of sprinting
if(event.isCancelled() && event.isSprinting()) { if(event.isCancelled() && event.isSprinting()) {
event.setCancelled(false); event.setCancelled(false);
} }
} }
@Override
public List<String> getActiveChecks(ConfigurationCacheStore cc) { public List<String> getActiveChecks(ConfigurationCacheStore cc) {
return Collections.emptyList(); return Collections.emptyList();
} }

View File

@ -19,23 +19,29 @@ public class FlyingCheck extends MovingCheck {
super(plugin, "moving.flying"); super(plugin, "moving.flying");
} }
// Determined by trial and error, the flying movement speed of the creative
// mode
private static final double creativeSpeed = 0.60D; private static final double creativeSpeed = 0.60D;
public PreciseLocation check(NoCheatPlayer player, MovingData data, MovingConfig ccmoving) { public PreciseLocation check(NoCheatPlayer player, MovingData data, MovingConfig ccmoving) {
// The setBack is the location that players may get teleported to when
// they fail the check
final PreciseLocation setBack = data.runflySetBackPoint; final PreciseLocation setBack = data.runflySetBackPoint;
final PreciseLocation from = data.from; final PreciseLocation from = data.from;
final PreciseLocation to = data.to; final PreciseLocation to = data.to;
// If we have no setback, define one now
if(!setBack.isSet()) { if(!setBack.isSet()) {
setBack.set(from); setBack.set(from);
} }
// Used to store the location where the player gets teleported to
PreciseLocation newToLocation = null; PreciseLocation newToLocation = null;
// Before doing anything, do a basic height check // Before doing anything, do a basic height check to determine if
// This is silent for now, will log messages later // players are flying too high
// probably
if(to.y - data.vertFreedom > ccmoving.flyingHeightLimit) { if(to.y - data.vertFreedom > ccmoving.flyingHeightLimit) {
newToLocation = new PreciseLocation(); newToLocation = new PreciseLocation();
newToLocation.set(setBack); newToLocation.set(setBack);
@ -43,33 +49,36 @@ public class FlyingCheck extends MovingCheck {
return newToLocation; return newToLocation;
} }
final double yDistance = to.y - from.y;
// Calculate some distances // Calculate some distances
final double yDistance = to.y - from.y;
final double xDistance = to.x - from.x; final double xDistance = to.x - from.x;
final double zDistance = to.z - from.z; final double zDistance = to.z - from.z;
// How far did the player move horizontally
final double horizontalDistance = Math.sqrt((xDistance * xDistance + zDistance * zDistance)); final double horizontalDistance = Math.sqrt((xDistance * xDistance + zDistance * zDistance));
double resultHoriz = 0; double resultHoriz = 0;
double resultVert = 0; double resultVert = 0;
double result = 0; double result = 0;
// In case of creative gamemode, give at least 0.60 speed limit // In case of creative game mode give at least 0.60 speed limit horizontal
// horizontal
double speedLimitHorizontal = player.isCreative() ? Math.max(creativeSpeed, ccmoving.flyingSpeedLimitHorizontal) : ccmoving.flyingSpeedLimitHorizontal; double speedLimitHorizontal = player.isCreative() ? Math.max(creativeSpeed, ccmoving.flyingSpeedLimitHorizontal) : ccmoving.flyingSpeedLimitHorizontal;
// If the player is affected by potion of swiftness
speedLimitHorizontal *= player.getSpeedAmplifier(); speedLimitHorizontal *= player.getSpeedAmplifier();
// Finally, determine how far the player went beyond the set limits
resultHoriz = Math.max(0.0D, horizontalDistance - data.horizFreedom - speedLimitHorizontal); resultHoriz = Math.max(0.0D, horizontalDistance - data.horizFreedom - speedLimitHorizontal);
boolean sprinting = player.isSprinting(); boolean sprinting = player.isSprinting();
data.bunnyhopdelay--; data.bunnyhopdelay--;
// Did he go too far?
if(resultHoriz > 0 && sprinting) { if(resultHoriz > 0 && sprinting) {
// Try to treat it as a the "bunnyhop" problem // Try to treat it as a the "bunnyhop" problem
// The bunnyhop problem is that landing and immediatly jumping
// again leads to a player moving almost twice as far in that step
if(data.bunnyhopdelay <= 0 && resultHoriz < 0.4D) { if(data.bunnyhopdelay <= 0 && resultHoriz < 0.4D) {
data.bunnyhopdelay = 9; data.bunnyhopdelay = 9;
resultHoriz = 0; resultHoriz = 0;
@ -78,6 +87,9 @@ public class FlyingCheck extends MovingCheck {
resultHoriz *= 100; resultHoriz *= 100;
// Is the player affected by the "jumping" potion
// This is really just a very, very crude estimation and far from
// reality
double jumpAmplifier = player.getJumpAmplifier(); double jumpAmplifier = player.getJumpAmplifier();
if(jumpAmplifier > data.lastJumpAmplifier) { if(jumpAmplifier > data.lastJumpAmplifier) {
data.lastJumpAmplifier = jumpAmplifier; data.lastJumpAmplifier = jumpAmplifier;
@ -89,14 +101,15 @@ public class FlyingCheck extends MovingCheck {
data.lastJumpAmplifier--; data.lastJumpAmplifier--;
} }
// super simple, just check distance compared to max distance // super simple, just check distance compared to max distance vertical
resultVert = Math.max(0.0D, yDistance - data.vertFreedom - speedLimitVertical) * 100; resultVert = Math.max(0.0D, yDistance - data.vertFreedom - speedLimitVertical) * 100;
result = resultHoriz + resultVert; result = resultHoriz + resultVert;
// The player went to far, either horizontal or vertical
if(result > 0) { if(result > 0) {
// Increment violation counter // Increment violation counter and statistics
data.runflyVL += result; data.runflyVL += result;
if(resultHoriz > 0) { if(resultHoriz > 0) {
incrementStatistics(player, Id.MOV_RUNNING, resultHoriz); incrementStatistics(player, Id.MOV_RUNNING, resultHoriz);
@ -106,6 +119,8 @@ public class FlyingCheck extends MovingCheck {
incrementStatistics(player, Id.MOV_FLYING, resultVert); incrementStatistics(player, Id.MOV_FLYING, resultVert);
} }
// Execute whatever actions are associated with this check and the
// violation level and find out if we should cancel the event
boolean cancel = executeActions(player, ccmoving.flyingActions, data.runflyVL); boolean cancel = executeActions(player, ccmoving.flyingActions, data.runflyVL);
// Was one of the actions a cancel? Then really do it // Was one of the actions a cancel? Then really do it
@ -114,10 +129,10 @@ public class FlyingCheck extends MovingCheck {
} }
} }
// Slowly reduce the level with each event // Slowly reduce the violation level with each event
data.runflyVL *= 0.97; data.runflyVL *= 0.97;
// Some other cleanup 'n' stuff // If the player did not get cancelled, define a new setback point
if(newToLocation == null) { if(newToLocation == null) {
setBack.set(to); setBack.set(to);
} }
@ -125,10 +140,11 @@ public class FlyingCheck extends MovingCheck {
return newToLocation; return newToLocation;
} }
@Override
public String getParameter(ParameterName wildcard, NoCheatPlayer player) { public String getParameter(ParameterName wildcard, NoCheatPlayer player) {
if(wildcard == ParameterName.VIOLATIONS) if(wildcard == ParameterName.VIOLATIONS)
return String.format(Locale.US, "%d", (int) getData(player.getDataStore()).runflyVL); return String.format(Locale.US, "%d", (int) getData(player).runflyVL);
else else
return super.getParameter(wildcard, player); return super.getParameter(wildcard, player);
} }

View File

@ -19,6 +19,7 @@ import cc.co.evenprime.bukkit.nocheat.data.Statistics.Id;
*/ */
public class MorePacketsCheck extends MovingCheck { public class MorePacketsCheck extends MovingCheck {
// 20 would be for perfect internet connections, 22 is good enough
private final static int packetsPerTimeframe = 22; private final static int packetsPerTimeframe = 22;
public MorePacketsCheck(NoCheat plugin) { public MorePacketsCheck(NoCheat plugin) {
@ -55,6 +56,8 @@ public class MorePacketsCheck extends MovingCheck {
data.packets = -data.morePacketsBuffer; data.packets = -data.morePacketsBuffer;
// Execute whatever actions are associated with this check and the
// violation level and find out if we should cancel the event
final boolean cancel = executeActions(player, cc.morePacketsActions, data.morePacketsVL); final boolean cancel = executeActions(player, cc.morePacketsActions, data.morePacketsVL);
if(cancel) if(cancel)
@ -100,9 +103,9 @@ public class MorePacketsCheck extends MovingCheck {
public String getParameter(ParameterName wildcard, NoCheatPlayer player) { public String getParameter(ParameterName wildcard, NoCheatPlayer player) {
if(wildcard == ParameterName.VIOLATIONS) if(wildcard == ParameterName.VIOLATIONS)
return String.format(Locale.US, "%d", (int) getData(player.getDataStore()).morePacketsVL); return String.format(Locale.US, "%d", (int) getData(player).morePacketsVL);
else if(wildcard == ParameterName.PACKETS) else if(wildcard == ParameterName.PACKETS)
return String.format(Locale.US, "%d", (int) getData(player.getDataStore()).packets); return String.format(Locale.US, "%d", (int) getData(player).packets);
else else
return super.getParameter(wildcard, player); return super.getParameter(wildcard, player);
} }

View File

@ -9,6 +9,10 @@ import cc.co.evenprime.bukkit.nocheat.config.ConfigurationCacheStore;
import cc.co.evenprime.bukkit.nocheat.data.DataStore; import cc.co.evenprime.bukkit.nocheat.data.DataStore;
import cc.co.evenprime.bukkit.nocheat.data.PreciseLocation; import cc.co.evenprime.bukkit.nocheat.data.PreciseLocation;
/**
* Abstract base class for Moving checks, provides some convenience
* methods for access to data and config that's relevant to this checktype
*/
public abstract class MovingCheck extends Check { public abstract class MovingCheck extends Check {
private static final String id = "moving"; private static final String id = "moving";
@ -21,21 +25,29 @@ public abstract class MovingCheck extends Check {
public String getParameter(ParameterName wildcard, NoCheatPlayer player) { public String getParameter(ParameterName wildcard, NoCheatPlayer player) {
if(wildcard == ParameterName.LOCATION) { if(wildcard == ParameterName.LOCATION) {
PreciseLocation from = getData(player.getDataStore()).from; PreciseLocation from = getData(player).from;
return String.format(Locale.US, "%.2f,%.2f,%.2f", from.x, from.y, from.z); return String.format(Locale.US, "%.2f,%.2f,%.2f", from.x, from.y, from.z);
} else if(wildcard == ParameterName.MOVEDISTANCE) { } else if(wildcard == ParameterName.MOVEDISTANCE) {
PreciseLocation from = getData(player.getDataStore()).from; PreciseLocation from = getData(player).from;
PreciseLocation to = getData(player.getDataStore()).to; PreciseLocation to = getData(player).to;
return String.format(Locale.US, "%.2f,%.2f,%.2f", to.x - from.x, to.y - from.y, to.z - from.z); return String.format(Locale.US, "%.2f,%.2f,%.2f", to.x - from.x, to.y - from.y, to.z - from.z);
} else if(wildcard == ParameterName.LOCATION_TO) { } else if(wildcard == ParameterName.LOCATION_TO) {
PreciseLocation to = getData(player.getDataStore()).to; PreciseLocation to = getData(player).to;
return String.format(Locale.US, "%.2f,%.2f,%.2f", to.x, to.y, to.z); return String.format(Locale.US, "%.2f,%.2f,%.2f", to.x, to.y, to.z);
} else } else
return super.getParameter(wildcard, player); return super.getParameter(wildcard, player);
} }
public static MovingData getData(DataStore base) { /**
* Get the "MovingData" object that belongs to the player. Will ensure
* that such a object exists and if not, create one
*
* @param player
* @return
*/
public static MovingData getData(NoCheatPlayer player) {
DataStore base = player.getDataStore();
MovingData data = base.get(id); MovingData data = base.get(id);
if(data == null) { if(data == null) {
data = new MovingData(); data = new MovingData();
@ -44,6 +56,17 @@ public abstract class MovingCheck extends Check {
return data; return data;
} }
/**
* Get the MovingConfig object that belongs to the world that the player
* currently resides in.
*
* @param player
* @return
*/
public static MovingConfig getConfig(NoCheatPlayer player) {
return getConfig(player.getConfigurationStore());
}
public static MovingConfig getConfig(ConfigurationCacheStore cache) { public static MovingConfig getConfig(ConfigurationCacheStore cache) {
MovingConfig config = cache.get(id); MovingConfig config = cache.get(id);
if(config == null) { if(config == null) {

View File

@ -24,10 +24,8 @@ import cc.co.evenprime.bukkit.nocheat.config.Permissions;
import cc.co.evenprime.bukkit.nocheat.data.PreciseLocation; import cc.co.evenprime.bukkit.nocheat.data.PreciseLocation;
/** /**
* The only place that listens to and modifies player_move events if necessary * Central location to listen to events that are
* * relevant for the moving checks
* Get the event, decide which checks should work on it and in what order,
* evaluate the check results and decide what to
* *
*/ */
public class MovingCheckListener implements Listener, EventManager { public class MovingCheckListener implements Listener, EventManager {
@ -56,7 +54,7 @@ public class MovingCheckListener implements Listener, EventManager {
* was already on top of that block and should be allowed to stay * was already on top of that block and should be allowed to stay
* there * there
* *
* @param event * @param event The BlockPlaceEvent
*/ */
@EventHandler(priority = EventPriority.MONITOR) @EventHandler(priority = EventPriority.MONITOR)
public void blockPlace(final BlockPlaceEvent event) { public void blockPlace(final BlockPlaceEvent event) {
@ -66,7 +64,7 @@ public class MovingCheckListener implements Listener, EventManager {
return; return;
final NoCheatPlayer player = plugin.getPlayer(event.getPlayer()); final NoCheatPlayer player = plugin.getPlayer(event.getPlayer());
final MovingConfig config = MovingCheck.getConfig(player.getConfigurationStore()); final MovingConfig config = MovingCheck.getConfig(player);
// If the player is allowed to fly anyway, the workaround is not needed // If the player is allowed to fly anyway, the workaround is not needed
// It's kind of expensive (looking up block types) therefore it makes // It's kind of expensive (looking up block types) therefore it makes
@ -76,7 +74,7 @@ public class MovingCheckListener implements Listener, EventManager {
} }
// Get the player-specific stored data that applies here // Get the player-specific stored data that applies here
final MovingData data = MovingCheck.getData(player.getDataStore()); final MovingData data = MovingCheck.getData(player);
final Block block = event.getBlockPlaced(); final Block block = event.getBlockPlaced();
@ -109,15 +107,15 @@ public class MovingCheckListener implements Listener, EventManager {
* it was NoCheat or another plugin. If it was NoCheat, the target * it was NoCheat or another plugin. If it was NoCheat, the target
* location should match the "data.teleportTo" value. * location should match the "data.teleportTo" value.
* *
* On teleports, reset some movement related data * On teleports, reset some movement related data that gets invalid
* *
* @param event * @param event The PlayerTeleportEvent
*/ */
@EventHandler(priority = EventPriority.HIGHEST) @EventHandler(priority = EventPriority.HIGHEST)
public void teleport(final PlayerTeleportEvent event) { public void teleport(final PlayerTeleportEvent event) {
NoCheatPlayer player = plugin.getPlayer(event.getPlayer()); NoCheatPlayer player = plugin.getPlayer(event.getPlayer());
final MovingData data = MovingCheck.getData(player.getDataStore()); final MovingData data = MovingCheck.getData(player);
// If it was a teleport initialized by NoCheat, do it anyway // If it was a teleport initialized by NoCheat, do it anyway
// even if another plugin said "no" // even if another plugin said "no"
@ -141,14 +139,15 @@ public class MovingCheckListener implements Listener, EventManager {
/** /**
* Just for security, if a player switches between worlds, reset the * Just for security, if a player switches between worlds, reset the
* runfly and morepackets checks. * runfly and morepackets checks data, because it is definitely invalid
* now
* *
* @param event * @param event The PlayerChangedWorldEvent
*/ */
@EventHandler(priority = EventPriority.MONITOR) @EventHandler(priority = EventPriority.MONITOR)
public void worldChange(final PlayerChangedWorldEvent event) { public void worldChange(final PlayerChangedWorldEvent event) {
// Maybe this helps with people teleporting through multiverse portals having problems? // Maybe this helps with people teleporting through multiverse portals having problems?
final MovingData data = MovingCheck.getData(plugin.getPlayer(event.getPlayer()).getDataStore()); final MovingData data = MovingCheck.getData(plugin.getPlayer(event.getPlayer()));
data.teleportTo.reset(); data.teleportTo.reset();
data.clearRunFlyData(); data.clearRunFlyData();
data.clearMorePacketsData(); data.clearMorePacketsData();
@ -162,7 +161,7 @@ public class MovingCheckListener implements Listener, EventManager {
*/ */
@EventHandler(priority = EventPriority.MONITOR) @EventHandler(priority = EventPriority.MONITOR)
public void portal(final PlayerPortalEvent event) { public void portal(final PlayerPortalEvent event) {
final MovingData data = MovingCheck.getData(plugin.getPlayer(event.getPlayer()).getDataStore()); final MovingData data = MovingCheck.getData(plugin.getPlayer(event.getPlayer()));
data.clearMorePacketsData(); data.clearMorePacketsData();
data.clearRunFlyData(); data.clearRunFlyData();
} }
@ -175,7 +174,7 @@ public class MovingCheckListener implements Listener, EventManager {
*/ */
@EventHandler(priority = EventPriority.MONITOR) @EventHandler(priority = EventPriority.MONITOR)
public void respawn(final PlayerRespawnEvent event) { public void respawn(final PlayerRespawnEvent event) {
final MovingData data = MovingCheck.getData(plugin.getPlayer(event.getPlayer()).getDataStore()); final MovingData data = MovingCheck.getData(plugin.getPlayer(event.getPlayer()));
data.clearMorePacketsData(); data.clearMorePacketsData();
data.clearRunFlyData(); data.clearRunFlyData();
} }
@ -184,22 +183,26 @@ public class MovingCheckListener implements Listener, EventManager {
* When a player moves, he will be checked for various * When a player moves, he will be checked for various
* suspicious behaviour. * suspicious behaviour.
* *
* @param event * @param event The PlayerMoveEvent
*/ */
@EventHandler(priority = EventPriority.LOWEST) @EventHandler(priority = EventPriority.LOWEST)
public void move(final PlayerMoveEvent event) { public void move(final PlayerMoveEvent event) {
// Don't care for vehicles
if(event.isCancelled() || event.getPlayer().isInsideVehicle()) if(event.isCancelled() || event.getPlayer().isInsideVehicle())
return; return;
// Don't care for movements that are very high distance or to another
// world (such that it is very likely the event data was modified by
// another plugin before we got it)
if(!event.getFrom().getWorld().equals(event.getTo().getWorld()) || event.getFrom().distanceSquared(event.getTo()) > 400) { if(!event.getFrom().getWorld().equals(event.getTo().getWorld()) || event.getFrom().distanceSquared(event.getTo()) > 400) {
return; return;
} }
final NoCheatPlayer player = plugin.getPlayer(event.getPlayer()); final NoCheatPlayer player = plugin.getPlayer(event.getPlayer());
final MovingConfig cc = MovingCheck.getConfig(player.getConfigurationStore()); final MovingConfig cc = MovingCheck.getConfig(player);
final MovingData data = MovingCheck.getData(player.getDataStore()); final MovingData data = MovingCheck.getData(player);
// Advance various counters and values that change per movement // Advance various counters and values that change per movement
// tick. They are needed to decide on how fast a player may // tick. They are needed to decide on how fast a player may
@ -210,9 +213,6 @@ public class MovingCheckListener implements Listener, EventManager {
data.from.set(event.getFrom()); data.from.set(event.getFrom());
final Location to = event.getTo(); final Location to = event.getTo();
data.to.set(to); data.to.set(to);
PreciseLocation newTo = null; PreciseLocation newTo = null;
@ -240,7 +240,8 @@ public class MovingCheckListener implements Listener, EventManager {
// Did one of the check(s) decide we need a new "to"-location? // Did one of the check(s) decide we need a new "to"-location?
if(newTo != null) { if(newTo != null) {
// Compose a new location based on coordinates of "newTo" and // Compose a new location based on coordinates of "newTo" and
// viewing direction of "event.getTo()" // viewing direction of "event.getTo()" to allow the player to
// look somewhere else despite getting pulled back by NoCheat
event.setTo(new Location(player.getPlayer().getWorld(), newTo.x, newTo.y, newTo.z, to.getYaw(), to.getPitch())); event.setTo(new Location(player.getPlayer().getWorld(), newTo.x, newTo.y, newTo.z, to.getYaw(), to.getPitch()));
// remember where we send the player to // remember where we send the player to
@ -279,16 +280,16 @@ public class MovingCheckListener implements Listener, EventManager {
/** /**
* Player got a velocity packet. The server can't keep track * Player got a velocity packet. The server can't keep track
* of actual velocity values (by design), so we have to try * of actual velocity values (by design), so we have to try
* and do that ourselves. * and do that ourselves. Very rough estimates.
* *
* @param event * @param event The PlayerVelocityEvent
*/ */
@EventHandler(priority = EventPriority.MONITOR) @EventHandler(priority = EventPriority.MONITOR)
public void velocity(final PlayerVelocityEvent event) { public void velocity(final PlayerVelocityEvent event) {
if(event.isCancelled()) if(event.isCancelled())
return; return;
final MovingData data = MovingCheck.getData(plugin.getPlayer(event.getPlayer()).getDataStore()); final MovingData data = MovingCheck.getData(plugin.getPlayer(event.getPlayer()));
final Vector v = event.getVelocity(); final Vector v = event.getVelocity();

View File

@ -9,45 +9,61 @@ import cc.co.evenprime.bukkit.nocheat.data.Statistics.Id;
*/ */
public class MovingData implements DataItem { public class MovingData implements DataItem {
// Keep track of the violation levels of the checks
public double runflyVL; public double runflyVL;
public double nofallVL; public double nofallVL;
public double morePacketsVL; public double morePacketsVL;
// Count how long a player is in the air
public int jumpPhase; public int jumpPhase;
// Remember how big the players last JumpAmplifier (potion effect) was
public double lastJumpAmplifier; public double lastJumpAmplifier;
// Remember for a short time that the player was on ice and therefore
// should be allowed to move a bit faster
public int onIce; public int onIce;
// Where should a player be teleported back to when failing the check
public final PreciseLocation runflySetBackPoint = new PreciseLocation(); public final PreciseLocation runflySetBackPoint = new PreciseLocation();
// Some values for estimating movement freedom
public double vertFreedom; public double vertFreedom;
public double vertVelocity; public double vertVelocity;
public int vertVelocityCounter; public int vertVelocityCounter;
public double horizFreedom; public double horizFreedom;
public int horizVelocityCounter; public int horizVelocityCounter;
public float fallDistance;
public float lastAddedFallDistance;
public double horizontalBuffer; public double horizontalBuffer;
public int bunnyhopdelay; public int bunnyhopdelay;
// Keep track of estimated fall distance to compare to real fall distance
public float fallDistance;
public float lastAddedFallDistance;
// Keep track of when "morePackets" last time checked and how much packets
// a player sent and may send before failing the check
public long morePacketsLastTime; public long morePacketsLastTime;
public int packets;
public int morePacketsBuffer = 50; public int morePacketsBuffer = 50;
// Where to teleport the player that fails the "morepackets" check
public final PreciseLocation morePacketsSetbackPoint = new PreciseLocation(); public final PreciseLocation morePacketsSetbackPoint = new PreciseLocation();
// When NoCheat does teleport the player, remember the target location to
// be able to distinguish "our" teleports from teleports of others
public final PreciseLocation teleportTo = new PreciseLocation(); public final PreciseLocation teleportTo = new PreciseLocation();
// For logging and convenience, make copies of the events locations
public final PreciseLocation from = new PreciseLocation(); public final PreciseLocation from = new PreciseLocation();
public final PreciseLocation to = new PreciseLocation(); public final PreciseLocation to = new PreciseLocation();
// For convenience, remember if the locations are considered "on ground"
// by NoCheat
public boolean fromOnOrInGround; public boolean fromOnOrInGround;
public boolean toOnOrInGround; public boolean toOnOrInGround;
public Id statisticCategory = Id.MOV_RUNNING; public Id statisticCategory = Id.MOV_RUNNING;
public int packets;
public void clearRunFlyData() { public void clearRunFlyData() {
runflySetBackPoint.reset(); runflySetBackPoint.reset();
jumpPhase = 0; jumpPhase = 0;

View File

@ -41,6 +41,9 @@ public class NoFallCheck extends MovingCheck {
data.fallDistance = player.getPlayer().getFallDistance(); data.fallDistance = player.getPlayer().getFallDistance();
data.nofallVL += data.fallDistance; data.nofallVL += data.fallDistance;
incrementStatistics(player, Id.MOV_NOFALL, data.fallDistance); incrementStatistics(player, Id.MOV_NOFALL, data.fallDistance);
// Execute whatever actions are associated with this check and the
// violation level and find out if we should cancel the event
final boolean cancel = executeActions(player, cc.nofallActions, data.nofallVL); final boolean cancel = executeActions(player, cc.nofallActions, data.nofallVL);
if(cancel) { if(cancel) {
player.dealFallDamage(); player.dealFallDamage();
@ -64,6 +67,8 @@ public class NoFallCheck extends MovingCheck {
data.nofallVL += difference; data.nofallVL += difference;
incrementStatistics(player, Id.MOV_NOFALL, difference); incrementStatistics(player, Id.MOV_NOFALL, difference);
// Execute whatever actions are associated with this check and the
// violation level and find out if we should cancel the event
final boolean cancel = executeActions(player, cc.nofallActions, data.nofallVL); final boolean cancel = executeActions(player, cc.nofallActions, data.nofallVL);
// If "cancelled", the fall damage gets dealt in a way that's // If "cancelled", the fall damage gets dealt in a way that's
@ -106,7 +111,7 @@ public class NoFallCheck extends MovingCheck {
} }
// Reduce falldamage violation level // Reduce falldamage violation level
data.nofallVL *= 0.99D; data.nofallVL *= 0.95D;
return; return;
} }
@ -115,9 +120,9 @@ public class NoFallCheck extends MovingCheck {
public String getParameter(ParameterName wildcard, NoCheatPlayer player) { public String getParameter(ParameterName wildcard, NoCheatPlayer player) {
if(wildcard == ParameterName.VIOLATIONS) if(wildcard == ParameterName.VIOLATIONS)
return String.format(Locale.US, "%d", (int) getData(player.getDataStore()).nofallVL); return String.format(Locale.US, "%d", (int) getData(player).nofallVL);
else if(wildcard == ParameterName.FALLDISTANCE) else if(wildcard == ParameterName.FALLDISTANCE)
return String.format(Locale.US, "%.2f", getData(player.getDataStore()).fallDistance); return String.format(Locale.US, "%.2f", getData(player).fallDistance);
else else
return super.getParameter(wildcard, player); return super.getParameter(wildcard, player);
} }

View File

@ -72,8 +72,8 @@ public class RunningCheck extends MovingCheck {
// Slowly reduce the level with each event // Slowly reduce the level with each event
data.runflyVL *= 0.95; data.runflyVL *= 0.95;
// Did the player move in unexpected ways?
if(result > 0) { if(result > 0) {
// Increment violation counter // Increment violation counter
data.runflyVL += result; data.runflyVL += result;
@ -92,15 +92,24 @@ public class RunningCheck extends MovingCheck {
} }
} else { } else {
// Decide if we should create a new setBack point
// These are the result of a lot of bug reports, experience and
// trial and error
if((toInGround && from.y >= to.y) || CheckUtil.isLiquid(toType)) { if((toInGround && from.y >= to.y) || CheckUtil.isLiquid(toType)) {
// Yes, if the player moved down "into" the ground or into liquid
setBack.set(to); setBack.set(to);
setBack.y = Math.ceil(setBack.y); setBack.y = Math.ceil(setBack.y);
data.jumpPhase = 0; data.jumpPhase = 0;
} else if(toOnGround && (from.y >= to.y || setBack.y <= Math.floor(to.y))) { } else if(toOnGround && (from.y >= to.y || setBack.y <= Math.floor(to.y))) {
// Yes, if the player moved down "onto" the ground and the new
// setback point is higher up than the old or at least at the
// same height
setBack.set(to); setBack.set(to);
setBack.y = Math.floor(setBack.y); setBack.y = Math.floor(setBack.y);
data.jumpPhase = 0; data.jumpPhase = 0;
} else if(fromOnGround || fromInGround || toOnGround || toInGround) { } else if(fromOnGround || fromInGround || toOnGround || toInGround) {
// The player at least touched the ground somehow
data.jumpPhase = 0; data.jumpPhase = 0;
} }
} }
@ -134,7 +143,7 @@ public class RunningCheck extends MovingCheck {
Id statisticsCategory = null; Id statisticsCategory = null;
// Player on ice? // Player on ice? Give him higher max speed
Block b = player.getPlayer().getLocation().getBlock(); Block b = player.getPlayer().getLocation().getBlock();
if(b.getType() == Material.ICE || b.getRelative(0, -1, 0).getType() == Material.ICE) { if(b.getType() == Material.ICE || b.getRelative(0, -1, 0).getType() == Material.ICE) {
data.onIce = 20; data.onIce = 20;
@ -208,6 +217,7 @@ public class RunningCheck extends MovingCheck {
// How much higher did the player move than expected?? // How much higher did the player move than expected??
double distanceAboveLimit = 0.0D; double distanceAboveLimit = 0.0D;
// Potion effect "Jump"
double jumpAmplifier = player.getJumpAmplifier(); double jumpAmplifier = player.getJumpAmplifier();
if(jumpAmplifier > data.lastJumpAmplifier) { if(jumpAmplifier > data.lastJumpAmplifier) {
data.lastJumpAmplifier = jumpAmplifier; data.lastJumpAmplifier = jumpAmplifier;
@ -239,9 +249,9 @@ public class RunningCheck extends MovingCheck {
if(wildcard == ParameterName.CHECK) if(wildcard == ParameterName.CHECK)
// Workaround for something until I find a better way to do it // Workaround for something until I find a better way to do it
return getData(player.getDataStore()).statisticCategory.toString(); return getData(player).statisticCategory.toString();
else if(wildcard == ParameterName.VIOLATIONS) else if(wildcard == ParameterName.VIOLATIONS)
return String.format(Locale.US, "%d", (int) getData(player.getDataStore()).runflyVL); return String.format(Locale.US, "%d", (int) getData(player).runflyVL);
else else
return super.getParameter(wildcard, player); return super.getParameter(wildcard, player);
} }

View File

@ -13,14 +13,19 @@ import org.bukkit.permissions.Permission;
import cc.co.evenprime.bukkit.nocheat.NoCheat; import cc.co.evenprime.bukkit.nocheat.NoCheat;
import cc.co.evenprime.bukkit.nocheat.config.Permissions; import cc.co.evenprime.bukkit.nocheat.config.Permissions;
/**
* Handle all NoCheat related commands in a common place
*/
public class CommandHandler { public class CommandHandler {
private final List<Permission> perms; private final List<Permission> perms;
public CommandHandler(NoCheat plugin) { public CommandHandler(NoCheat plugin) {
// Make a copy to allow sorting // Make a copy to allow sorting
perms = new LinkedList<Permission>(plugin.getDescription().getPermissions()); perms = new LinkedList<Permission>(plugin.getDescription().getPermissions());
// Sort NoCheats permission by name and parent-child relation with
// a custom sorting method
Collections.sort(perms, new Comparator<Permission>() { Collections.sort(perms, new Comparator<Permission>() {
public int compare(Permission o1, Permission o2) { public int compare(Permission o1, Permission o2) {
@ -44,10 +49,19 @@ public class CommandHandler {
}); });
} }
/**
* Handle a command that is directed at NoCheat
* @param plugin
* @param sender
* @param command
* @param label
* @param args
* @return
*/
public boolean handleCommand(NoCheat plugin, CommandSender sender, Command command, String label, String[] args) { public boolean handleCommand(NoCheat plugin, CommandSender sender, Command command, String label, String[] args) {
boolean result = false; boolean result = false;
// Not our command // Not our command, how did it get here?
if(!command.getName().equalsIgnoreCase("nocheat") || args.length == 0) { if(!command.getName().equalsIgnoreCase("nocheat") || args.length == 0) {
result = false; result = false;
} else if(args[0].equalsIgnoreCase("permlist") && args.length >= 2) { } else if(args[0].equalsIgnoreCase("permlist") && args.length >= 2) {
@ -60,7 +74,7 @@ public class CommandHandler {
} }
else if(args[0].equalsIgnoreCase("playerinfo") && args.length >= 2) { else if(args[0].equalsIgnoreCase("playerinfo") && args.length >= 2) {
// performance command was used // playerinfo command was used
result = handlePlayerInfoCommand(plugin, sender, args); result = handlePlayerInfoCommand(plugin, sender, args);
} }
@ -117,9 +131,8 @@ public class CommandHandler {
sender.sendMessage("[NoCheat] Reloading configuration"); sender.sendMessage("[NoCheat] Reloading configuration");
plugin.reloadConfiguration(); plugin.reloadConfiguration();
sender.sendMessage("[NoCheat] Configuration reloaded"); sender.sendMessage("[NoCheat] Configuration reloaded");
} } else {
else { sender.sendMessage("You lack the " + Permissions.ADMIN_RELOAD + " permission to use 'reload'");
sender.sendMessage("You lack the "+Permissions.ADMIN_RELOAD+ " permission to use 'reload'");
} }
return true; return true;

View File

@ -11,6 +11,10 @@ import cc.co.evenprime.bukkit.nocheat.actions.types.DummyAction;
import cc.co.evenprime.bukkit.nocheat.actions.types.LogAction; import cc.co.evenprime.bukkit.nocheat.actions.types.LogAction;
import cc.co.evenprime.bukkit.nocheat.actions.types.SpecialAction; import cc.co.evenprime.bukkit.nocheat.actions.types.SpecialAction;
/**
* Helps with creating Actions out of text string definitions
*
*/
public class ActionFactory { public class ActionFactory {
private static final Map<String, Object> lib = new HashMap<String, Object>(); private static final Map<String, Object> lib = new HashMap<String, Object>();

View File

@ -2,6 +2,8 @@ package cc.co.evenprime.bukkit.nocheat.config;
/** /**
* Paths for the configuration options * Paths for the configuration options
* Making everything final static prevents accidentially modifying any
* of these
* *
*/ */
public abstract class ConfPaths { public abstract class ConfPaths {

View File

@ -6,6 +6,10 @@ import cc.co.evenprime.bukkit.nocheat.EventManager;
import cc.co.evenprime.bukkit.nocheat.NoCheat; import cc.co.evenprime.bukkit.nocheat.NoCheat;
import cc.co.evenprime.bukkit.nocheat.config.ConfigurationCacheStore; import cc.co.evenprime.bukkit.nocheat.config.ConfigurationCacheStore;
/**
* Prints the list of active checks per world on startup, if requested
*
*/
public class ActiveCheckPrinter { public class ActiveCheckPrinter {
public static void printActiveChecks(NoCheat plugin, List<EventManager> eventManagers) { public static void printActiveChecks(NoCheat plugin, List<EventManager> eventManagers) {

View File

@ -3,6 +3,12 @@ package cc.co.evenprime.bukkit.nocheat.debug;
import org.bukkit.World; import org.bukkit.World;
import cc.co.evenprime.bukkit.nocheat.NoCheat; import cc.co.evenprime.bukkit.nocheat.NoCheat;
/**
* A task running in the background that measures tick time vs. real time
*
* @author Evenprime
*
*/
public class LagMeasureTask implements Runnable { public class LagMeasureTask implements Runnable {
private int ingameseconds = 1; private int ingameseconds = 1;