Add Minecraft version detection and version-dependent default values.

We can now decide on base of the Minecraft version, which value to use,
with the config entry set to "default". Used with
pvp-knock-back-velocity and enforce-location (first move exploit).

Activated features are shown in the version info ("ncp version"). The
version info is now logged to the log file after post-enable and after
reloading the configuration.
This commit is contained in:
asofold 2015-01-11 18:52:08 +01:00
parent 02a95f6cab
commit d2da3f1e9e
14 changed files with 685 additions and 142 deletions

View File

@ -10,6 +10,8 @@ import fr.neatmonster.nocheatplus.checks.CheckType;
import fr.neatmonster.nocheatplus.checks.access.ACheckConfig;
import fr.neatmonster.nocheatplus.checks.access.CheckConfigFactory;
import fr.neatmonster.nocheatplus.checks.access.ICheckConfig;
import fr.neatmonster.nocheatplus.compat.AlmostBoolean;
import fr.neatmonster.nocheatplus.compat.versions.Bugs;
import fr.neatmonster.nocheatplus.config.ConfPaths;
import fr.neatmonster.nocheatplus.config.ConfigFile;
import fr.neatmonster.nocheatplus.config.ConfigManager;
@ -177,7 +179,8 @@ public class FightConfig extends ACheckConfig {
yawRateCheck = data.getBoolean(ConfPaths.FIGHT_YAWRATE_CHECK, true);
cancelDead = data.getBoolean(ConfPaths.FIGHT_CANCELDEAD);
knockBackVelocityPvP = data.getBoolean(ConfPaths.FIGHT_PVP_KNOCKBACKVELOCITY);
AlmostBoolean ref = data.getAlmostBoolean(ConfPaths.FIGHT_PVP_KNOCKBACKVELOCITY, AlmostBoolean.MAYBE);
knockBackVelocityPvP = ref == AlmostBoolean.MAYBE ? Bugs.shouldPvpKnockBackVelocity() : ref.decide();
}
/* (non-Javadoc)
@ -186,28 +189,28 @@ public class FightConfig extends ACheckConfig {
@Override
public final boolean isEnabled(final CheckType checkType) {
switch (checkType) {
case FIGHT_ANGLE:
return angleCheck;
case FIGHT_CRITICAL:
return criticalCheck;
case FIGHT_DIRECTION:
return directionCheck;
case FIGHT_GODMODE:
return godModeCheck;
case FIGHT_KNOCKBACK:
return knockbackCheck;
case FIGHT_NOSWING:
return noSwingCheck;
case FIGHT_REACH:
return reachCheck;
case FIGHT_SPEED:
return speedCheck;
case FIGHT_SELFHIT:
return selfHitCheck;
case FIGHT_FASTHEAL:
return fastHealCheck;
default:
return true;
case FIGHT_ANGLE:
return angleCheck;
case FIGHT_CRITICAL:
return criticalCheck;
case FIGHT_DIRECTION:
return directionCheck;
case FIGHT_GODMODE:
return godModeCheck;
case FIGHT_KNOCKBACK:
return knockbackCheck;
case FIGHT_NOSWING:
return noSwingCheck;
case FIGHT_REACH:
return reachCheck;
case FIGHT_SPEED:
return speedCheck;
case FIGHT_SELFHIT:
return selfHitCheck;
case FIGHT_FASTHEAL:
return fastHealCheck;
default:
return true;
}
}
}

View File

@ -11,6 +11,8 @@ import fr.neatmonster.nocheatplus.checks.access.ACheckConfig;
import fr.neatmonster.nocheatplus.checks.access.CheckConfigFactory;
import fr.neatmonster.nocheatplus.checks.access.ICheckConfig;
import fr.neatmonster.nocheatplus.command.CommandUtil;
import fr.neatmonster.nocheatplus.compat.AlmostBoolean;
import fr.neatmonster.nocheatplus.compat.versions.Bugs;
import fr.neatmonster.nocheatplus.config.ConfPaths;
import fr.neatmonster.nocheatplus.config.ConfigFile;
import fr.neatmonster.nocheatplus.config.ConfigManager;
@ -244,9 +246,15 @@ public class MovingConfig extends ACheckConfig {
sprintingGrace = Math.max(0L, (long) (config.getDouble(ConfPaths.MOVING_SPRINTINGGRACE) * 1000.0)); // Config: seconds.
assumeSprint = config.getBoolean(ConfPaths.MOVING_ASSUMESPRINT);
speedGrace = Math.max(0, (int) Math.round(config.getDouble(ConfPaths.MOVING_SPEEDGRACE) * 20.0)); // Config: seconds
enforceLocation = config.getBoolean(ConfPaths.MOVING_ENFORCELOCATION);
AlmostBoolean ref = config.getAlmostBoolean(ConfPaths.MOVING_ENFORCELOCATION, AlmostBoolean.MAYBE);
if (ref == AlmostBoolean.MAYBE) {
enforceLocation = Bugs.shouldEnforceLocation();
} else {
enforceLocation = ref.decide();
}
vehicleEnforceLocation = config.getBoolean(ConfPaths.MOVING_VEHICLES_ENFORCELOCATION);
ref = config.getAlmostBoolean(ConfPaths.MOVING_VEHICLES_ENFORCELOCATION, AlmostBoolean.MAYBE);
vehicleEnforceLocation = ref.decideOptimistically(); // Currently rather enabled.
vehiclePreventDestroyOwn = config.getBoolean(ConfPaths.MOVING_VEHICLES_PREVENTDESTROYOWN);
traceSize = config.getInt(ConfPaths.MOVING_TRACE_SIZE);
@ -262,20 +270,20 @@ public class MovingConfig extends ACheckConfig {
@Override
public final boolean isEnabled(final CheckType checkType) {
switch (checkType) {
case MOVING_NOFALL:
return noFallCheck;
case MOVING_SURVIVALFLY:
return survivalFlyCheck;
case MOVING_PASSABLE:
return passableCheck;
case MOVING_MOREPACKETS:
return morePacketsCheck;
case MOVING_MOREPACKETSVEHICLE:
return morePacketsVehicleCheck;
case MOVING_CREATIVEFLY:
return creativeFlyCheck;
default:
return true;
case MOVING_NOFALL:
return noFallCheck;
case MOVING_SURVIVALFLY:
return survivalFlyCheck;
case MOVING_PASSABLE:
return passableCheck;
case MOVING_MOREPACKETS:
return morePacketsCheck;
case MOVING_MOREPACKETSVEHICLE:
return morePacketsVehicleCheck;
case MOVING_CREATIVEFLY:
return creativeFlyCheck;
default:
return true;
}
}
}

View File

@ -19,6 +19,7 @@ import fr.neatmonster.nocheatplus.logging.LogManager;
import fr.neatmonster.nocheatplus.logging.Streams;
import fr.neatmonster.nocheatplus.permissions.Permissions;
import fr.neatmonster.nocheatplus.players.DataManager;
import fr.neatmonster.nocheatplus.utilities.StringUtil;
public class ReloadCommand extends BaseCommand {
@ -87,6 +88,7 @@ public class ReloadCommand extends BaseCommand {
sender.sendMessage(TAG + "Configuration reloaded!");
}
logManager.info(Streams.INIT, "[NoCheatPlus] Configuration reloaded.");
logManager.info(Streams.DEFAULT_FILE, StringUtil.join(VersionCommand.getVersionInfo(), "\n")); // Queued (!).
}
}

View File

@ -1,5 +1,6 @@
package fr.neatmonster.nocheatplus.command.admin;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
@ -16,6 +17,7 @@ import org.bukkit.plugin.java.JavaPlugin;
import fr.neatmonster.nocheatplus.NCPAPIProvider;
import fr.neatmonster.nocheatplus.command.BaseCommand;
import fr.neatmonster.nocheatplus.compat.MCAccess;
import fr.neatmonster.nocheatplus.compat.versions.ServerVersion;
import fr.neatmonster.nocheatplus.hooks.NCPHook;
import fr.neatmonster.nocheatplus.hooks.NCPHookManager;
import fr.neatmonster.nocheatplus.permissions.Permissions;
@ -28,17 +30,24 @@ public class VersionCommand extends BaseCommand{
}
@Override
public boolean onCommand(CommandSender sender, Command command,
String label, String[] args) {
final MCAccess mc = NCPAPIProvider.getNoCheatPlusAPI().getMCAccess();
sender.sendMessage(new String[]{
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
List<String> lines = getVersionInfo();
sender.sendMessage(lines.toArray(new String[lines.size()]));
return true;
}
public static List<String> getVersionInfo() {
final List<String> lines = new LinkedList<String>();
final MCAccess mcAccess = NCPAPIProvider.getNoCheatPlusAPI().getMCAccess();
lines.addAll(Arrays.asList(new String[]{
"---- Version information ----",
"#### Server ####",
Bukkit.getServer().getVersion(),
"(detected: " + ServerVersion.getMinecraftVersion() + ")",
"#### NoCheatPlus ####",
"Plugin: " + access.getDescription().getVersion(),
"MCAccess: " + mc.getMCVersion() + " / " + mc.getServerVersionTag(),
});
"Plugin: " + Bukkit.getPluginManager().getPlugin("NoCheatPlus").getDescription().getVersion(),
"MCAccess: " + mcAccess.getMCVersion() + " / " + mcAccess.getServerVersionTag(),
}));
final Map<String, Set<String>> featureTags = NCPAPIProvider.getNoCheatPlusAPI().getAllFeatureTags();
if (!featureTags.isEmpty()) {
final List<String> features = new LinkedList<String>();
@ -49,7 +58,7 @@ public class VersionCommand extends BaseCommand{
// Sort and add.
Collections.sort(features, String.CASE_INSENSITIVE_ORDER);
features.add(0, "Features:");
sender.sendMessage(features.toArray(new String[features.size()]));
lines.addAll(features);
}
final Collection<NCPHook> hooks = NCPHookManager.getAllHooks();
if (!hooks.isEmpty()){
@ -58,9 +67,9 @@ public class VersionCommand extends BaseCommand{
fullNames.add(hook.getHookName() + " " + hook.getHookVersion());
}
Collections.sort(fullNames, String.CASE_INSENSITIVE_ORDER);
sender.sendMessage("Hooks: " + StringUtil.join(fullNames, " | "));
lines.add("Hooks: " + StringUtil.join(fullNames, " | "));
}
return true;
return lines;
}
}

View File

@ -19,6 +19,29 @@ public enum AlmostBoolean{
return value ? YES : NO;
}
/**
* Match yes/true/y, no/false/n, maybe/default, otherwise returns null.
* @param input Can be null.
* @return
*/
public static final AlmostBoolean match(String input) {
if (input == null) {
return null;
}
input = input.trim().toLowerCase();
if (input.equals("true") || input.equals("yes") || input.equals("y")) {
return AlmostBoolean.YES;
}
else if (input.equals("false") || input.equals("no") || input.equals("n")) {
return AlmostBoolean.NO;
}
else if (input.equals("default") || input.equals("maybe")) {
return AlmostBoolean.MAYBE;
} else {
return null;
}
}
/**
* Pessimistic interpretation: true iff YES.
* @return

View File

@ -0,0 +1,69 @@
package fr.neatmonster.nocheatplus.compat.versions;
import java.util.LinkedList;
import java.util.List;
import org.bukkit.Bukkit;
import fr.neatmonster.nocheatplus.NCPAPIProvider;
import fr.neatmonster.nocheatplus.components.NoCheatPlusAPI;
/**
* Feature selection, based on the version.
* @author web4web1
*
*/
public class Bugs {
private static boolean enforceLocation = false;
private static boolean pvpKnockBackVelocity = false;
protected static void init() {
final String mcVersion = ServerVersion.getMinecraftVersion();
final String serverVersion = Bukkit.getServer().getVersion().toLowerCase();
final NoCheatPlusAPI api = NCPAPIProvider.getNoCheatPlusAPI();
final List<String> noteWorthy = new LinkedList<String>();
// Need to add velocity (only internally) because the server does not.
pvpKnockBackVelocity = ServerVersion.select("1.8", false, true, true, false);
if (pvpKnockBackVelocity) {
noteWorthy.add("pvpKnockBackVelocity");
}
// First move exploit (classic CraftBukkit or Spigot before 1.7.5).
if (mcVersion == ServerVersion.UNKNOWN_VERSION) {
// Assume something where it's not an issue.
enforceLocation = false;
}
else if (ServerVersion.compareVersions(mcVersion, "1.8") >= 0) {
// Assume Spigot + fixed.
enforceLocation = false;
} else if (serverVersion.indexOf("spigot") >= 0 && ServerVersion.compareVersions(mcVersion, "1.7.5") >= 0) {
// Fixed in Spigot just before 1.7.5.
enforceLocation = false;
} else if (serverVersion.indexOf("craftbukkit") != 0){
// Assume classic CraftBukkit (not fixed).
enforceLocation = true;
} else {
// Assume something where it's not an issue.
enforceLocation = false;
}
if (enforceLocation) {
noteWorthy.add("enforceLocation");
}
if (!noteWorthy.isEmpty()) {
api.addFeatureTags("defaults", noteWorthy); // Not sure how to name these.
// Consider Bukkit.getLogger, or put to status after post-enable.
}
}
public static boolean shouldEnforceLocation() {
return enforceLocation;
}
public static boolean shouldPvpKnockBackVelocity() {
return pvpKnockBackVelocity;
}
}

