Use a class for version checking with ProtocolLib.

This commit is contained in:
asofold 2016-06-14 00:12:28 +02:00
parent db945a7559
commit 8aced9d116
4 changed files with 492 additions and 47 deletions

View File

@ -0,0 +1,387 @@
package fr.neatmonster.nocheatplus.compat.versions;
import java.util.LinkedList;
import java.util.List;
import org.bukkit.Bukkit;
import org.bukkit.plugin.Plugin;
import fr.neatmonster.nocheatplus.components.registry.IActivation;
import fr.neatmonster.nocheatplus.components.registry.IDescriptiveActivation;
import fr.neatmonster.nocheatplus.utilities.ReflectionUtil;
/**
* Convenience class for activation conditions for features, meant for
* convenient setup with chaining. Adding no conditions means that the feature
* will always activate. If one sub-condition is not met, the feature will not
* activate. The implementation is not very efficient, naturally it is meant for
* registry setup. Conditions are checked at the time of calling isAvailble, not
* at the time of object creation.
*
* @author asofold
*
*/
public class Activation implements IDescriptiveActivation {
/**
* This attempt to transform/parse the plugin version such that the result
* can be used for comparison with a server version. The plugin is fetched
* for convenience.
*
* @param pluginName
* @return null in case of not being able to parse properly.
*/
public static String guessUsablePluginVersion(String pluginName) {
final Plugin plugin = Bukkit.getServer().getPluginManager().getPlugin(pluginName);
final String version = plugin.getDescription().getVersion().toLowerCase();
String pV = GenericVersion.collectVersion(version, 0);
if (pV == null) {
pV = GenericVersion.parseVersionDelimiters(version, "", "-snapshot");
}
if (pV == null) {
pV = GenericVersion.parseVersionDelimiters(version, "", "-b");
}
if (pV == null) {
int i = 0;
while (i < version.length() && !Character.isDigit(version.charAt(i))) {
i++;
}
if (i < version.length()) {
pV = GenericVersion.collectVersion(version, i);
if (pV == null) {
// TODO: Consider find something in the end as delimiter.
}
}
}
return pV;
}
private final List<IActivation> conditions = new LinkedList<IActivation>();
private String neutralDescription = null;
@Override
public boolean isAvailable() {
for (IActivation condition : conditions) {
if (!condition.isAvailable()) {
return false;
}
}
return true;
}
public Activation neutralDescription(String neutralDescription) {
// TODO: Another interface to combine description with activation (e.g. IFeature)?
this.neutralDescription = neutralDescription;
return this;
}
@Override
public String getNeutralDescription() {
return neutralDescription;
}
/**
* Demand the plugin to exist (needn't be enabled).
*
* @param pluginName
* @return This Activation instance.
*/
public Activation pluginExist(final String pluginName) {
conditions.add(new IActivation() {
@Override
public boolean isAvailable() {
return Bukkit.getServer().getPluginManager().getPlugin(pluginName) != null;
}
});
return this;
}
/**
* Demand the plugin to exist and to be enabled.
*
* @param pluginName
* @return This Activation instance.
*/
public Activation pluginEnabled(final String pluginName) {
conditions.add(new IActivation() {
@Override
public boolean isAvailable() {
return Bukkit.getServer().getPluginManager().isPluginEnabled(pluginName);
}
});
return this;
}
/**
* Demand the plugin to exist (needn't be enabled) and the plugin version to
* be greater than the given one (or equal, depending on allowEQ).
*
* @param pluginName
* @param version
* The plugin is demanded to have a greater version than this.
* @param allowEQ
* If to allow the versions to be equal.
* @return This Activation instance.
*/
public Activation pluginVersionGT(final String pluginName, final String version, final boolean allowEQ) {
conditions.add(new IActivation() {
@Override
public boolean isAvailable() {
String pluginVersion = guessUsablePluginVersion(pluginName);
if (pluginVersion == null) {
return false;
}
else {
int cmp = GenericVersion.compareVersions(pluginVersion, version);
return cmp == 1 || allowEQ && cmp == 0;
}
}
});
return this;
}
/**
* Demand the plugin to exist (needn't be enabled) and the plugin version to
* be lesser than the given one (or equal, depending on allowEQ).
*
* @param pluginName
* @param version
* The plugin is demanded to have a lesser version than this.
* @param allowEQ
* If to allow the versions to be equal.
* @return This Activation instance.
*/
public Activation pluginVersionLT(final String pluginName, final String version, final boolean allowEQ) {
conditions.add(new IActivation() {
@Override
public boolean isAvailable() {
String pluginVersion = guessUsablePluginVersion(pluginName);
if (pluginVersion == null) {
return false;
}
else {
int cmp = GenericVersion.compareVersions(pluginVersion, version);
return cmp == -1 || allowEQ && cmp == 0;
}
}
});
return this;
}
/**
* Demand the plugin to exist (needn't be enabled) and the plugin version to
* be equal to the given one.
*
* @param pluginName
* @param version
* The plugin is demanded to have a lesser version than this.
*
* @return This Activation instance.
*/
public Activation pluginVersionEQ(final String pluginName, final String version) {
conditions.add(new IActivation() {
@Override
public boolean isAvailable() {
String pluginVersion = guessUsablePluginVersion(pluginName);
if (pluginVersion == null) {
return false;
}
else {
return GenericVersion.compareVersions(pluginVersion, version) == 0;
}
}
});
return this;
}
/**
* Demand the plugin to exist (needn't be enabled) and the plugin version to
* be between the given ones (with allowing equality as given with flags).
*
* @param pluginName
* @param versionLow
* Plugin version must be greater than this. Equality is accepted
* iff allowEQlow is set.
* @param allowEQlow
* @param versionHigh
* Plugin version must be lesser than this. Equality is accepted
* iff allowEQlhigh is set.
* @param allowEQhigh
* @return
*/
public Activation pluginVersionBetween(final String pluginName,
final String versionLow, final boolean allowEQlow,
final String versionHigh, final boolean allowEQhigh) {
conditions.add(new IActivation() {
@Override
public boolean isAvailable() {
String pluginVersion = guessUsablePluginVersion(pluginName);
if (pluginVersion == null) {
return false;
}
else {
return GenericVersion.isVersionBetween(pluginVersion,
versionLow, allowEQlow, versionHigh, allowEQhigh);
}
}
});
return this;
}
/**
* Demand the Minecraft version to be greater than the given one (or equal, if
* allowEQ is set).
*
* @param version
* @param allowEQ
* @return
*/
public Activation minecraftVersionGT(final String version, final boolean allowEQ) {
conditions.add(new IActivation() {
@Override
public boolean isAvailable() {
int cmp = ServerVersion.compareMinecraftVersion(version);
return cmp == 1 || allowEQ && cmp == 0;
}
});
return this;
}
/**
* Demand the server version to be lesser than the given one (or equal, if
* allowEQ is set).
*
* @param version
* @param allowEQ
* @return
*/
public Activation minecraftVersionLT(final String version, final boolean allowEQ) {
conditions.add(new IActivation() {
@Override
public boolean isAvailable() {
int cmp = ServerVersion.compareMinecraftVersion(version);
return cmp == -1 || allowEQ && cmp == 0;
}
});
return this;
}
/**
* Demand the server version to be equal to the given one.
*
* @param version
* @return
*/
public Activation minecraftVersionEQ(final String version) {
conditions.add(new IActivation() {
@Override
public boolean isAvailable() {
return ServerVersion.compareMinecraftVersion(version) == 0;
}
});
return this;
}
/**
* Demand the Minecraft version to be between the given ones (with allowing
* equality as given with flags).
*
* @param pluginName
* @param versionLow
* Minecraft version must be greater than this. Equality is
* accepted iff allowEQlow is set.
* @param allowEQlow
* @param versionHigh
* Minecraft version must be lesser than this. Equality is
* accepted iff allowEQlhigh is set.
* @param allowEQhigh
* @return
*/
public Activation minecraftVersionBetween(
final String versionLow, final boolean allowEQlow,
final String versionHigh, final boolean allowEQhigh) {
conditions.add(new IActivation() {
@Override
public boolean isAvailable() {
return ServerVersion.isMinecraftVersionBetween(
versionLow, allowEQlow, versionHigh, allowEQhigh);
}
});
return this;
}
/**
* Case sensitive contains for the server version string (specific to the
* mod).
*
* @param content
* @return
*/
public Activation serverVersionContains(final String content) {
conditions.add(new IActivation() {
@Override
public boolean isAvailable() {
return Bukkit.getServer().getVersion().contains(content);
}
});
return this;
}
/**
* Case insensitive contains for the server version string (specific to the
* mod).
*
* @param content
* @return
*/
public Activation serverVersionContainsIgnoreCase(final String content) {
conditions.add(new IActivation() {
@Override
public boolean isAvailable() {
return Bukkit.getServer().getVersion().toLowerCase().contains(content.toLowerCase());
}
});
return this;
}
/**
* Demand the class to exist.
*
* @param className
* @return This Activation instance.
*/
public Activation classExist(final String className) {
conditions.add(new IActivation() {
@Override
public boolean isAvailable() {
return ReflectionUtil.getClass(className) != null;
}
});
return this;
}
/**
* Demand the class not to exist.
*
* @param className
* @return This Activation instance.
*/
public Activation classNotExist(final String className) {
conditions.add(new IActivation() {
@Override
public boolean isAvailable() {
return ReflectionUtil.getClass(className) == null;
}
});
return this;
}
// TODO: set operation ( AND / OR ( / ...) ) + addCondition(IActivation).
// TODO: server version not contains (+ignore case).
// TODO: Might use testing methods for parts: meetsServerVersionRequirements(), more complicated...
// (Well... specific member types, methods, :p...)
}

