Remove "timedEventManager" task when disabled, avoid using

"server.getOnlinePlayers()" because it's a performance hog.
Updated plugin.yml
This commit is contained in:
Evenprime 2011-10-25 18:58:35 +02:00
parent d31525ba3a
commit de16694698
5 changed files with 115 additions and 50 deletions

View File

@ -3,7 +3,7 @@ name: NoCheat
author: Evenprime
main: cc.co.evenprime.bukkit.nocheat.NoCheat
version: 2.13c
version: 2.14
commands:
nocheat:
@ -72,3 +72,8 @@ permissions:
description: Allow a player to attack players and monster even if they are not in his field of view
nocheat.checks.fight.selfhit:
description: Allow a player to attack himself with close combat attacks (punching, swords, etc.)
nocheat.checks.timed:
description: Allow the player to bypass all timed checks
children:
nocheat.checks.timed.godmode:
description: Allow a player to use the godmode hack to make himself invincible to damage

View File

@ -54,6 +54,8 @@ public class NoCheat extends JavaPlugin {
private LagMeasureTask lagMeasureTask;
private int taskId = -1;
public NoCheat() {
}
@ -62,6 +64,11 @@ public class NoCheat extends JavaPlugin {
PluginDescriptionFile pdfFile = this.getDescription();
if(taskId != -1) {
getServer().getScheduler().cancelTask(taskId);
taskId = -1;
}
if(lagMeasureTask != null) {
lagMeasureTask.cancel();
lagMeasureTask = null;
@ -72,6 +79,9 @@ public class NoCheat extends JavaPlugin {
conf = null;
}
// Just to be sure nothing gets left out
getServer().getScheduler().cancelTasks(this);
log.logToConsole(LogLevel.LOW, "[NoCheat] version [" + pdfFile.getVersion() + "] is disabled.");
}
@ -80,7 +90,7 @@ public class NoCheat extends JavaPlugin {
// First set up logging
this.log = new LogManager();
log.logToConsole(LogLevel.LOW, "[NoCheat] This version is for CB #1317. It may break at any time and for any other version.");
log.logToConsole(LogLevel.LOW, "[NoCheat] This version is for CB #1337. It may break at any time and for any other version.");
// Then set up in memory per player data storage
this.data = new DataManager();
@ -102,7 +112,8 @@ public class NoCheat extends JavaPlugin {
eventManagers.add(new BlockBreakEventManager(this));
eventManagers.add(new BlockPlaceEventManager(this));
eventManagers.add(new EntityDamageEventManager(this));
eventManagers.add(new TimedEventManager(this));
TimedEventManager m = new TimedEventManager(this);
eventManagers.add(m);
// Then set up a task to monitor server lag
if(lagMeasureTask == null) {

View File

@ -6,6 +6,7 @@ import org.bukkit.craftbukkit.entity.CraftPlayer;
import org.bukkit.entity.Player;
import cc.co.evenprime.bukkit.nocheat.NoCheat;
import cc.co.evenprime.bukkit.nocheat.config.Permissions;
import cc.co.evenprime.bukkit.nocheat.config.cache.ConfigurationCache;
import cc.co.evenprime.bukkit.nocheat.data.BaseData;
@ -24,61 +25,65 @@ public class TimedCheck {
if(plugin.skipCheck())
return;
boolean cancel = false;
if(cc.timed.godmodeCheck && !player.hasPermission(Permissions.TIMED_GODMODE)) {
BaseData data = plugin.getData(player.getName());
boolean cancel = false;
EntityPlayer p = ((CraftPlayer) player).getHandle();
BaseData data = plugin.getData(player.getName());
// Compare ingame record of players ticks to our last observed value
int difference = p.ticksLived - data.timed.ticksLived;
EntityPlayer p = ((CraftPlayer) player).getHandle();
// difference should be >= tickTime for perfect synchronization
if(difference > tickTime) {
// player was faster than expected, give him credit for the
// difference
data.timed.ticksBehind -= (difference - tickTime);
// Reduce violation level over time
data.timed.godmodeVL *= 0.90D;
// Compare ingame record of players ticks to our last observed value
int difference = p.ticksLived - data.timed.ticksLived;
} else if(difference >= tickTime / 2) {
// close enough, let it pass
// difference should be >= tickTime for perfect synchronization
if(difference > tickTime) {
// player was faster than expected, give him credit for the
// difference
data.timed.ticksBehind -= (difference - tickTime);
// Reduce violation level over time
data.timed.godmodeVL *= 0.90D;
// Reduce violation level over time
data.timed.godmodeVL *= 0.95D;
} else {
// That's a bit suspicious, why is the player more than half the
// ticktime behind? Keep that in mind
data.timed.ticksBehind += tickTime - difference;
} else if(difference >= tickTime / 2) {
// close enough, let it pass
// Is he way too far behind, then correct that
if(data.timed.ticksBehind > cc.timed.godmodeTicksLimit) {
// Reduce violation level over time
data.timed.godmodeVL *= 0.95D;
} else {
// That's a bit suspicious, why is the player more than half the
// ticktime behind? Keep that in mind
data.timed.ticksBehind += tickTime - difference;
data.timed.godmodeVL += tickTime - difference;
// Is he way too far behind, then correct that
if(data.timed.ticksBehind > cc.timed.godmodeTicksLimit) {
// Enough is enough
data.log.check = "timed.godmode";
data.log.godmodeTicksBehind = data.timed.ticksBehind;
data.timed.godmodeVL += tickTime - difference;
cancel = plugin.execute(player, cc.timed.godmodeActions, (int) data.timed.godmodeVL, data.timed.history, cc);
// Enough is enough
data.log.check = "timed.godmode";
data.log.godmodeTicksBehind = data.timed.ticksBehind;
// Reduce the time the player is behind accordingly
data.timed.ticksBehind -= tickTime;
cancel = plugin.execute(player, cc.timed.godmodeActions, (int) data.timed.godmodeVL, data.timed.history, cc);
// Reduce the time the player is behind accordingly
data.timed.ticksBehind -= tickTime;
}
}
}
if(data.timed.ticksBehind < 0) {
data.timed.ticksBehind = 0;
}
if(cancel) {
// Catch up for at least some of the ticks
for(int i = 0; i < tickTime; i++) {
p.b(true); // Catch up with the server, one tick at a time
if(data.timed.ticksBehind < 0) {
data.timed.ticksBehind = 0;
}
}
// setup data for next time
data.timed.ticksLived = p.ticksLived;
if(cancel) {
// Catch up for at least some of the ticks
for(int i = 0; i < tickTime; i++) {
p.b(true); // Catch up with the server, one tick at a time
}
}
// setup data for next time
data.timed.ticksLived = p.ticksLived;
}
}
}

View File

@ -18,6 +18,7 @@ public class Explainations {
set(Configuration.LOGGING_ACTIVE, "Should NoCheat related messages get logged at all. Some messages may still appear, e.g. error\n messages, even if this option is deactivated");
set(Configuration.LOGGING_PREFIX, "The short text that appears in front of messages by NoCheat. Color codes are &0-&9 and &A-&F");
set(Configuration.LOGGING_FILENAME, "Where logs that go to the logfile are stored. You can have different files for different worlds.");
set(Configuration.LOGGING_FILELEVEL, "What log-level need messages to have to get stored in the logfile. Values are:\n low: all messages\n med: med and high messages only\n high: high messages only\n off: no messages at all.");
set(Configuration.LOGGING_CONSOLELEVEL, "What log-level need messages to have to get displayed in your server console. Values are:\n low: all messages\n med: med and high messages only\n high: high messages only\n off: no messages at all.");
@ -84,6 +85,15 @@ public class Explainations {
set(Configuration.FIGHT_DIRECTION_PRECISION, "Set how precise the check should be. If you experience the check to be too zealous, increase this value. \nIf you want to make it tighter, reduce this value. Default is 100.");
set(Configuration.FIGHT_DIRECTION_PENALTYTIME, "If a player fails the check, he will be unable to attack for this amount of time (in milliseconds), default is 500.");
set(Configuration.FIGHT_DIRECTION_ACTIONS, "What should be done if a player attacks entities that are not in his field of view.\nUnit is number of attacks on entities out of view.");
set(Configuration.FIGHT_SELFHIT_CHECK, "If true, check if a player is attacking itself, which should normally be impossible.");
set(Configuration.FIGHT_SELFHIT_ACTIONS, "What should be done if a player attacks himself.\nUnit is number of attacks on himself.");
set(Configuration.TIMED_CHECK, "If true, do various checks on things related to server and client time.");
set(Configuration.TIMED_GODMODE_CHECK, "If true, check or prevent if a player made himself invulnerable by exploiting a time-related bug.\nThis 'godmode' exploit looks similar to a player with huge lag, so be careful when punishing people for it.");
set(Configuration.TIMED_GODMODE_TICKSLIMIT, "How many ticks may a player be behind the server time before NoCheat reacts. Default is 50.");
set(Configuration.TIMED_GODMODE_ACTIONS, "What should be done if a player is considered using 'godmode'.\nUnit is number of ticks of potential godmode usage.");
}
private static void set(OptionNode id, String text) {
@ -94,6 +104,7 @@ public class Explainations {
String result = explainations.get(id);
if(result == null) {
System.out.println("Missing description for "+id.getName());
result = "No description available";
}

View File

@ -1,8 +1,14 @@
package cc.co.evenprime.bukkit.nocheat.events;
import java.util.ArrayList;
import java.util.ConcurrentModificationException;
import java.util.LinkedList;
import java.util.List;
import net.minecraft.server.EntityPlayer;
import org.bukkit.craftbukkit.CraftServer;
import org.bukkit.craftbukkit.entity.CraftEntity;
import org.bukkit.entity.Player;
import cc.co.evenprime.bukkit.nocheat.NoCheat;
@ -20,6 +26,8 @@ public class TimedEventManager implements EventManager {
private final Performance timedPerformance;
public int taskId = -1;
public TimedEventManager(final NoCheat plugin) {
this.plugin = plugin;
@ -28,11 +36,14 @@ public class TimedEventManager implements EventManager {
this.timedPerformance = plugin.getPerformance(Type.TIMED);
// "register a listener" for passed time
plugin.getServer().getScheduler().scheduleSyncRepeatingTask(plugin, new Runnable() {
taskId = plugin.getServer().getScheduler().scheduleSyncRepeatingTask(plugin, new Runnable() {
private int executions = 0;
private int loopsize = 10;
private int executions = 0;
private int loopsize = 10;
private final List<EntityPlayer> entities = new ArrayList<EntityPlayer>(20);
@SuppressWarnings("unchecked")
public void run() {
executions++;
@ -41,11 +52,33 @@ public class TimedEventManager implements EventManager {
executions = 0;
}
for(Player p : plugin.getServer().getOnlinePlayers()) {
if((p.hashCode() & 0x7FFFFFFF) % loopsize == executions) {
onTimedEvent(p, loopsize);
// For performance reasons, we take some shortcuts here
CraftServer server = (CraftServer) plugin.getServer();
try {
// Only collect the entities that we want to check this time
for(EntityPlayer p : (List<EntityPlayer>) server.getHandle().players) {
if(p.id % loopsize == executions) {
entities.add(p);
}
}
} catch(ConcurrentModificationException e) {
// Bad luck, better luck next time
} catch(Exception e) {
e.printStackTrace();
}
// Now initialize the checks one by one
for(EntityPlayer p : entities) {
try {
onTimedEvent((Player) CraftEntity.getEntity(server, p), loopsize);
} catch(Exception e) {
e.printStackTrace();
}
}
// Clear the list for next time
entities.clear();
}
}, 0, 1);
}