View File

@ -0,0 +1,93 @@
package fr.neatmonster.nocheatplus.compat.versions;
import org.bukkit.Bukkit;
import org.bukkit.Server;
/**
* Bukkit-specific static access API, adding initialization methods. Note that Bukkit.getVersion() should be used to get the version of Bukkit (...).
* <hr/>
* Taken from the TrustCore plugin.
* @author mc_dev
*
*/
public class BukkitVersion {
private static boolean initialized = false;
private static boolean uniqueIdOnline = false;
private static boolean uniqueIdOffline = false;
/**
* Initialize ServerVersion, BukkitVersion, Bugs.
*/
public static boolean init() {
if (initialized) {
return false;
}
// Initialize server version.
final Server server = Bukkit.getServer();
ServerVersion.setMinecraftVersion(ServerVersion.parseMinecraftVersion(server.getBukkitVersion(), server.getVersion()));
// Test availability/reliability of certain features.
boolean uuidOnline = false;
boolean uuidOffline = false;
// TODO: Check for some versions explicitly.
String mcVersion = ServerVersion.getMinecraftVersion();
int cmp = -1;
try {
cmp = ServerVersion.compareVersions(mcVersion, "1.7.5");
} catch (IllegalArgumentException e) {
mcVersion = ServerVersion.UNKNOWN_VERSION; // Fake but somewhat true :p.
}
if (ServerVersion.UNKNOWN_VERSION.equals(mcVersion)) {
// TODO: Might test for features.
} else if (cmp == 1) {
uuidOnline = true;
uuidOffline = true;
} else if (ServerVersion.compareVersions(mcVersion, "1.7") == 1) {
// 1.7.x
uuidOnline = true; // TODO: Test, if REALLY.
uuidOffline = false;
}
uniqueIdOnline = uuidOnline;
uniqueIdOffline = uuidOffline;
initialized = true;
Bugs.init();
return true;
}
/**
* Test if Player.getUniqueId be used for online players.
* @return
*/
public static boolean uniqueIdOnline() {
return uniqueIdOnline;
}
public static void setUniqueIdOnline(boolean uniqueIdOnline) {
BukkitVersion.uniqueIdOnline = uniqueIdOnline;
}
/**
* Test if OfflinePlayer.getUniqueId can be used for offline payers [provided it exists :p].<br>
* Not sure this makes sense :p.
* <hr>
* IMPORTANT NOTE: DO NOT MIX UP WITH OFFLINE MODE!<br>
* @return
*/
public static boolean uniqueIdOffline() {
return uniqueIdOffline;
}
public static void setUniqueIdOffline(boolean uniqueIdOffline) {
BukkitVersion.uniqueIdOffline = uniqueIdOffline;
}
/**
* Delegating to ServerVersion.getMinecraftVersion.
* @return
*/
public static String getMinecraftVersion() {
return ServerVersion.getMinecraftVersion();
}
}

