mirror of
https://github.com/NoCheatPlus/NoCheatPlus.git
synced 2024-12-30 20:37:52 +01:00
Remove "timedEventManager" task when disabled, avoid using
"server.getOnlinePlayers()" because it's a performance hog. Updated plugin.yml
This commit is contained in:
parent
d31525ba3a
commit
de16694698
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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";
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user