View File

@ -0,0 +1,17 @@
package fr.neatmonster.nocheatplus.components.registry;
/**
* The consumer side of testing for feature activation.
*
* @author asofold
*
*/
public interface IActivation {
/**
* Test if the feature is available.
* @return
*/
public boolean isAvailable();
}

View File

@ -0,0 +1,22 @@
package fr.neatmonster.nocheatplus.components.registry;
/**
* An activation checker with description (can be checked for activation
* conditions and has some kind of description for logging). Possibly just a
* delegate thing.
*
* @author asofold
*
*/
public interface IDescriptiveActivation extends IActivation {
/**
* Retrieve a neutral (short) description fit for logging under which
* conditions this feature can be used, regardless of what isAvailable may
* have returned.
*
* @return
*/
public String getNeutralDescription();
}

View File

@ -14,26 +14,26 @@
*/
package fr.neatmonster.nocheatplus.compat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import org.bukkit.Bukkit;
import org.bukkit.plugin.Plugin;
import fr.neatmonster.nocheatplus.NCPAPIProvider;
import fr.neatmonster.nocheatplus.NoCheatPlus;
import fr.neatmonster.nocheatplus.checks.inventory.FastConsume;
import fr.neatmonster.nocheatplus.checks.inventory.Gutenberg;
import fr.neatmonster.nocheatplus.checks.inventory.HotFixFallingBlockPortalEnter;
import fr.neatmonster.nocheatplus.checks.net.protocollib.ProtocolLibComponent;
import fr.neatmonster.nocheatplus.compat.versions.GenericVersion;
import fr.neatmonster.nocheatplus.compat.versions.ServerVersion;
import fr.neatmonster.nocheatplus.compat.versions.Activation;
import fr.neatmonster.nocheatplus.components.registry.IActivation;
import fr.neatmonster.nocheatplus.components.registry.IDescriptiveActivation;
import fr.neatmonster.nocheatplus.config.ConfPaths;
import fr.neatmonster.nocheatplus.config.ConfigManager;
import fr.neatmonster.nocheatplus.logging.StaticLog;
import fr.neatmonster.nocheatplus.logging.Streams;
import fr.neatmonster.nocheatplus.utilities.StringUtil;
/**
* Default factory for add-in components which might only be available under certain circumstances.
@ -42,6 +42,46 @@ import fr.neatmonster.nocheatplus.logging.Streams;
*/
public class DefaultComponentFactory {
private final IActivation protocolLibPresent = new Activation().pluginExist("ProtocolLib");
private final List<IDescriptiveActivation> protocolLibActivation = new ArrayList<IDescriptiveActivation>();
public DefaultComponentFactory() {
for (Activation condition : Arrays.asList(
new Activation()
.neutralDescription("ProtocolLib 4.0.2 for Minecraft 1.10")
.pluginVersionEQ("ProtocolLib", "4.0.2")
.minecraftVersionBetween("1.10", true, "1.11", false)
,
new Activation()
.neutralDescription("ProtocolLib 4.0.1 or 4.0.0 for Minecraft 1.9.x")
.pluginVersionBetween("ProtocolLib", "4.0.0", true, "4.0.1", true)
.minecraftVersionBetween("1.9", true, "1.10", false)
,
new Activation()
.neutralDescription("ProtocolLib 3.7.0 or 3.7 for Minecraft 1.9.x")
.pluginVersionBetween("ProtocolLib", "3.7", true, "3.7.0", true) // Might want other types...
.minecraftVersionBetween("1.9", true, "1.10", false)
,
new Activation()
.neutralDescription("ProtocolLib 3.6.5 or 3.6.4 for Minecraft 1.8.x")
.pluginVersionBetween("ProtocolLib", "3.6.4", true, "3.6.5", true)
.minecraftVersionBetween("1.8", true, "1.9", false)
,
new Activation()
.neutralDescription("ProtocolLib 3.6.6 for PaperSpigot 1.8.x")
.pluginVersionEQ("ProtocolLib", "3.6.6")
.serverVersionContainsIgnoreCase("paperspigot")
.minecraftVersionBetween("1.8", true, "1.9", false)
,
new Activation()
.neutralDescription("ProtocolLib 3.6.4 before Minecraft 1.9")
.pluginVersionEQ("ProtocolLib", "3.6.4")
.minecraftVersionBetween("1.2.5", true, "1.9", false)
)) {
protocolLibActivation.add(condition);
};
}
/**
* 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
@ -81,58 +121,37 @@ public class DefaultComponentFactory {
}
catch (RuntimeException e) {}
// Version dependent activation of components.
final String vServerLc = Bukkit.getServer().getVersion().toLowerCase();
// TODO: Consider using a class for confining plugin vs. server versions.
// ProtocolLib dependencies.
Plugin pluginProtocolLib = Bukkit.getPluginManager().getPlugin("ProtocolLib");
boolean protocolLibAvailable = false;
if (pluginProtocolLib != null) {
String _pV = pluginProtocolLib.getDescription().getVersion().toLowerCase();
String pV = GenericVersion.collectVersion(_pV, 0);
if (pV == null) {
pV = GenericVersion.parseVersionDelimiters(_pV, "", "-snapshot");
if (protocolLibPresent.isAvailable()) {
// Check conditions.
boolean protocolLibAvailable = false;
for (final IActivation condition : protocolLibActivation) {
if (condition.isAvailable()) {
protocolLibAvailable = true;
break;
}
}
if (pV == null) {
pV = GenericVersion.parseVersionDelimiters(_pV, "", "-b");
}
if (pV == null) {
// TODO: Was another (specific) attempt parsing planned here !?
StaticLog.logWarning("Could not interpret the version of ProtocolLib, won't activate hooks.");
}
else {
// Attempt to react.
if (protocolLibAvailable) {
try {
boolean vP3_6_4 = GenericVersion.compareVersions("3.6.4", pV) == 0;
boolean vP3_6_5 = GenericVersion.compareVersions("3.6.5", pV) == 0;
boolean vP3_6_6 = GenericVersion.compareVersions("3.6.6", pV) == 0;
boolean vP3_7_0 = GenericVersion.isVersionBetween(pV, "3.7", true, "3.7.0", true);
boolean vP4_0_0 = GenericVersion.compareVersions("4.0.0", pV) <= 0; // 4.0.0 or later until next MC version is out.
boolean vP4_0_2 = GenericVersion.compareVersions("4.0.2", pV) <= 0; // 4.0.2 or later until next MC version is out.
if (
ServerVersion.isMinecraftVersionBetween("1.10", true, "1.11", false) && vP4_0_2
|| ServerVersion.isMinecraftVersionBetween("1.9", true, "1.10", false) && vP4_0_0
|| ServerVersion.isMinecraftVersionBetween("1.9", true, "1.10", false) && vP3_7_0
|| ServerVersion.isMinecraftVersionBetween("1.8", true, "1.9", false) && (vP3_6_4 || vP3_6_5)
|| ServerVersion.isMinecraftVersionBetween("1.8", true, "1.9", false) && vP3_6_6
&& vServerLc.indexOf("paperspigot") != -1
|| ServerVersion.isMinecraftVersionBetween("1.2.5", true, "1.9", false) && vP3_6_4
) {
available.add(new ProtocolLibComponent(plugin));
protocolLibAvailable = true;
}
available.add(new ProtocolLibComponent(plugin));
} catch (Throwable t){
StaticLog.logWarning("Failed to set up packet level hooks.");
StaticLog.logWarning("Failed to set up packet level access with ProtocolLib.");
if (ConfigManager.getConfigFile().getBoolean(ConfPaths.LOGGING_EXTENDED_STATUS)) {
NCPAPIProvider.getNoCheatPlusAPI().getLogManager().debug(Streams.INIT, t);
}
}
}
}
if (!protocolLibAvailable) {
if (pluginProtocolLib != null) {
StaticLog.logWarning("NoCheatPlus supports ProtocolLib 3.6.4 on Minecraft 1.7.10 and earlier, ProtocolLib 3.6.4 or 3.6.5 on Minecraft 1.8, ProtocolLib 3.7 on Minecraft 1.9, ProtocolLib 4.0.0 or later on Minecraft 1.9, ProtocolLib 4.0.2 or later on Minecraft 1.10.");
else {
List<String> parts = new LinkedList<String>();
parts.add("Packet level access via ProtocolLib not available, supported configurations: ");
for (IDescriptiveActivation cond : protocolLibActivation) {
parts.add(cond.getNeutralDescription());
}
StaticLog.logWarning(StringUtil.join(parts, " | "));
}
}
else {
StaticLog.logInfo("Packet level access: ProtocolLib is not available.");
}