View File

@ -0,0 +1,233 @@
package fr.neatmonster.nocheatplus.compat.versions;
/**
* Static utility that stores the Minecraft version, providing some parsing methods.
* This has to be initialized from an external source (e.g. a plugin calling BukkitVersion.init).
* <hr/>
* Taken from the TrustCore plugin.
* @author mc_dev
*
*/
public class ServerVersion {
public static final String UNKNOWN_VERSION = "unknown";
private static String minecraftVersion = UNKNOWN_VERSION;
private static final String[][] versionTagsMatch = {
{"1.7.2-r", "1.7.2"}, // Example. Probably this method will just be removed.
};
/**
* Attempt to return the Minecraft version for a given server version string.
* @param serverVersion As returned by Bukkit.getServer().getVersion();
* @return null if not known/parsable.
*/
public static String parseMinecraftVersion(String... versionCandidates) {
for (String serverVersion : versionCandidates) {
for (String minecraftVersion : new String[]{
collectVersion(serverVersion, 0),
parseMinecraftVersionGeneric(serverVersion),
parseMinecraftVersionTokens(serverVersion)
}) {
// Validate if not null, might mean double validation.
if (minecraftVersion != null && validateMinecraftVersion(minecraftVersion)) {
return minecraftVersion;
}
}
}
return null;
}
/**
* Simple consistency check.
* @param minecraftVersion
* @return
*/
private static boolean validateMinecraftVersion(String minecraftVersion) {
return collectVersion(minecraftVersion, 0) != null;
}
/**
* Match directly versus hard coded examples. Not for direct use.
* @param serverVersion
* @return
*/
private static String parseMinecraftVersionTokens(String serverVersion) {
serverVersion = serverVersion.trim().toLowerCase();
for (String[] entry : versionTagsMatch) {
if (serverVersion.contains(entry[0])) {
return entry[1];
}
}
return null;
}
/**
* Collect a version of the type X.Y.Z with X, Y, Z being numbers. Demands at least one number,
* but allows an arbitrary amount of sections X....Y. Rigid character check, probably not fit for snapshots.
* @param input
* @param beginIndex
* @return null if not successful.
*/
private static String collectVersion(String input, int beginIndex) {
StringBuilder buffer = new StringBuilder(128);
// Rigid scan by character.
boolean numberFound = false;
char[] chars = input.toCharArray();
for (int i = beginIndex; i < input.length(); i++) {
char c = chars[i];
if (c == '.') {
if (numberFound) {
// Reset, expecting a number to follow.
numberFound = false;
} else {
// Failure.
return null;
}
} else if (!Character.isDigit(c)) {
// Failure.
return null;
} else {
numberFound = true;
}
buffer.append(c);
}
if (numberFound) {
return buffer.toString();
} else {
return null;
}
}
/**
*
* @param serverVersion
* @return
*/
private static String parseMinecraftVersionGeneric(String serverVersion) {
String lcServerVersion = serverVersion.trim().toLowerCase();
for (String candidate : new String[] {
// git-bukkit-MC_VERSION-rX.Y
parseVersionDelimiters(lcServerVersion, "git-bukkit-", "-r"),
parseVersionDelimiters(lcServerVersion, "", "-r"),
// TODO: Other server mods + custom builds !?.
}) {
if (candidate != null) {
return candidate;
}
}
return null;
}
/**
* Exact case, no trim.
* @param input
* @param prefix
* @param suffix
* @return
*/
private static String parseVersionDelimiters(String input, String prefix, String suffix) {
int preIndex = prefix.isEmpty() ? 0 : input.indexOf(prefix);
if (preIndex != -1) {
String candidate = input.substring(preIndex + prefix.length());
int postIndex = suffix.isEmpty() ? candidate.length() : candidate.indexOf(suffix);
if (postIndex != -1) {
return collectVersion(candidate.substring(0, postIndex), 0);
}
}
return null;
}
/**
* Sets lower-case Minecraft version - or UNKNOWN_VERSION, if null or empty.
* @param version Can be null (resulting in UNKNOWN_VERSION).
* @return The String that minecraftVersion has been set to.
*/
public static String setMinecraftVersion(String version) {
if (version == null) {
minecraftVersion = UNKNOWN_VERSION;
} else {
version = version.trim().toLowerCase();
if (version.isEmpty()) {
minecraftVersion = UNKNOWN_VERSION;
} else {
minecraftVersion = version;
}
}
return minecraftVersion;
}
/**
*
* @return Some version string, or UNKNOWN_VERSION.
*/
public static String getMinecraftVersion() {
return minecraftVersion;
}
/**
* Simple x.y.z versions. Returns 0 on equality, -1 if version 1 is smaller than version 2, 1 if version 1 is greater than version 2.
* @param version1 Can be unknown.
* @param version2 Must not be unknown.
* @return
*/
public static int compareVersions(String version1, String version2) {
if (version2.equals(UNKNOWN_VERSION)) {
throw new IllegalArgumentException("version2 must not be 'unknown'.");
} else if (version1.equals(UNKNOWN_VERSION)) {
// Assume smaller than any given version.
return -1;
}
if (version1.equals(version2)) {
return 0;
}
try {
int[] v1Int = versionToInt(version1);
int[] v2Int = versionToInt(version2);
for (int i = 0; i < Math.min(v1Int.length, v2Int.length); i++) {
if (v1Int[i] < v2Int[i]) {
return -1;
} else if (v1Int[i] > v2Int[i]) {
return 1;
}
}
// Support sub-sub-sub-...-....-marines.
if (v1Int.length < v2Int.length) {
return -1;
}
else if (v1Int.length > v2Int.length) {
return 1;
}
} catch (NumberFormatException e) {}
// Equality was tested above, so it would seem.
throw new IllegalArgumentException("Bad version input.");
}
public static int[] versionToInt(String version) {
String[] split = version.split("\\.");
int[] num = new int[split.length];
for (int i = 0; i < split.length; i++) {
num[i] = Integer.parseInt(split[i]);
}
return num;
}
public static <V> V select(final String cmpVersion, final V valueLT, final V valueEQ, final V valueGT, final V valueUnknown) {
final String mcVersion = ServerVersion.getMinecraftVersion();
if (mcVersion == ServerVersion.UNKNOWN_VERSION) {
return valueUnknown;
} else {
final int cmp = ServerVersion.compareVersions(mcVersion, cmpVersion);
if (cmp == 0) {
return valueEQ;
} else if (cmp < 0) {
return valueLT;
} else {
return valueGT;
}
}
}
}

View File

@ -12,11 +12,12 @@ import org.bukkit.Material;
*/
public class DefaultConfig extends ConfigFile {
/**
* NCP build needed for this config.
* (Should only increment with changing or removing paths.)
/**
* NCP build number, for which an existing entry has been changed. (Should
* only increment, if the user is advised to change to that value instead of
* the former default one.)
*/
public static final int buildNumber = 754;
public static final int buildNumber = 785;
// TODO: auto input full version or null to an extra variable or several [fail safe for other syntax checking]?
@ -262,7 +263,7 @@ public class DefaultConfig extends ConfigFile {
// FIGHT
set(ConfPaths.FIGHT_CANCELDEAD, true);
set(ConfPaths.FIGHT_TOOLCHANGEPENALTY, 500L);
set(ConfPaths.FIGHT_PVP_KNOCKBACKVELOCITY, true);
set(ConfPaths.FIGHT_PVP_KNOCKBACKVELOCITY, "default");
set(ConfPaths.FIGHT_YAWRATE_CHECK, true);
@ -413,7 +414,7 @@ public class DefaultConfig extends ConfigFile {
// Vehicles.
set(ConfPaths.MOVING_VEHICLES_PREVENTDESTROYOWN, true);
set(ConfPaths.MOVING_VEHICLES_ENFORCELOCATION, true);
set(ConfPaths.MOVING_VEHICLES_ENFORCELOCATION, "default");
// Velocity.
set(ConfPaths.MOVING_VELOCITY_GRACETICKS, 20);
@ -427,7 +428,7 @@ public class DefaultConfig extends ConfigFile {
set(ConfPaths.MOVING_SPRINTINGGRACE, 2.0);
set(ConfPaths.MOVING_ASSUMESPRINT, true);
set(ConfPaths.MOVING_SPEEDGRACE, 4.0);
set(ConfPaths.MOVING_ENFORCELOCATION, true);
set(ConfPaths.MOVING_ENFORCELOCATION, "default");
// NET

View File

@ -8,14 +8,16 @@ import org.bukkit.Material;
import org.bukkit.configuration.file.YamlConfiguration;
import org.yaml.snakeyaml.DumperOptions;
import fr.neatmonster.nocheatplus.compat.AlmostBoolean;
import fr.neatmonster.nocheatplus.compat.versions.ServerVersion;
import fr.neatmonster.nocheatplus.logging.StaticLog;
public class RawConfigFile extends YamlConfiguration{
private static String prepareMatchMaterial(String content) {
return content.replace(' ', '_').replace('-', '_').replace('.', '_');
}
private static String prepareMatchMaterial(String content) {
return content.replace(' ', '_').replace('-', '_').replace('.', '_');
}
/**
* Attempt to get an int id from a string.<br>
* Will return out of range numbers, attempts to parse materials.
@ -23,7 +25,7 @@ public class RawConfigFile extends YamlConfiguration{
* @return
*/
@SuppressWarnings("deprecation")
public static Integer parseTypeId(String content) {
public static Integer parseTypeId(String content) {
content = content.trim().toUpperCase();
try{
return Integer.parseInt(content);
@ -32,13 +34,13 @@ public class RawConfigFile extends YamlConfiguration{
try{
Material mat = Material.matchMaterial(prepareMatchMaterial(content));
if (mat != null) {
return mat.getId();
return mat.getId();
}
}
catch (Exception e) {}
return null;
}
/**
* Attempt to get a Material from a string.<br>
* Will attempt to match the name but also type ids.
@ -46,11 +48,11 @@ public class RawConfigFile extends YamlConfiguration{
* @return
*/
@SuppressWarnings("deprecation")
public static Material parseMaterial(String content) {
public static Material parseMaterial(String content) {
content = content.trim().toUpperCase();
try{
Integer id = Integer.parseInt(content);
return Material.getMaterial(id);
return Material.getMaterial(id);
}
catch(NumberFormatException e){}
try{
@ -59,11 +61,24 @@ public class RawConfigFile extends YamlConfiguration{
catch (Exception e) {}
return null;
}
////////////////
// Not static.
////////////////
/**
* Set a value depending on the detected Minecraft version.
* @param path
* @param cmpVersion The version to compare to (N.N.N).
* @param valueLT Value to use if the detected version is smaller/less than the given one.
* @param valueEQ Value to use if the detected version is equal to the given one.
* @param valueGT Value to use if the detected version is greater than the detected one.
* @param valueUnknown Value to use if the version could not be detected (e.g. unknown format).
*/
public void setVersionDependent(final String path, final String cmpVersion, final Object valueLT, final Object valueEQ, final Object valueGT, final Object valueUnknown) {
set(path, ServerVersion.select(cmpVersion, valueLT, valueEQ, valueGT, valueUnknown));
}
/**
* Return a double value within given bounds, with preset.
*
@ -75,12 +90,12 @@ public class RawConfigFile extends YamlConfiguration{
* @return
*/
public double getDouble(final String path, final double min, final double max, final double preset){
final double value = getDouble(path, preset);
if (value < min) return min;
else if (value > max) return max;
final double value = getDouble(path, preset);
if (value < min) return min;
else if (value > max) return max;
else return value;
}
/**
* Return a long value within given bounds, with preset.
*
@ -92,12 +107,12 @@ public class RawConfigFile extends YamlConfiguration{
* @return
*/
public long getLong(final String path, final long min, final long max, final long preset){
final long value = getLong(path, preset);
if (value < min) return min;
else if (value > max) return max;
final long value = getLong(path, preset);
if (value < min) return min;
else if (value > max) return max;
else return value;
}
/**
* Return an int value within given bounds, with preset.
*
@ -109,12 +124,12 @@ public class RawConfigFile extends YamlConfiguration{
* @return
*/
public long getInt(final String path, final int min, final int max, final int preset){
final int value = getInt(path, preset);
if (value < min) return min;
else if (value > max) return max;
final int value = getInt(path, preset);
if (value < min) return min;
else if (value > max) return max;
else return value;
}
/**
* Attempt to get a type id from the path somehow, return null if nothing found.<br>
* Will attempt to interpret strings, will return negative or out of range values.
@ -126,7 +141,7 @@ public class RawConfigFile extends YamlConfiguration{
public Integer getTypeId(final String path){
return getTypeId(path, null);
}
/**
* Attempt to get a type id from the path somehow, return preset if nothing found.<br>
* Will attempt to interpret strings, will return negative or out of range values.
@ -145,52 +160,57 @@ public class RawConfigFile extends YamlConfiguration{
int id = getInt(path, Integer.MAX_VALUE);
return id == Integer.MAX_VALUE ? preset : id;
}
/**
* Outputs warnings to console.
* @param path
* @param target Collection to fill ids into.
*/
public void readMaterialIdsFromList(final String path, final Collection<Integer> target) {
final List<String> content = getStringList(path);
if (content == null || content.isEmpty()) return;
for (final String entry : content){
final Integer id = parseTypeId(entry);
if (id == null){
StaticLog.logWarning("[NoCheatPlus] Bad material entry (" + path +"): " + entry);
}
else{
target.add(id);
}
}
}
/**
public void readMaterialIdsFromList(final String path, final Collection<Integer> target) {
final List<String> content = getStringList(path);
if (content == null || content.isEmpty()) return;
for (final String entry : content){
final Integer id = parseTypeId(entry);
if (id == null){
StaticLog.logWarning("[NoCheatPlus] Bad material entry (" + path +"): " + entry);
}
else{
target.add(id);
}
}
}
public AlmostBoolean getAlmostBoolean(final String path, final AlmostBoolean defaultValue) {
final AlmostBoolean choice = AlmostBoolean.match(getString(path, null));
return choice == null ? defaultValue : choice;
}
/**
* Outputs warnings to console.
* @param path
* @param target Collection to fill materials into.
*/
public void readMaterialFromList(final String path, final Collection<Material> target) {
final List<String> content = getStringList(path);
if (content == null || content.isEmpty()) return;
for (final String entry : content){
final Material mat = parseMaterial(entry);
if (mat == null){
StaticLog.logWarning("[NoCheatPlus] Bad material entry (" + path +"): " + entry);
}
else{
target.add(mat);
}
}
}
public void readMaterialFromList(final String path, final Collection<Material> target) {
final List<String> content = getStringList(path);
if (content == null || content.isEmpty()) return;
for (final String entry : content){
final Material mat = parseMaterial(entry);
if (mat == null){
StaticLog.logWarning("[NoCheatPlus] Bad material entry (" + path +"): " + entry);
}
else{
target.add(mat);
}
}
}
/* (non-Javadoc)
* @see org.bukkit.configuration.file.YamlConfiguration#saveToString()
*/
@Override
public String saveToString() {
// Some reflection wizardly to avoid having a lot of linebreaks in the yaml file, and get a "footer" into the file.
// TODO: Interesting, but review this: still necessary/useful in CB-1.4 ?.
// TODO: Interesting, but review this: still necessary/useful in CB-1.4 ?.
try {
Field op;
op = YamlConfiguration.class.getDeclaredField("yamlOptions");

View File

@ -0,0 +1,41 @@
package fr.neatmonster.nocheatplus;
import static org.junit.Assert.fail;
import org.junit.Test;
import fr.neatmonster.nocheatplus.utilities.IdUtil;
public class TestIdUtil {
@Test
public void testMinecraftUserNames() {
String[] valid = new String[] {
"xdxdxd",
"XDXDXD",
"sa_Sd_ASD"
};
for (String name : valid) {
if (!IdUtil.isValidMinecraftUserName(name)) {
fail("Expect user name to be valid: " + name);
}
}
String[] inValid = new String[] {
"xd xd xd",
"",
"x",
"0123456789abcdefX",
"*§$FUJAL"
};
for (String name : inValid) {
if (IdUtil.isValidMinecraftUserName(name)) {
fail("Expect user name to be invalid: " + name);
}
}
}
}

View File

@ -0,0 +1,30 @@
package fr.neatmonster.nocheatplus;
import static org.junit.Assert.fail;
import org.junit.Test;
import fr.neatmonster.nocheatplus.compat.versions.ServerVersion;
public class TestMinecraftVersion {
@Test
public void testParseMinecraftVersion() {
for (String[] pair : new String[][] {
{null, ""},
{"1.7.5", "1.7.5"},
{"1.7.5", "1.7.5-R0.1-SNAPSHOT"},
{"1.7.2", "git-Bukkit-1.7.2-R0.3-14-g8f8716c-b3042jnks"},
}) {
String parsed = ServerVersion.parseMinecraftVersion(pair[1]);
if (pair[0] == null) {
if (parsed != null) {
fail("Expect null output on: " + pair[1] + ", got instead: " + parsed);
}
} else if (!pair[0].equals(parsed)) {
fail("Expect " + pair[0] + " for input: " + pair[1] + ", got instead: " + parsed);
}
}
}
}

View File

@ -45,10 +45,13 @@ import fr.neatmonster.nocheatplus.checks.inventory.InventoryListener;
import fr.neatmonster.nocheatplus.checks.moving.MovingListener;
import fr.neatmonster.nocheatplus.clients.ModUtil;
import fr.neatmonster.nocheatplus.command.NoCheatPlusCommand;
import fr.neatmonster.nocheatplus.command.admin.VersionCommand;
import fr.neatmonster.nocheatplus.compat.BridgeMisc;
import fr.neatmonster.nocheatplus.compat.DefaultComponentFactory;
import fr.neatmonster.nocheatplus.compat.MCAccess;
import fr.neatmonster.nocheatplus.compat.MCAccessFactory;
import fr.neatmonster.nocheatplus.compat.versions.BukkitVersion;
import fr.neatmonster.nocheatplus.compat.versions.ServerVersion;
import fr.neatmonster.nocheatplus.components.ComponentRegistry;
import fr.neatmonster.nocheatplus.components.ComponentWithName;
import fr.neatmonster.nocheatplus.components.ConsistencyChecker;
@ -86,6 +89,7 @@ import fr.neatmonster.nocheatplus.utilities.BlockProperties;
import fr.neatmonster.nocheatplus.utilities.ColorUtil;
import fr.neatmonster.nocheatplus.utilities.OnDemandTickListener;
import fr.neatmonster.nocheatplus.utilities.ReflectionUtil;
import fr.neatmonster.nocheatplus.utilities.StringUtil;
import fr.neatmonster.nocheatplus.utilities.TickTask;
/**
@ -721,12 +725,14 @@ public class NoCheatPlus extends JavaPlugin implements NoCheatPlusAPI {
public void onLoad() {
Bukkit.getLogger().info("[NoCheatPlus] Setting up static API, config, logging.");
NCPAPIProvider.setNoCheatPlusAPI(this);
BukkitVersion.init();
// Read the configuration files.
ConfigManager.init(this); // TODO: Only load the bootstrap config (not all).
logManager = new BukkitLogManager(this);
StaticLog.setStreamID(Streams.INIT);
StaticLog.setUseLogManager(true);
logManager.info(Streams.INIT, "[NoCheatPlus] Logging system initialized.");
logManager.info(Streams.INIT, "[NoCheatPlus] Detected Minecraft version: " + ServerVersion.getMinecraftVersion());
}
/* (non-Javadoc)
@ -932,6 +938,7 @@ public class NoCheatPlus extends JavaPlugin implements NoCheatPlusAPI {
}
// TODO: if (online.lenght > 0) LogUtils.logInfo("[NCP] Updated " + online.length + "players (post-enable).")
logManager.info(Streams.INIT, "[NoCheatPlus] Post-enable finished.");
logManager.info(Streams.DEFAULT_FILE, StringUtil.join(VersionCommand.getVersionInfo(), "\n")); // Queued (!).
}
/**

View File

@ -8,6 +8,8 @@ import java.util.List;
import fr.neatmonster.nocheatplus.NCPAPIProvider;
import fr.neatmonster.nocheatplus.NoCheatPlus;
import fr.neatmonster.nocheatplus.checks.inventory.FastConsume;
import fr.neatmonster.nocheatplus.config.ConfPaths;
import fr.neatmonster.nocheatplus.config.ConfigManager;
import fr.neatmonster.nocheatplus.logging.StaticLog;
import fr.neatmonster.nocheatplus.net.protocollib.ProtocolLibComponent;
@ -17,34 +19,36 @@ import fr.neatmonster.nocheatplus.net.protocollib.ProtocolLibComponent;
* @author mc_dev
*/
public class DefaultComponentFactory {
/**
* This will be called from within the plugin in onEnable, after registration of all core listeners and components. After each components addition processQueuedSubComponentHolders() will be called to allow registries for further optional components.
* @param plugin
* @return
*/
public Collection<Object> getAvailableComponentsOnEnable(NoCheatPlus plugin){
final List<Object> available = new LinkedList<Object>();
// Add components (try-catch).
// Check: inventory.fastconsume.
try{
// TODO: Static test methods !?
FastConsume.testAvailability();
available.add(new FastConsume());
NCPAPIProvider.getNoCheatPlusAPI().addFeatureTags("checks", Arrays.asList(FastConsume.class.getSimpleName()));
}
catch (Throwable t){
StaticLog.logInfo("[NoCheatPlus] Inventory checks: FastConsume is not available.");
}
// ProtocolLib dependencies.
try {
available.add(new ProtocolLibComponent(plugin));
} catch (Throwable t){
StaticLog.logInfo("[NoCheatPlus] Packet level access: ProtocolLib is not available.");
}
return available;
}
/**
* This will be called from within the plugin in onEnable, after registration of all core listeners and components. After each components addition processQueuedSubComponentHolders() will be called to allow registries for further optional components.
* @param plugin
* @return
*/
public Collection<Object> getAvailableComponentsOnEnable(NoCheatPlus plugin){
final List<Object> available = new LinkedList<Object>();
// Add components (try-catch).
// Check: inventory.fastconsume.
try{
// TODO: Static test methods !?
FastConsume.testAvailability();
available.add(new FastConsume());
if (ConfigManager.isTrueForAnyConfig(ConfPaths.INVENTORY_FASTCONSUME_CHECK)) {
NCPAPIProvider.getNoCheatPlusAPI().addFeatureTags("checks", Arrays.asList(FastConsume.class.getSimpleName()));
}
}
catch (Throwable t){
StaticLog.logInfo("[NoCheatPlus] Inventory checks: FastConsume is not available.");
}
// ProtocolLib dependencies.
try {
available.add(new ProtocolLibComponent(plugin));
} catch (Throwable t){
StaticLog.logInfo("[NoCheatPlus] Packet level access: ProtocolLib is not available.");
}
return available;
}
}