From 2f50cca03d95d55e1c2f97512651e11043c2a10b Mon Sep 17 00:00:00 2001 From: asofold Date: Sat, 6 Jun 2015 16:14:36 +0200 Subject: [PATCH] [BLEEDING] Fixes and additions for the compatibility layer. * Make attribute methods consistent (remove the sprint boost modifier from the generic speed multiplier, because it's inconsistent). * Add missing implementations. * Adjust default sprinting speed modifier. * Add more guards for the latest compat module (1.8_R3). * Add a reflection based compat module for CB, to cover minor updates. * Possibly other minor fixes/changes. [Hail "insufficient data written"!] --- .../nocheatplus/utilities/ReflectionUtil.java | 578 ++++++++++++------ .../compat/bukkit/BlockCacheBukkit.java | 2 +- .../compat/bukkit/MCAccessBukkit.java | 303 +-------- .../compat/bukkit/MCAccessBukkitBase.java | 306 ++++++++++ .../compat/cbreflect/BlockCacheCBReflect.java | 51 ++ .../compat/cbreflect/MCAccessCBReflect.java | 248 ++++++++ .../reflect/ReflectAttributeInstance.java | 48 ++ .../reflect/ReflectAttributeModifier.java | 21 + .../compat/cbreflect/reflect/ReflectBase.java | 38 ++ .../cbreflect/reflect/ReflectBlock.java | 167 +++++ .../reflect/ReflectBlockPosition.java | 18 + .../reflect/ReflectDamageSource.java | 20 + .../cbreflect/reflect/ReflectEntity.java | 46 ++ .../reflect/ReflectGenericAttributes.java | 22 + .../cbreflect/reflect/ReflectHelper.java | 411 +++++++++++++ .../reflect/ReflectLivingEntity.java | 20 + .../cbreflect/reflect/ReflectMaterial.java | 18 + .../cbreflect/reflect/ReflectPlayer.java | 37 ++ .../cbreflect/reflect/ReflectWorld.java | 18 + .../compat/cb2512/MCAccessCB2512.java | 2 +- .../compat/cb2545/MCAccessCB2545.java | 2 +- .../compat/cb2602/MCAccessCB2602.java | 2 +- .../compat/cb2645/MCAccessCB2645.java | 2 +- .../compat/cb2691/MCAccessCB2691.java | 2 +- .../compat/cb2763/MCAccessCB2763.java | 2 +- .../compat/cb2794/MCAccessCB2794.java | 10 +- .../compat/cb2808/MCAccessCB2808.java | 10 +- .../compat/cb2882/MCAccessCB2882.java | 10 +- .../compat/cb2922/MCAccessCB2922.java | 10 +- .../compat/cb3026/MCAccessCB3026.java | 17 +- .../compat/cb3043/MCAccessCB3043.java | 8 +- .../compat/cb3100/MCAccessCB3100.java | 8 +- .../compat/cbdev/BlockCacheCBDev.java | 2 +- .../compat/cbdev/MCAccessCBDev.java | 60 +- .../MCAccessSpigotCB1_8_R1.java | 8 +- .../MCAccessSpigotCB1_8_R2.java | 8 +- .../BlockCacheSpigotCB1_8_R3.java | 4 +- .../MCAccessSpigotCB1_8_R3.java | 36 +- .../checks/combined/CombinedListener.java | 11 +- .../nocheatplus/checks/fight/GodMode.java | 2 +- .../nocheatplus/checks/moving/MovingData.java | 2 +- .../nocheatplus/checks/moving/NoFall.java | 5 +- .../checks/moving/SurvivalFly.java | 4 +- .../nocheatplus/compat/BridgeHealth.java | 2 +- .../nocheatplus/compat/BridgeMisc.java | 26 +- .../nocheatplus/compat/MCAccess.java | 8 +- .../nocheatplus/players/DataManager.java | 3 +- .../nocheatplus/compat/MCAccessFactory.java | 7 +- 48 files changed, 2070 insertions(+), 575 deletions(-) create mode 100644 NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/bukkit/MCAccessBukkitBase.java create mode 100644 NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/cbreflect/BlockCacheCBReflect.java create mode 100644 NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/cbreflect/MCAccessCBReflect.java create mode 100644 NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/cbreflect/reflect/ReflectAttributeInstance.java create mode 100644 NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/cbreflect/reflect/ReflectAttributeModifier.java create mode 100644 NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/cbreflect/reflect/ReflectBase.java create mode 100644 NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/cbreflect/reflect/ReflectBlock.java create mode 100644 NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/cbreflect/reflect/ReflectBlockPosition.java create mode 100644 NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/cbreflect/reflect/ReflectDamageSource.java create mode 100644 NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/cbreflect/reflect/ReflectEntity.java create mode 100644 NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/cbreflect/reflect/ReflectGenericAttributes.java create mode 100644 NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/cbreflect/reflect/ReflectHelper.java create mode 100644 NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/cbreflect/reflect/ReflectLivingEntity.java create mode 100644 NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/cbreflect/reflect/ReflectMaterial.java create mode 100644 NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/cbreflect/reflect/ReflectPlayer.java create mode 100644 NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/cbreflect/reflect/ReflectWorld.java diff --git a/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/utilities/ReflectionUtil.java b/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/utilities/ReflectionUtil.java index 5c4e7815..a2978090 100644 --- a/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/utilities/ReflectionUtil.java +++ b/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/utilities/ReflectionUtil.java @@ -1,209 +1,383 @@ package fr.neatmonster.nocheatplus.utilities; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +/** + * Auxiliary methods for dealing with reflection. + * @author asofold + * + */ public class ReflectionUtil { - - /** - * Convenience method to check if members exist and fail if not. This checks getField(...) == null. - * @param prefix - * @param specs - * @throws RuntimeException If any member is not present. - */ - public static void checkMembers(String prefix, String[]... specs){ - try { - for (String[] spec : specs){ - Class clazz = Class.forName(prefix + spec[0]); - for (int i = 1; i < spec.length; i++){ - if (clazz.getField(spec[i]) == null) throw new NoSuchFieldException(prefix + spec[0] + " : " + spec[i]); - } - } - } catch (SecurityException e) { - // Let this one pass. - //throw new RuntimeException(e); - } catch (Throwable t) { - throw new RuntimeException(t); - } - } - - /** - * Check for the given names if the method returns the desired type of result (exact check). - * @param methodNames - * @param returnType - * @throws RuntimeException If one method is not existing or not matching return type or has arguments. - */ - public static void checkMethodReturnTypesNoArgs(Class objClass, String[] methodNames, Class returnType){ - // TODO: Add check: boolean isStatic. - // TODO: Overloading !? - try { - for (String methodName : methodNames){ - Method m = objClass.getMethod(methodName); - if (m.getParameterTypes().length != 0){ - throw new RuntimeException("Expect method without arguments for " + objClass.getName() + "." + methodName); - } - if (m.getReturnType() != returnType){ - throw new RuntimeException("Wrong return type for: " + objClass.getName() + "." + methodName); - } - } - } catch (SecurityException e) { - // Let this one pass. - //throw new RuntimeException(e); - } catch (Throwable t) { - throw new RuntimeException(t); - } - } - - /** - * Dirty method to call a declared method with a generic parameter type. Does try+catch for method invocation and should not throw anything for the normal case. Purpose for this is generic factory registration, having methods with type Object alongside methods with more specialized types. - * @param obj - * @param methodName - * @param arg Argument or invoke the method with. - * @return null in case of errors (can not be distinguished). - */ - public static Object invokeGenericMethodOneArg(final Object obj, final String methodName, final Object arg){ - // TODO: Isn't there a one-line-call for this ?? - final Class objClass = obj.getClass(); - final Class argClass = arg.getClass(); - // Collect methods that might work. - Method methodFound = null; - boolean denyObject = false; - for (final Method method : objClass.getDeclaredMethods()){ - if (method.getName().equals(methodName)){ - final Class[] parameterTypes = method.getParameterTypes(); - if (parameterTypes.length == 1 ){ - // Prevent using Object as argument if there exists a method with a specialized argument. - if (parameterTypes[0] != Object.class && !parameterTypes[0].isAssignableFrom(argClass)){ - denyObject = true; - } - // Override the found method if none found yet and assignment is possible, or if it has a specialized argument of an already found one. - if ((methodFound == null && parameterTypes[0].isAssignableFrom(argClass) || methodFound != null && methodFound.getParameterTypes()[0].isAssignableFrom(parameterTypes[0]))){ - methodFound = method; - } - } - } - } - if (denyObject && methodFound.getParameterTypes()[0] == Object.class){ - // TODO: Throw something !? - return null; - } - else if (methodFound != null && methodFound.getParameterTypes()[0].isAssignableFrom(argClass)){ - try{ - final Object res = methodFound.invoke(obj, arg); - return res; - } - catch (Throwable t){ - // TODO: Throw something !? - return null; - } - } - else{ - // TODO: Throw something !? - return null; - } - } - - /** - * Invoke a method without arguments, get the method matching the return types best, i.e. first type is preferred. At present a result is returned, even if the return type does not match at all. - * @param obj - * @param methodName - * @param returnTypePreference Most preferred return type first, might return null, might return a method with a completely different return type, comparison with ==, no isAssignableForm. TODO: really ? - * @return - */ - public static Object invokeMethodNoArgs(final Object obj, final String methodName, final Class ... returnTypePreference){ - // TODO: Isn't there a one-line-call for this ?? - final Class objClass = obj.getClass(); - // Try to get it directly first. - Method methodFound = getMethodNoArgs(objClass, methodName, returnTypePreference); - if (methodFound == null){ - // Fall-back to seek it. - methodFound = seekMethodNoArgs(objClass, methodName, returnTypePreference); - } - // Invoke if found. - if (methodFound != null){ - try{ - final Object res = methodFound.invoke(obj); - return res; - } - catch (Throwable t){ - // TODO: Throw something !? - return null; - } - } - else{ - // TODO: Throw something !? - return null; - } - } - /** - * Direct getMethod attempt. - * @param objClass - * @param methodName - * @param returnTypePreference - * @return - */ - public static Method getMethodNoArgs(final Class objClass, final String methodName, final Class[] returnTypePreference) { - try { - final Method methodFound = objClass.getMethod(methodName); - if (methodFound != null) { - final Class returnType = methodFound.getReturnType(); - for (int i = 0; i < returnTypePreference.length; i++){ - if (returnType == returnTypePreference[i]){ - return methodFound; - } - } - } - } catch (SecurityException e) { - } catch (NoSuchMethodException e) { - } - return null; - } - - /** - * Iterate over all methods, attempt to return best matching return type (earliest in array). - * @param objClass - * @param methodName - * @param returnTypePreference - * @return - */ - public static Method seekMethodNoArgs(final Class objClass, final String methodName, final Class[] returnTypePreference) { - // Collect methods that might work. - Method methodFound = null; - int returnTypeIndex = returnTypePreference.length; // This can be 0 for no preferences given. - // TODO: Does there exist an optimized method for getting all by name? - for (final Method method : objClass.getMethods()){ - if (method.getName().equals(methodName)){ - final Class[] parameterTypes = method.getParameterTypes(); - if (parameterTypes.length == 0){ - // Override the found method if none found yet or if the return type matches the preferred policy. - final Class returnType = method.getReturnType(); - if (methodFound == null){ - methodFound = method; - for (int i = 0; i < returnTypeIndex; i++){ - if (returnTypePreference[i] == returnType){ - returnTypeIndex = i; - break; - } - } - } - else{ - // Check if the return type is preferred over previously found ones. - for (int i = 0; i < returnTypeIndex; i++){ - if (returnTypePreference[i] == returnType){ - methodFound = method; - returnTypeIndex = i; - break; - } - } - } - if (returnTypeIndex == 0){ - // "Quick" return. - break; - } - } - } - } - return methodFound; - } - + /** + * Convenience method to check if members exist and fail if not. This checks getField(...) == null. + * @param prefix + * @param specs + * @throws RuntimeException If any member is not present. + */ + public static void checkMembers(String prefix, String[]... specs){ + try { + for (String[] spec : specs){ + Class clazz = Class.forName(prefix + spec[0]); + for (int i = 1; i < spec.length; i++){ + if (clazz.getField(spec[i]) == null) { + throw new NoSuchFieldException(prefix + spec[0] + " : " + spec[i]); + } + } + } + } catch (SecurityException e) { + // Let this one pass. + //throw new RuntimeException(e); + } catch (Throwable t) { + throw new RuntimeException(t); + } + } + + /** + * Check for the given names if the method returns the desired type of result (exact check). + * @param methodNames + * @param returnType + * @throws RuntimeException If one method is not existing or not matching return type or has arguments. + */ + public static void checkMethodReturnTypesNoArgs(Class objClass, String[] methodNames, Class returnType){ + // TODO: Add check: boolean isStatic. + // TODO: Overloading !? + try { + for (String methodName : methodNames){ + Method m = objClass.getMethod(methodName); + if (m.getParameterTypes().length != 0){ + throw new RuntimeException("Expect method without arguments for " + objClass.getName() + "." + methodName); + } + if (m.getReturnType() != returnType){ + throw new RuntimeException("Wrong return type for: " + objClass.getName() + "." + methodName); + } + } + } catch (SecurityException e) { + // Let this one pass. + //throw new RuntimeException(e); + } catch (Throwable t) { + throw new RuntimeException(t); + } + } + + /** + * Dirty method to call a declared method with a generic parameter type. Does try+catch for method invocation and should not throw anything for the normal case. Purpose for this is generic factory registration, having methods with type Object alongside methods with more specialized types. + * @param obj + * @param methodName + * @param arg Argument or invoke the method with. + * @return null in case of errors (can not be distinguished). + */ + public static Object invokeGenericMethodOneArg(final Object obj, final String methodName, final Object arg){ + // TODO: Isn't there a one-line-call for this ?? + final Class objClass = obj.getClass(); + final Class argClass = arg.getClass(); + // Collect methods that might work. + Method methodFound = null; + boolean denyObject = false; + for (final Method method : objClass.getDeclaredMethods()){ + if (method.getName().equals(methodName)){ + final Class[] parameterTypes = method.getParameterTypes(); + if (parameterTypes.length == 1 ){ + // Prevent using Object as argument if there exists a method with a specialized argument. + if (parameterTypes[0] != Object.class && !parameterTypes[0].isAssignableFrom(argClass)){ + denyObject = true; + } + // Override the found method if none found yet and assignment is possible, or if it has a specialized argument of an already found one. + if ((methodFound == null && parameterTypes[0].isAssignableFrom(argClass) || methodFound != null && methodFound.getParameterTypes()[0].isAssignableFrom(parameterTypes[0]))){ + methodFound = method; + } + } + } + } + if (denyObject && methodFound.getParameterTypes()[0] == Object.class){ + // TODO: Throw something !? + return null; + } + else if (methodFound != null && methodFound.getParameterTypes()[0].isAssignableFrom(argClass)){ + try{ + final Object res = methodFound.invoke(obj, arg); + return res; + } + catch (Throwable t){ + // TODO: Throw something !? + return null; + } + } + else{ + // TODO: Throw something !? + return null; + } + } + + /** + * Invoke a method without arguments, get the method matching the return types best, i.e. first type is preferred. At present a result is returned, even if the return type does not match at all. + * @param obj + * @param methodName + * @param returnTypePreference Most preferred return type first, might return null, might return a method with a completely different return type, comparison with ==, no isAssignableForm. TODO: really ? + * @return + */ + public static Object invokeMethodNoArgs(final Object obj, final String methodName, final Class ... returnTypePreference){ + // TODO: Isn't there a one-line-call for this ?? + final Class objClass = obj.getClass(); + // Try to get it directly first. + Method methodFound = getMethodNoArgs(objClass, methodName, returnTypePreference); + if (methodFound == null){ + // Fall-back to seek it. + methodFound = seekMethodNoArgs(objClass, methodName, returnTypePreference); + } + // Invoke if found. + if (methodFound != null){ + try{ + final Object res = methodFound.invoke(obj); + return res; + } + catch (Throwable t){ + // TODO: Throw something !? + return null; + } + } + else{ + // TODO: Throw something !? + return null; + } + } + + /** + * More fail-safe method invocation. + * @param method + * @param object + * @return null in case of failures (!). + */ + public static Object invokeMethodNoArgs(Method method, Object object) { + try { + return method.invoke(object); + } + catch (IllegalAccessException e) {} + catch (IllegalArgumentException e) {} + catch (InvocationTargetException e) {} + return null; + } + + /** + * Fail-safe call. + * @param method + * @param object + * @param arguments + * @return null in case of errors. + */ + public static Object invokeMethod(Method method, Object object, Object... arguments) { + try { + return method.invoke(object, arguments); + } + catch (IllegalAccessException e) {} + catch (IllegalArgumentException e) {} + catch (InvocationTargetException e) {} + return null; + } + + /** + * Direct getMethod attempt. + * @param objClass + * @param methodName + * @param returnTypePreference + * @return + */ + public static Method getMethodNoArgs(final Class objClass, final String methodName, final Class... returnTypePreference) { + try { + final Method methodFound = objClass.getMethod(methodName); + if (methodFound != null) { + if (returnTypePreference == null || returnTypePreference.length == 0) { + return methodFound; + } + final Class returnType = methodFound.getReturnType(); + for (int i = 0; i < returnTypePreference.length; i++){ + if (returnType == returnTypePreference[i]){ + return methodFound; + } + } + } + } catch (SecurityException e) { + } catch (NoSuchMethodException e) { + } + return null; + } + + /** + * Iterate over all methods, attempt to return best matching return type (earliest in array). + * @param objClass + * @param methodName + * @param returnTypePreference + * @return + */ + public static Method seekMethodNoArgs(final Class objClass, final String methodName, final Class[] returnTypePreference) { + // Collect methods that might work. + Method methodFound = null; + int returnTypeIndex = returnTypePreference.length; // This can be 0 for no preferences given. + // TODO: Does there exist an optimized method for getting all by name? + for (final Method method : objClass.getMethods()){ + if (method.getName().equals(methodName)){ + final Class[] parameterTypes = method.getParameterTypes(); + if (parameterTypes.length == 0){ + // Override the found method if none found yet or if the return type matches the preferred policy. + final Class returnType = method.getReturnType(); + if (methodFound == null){ + methodFound = method; + for (int i = 0; i < returnTypeIndex; i++){ + if (returnTypePreference[i] == returnType){ + returnTypeIndex = i; + break; + } + } + } + else{ + // Check if the return type is preferred over previously found ones. + for (int i = 0; i < returnTypeIndex; i++){ + if (returnTypePreference[i] == returnType){ + methodFound = method; + returnTypeIndex = i; + break; + } + } + } + if (returnTypeIndex == 0){ + // "Quick" return. + break; + } + } + } + } + return methodFound; + } + + /** + * Get the field by name (and type). Failsafe. + * @param clazz + * @param fieldName + * @param type Set to null to get any type of field. + * @return Field or null. + */ + public static Field getField(Class clazz, String fieldName, Class type) { + try { + Field field = clazz.getField(fieldName); + if (type == null || field.getType() == type) { + return field; + } + } + catch (NoSuchFieldException e) {} + catch (SecurityException e) {} + return null; + } + + /** + * Set the field fail-safe. + * @param field + * @param object + * @param value + * @return + */ + public static boolean set(Field field, Object object, Object value) { + try { + field.set(object, value); + return true; + } + catch (IllegalArgumentException e) {} + catch (IllegalAccessException e) {} + return false; + } + + public static boolean getBoolean(Field field, Object object, boolean defaultValue) { + try { + return field.getBoolean(object); + } + catch (IllegalArgumentException e) {} + catch (IllegalAccessException e) {} + return defaultValue; + } + + public static int getInt(Field field, Object object, int defaultValue) { + try { + return field.getInt(object); + } + catch (IllegalArgumentException e) {} + catch (IllegalAccessException e) {} + return defaultValue; + } + + public static Object get(Field field, Object object, Object defaultValue) { + try { + return field.get(object); + } + catch (IllegalArgumentException e) {} + catch (IllegalAccessException e) {} + return defaultValue; + } + + /** + * Fail-safe getMethod. + * @param clazz + * @param methodName + * @param arguments + * @return null in case of errors. + */ + public static Method getMethod(Class clazz, String methodName, Class... arguments) { + try { + return clazz.getMethod(methodName, arguments); + } + catch (NoSuchMethodException e) {} + catch (SecurityException e) {} + return null; + } + + /** + * Get a method matching one of the declared argument specifications. + * @param clazz + * @param methodName + * @param argumentLists + * @return The first matching method (given order). + */ + public static Method getMethod(Class clazz, String methodName, Class[]... argumentLists) { + Method method = null; + for (Class[] arguments : argumentLists) { + method = getMethod(clazz, methodName, arguments); + if (method != null) { + return method; + } + } + return null; + } + + /** + * Fail-safe. + * @param clazz + * @param parameterTypes + * @return null on errors. + */ + public static Constructor getConstructor(Class clazz, Class... parameterTypes) { + try { + return clazz.getConstructor(parameterTypes); + } + catch (NoSuchMethodException e) {} + catch (SecurityException e) {} + return null; + } + + /** + * Fail-safe. + * @param constructor + * @param arguments + * @return null on errors. + */ + public static Object newInstance(Constructor constructor, Object... arguments) { + try { + return constructor.newInstance(arguments); + } catch (InstantiationException e) {} + catch (IllegalAccessException e) {} + catch (IllegalArgumentException e) {} + catch (InvocationTargetException e) {} + return null; + } + } diff --git a/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/bukkit/BlockCacheBukkit.java b/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/bukkit/BlockCacheBukkit.java index eabd1e83..deff8f4e 100644 --- a/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/bukkit/BlockCacheBukkit.java +++ b/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/bukkit/BlockCacheBukkit.java @@ -7,7 +7,7 @@ import org.bukkit.entity.EntityType; import fr.neatmonster.nocheatplus.utilities.BlockCache; -public class BlockCacheBukkit extends BlockCache{ +public class BlockCacheBukkit extends BlockCache { protected World world; diff --git a/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/bukkit/MCAccessBukkit.java b/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/bukkit/MCAccessBukkit.java index 59f1abfe..dc90c939 100644 --- a/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/bukkit/MCAccessBukkit.java +++ b/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/bukkit/MCAccessBukkit.java @@ -4,288 +4,16 @@ package fr.neatmonster.nocheatplus.compat.bukkit; import java.util.HashSet; import java.util.Set; -import org.bukkit.Bukkit; import org.bukkit.Material; -import org.bukkit.World; -import org.bukkit.command.CommandMap; -import org.bukkit.entity.ComplexEntityPart; -import org.bukkit.entity.ComplexLivingEntity; -import org.bukkit.entity.Entity; -import org.bukkit.entity.EntityType; -import org.bukkit.entity.LivingEntity; -import org.bukkit.entity.Minecart; -import org.bukkit.entity.Player; -import org.bukkit.entity.Slime; -import org.bukkit.event.entity.EntityDamageEvent.DamageCause; -import org.bukkit.potion.PotionEffectType; -import fr.neatmonster.nocheatplus.compat.AlmostBoolean; -import fr.neatmonster.nocheatplus.compat.BridgeHealth; -import fr.neatmonster.nocheatplus.compat.MCAccess; import fr.neatmonster.nocheatplus.compat.blocks.BlockPropertiesSetup; import fr.neatmonster.nocheatplus.config.WorldConfigProvider; -import fr.neatmonster.nocheatplus.utilities.BlockCache; import fr.neatmonster.nocheatplus.utilities.BlockProperties; -import fr.neatmonster.nocheatplus.utilities.PotionUtil; -import fr.neatmonster.nocheatplus.utilities.ReflectionUtil; -public class MCAccessBukkit implements MCAccess, BlockPropertiesSetup{ +public class MCAccessBukkit extends MCAccessBukkitBase implements BlockPropertiesSetup{ - // private AlmostBoolean entityPlayerAvailable = AlmostBoolean.MAYBE; - - /** - * Constructor to let it fail. - */ public MCAccessBukkit() { - // TODO: Add more that might fail if not supported ? - Material.AIR.isSolid(); - Material.AIR.isOccluding(); - Material.AIR.isTransparent(); - // TODO: Deactivate checks that might not work. => MCAccess should have availability method, NCP deactivates check on base of that. - } - - @Override - public String getMCVersion() { - // Bukkit API. - // TODO: maybe output something else. - return "1.4.6|1.4.7|1.5.x|1.6.x|1.7.x|1.8.x|?"; // 1.8.x is bold! - } - - @Override - public String getServerVersionTag() { - return "Bukkit-API"; - } - - @Override - public CommandMap getCommandMap() { - try{ - return (CommandMap) ReflectionUtil.invokeMethodNoArgs(Bukkit.getServer(), "getCommandMap"); - } catch (Throwable t) { - // Nasty. - return null; - } - } - - @Override - public BlockCache getBlockCache(final World world) { - return new BlockCacheBukkit(world); - } - - @Override - public double getHeight(final Entity entity) { - // TODO: Copy defaults like with widths. - final double entityHeight = 1.0; - if (entity instanceof LivingEntity) { - return Math.max(((LivingEntity) entity).getEyeHeight(), entityHeight); - } else { - return entityHeight; - } - } - - @Override - public AlmostBoolean isBlockSolid(final int id) { - @SuppressWarnings("deprecation") - final Material mat = Material.getMaterial(id); - if (mat == null) { - return AlmostBoolean.MAYBE; - } - else { - return AlmostBoolean.match(mat.isSolid()); - } - } - - @Override - public double getWidth(final Entity entity) { - // TODO: Make readable from file for defaults + register individual getters where appropriate. - // TODO: For height too. [Automatize most by spawning + checking?] - // Values taken from 1.7.10. - final EntityType type = entity.getType(); - switch(type){ - // TODO: case COMPLEX_PART: - case ENDER_SIGNAL: // this.a(0.25F, 0.25F); - case FIREWORK: // this.a(0.25F, 0.25F); - case FISHING_HOOK: // this.a(0.25F, 0.25F); - case DROPPED_ITEM: // this.a(0.25F, 0.25F); - case SNOWBALL: // (projectile) this.a(0.25F, 0.25F); - return 0.25; - case CHICKEN: // this.a(0.3F, 0.7F); - case SILVERFISH: // this.a(0.3F, 0.7F); - return 0.3f; - case SMALL_FIREBALL: // this.a(0.3125F, 0.3125F); - case WITHER_SKULL: // this.a(0.3125F, 0.3125F); - return 0.3125f; - case GHAST: // this.a(4.0F, 4.0F); - case SNOWMAN: // this.a(0.4F, 1.8F); - return 0.4f; - case ARROW: // this.a(0.5F, 0.5F); - case BAT: // this.a(0.5F, 0.9F); - case EXPERIENCE_ORB: // this.a(0.5F, 0.5F); - case ITEM_FRAME: // hanging: this.a(0.5F, 0.5F); - case PAINTING: // hanging: this.a(0.5F, 0.5F); - return 0.5f; - case PLAYER: // FAST RETURN - case ZOMBIE: - case PIG_ZOMBIE: - case SKELETON: - case CREEPER: - case ENDERMAN: - case OCELOT: - case BLAZE: - case VILLAGER: - case WITCH: - case WOLF: - return 0.6f; // (Default entity width.) - case CAVE_SPIDER: // this.a(0.7F, 0.5F); - return 0.7f; - case COW: // this.a(0.9F, 1.3F); - case MUSHROOM_COW: // this.a(0.9F, 1.3F); - case PIG: // this.a(0.9F, 0.9F); - case SHEEP: // this.a(0.9F, 1.3F); - case WITHER: // this.a(0.9F, 4.0F); - return 0.9f; - case SQUID: // this.a(0.95F, 0.95F); - return 0.95f; - case PRIMED_TNT: // this.a(0.98F, 0.98F); - return 0.98f; - case FIREBALL: // (EntityFireball) this.a(1.0F, 1.0F); - return 1.0f; - case IRON_GOLEM: // this.a(1.4F, 2.9F); - case SPIDER: // this.a(1.4F, 0.9F); - return 1.4f; - case BOAT: // this.a(1.5F, 0.6F); - return 1.5f; - case ENDER_CRYSTAL: // this.a(2.0F, 2.0F); - return 2.0f; - case GIANT: // this.height *= 6.0F; this.a(this.width * 6.0F, this.length * 6.0F); - return 3.6f; // (Better than nothing.) - case ENDER_DRAGON: // this.a(16.0F, 8.0F); - return 16.0f; - // Variable size: - case SLIME: - case MAGMA_CUBE: - if (entity instanceof Slime) { - // setSize(i): this.a(0.6F * (float) i, 0.6F * (float) i); - return 0.6f * ((Slime) entity).getSize(); - } - default: - break; - } - // Check by instance for minecarts (too many). - if (entity instanceof Minecart) { - return 0.98f; // this.a(0.98F, 0.7F); - } - // Latest Bukkit API. - try { - switch (type) { - case LEASH_HITCH: // hanging: this.a(0.5F, 0.5F); - return 0.5f; - case HORSE: // this.a(1.4F, 1.6F); - return 1.4f; - // 1.8 - case ENDERMITE: // this.setSize(0.4F, 0.3F); - return 0.4f; - case ARMOR_STAND: // this.setSize(0.5F, 1.975F); - return 0.5f; - case RABBIT: // this.setSize(0.6F, 0.7F); - return 0.6f; - case GUARDIAN: // this.setSize(0.85F, 0.85F); - return 0.95f; - default: - break; - } - } catch (Throwable t) {} - // Default entity width. - return 0.6f; - - } - - @Override - public AlmostBoolean isBlockLiquid(final int id) { - @SuppressWarnings("deprecation") - final Material mat = Material.getMaterial(id); - if (mat == null) return AlmostBoolean.MAYBE; - switch (mat) { - case STATIONARY_LAVA: - case STATIONARY_WATER: - case WATER: - case LAVA: - return AlmostBoolean.YES; - default: - return AlmostBoolean.NO; - } - } - - @Override - public AlmostBoolean isIllegalBounds(final Player player) { - if (player.isDead()) { - return AlmostBoolean.NO; - } - if (!player.isSleeping()) { // TODO: ignored sleeping ? - // TODO: This can test like ... nothing ! - // (Might not be necessary.) - } - return AlmostBoolean.MAYBE; - } - - @Override - public double getJumpAmplifier(final Player player) { - return PotionUtil.getPotionEffectAmplifier(player, PotionEffectType.JUMP); - } - - @Override - public double getFasterMovementAmplifier(final Player player) { - return PotionUtil.getPotionEffectAmplifier(player, PotionEffectType.SPEED); - } - - @Override - public double getSpeedAttributeMultiplier(Player player) { - // TODO: Reflection (scan for the attribute to get on startup and check for methods.) - return 1.0; - } - - @Override - public double getSprintAttributeMultiplier(Player player) { - return player.isSprinting() ? 1.3 : 1.0; - } - - @Override - public int getInvulnerableTicks(final Player player) { - // TODO: Ahhh... - return player.getNoDamageTicks(); - } - - @Override - public void setInvulnerableTicks(final Player player, final int ticks) { - // TODO: Not really. - player.setLastDamageCause(BridgeHealth.getEntityDamageEvent(player, DamageCause.CUSTOM, 500.0)); - player.setNoDamageTicks(ticks); - } - - @Override - public void dealFallDamage(final Player player, final double damage) { - // TODO: Document in knowledge base. - // TODO: Account for armor, other. - // TODO: use setLastDamageCause here ? - BridgeHealth.damage(player, damage); - } - - @Override - public boolean isComplexPart(final Entity entity) { - return entity instanceof ComplexEntityPart || entity instanceof ComplexLivingEntity; - } - - @Override - public boolean shouldBeZombie(final Player player) { - // Not sure :) ... - return BridgeHealth.getHealth(player) <= 0.0 && !player.isDead(); - } - - @Override - public void setDead(final Player player, final int deathTicks) { - // TODO: Test / kick ? ... - BridgeHealth.setHealth(player, 0.0); - // TODO: Might try stuff like setNoDamageTicks. - BridgeHealth.damage(player, 1.0); + super(); } @Override @@ -327,31 +55,4 @@ public class MCAccessBukkit implements MCAccess, BlockPropertiesSetup{ } } - @Override - public boolean hasGravity(final Material mat) { - try{ - return mat.hasGravity(); - } - catch(Throwable t) { - // Backwards compatibility. - switch(mat) { - case SAND: - case GRAVEL: - return true; - default: - return false; - } - } - } - - @Override - public AlmostBoolean dealFallDamageFiresAnEvent() { - return AlmostBoolean.NO; // Assumption. - } - - // @Override - // public void correctDirection(Player player) { - // // TODO: Consider using reflection (detect CraftPlayer, access EntityPlayer + check if possible (!), use flags for if valid or invalid.) - // } - } diff --git a/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/bukkit/MCAccessBukkitBase.java b/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/bukkit/MCAccessBukkitBase.java new file mode 100644 index 00000000..1783c62c --- /dev/null +++ b/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/bukkit/MCAccessBukkitBase.java @@ -0,0 +1,306 @@ +package fr.neatmonster.nocheatplus.compat.bukkit; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.command.CommandMap; +import org.bukkit.entity.ComplexEntityPart; +import org.bukkit.entity.ComplexLivingEntity; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Minecart; +import org.bukkit.entity.Player; +import org.bukkit.entity.Slime; +import org.bukkit.potion.PotionEffectType; + +import fr.neatmonster.nocheatplus.compat.AlmostBoolean; +import fr.neatmonster.nocheatplus.compat.BridgeHealth; +import fr.neatmonster.nocheatplus.compat.MCAccess; +import fr.neatmonster.nocheatplus.utilities.BlockCache; +import fr.neatmonster.nocheatplus.utilities.PotionUtil; +import fr.neatmonster.nocheatplus.utilities.ReflectionUtil; + +public class MCAccessBukkitBase implements MCAccess { + + // private AlmostBoolean entityPlayerAvailable = AlmostBoolean.MAYBE; + + /** + * Constructor to let it fail. + */ + public MCAccessBukkitBase() { + // TODO: Add more that might fail if not supported ? + Material.AIR.isSolid(); + Material.AIR.isOccluding(); + Material.AIR.isTransparent(); + // TODO: Deactivate checks that might not work. => MCAccess should have availability method, NCP deactivates check on base of that. + } + + @Override + public String getMCVersion() { + // Bukkit API. + // TODO: maybe output something else. + return "1.4.6|1.4.7|1.5.x|1.6.x|1.7.x|1.8.x|?"; // 1.8.x is bold! + } + + @Override + public String getServerVersionTag() { + return "Bukkit-API"; + } + + @Override + public CommandMap getCommandMap() { + try{ + return (CommandMap) ReflectionUtil.invokeMethodNoArgs(Bukkit.getServer(), "getCommandMap"); + } catch (Throwable t) { + // Nasty. + return null; + } + } + + @Override + public BlockCache getBlockCache(final World world) { + return new BlockCacheBukkit(world); + } + + @Override + public double getHeight(final Entity entity) { + // TODO: Copy defaults like with widths. + final double entityHeight = 1.0; + if (entity instanceof LivingEntity) { + return Math.max(((LivingEntity) entity).getEyeHeight(), entityHeight); + } else { + return entityHeight; + } + } + + @Override + public AlmostBoolean isBlockSolid(final int id) { + @SuppressWarnings("deprecation") + final Material mat = Material.getMaterial(id); + if (mat == null) { + return AlmostBoolean.MAYBE; + } + else { + return AlmostBoolean.match(mat.isSolid()); + } + } + + @Override + public double getWidth(final Entity entity) { + // TODO: Make readable from file for defaults + register individual getters where appropriate. + // TODO: For height too. [Automatize most by spawning + checking?] + // Values taken from 1.7.10. + final EntityType type = entity.getType(); + switch(type){ + // TODO: case COMPLEX_PART: + case ENDER_SIGNAL: // this.a(0.25F, 0.25F); + case FIREWORK: // this.a(0.25F, 0.25F); + case FISHING_HOOK: // this.a(0.25F, 0.25F); + case DROPPED_ITEM: // this.a(0.25F, 0.25F); + case SNOWBALL: // (projectile) this.a(0.25F, 0.25F); + return 0.25; + case CHICKEN: // this.a(0.3F, 0.7F); + case SILVERFISH: // this.a(0.3F, 0.7F); + return 0.3f; + case SMALL_FIREBALL: // this.a(0.3125F, 0.3125F); + case WITHER_SKULL: // this.a(0.3125F, 0.3125F); + return 0.3125f; + case GHAST: // this.a(4.0F, 4.0F); + case SNOWMAN: // this.a(0.4F, 1.8F); + return 0.4f; + case ARROW: // this.a(0.5F, 0.5F); + case BAT: // this.a(0.5F, 0.9F); + case EXPERIENCE_ORB: // this.a(0.5F, 0.5F); + case ITEM_FRAME: // hanging: this.a(0.5F, 0.5F); + case PAINTING: // hanging: this.a(0.5F, 0.5F); + return 0.5f; + case PLAYER: // FAST RETURN + case ZOMBIE: + case PIG_ZOMBIE: + case SKELETON: + case CREEPER: + case ENDERMAN: + case OCELOT: + case BLAZE: + case VILLAGER: + case WITCH: + case WOLF: + return 0.6f; // (Default entity width.) + case CAVE_SPIDER: // this.a(0.7F, 0.5F); + return 0.7f; + case COW: // this.a(0.9F, 1.3F); + case MUSHROOM_COW: // this.a(0.9F, 1.3F); + case PIG: // this.a(0.9F, 0.9F); + case SHEEP: // this.a(0.9F, 1.3F); + case WITHER: // this.a(0.9F, 4.0F); + return 0.9f; + case SQUID: // this.a(0.95F, 0.95F); + return 0.95f; + case PRIMED_TNT: // this.a(0.98F, 0.98F); + return 0.98f; + case FIREBALL: // (EntityFireball) this.a(1.0F, 1.0F); + return 1.0f; + case IRON_GOLEM: // this.a(1.4F, 2.9F); + case SPIDER: // this.a(1.4F, 0.9F); + return 1.4f; + case BOAT: // this.a(1.5F, 0.6F); + return 1.5f; + case ENDER_CRYSTAL: // this.a(2.0F, 2.0F); + return 2.0f; + case GIANT: // this.height *= 6.0F; this.a(this.width * 6.0F, this.length * 6.0F); + return 3.6f; // (Better than nothing.) + case ENDER_DRAGON: // this.a(16.0F, 8.0F); + return 16.0f; + // Variable size: + case SLIME: + case MAGMA_CUBE: + if (entity instanceof Slime) { + // setSize(i): this.a(0.6F * (float) i, 0.6F * (float) i); + return 0.6f * ((Slime) entity).getSize(); + } + default: + break; + } + // Check by instance for minecarts (too many). + if (entity instanceof Minecart) { + return 0.98f; // this.a(0.98F, 0.7F); + } + // Latest Bukkit API. + try { + switch (type) { + case LEASH_HITCH: // hanging: this.a(0.5F, 0.5F); + return 0.5f; + case HORSE: // this.a(1.4F, 1.6F); + return 1.4f; + // 1.8 + case ENDERMITE: // this.setSize(0.4F, 0.3F); + return 0.4f; + case ARMOR_STAND: // this.setSize(0.5F, 1.975F); + return 0.5f; + case RABBIT: // this.setSize(0.6F, 0.7F); + return 0.6f; + case GUARDIAN: // this.setSize(0.85F, 0.85F); + return 0.95f; + default: + break; + } + } catch (Throwable t) {} + // Default entity width. + return 0.6f; + + } + + @Override + public AlmostBoolean isBlockLiquid(final int id) { + @SuppressWarnings("deprecation") + final Material mat = Material.getMaterial(id); + if (mat == null) return AlmostBoolean.MAYBE; + switch (mat) { + case STATIONARY_LAVA: + case STATIONARY_WATER: + case WATER: + case LAVA: + return AlmostBoolean.YES; + default: + return AlmostBoolean.NO; + } + } + + @Override + public AlmostBoolean isIllegalBounds(final Player player) { + if (player.isDead()) { + return AlmostBoolean.NO; + } + if (!player.isSleeping()) { // TODO: ignored sleeping ? + // TODO: This can test like ... nothing ! + // (Might not be necessary.) + } + return AlmostBoolean.MAYBE; + } + + @Override + public double getJumpAmplifier(final Player player) { + return PotionUtil.getPotionEffectAmplifier(player, PotionEffectType.JUMP); + } + + @Override + public double getFasterMovementAmplifier(final Player player) { + return PotionUtil.getPotionEffectAmplifier(player, PotionEffectType.SPEED); + } + + @Override + public double getSpeedAttributeMultiplier(Player player) { + return 1.0; + } + + @Override + public double getSprintAttributeMultiplier(Player player) { + return player.isSprinting() ? 1.30000002 : 1.0; + } + + @Override + public int getInvulnerableTicks(final Player player) { + return Integer.MAX_VALUE; // NOT SUPPORTED. + } + + @Override + public void setInvulnerableTicks(final Player player, final int ticks) { + // IGNORE. + } + + @Override + public void dealFallDamage(final Player player, final double damage) { + // TODO: Document in knowledge base. + // TODO: Account for armor, other. + // TODO: use setLastDamageCause here ? + BridgeHealth.damage(player, damage); + } + + @Override + public boolean isComplexPart(final Entity entity) { + return entity instanceof ComplexEntityPart || entity instanceof ComplexLivingEntity; + } + + @Override + public boolean shouldBeZombie(final Player player) { + // Not sure :) ... + return BridgeHealth.getHealth(player) <= 0.0 && !player.isDead(); + } + + @Override + public void setDead(final Player player, final int deathTicks) { + // TODO: Test / kick ? ... + BridgeHealth.setHealth(player, 0.0); + // TODO: Might try stuff like setNoDamageTicks. + BridgeHealth.damage(player, 1.0); + } + + @Override + public boolean hasGravity(final Material mat) { + try{ + return mat.hasGravity(); + } + catch(Throwable t) { + // Backwards compatibility. + switch(mat) { + case SAND: + case GRAVEL: + return true; + default: + return false; + } + } + } + + @Override + public AlmostBoolean dealFallDamageFiresAnEvent() { + return AlmostBoolean.NO; // Assumption. + } + + // @Override + // public void correctDirection(Player player) { + // // TODO: Consider using reflection (detect CraftPlayer, access EntityPlayer + check if possible (!), use flags for if valid or invalid.) + // } + +} diff --git a/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/cbreflect/BlockCacheCBReflect.java b/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/cbreflect/BlockCacheCBReflect.java new file mode 100644 index 00000000..b2f27ef1 --- /dev/null +++ b/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/cbreflect/BlockCacheCBReflect.java @@ -0,0 +1,51 @@ +package fr.neatmonster.nocheatplus.compat.cbreflect; + +import org.bukkit.World; +import org.bukkit.entity.Entity; + +import fr.neatmonster.nocheatplus.compat.bukkit.BlockCacheBukkit; +import fr.neatmonster.nocheatplus.compat.cbreflect.reflect.ReflectHelper; +import fr.neatmonster.nocheatplus.compat.cbreflect.reflect.ReflectHelper.ReflectFailureException; + +public class BlockCacheCBReflect extends BlockCacheBukkit { + + // TODO: Not sure if reflection can gain speed over Bukkit API anywhere (who wants to try?). + + protected final ReflectHelper helper; + + protected Object nmsWorld = null; + + public BlockCacheCBReflect(ReflectHelper reflectHelper, World world) { + super(world); + this.helper = reflectHelper; + } + + @Override + public void setAccess(World world) { + super.setAccess(world); + this.nmsWorld = world == null ? null : helper.getHandle(world); + } + + @Override + public double[] fetchBounds(int x, int y, int z) { + try { + return helper.nmsWorld_fetchBlockShape(this.nmsWorld, this.getTypeId(x, y, z), x, y, z); + } + catch (ReflectFailureException ex) { + return super.fetchBounds(x, y, z); + } + } + + @Override + public boolean standsOnEntity(Entity entity, double minX, double minY, double minZ, double maxX, double maxY, double maxZ) { + // TODO: Implement once relevant. + return super.standsOnEntity(entity, minX, minY, minZ, maxX, maxY, maxZ); + } + + @Override + public void cleanup() { + super.cleanup(); + this.nmsWorld = null; + } + +} diff --git a/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/cbreflect/MCAccessCBReflect.java b/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/cbreflect/MCAccessCBReflect.java new file mode 100644 index 00000000..651fa9f2 --- /dev/null +++ b/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/cbreflect/MCAccessCBReflect.java @@ -0,0 +1,248 @@ +package fr.neatmonster.nocheatplus.compat.cbreflect; + +import org.bukkit.World; +import org.bukkit.entity.Player; + +import fr.neatmonster.nocheatplus.NCPAPIProvider; +import fr.neatmonster.nocheatplus.compat.AlmostBoolean; +import fr.neatmonster.nocheatplus.compat.bukkit.BlockCacheBukkit; +import fr.neatmonster.nocheatplus.compat.bukkit.MCAccessBukkitBase; +import fr.neatmonster.nocheatplus.compat.cbreflect.reflect.ReflectHelper; +import fr.neatmonster.nocheatplus.compat.cbreflect.reflect.ReflectHelper.ReflectFailureException; +import fr.neatmonster.nocheatplus.compat.versions.ServerVersion; +import fr.neatmonster.nocheatplus.logging.Streams; +import fr.neatmonster.nocheatplus.utilities.BlockCache; + +public class MCAccessCBReflect extends MCAccessBukkitBase { + + protected final ReflectHelper helper; + + /** Generally supported Minecraft version (know for sure). */ + protected final boolean knownSupportedVersion; + /** We know for sure that dealFallDamage will fire a damage event. */ + protected final boolean dealFallDamageFiresAnEvent; + + public MCAccessCBReflect() throws ReflectFailureException { + // TODO: Add unavailable stuff to features / missing (TBD). + helper = new ReflectHelper(); + // Version Envelope tests (1.4.5-R1.0 ... 1.8.x is considered to be ok). + final String mcVersion = ServerVersion.getMinecraftVersion(); + if (mcVersion == ServerVersion.UNKNOWN_VERSION) { + NCPAPIProvider.getNoCheatPlusAPI().getLogManager().warning(Streams.INIT, "[NoCheatPlus] The Minecraft version could not be detected, Compat-CB-Reflect might or might not work."); + this.knownSupportedVersion = false; + } + else if (ServerVersion.compareVersions(mcVersion, "1.4.5") < 0) { + NCPAPIProvider.getNoCheatPlusAPI().getLogManager().warning(Streams.INIT, "[NoCheatPlus] The Minecraft version seems to be older than what Compat-CB-Reflect can support."); + this.knownSupportedVersion = false; + } + else if (ServerVersion.compareVersions(mcVersion, "1.9") >= 0) { + this.knownSupportedVersion = false; + NCPAPIProvider.getNoCheatPlusAPI().getLogManager().warning(Streams.INIT, "[NoCheatPlus] The Minecraft version seems to be more recent than the one Compat-CB-Reflect has been built with - this might work, but there could be incompatibilities."); + } else { + this.knownSupportedVersion = true; + } + // Fall damage / event. TODO: Tests between 1.8 and 1.7.2. How about spigot vs. CB? + if (mcVersion == ServerVersion.UNKNOWN_VERSION || ServerVersion.compareVersions(mcVersion, "1.8") < 0) { + dealFallDamageFiresAnEvent = false; + } else { + // Assume higher versions to fire an event. + dealFallDamageFiresAnEvent = true; + } + } + + @Override + public String getMCVersion() { + // Potentially all :p. + return "1.4.5-1.8.6|?"; + } + + @Override + public String getServerVersionTag() { + return "CB-Reflect"; + } + + @Override + public BlockCache getBlockCache(World world) { + try { + return new BlockCacheCBReflect(helper, world); + } + catch (ReflectFailureException ex) { + return new BlockCacheBukkit(world); + } + } + + @Override + public boolean shouldBeZombie(Player player) { + try { + Object handle = helper.getHandle(player); + return !helper.nmsPlayer_dead(handle) && helper.nmsPlayer_getHealth(handle) <= 0.0; + } + catch (ReflectFailureException ex) { + // Fall back to Bukkit. + return super.shouldBeZombie(player); + } + } + + @Override + public void setDead(Player player, int deathTicks) { + try { + Object handle = helper.getHandle(player); + helper.nmsPlayer_dead(handle, true); + helper.nmsPlayer_deathTicks(handle, deathTicks); + } + catch (ReflectFailureException ex) { + super.setDead(player, deathTicks); + } + } + + @Override + public AlmostBoolean dealFallDamageFiresAnEvent() { + if (!dealFallDamageFiresAnEvent) { + return AlmostBoolean.NO; + } + return AlmostBoolean.match(this.helper.canDealFallDamage()); + } + + @Override + public void dealFallDamage(final Player player, final double damage) { + try { + helper.dealFallDamage(player, damage); + } + catch (ReflectFailureException ex) { + // TODO: Fire an event ? + super.dealFallDamage(player, damage); + } + } + + @Override + public int getInvulnerableTicks(final Player player) { + try { + return helper.getInvulnerableTicks(player); + } + catch (ReflectFailureException ex) { + return super.getInvulnerableTicks(player); + } + } + + @Override + public void setInvulnerableTicks(final Player player, final int ticks) { + try { + helper.setInvulnerableTicks(player, ticks); + } + catch (ReflectFailureException ex) { + super.setInvulnerableTicks(player, ticks); + } + } + + @Override + public double getSpeedAttributeMultiplier(Player player) { + try { + return helper.getSpeedAttributeMultiplier(player, true); + } + catch (ReflectFailureException ex) { + return super.getSpeedAttributeMultiplier(player); + } + } + + @Override + public double getSprintAttributeMultiplier(Player player) { + try { + return helper.getSprintAttributeMultiplier(player); + } + catch (ReflectFailureException ex) { + return super.getSprintAttributeMultiplier(player); + } + } + + @Override + public AlmostBoolean isBlockSolid(final int id) { + try { + return helper.isBlockSolid(id); + } + catch (ReflectFailureException ex) { + return super.isBlockSolid(id); + } + } + + @Override + public AlmostBoolean isBlockLiquid(final int id) { + try { + return helper.isBlockLiquid(id); + } + catch (ReflectFailureException ex) { + return super.isBlockLiquid(id); + } + } + + + // TODO: ---- Missing (better to implement these) ---- + + // @Override + // public double getHeight(final Entity entity) { + // final net.minecraft.server.v1_8_R3.Entity mcEntity = ((CraftEntity) entity).getHandle(); + // AxisAlignedBB boundingBox = mcEntity.getBoundingBox(); + // final double entityHeight = Math.max(mcEntity.length, Math.max(mcEntity.getHeadHeight(), boundingBox.e - boundingBox.b)); + // if (entity instanceof LivingEntity) { + // return Math.max(((LivingEntity) entity).getEyeHeight(), entityHeight); + // } else return entityHeight; + // } + + // @Override + // public double getWidth(final Entity entity) { + // return ((CraftEntity) entity).getHandle().width; + // } + + // @Override + // public AlmostBoolean isIllegalBounds(final Player player) { + // final EntityPlayer entityPlayer = ((CraftPlayer) player).getHandle(); + // if (entityPlayer.dead) { + // return AlmostBoolean.NO; + // } + // // TODO: Does this need a method call for the "real" box? Might be no problem during moving events, though. + // final AxisAlignedBB box = entityPlayer.getBoundingBox(); + // if (!entityPlayer.isSleeping()) { + // // This can not really test stance but height of bounding box. + // final double dY = Math.abs(box.e - box.b); + // if (dY > 1.8) { + // return AlmostBoolean.YES; // dY > 1.65D || + // } + // if (dY < 0.1D && entityPlayer.length >= 0.1) { + // return AlmostBoolean.YES; + // } + // } + // return AlmostBoolean.MAYBE; + // } + + + // ---- Missing (probably ok with Bukkit only) ---- + + // @Override + // public double getJumpAmplifier(final Player player) { + // final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); + // if (mcPlayer.hasEffect(MobEffectList.JUMP)) { + // return mcPlayer.getEffect(MobEffectList.JUMP).getAmplifier(); + // } + // else { + // return Double.NEGATIVE_INFINITY; + // } + // } + + // @Override + // public double getFasterMovementAmplifier(final Player player) { + // final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); + // if (mcPlayer.hasEffect(MobEffectList.FASTER_MOVEMENT)) { + // return mcPlayer.getEffect(MobEffectList.FASTER_MOVEMENT).getAmplifier(); + // } + // else { + // return Double.NEGATIVE_INFINITY; + // } + // } + + // @Override + // public boolean isComplexPart(final Entity entity) { + // return ((CraftEntity) entity).getHandle() instanceof EntityComplexPart; + // } + + // (getCommandMap already uses reflection, but could be more speedy.). + +} diff --git a/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/cbreflect/reflect/ReflectAttributeInstance.java b/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/cbreflect/reflect/ReflectAttributeInstance.java new file mode 100644 index 00000000..7552fc98 --- /dev/null +++ b/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/cbreflect/reflect/ReflectAttributeInstance.java @@ -0,0 +1,48 @@ +package fr.neatmonster.nocheatplus.compat.cbreflect.reflect; + +import java.lang.reflect.Method; +import java.util.UUID; + +import fr.neatmonster.nocheatplus.utilities.ReflectionUtil; + +public class ReflectAttributeInstance { + + /** (Custom naming.) */ + public final Method nmsGetBaseValue; + public final Method nmsGetValue; + + /** (Custom naming.) */ + public final Method nmsGetAttributeModifier; + + public ReflectAttributeInstance(ReflectBase base) throws ClassNotFoundException { + Class clazz = Class.forName(base.nmsPackageName + ".AttributeInstance"); + // Base value. + Method method = ReflectionUtil.getMethodNoArgs(clazz, "b", double.class); + if (method == null) { + // TODO: Consider to search (as long as only two exist). + method = ReflectionUtil.getMethodNoArgs(clazz, "getBaseValue", double.class); + if (method == null) { + method = ReflectionUtil.getMethodNoArgs(clazz, "getBase", double.class); + } + } + nmsGetBaseValue = method; + // Value (final value). + method = ReflectionUtil.getMethodNoArgs(clazz, "getValue", double.class); + if (method == null) { + // TODO: Consider to search (as long as only two exist). + method = ReflectionUtil.getMethodNoArgs(clazz, "e", double.class); // 1.6.1 + } + nmsGetValue = method; + // Get AttributeModifier. + // TODO: If name changes: scan. + method = ReflectionUtil.getMethod(clazz, "a", UUID.class); + if (method == null) { + method = ReflectionUtil.getMethod(clazz, "getAttributeModifier", UUID.class); + if (method == null) { + method = ReflectionUtil.getMethod(clazz, "getModifier", UUID.class); + } + } + nmsGetAttributeModifier = method; + } + +} diff --git a/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/cbreflect/reflect/ReflectAttributeModifier.java b/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/cbreflect/reflect/ReflectAttributeModifier.java new file mode 100644 index 00000000..bc7160eb --- /dev/null +++ b/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/cbreflect/reflect/ReflectAttributeModifier.java @@ -0,0 +1,21 @@ +package fr.neatmonster.nocheatplus.compat.cbreflect.reflect; + +import java.lang.reflect.Method; + +import fr.neatmonster.nocheatplus.utilities.ReflectionUtil; + +public class ReflectAttributeModifier { + + /** (Custom naming.) */ + public Method nmsGetOperation; + /** (Custom naming.) */ + public Method nmsGetValue; + + public ReflectAttributeModifier(ReflectBase base) throws ClassNotFoundException { + Class clazz = Class.forName(base.nmsPackageName + ".AttributeModifier"); + // TODO: Scan in a more future proof way. + nmsGetOperation = ReflectionUtil.getMethodNoArgs(clazz, "c", int.class); + nmsGetValue = ReflectionUtil.getMethodNoArgs(clazz, "d", double.class); + } + +} diff --git a/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/cbreflect/reflect/ReflectBase.java b/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/cbreflect/reflect/ReflectBase.java new file mode 100644 index 00000000..55678368 --- /dev/null +++ b/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/cbreflect/reflect/ReflectBase.java @@ -0,0 +1,38 @@ +package fr.neatmonster.nocheatplus.compat.cbreflect.reflect; + +import org.bukkit.Bukkit; +import org.bukkit.Server; + +import fr.neatmonster.nocheatplus.utilities.ReflectionUtil; + +public class ReflectBase { + + // TODO: Envelope check, enum for what envelope level (within expected version range, before / after). + + public final String obcPackageName; + + public final String nmsPackageName; + + public ReflectBase() { + final Server server = Bukkit.getServer(); + // TODO: Confine even more closely (detect v... package part, sequence of indices). + // obc + Class clazz = server.getClass(); + String name = clazz.getPackage().getName(); + if (name.indexOf("org.") == 0 && name.indexOf(".bukkit.") != -1 && name.indexOf(".craftbukkit.") != -1) { + obcPackageName = name; + } else { + obcPackageName = null; + } + // nms + Object obj = ReflectionUtil.invokeMethodNoArgs(server, "getHandle"); + clazz = obj.getClass(); + name = clazz.getPackage().getName(); + if (name.indexOf("net.") == 0 && name.indexOf(".minecraft.") != -1 && name.indexOf(".server.") != -1) { + nmsPackageName = name; + } else { + nmsPackageName = null; + } + } + +} diff --git a/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/cbreflect/reflect/ReflectBlock.java b/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/cbreflect/reflect/ReflectBlock.java new file mode 100644 index 00000000..47adc15e --- /dev/null +++ b/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/cbreflect/reflect/ReflectBlock.java @@ -0,0 +1,167 @@ +package fr.neatmonster.nocheatplus.compat.cbreflect.reflect; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import fr.neatmonster.nocheatplus.NCPAPIProvider; +import fr.neatmonster.nocheatplus.config.ConfPaths; +import fr.neatmonster.nocheatplus.config.ConfigManager; +import fr.neatmonster.nocheatplus.logging.Streams; +import fr.neatmonster.nocheatplus.utilities.ReflectionUtil; +import fr.neatmonster.nocheatplus.utilities.StringUtil; + +public class ReflectBlock { + + /** Obfuscated nms names, allowing to find the order in the source code under certain circumstances. */ + private static final List possibleNames = new ArrayList(); + + static { + // These might suffice for a while. + for (char c : "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray()) { + possibleNames.add("" + c); + } + } + + /** (static) */ + public final Method nmsGetById; + + public final Method nmsGetMaterial; + public final boolean useBlockPosition; + public final Method nmsUpdateShape; + // Block bounds in the order the methods (used to) appear in the nms class. + /** If this is null, all other nmsGetMin/Max... methods are null too. */ + public final Method nmsGetMinX; + public final Method nmsGetMaxX; + public final Method nmsGetMinY; + public final Method nmsGetMaxY; + public final Method nmsGetMinZ; + public final Method nmsGetMaxZ; + + public ReflectBlock(ReflectBase base, ReflectBlockPosition blockPosition) throws ClassNotFoundException { + final Class clazz = Class.forName(base.nmsPackageName + ".Block"); + // byID (static) + nmsGetById = ReflectionUtil.getMethod(clazz, "getById", int.class); + // getMaterial + nmsGetMaterial = ReflectionUtil.getMethodNoArgs(clazz, "getMaterial"); + // updateShape + Method method = null; + Class clazzIBlockAccess = Class.forName(base.nmsPackageName + ".IBlockAccess"); + if (blockPosition != null) { + method = ReflectionUtil.getMethod(clazz, "updateShape", clazzIBlockAccess, blockPosition.nmsClass); + } + if (method == null) { + method = ReflectionUtil.getMethod(clazz, "updateShape", clazzIBlockAccess, int.class, int.class, int.class); + useBlockPosition = false; + } else { + useBlockPosition = true; + } + nmsUpdateShape = method; + // Block bounds fetching. The array uses the order the methods (used to) appear in the nms class. + String[] names = new String[] {"getMinX", "getMaxX", "getMinY", "getMaxY", "getMinZ", "getMaxZ"}; // FUTURE GUESS. + Method[] methods = tryBoundsMethods(clazz, names); + if (methods == null) { + names = guessBoundsMethodNames(clazz); + if (names != null) { + methods = tryBoundsMethods(clazz, names); + } + if (methods == null) { + methods = new Method[] {null, null, null, null, null, null}; + } + } + // TODO: Test which is which [ALLOW to configure and also save used ones to config, by mc version]. + // TODO: Dynamically test these ? [needs an extra world/space to place blocks inside of...] + if (ConfigManager.getConfigFile().getBoolean(ConfPaths.LOGGING_EXTENDED_STATUS)) { + NCPAPIProvider.getNoCheatPlusAPI().getLogManager().debug(Streams.INIT, "[NoCheatPlus] ReflectBlock: Use methods for shape: " + StringUtil.join(Arrays.asList(names), ", ")); + } + this.nmsGetMinX = methods[0]; + this.nmsGetMaxX = methods[1]; + this.nmsGetMinY = methods[2]; + this.nmsGetMaxY = methods[3]; + this.nmsGetMinZ = methods[4]; + this.nmsGetMaxZ = methods[5]; + } + + /** + * + * @param names + * @return null on failure, otherwise the methods in order. + */ + private Method[] tryBoundsMethods(Class clazz, String[] names) { + Method[] methods = new Method[6]; + for (int i = 0; i < 6; i++) { + methods[i] = ReflectionUtil.getMethodNoArgs(clazz, names[i], double.class); + if (methods[i] == null) { + return null; + } + } + return methods; + } + + private String[] guessBoundsMethodNames(Class clazz) { + // Filter accepted method names. + List names = new ArrayList(); + for (Method method : clazz.getMethods()) { + if (method.getReturnType() == double.class && method.getParameterTypes().length == 0 && possibleNames.contains(method.getName())) { + names.add(method.getName()); + } + } + if (names.size() < 6) { + return null; + } + // Sort in the expected order. + Collections.sort(names, new Comparator() { + @Override + public int compare(String o1, String o2) { + return Integer.compare(possibleNames.indexOf(o1), possibleNames.indexOf(o2)); + } + }); + // Test for a sequence of exactly 6 consecutive entries. + int startIndex = 0; + if (names.size() > 6) { + // Attempt to guess the start index. Currently FUTURE, as there are only six such methods. + startIndex = -1; // Start index within list (!). + int lastIndex = -2; // Index of a sequence within possibleNames. + int currentStart = -1; // Possibly incomplete sequence. + for (int i = 0; i < names.size(); i++) { + String name = names.get(i); + int nameIndex = possibleNames.indexOf(name); + if (nameIndex - lastIndex == 1) { + if (currentStart == -1) { + currentStart = nameIndex - 1; + } else { + int length = nameIndex - currentStart + 1; + if (length > 6) { + // Can't decide (too many). + return null; + } + else if (length == 6) { + if (startIndex != -1) { + // Can't decide (too many). + return null; + } else { + startIndex = i + 1 - length; + // (Not reset currentStart to allow detecting too long sequences.) + } + } + } + } else { + currentStart = -1; + } + lastIndex = nameIndex; + } + if (startIndex == -1) { + return null; + } + } + String[] res = new String[6]; + for (int i = 0; i < 6; i++) { + res[i] = names.get(startIndex + i); + } + return res; + } + +} diff --git a/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/cbreflect/reflect/ReflectBlockPosition.java b/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/cbreflect/reflect/ReflectBlockPosition.java new file mode 100644 index 00000000..ec3b4057 --- /dev/null +++ b/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/cbreflect/reflect/ReflectBlockPosition.java @@ -0,0 +1,18 @@ +package fr.neatmonster.nocheatplus.compat.cbreflect.reflect; + +import java.lang.reflect.Constructor; + +import fr.neatmonster.nocheatplus.utilities.ReflectionUtil; + +public class ReflectBlockPosition { + + public final Class nmsClass; + + public final Constructor new_nmsBlockPosition; + + public ReflectBlockPosition(ReflectBase base) throws ClassNotFoundException { + nmsClass = Class.forName(base.nmsPackageName + ".BlockPosition"); + new_nmsBlockPosition = ReflectionUtil.getConstructor(nmsClass, int.class, int.class, int.class); + } + +} diff --git a/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/cbreflect/reflect/ReflectDamageSource.java b/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/cbreflect/reflect/ReflectDamageSource.java new file mode 100644 index 00000000..5193d759 --- /dev/null +++ b/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/cbreflect/reflect/ReflectDamageSource.java @@ -0,0 +1,20 @@ +package fr.neatmonster.nocheatplus.compat.cbreflect.reflect; + +import java.lang.reflect.Field; + +import fr.neatmonster.nocheatplus.utilities.ReflectionUtil; + +public class ReflectDamageSource { + + public final Class nmsClass; + + public final Object nmsFALL; + + public ReflectDamageSource(ReflectBase base) throws ClassNotFoundException { + Class nmsClass = Class.forName(base.nmsPackageName + ".DamageSource"); + this.nmsClass = nmsClass; + Field field = ReflectionUtil.getField(nmsClass, "FALL", nmsClass); + nmsFALL = field == null ? null : ReflectionUtil.get(field, nmsClass, null); + } + +} diff --git a/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/cbreflect/reflect/ReflectEntity.java b/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/cbreflect/reflect/ReflectEntity.java new file mode 100644 index 00000000..7f7794c6 --- /dev/null +++ b/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/cbreflect/reflect/ReflectEntity.java @@ -0,0 +1,46 @@ +package fr.neatmonster.nocheatplus.compat.cbreflect.reflect; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +import fr.neatmonster.nocheatplus.utilities.ReflectionUtil; + +/** + * Reflection for entity. + * @author asofold + * + */ +public class ReflectEntity { + + public final Field nmsDead; + + public final Method obcGetHandle; + + public final Method nmsDamageEntity; + + public final boolean nmsDamageEntityInt; + + public ReflectEntity(ReflectBase base, ReflectDamageSource damageSource) throws ClassNotFoundException { + this(base, damageSource, Class.forName(base.obcPackageName + ".entity.CraftEntity"), Class.forName(base.nmsPackageName + ".Entity")); + + } + + public ReflectEntity(ReflectBase base, ReflectDamageSource damageSource, Class obcClass, Class nmsClass) throws ClassNotFoundException { + // getHandle + obcGetHandle = ReflectionUtil.getMethodNoArgs(obcClass, "getHandle"); + // TODO: Consider throw in case of getHandle missing. + + // dead + nmsDead = ReflectionUtil.getField(nmsClass, "dead", boolean.class); + + // damageEntity(...) + nmsDamageEntity = ReflectionUtil.getMethod(nmsClass, "damageEntity", + new Class[]{damageSource.nmsClass, float.class}, new Class[]{damageSource.nmsClass, int.class}); + if (nmsDamageEntity != null) { + nmsDamageEntityInt = nmsDamageEntity.getParameterTypes()[1] == int.class; + } else { + nmsDamageEntityInt = true; // Uncertain. + } + } + +} diff --git a/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/cbreflect/reflect/ReflectGenericAttributes.java b/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/cbreflect/reflect/ReflectGenericAttributes.java new file mode 100644 index 00000000..c1628445 --- /dev/null +++ b/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/cbreflect/reflect/ReflectGenericAttributes.java @@ -0,0 +1,22 @@ +package fr.neatmonster.nocheatplus.compat.cbreflect.reflect; + +import java.lang.reflect.Field; + +import fr.neatmonster.nocheatplus.utilities.ReflectionUtil; + +public class ReflectGenericAttributes { + + public final Object nmsMOVEMENT_SPEED; + + public ReflectGenericAttributes(ReflectBase base) throws ClassNotFoundException { + Class clazz = Class.forName(base.nmsPackageName + ".GenericAttributes"); + Field field = ReflectionUtil.getField(clazz, "MOVEMENT_SPEED", null); + if (field != null) { + nmsMOVEMENT_SPEED = ReflectionUtil.get(field, clazz, null); + } + else { + nmsMOVEMENT_SPEED = null; + } + } + +} diff --git a/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/cbreflect/reflect/ReflectHelper.java b/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/cbreflect/reflect/ReflectHelper.java new file mode 100644 index 00000000..95f7b26a --- /dev/null +++ b/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/cbreflect/reflect/ReflectHelper.java @@ -0,0 +1,411 @@ +package fr.neatmonster.nocheatplus.compat.cbreflect.reflect; + +import java.lang.reflect.Field; +import java.util.LinkedList; +import java.util.List; + +import org.bukkit.World; +import org.bukkit.entity.Player; + +import fr.neatmonster.nocheatplus.NCPAPIProvider; +import fr.neatmonster.nocheatplus.compat.AlmostBoolean; +import fr.neatmonster.nocheatplus.config.ConfPaths; +import fr.neatmonster.nocheatplus.config.ConfigManager; +import fr.neatmonster.nocheatplus.logging.Streams; +import fr.neatmonster.nocheatplus.utilities.AttribUtil; +import fr.neatmonster.nocheatplus.utilities.ReflectionUtil; +import fr.neatmonster.nocheatplus.utilities.StringUtil; + +/** + * More handy high level methods throwing one type of exception. + * @author asofold + * + */ +public class ReflectHelper { + + // TODO: Many possible exceptions are not yet caught (...). + // TODO: Some places: should actually try-catch and fail() instead of default values and null return values. + + /** Failure to use / apply [ / setup ? ]. */ + public static class ReflectFailureException extends RuntimeException { + + /** + * + */ + private static final long serialVersionUID = -3934791920291782604L; + + public ReflectFailureException() { + super(); + } + + public ReflectFailureException(ClassNotFoundException ex) { + super(ex); + } + + // TODO: Might add a sub-error enum/code/thing support. + + } + + protected final ReflectBase reflectBase; + + protected final ReflectBlockPosition reflectBlockPosition; + protected final ReflectBlock reflectBlock; + protected final ReflectMaterial reflectMaterial; + protected final ReflectWorld reflectWorld; + + protected final ReflectDamageSource reflectDamageSource; + protected final ReflectEntity reflectEntity; + protected final ReflectPlayer reflectPlayer; + + protected final ReflectGenericAttributes reflectGenericAttributes; + protected final ReflectAttributeInstance reflectAttributeInstance; + protected final ReflectAttributeModifier reflectAttributeModifier; + protected final boolean hasAttributes; + + public ReflectHelper() throws ReflectFailureException { + // TODO: Allow some to not work? + try { + this.reflectBase = new ReflectBase(); + ReflectBlockPosition reflectBlockPosition = null; + try { + reflectBlockPosition = new ReflectBlockPosition(this.reflectBase); + } + catch (ClassNotFoundException ex) {} + this.reflectBlockPosition = reflectBlockPosition; + this.reflectBlock = new ReflectBlock(this.reflectBase, this.reflectBlockPosition); + this.reflectMaterial = new ReflectMaterial(this.reflectBase); + this.reflectWorld = new ReflectWorld(this.reflectBase); + + this.reflectDamageSource = new ReflectDamageSource(this.reflectBase); + this.reflectEntity = new ReflectEntity(this.reflectBase, this.reflectDamageSource); + this.reflectPlayer = new ReflectPlayer(this.reflectBase, this.reflectDamageSource); + ReflectGenericAttributes reflectGenericAttributes = null; + ReflectAttributeInstance reflectAttributeInstance = null; + ReflectAttributeModifier reflectAttributeModifier = null; + boolean hasAttributes = false; + try { + reflectGenericAttributes = new ReflectGenericAttributes(this.reflectBase); + reflectAttributeInstance = new ReflectAttributeInstance(this.reflectBase); + reflectAttributeModifier = new ReflectAttributeModifier(this.reflectBase); + hasAttributes = true; // TODO: null checks (...). + } + catch (ClassNotFoundException ex) {} + this.reflectGenericAttributes = reflectGenericAttributes; + this.reflectAttributeInstance = reflectAttributeInstance; + this.reflectAttributeModifier = reflectAttributeModifier; + this.hasAttributes = hasAttributes; + } + catch (ClassNotFoundException ex) { + throw new ReflectFailureException(ex); + } + if (ConfigManager.getConfigFile().getBoolean(ConfPaths.LOGGING_EXTENDED_STATUS)) { + List parts = new LinkedList(); + for (Field rootField : this.getClass().getDeclaredFields()) { + boolean accessible = rootField.isAccessible(); + if (!accessible) { + rootField.setAccessible(true); + } + Object obj = ReflectionUtil.get(rootField, this, null); + if (obj == null) { + parts.add("(Not available: " + rootField.getName() + ")"); + continue; + } + else if (rootField.getName().startsWith("reflect")) { + Class clazz = obj.getClass(); + // TODO: Skip attributes silently before 1.6.1 (and not unknown version). + for (Field field : clazz.getFields()) { + if (ReflectionUtil.get(field, obj, null) == null) { + parts.add(clazz.getName() + "." + field.getName()); + } + } + } + if (!accessible) { + rootField.setAccessible(false); + } + } + if (!parts.isEmpty()) { + parts.add(0, "[NoCheatPlus] CompatCBReflect: The following properties could not be set:"); + NCPAPIProvider.getNoCheatPlusAPI().getLogManager().warning(Streams.INIT, StringUtil.join(parts, "\n")); + } + } + } + + /** + * Quick fail with exception. + */ + private void fail() { + throw new ReflectFailureException(); + } + + public Object getHandle(Player player) { + // TODO: CraftPlayer check (isAssignableFrom)? + if (this.reflectPlayer.obcGetHandle == null) { + fail(); + } + Object handle = ReflectionUtil.invokeMethodNoArgs(this.reflectPlayer.obcGetHandle, player); + if (handle == null) { + fail(); + } + return handle; + } + + public double nmsPlayer_getHealth(Object handle) { + if (this.reflectPlayer.nmsGetHealth == null) { + fail(); + } + return ((Number) ReflectionUtil.invokeMethodNoArgs(this.reflectPlayer.nmsGetHealth, handle)).doubleValue(); + } + + public boolean nmsPlayer_dead(Object handle) { + if (this.reflectPlayer.nmsDead == null) { + fail(); + } + return ReflectionUtil.getBoolean(this.reflectPlayer.nmsDead, handle, true); + } + + /** + * Set the value for the dead field. + * @param handle + * @param value + */ + public void nmsPlayer_dead(Object handle, boolean value) { + if (this.reflectPlayer.nmsDead == null || !ReflectionUtil.set(this.reflectPlayer.nmsDead, handle, value)) { + fail(); + } + } + + /** + * Set the value for the dead field. + * @param handle + * @param value + */ + public void nmsPlayer_deathTicks(Object handle, int value) { + if (this.reflectPlayer.nmsDeathTicks == null || !ReflectionUtil.set(this.reflectPlayer.nmsDeathTicks, handle, value)) { + fail(); + } + } + + public boolean canDealFallDamage() { + return this.reflectPlayer.nmsDamageEntity != null && this.reflectDamageSource.nmsFALL != null; + } + + public void dealFallDamage(Player player, double damage) { + if (this.reflectDamageSource.nmsFALL == null) { + fail(); + } + Object handle = getHandle(player); + nmsPlayer_dealDamage(handle, this.reflectDamageSource.nmsFALL, damage); + } + + public void nmsPlayer_dealDamage(Object handle, Object damage_source, double damage) { + if (this.reflectPlayer.nmsDamageEntity == null) { + fail(); + } + if (this.reflectPlayer.nmsDamageEntityInt) { + ReflectionUtil.invokeMethod(this.reflectPlayer.nmsDamageEntity, handle, damage_source, (int) damage); + } else { + ReflectionUtil.invokeMethod(this.reflectPlayer.nmsDamageEntity, handle, damage_source, (float) damage); + } + } + + public int getInvulnerableTicks(Player player) { + if (this.reflectPlayer.nmsInvulnerableTicks == null) { + fail(); + } + Object handle = getHandle(player); + return ReflectionUtil.getInt(this.reflectPlayer.nmsInvulnerableTicks, handle, player.getNoDamageTicks() / 2); + } + + public void setInvulnerableTicks(Player player, int ticks) { + if (this.reflectPlayer.nmsInvulnerableTicks == null) { + fail(); + } + Object handle = getHandle(player); + if (!ReflectionUtil.set(this.reflectPlayer.nmsInvulnerableTicks, handle, ticks)) { + fail(); + } + } + + /** + * Get the speed attribute (MOVEMENT_SPEED) for a player. + * @param handle EntityPlayer + * @return AttributeInstance + */ + public Object getMovementSpeedAttributeInstance(Player player) { + if (!hasAttributes || this.reflectPlayer.nmsGetAttributeInstance == null || this.reflectGenericAttributes.nmsMOVEMENT_SPEED == null) { + fail(); + } + return ReflectionUtil.invokeMethod(this.reflectPlayer.nmsGetAttributeInstance, getHandle(player), this.reflectGenericAttributes.nmsMOVEMENT_SPEED); + } + + /** + * + * @param player + * @param removeSprint If to calculate away the sprint boost modifier. + * @return + */ + public double getSpeedAttributeMultiplier(Player player, boolean removeSprint) { + if (!hasAttributes || this.reflectAttributeInstance.nmsGetValue == null || + this.reflectAttributeInstance.nmsGetBaseValue == null) { + fail(); + } + Object attributeInstance = getMovementSpeedAttributeInstance(player); + double val = ((Double) ReflectionUtil.invokeMethodNoArgs(this.reflectAttributeInstance.nmsGetValue, attributeInstance)).doubleValue() / ((Double) ReflectionUtil.invokeMethodNoArgs(this.reflectAttributeInstance.nmsGetBaseValue, attributeInstance)).doubleValue(); + if (!removeSprint) { + return val; + } + else { + return val / nmsAttributeInstance_getAttributeModifierMultiplier(attributeInstance); + } + } + + public double getSprintAttributeMultiplier(Player player) { + + if (!hasAttributes || this.reflectAttributeModifier.nmsGetOperation == null || this.reflectAttributeModifier.nmsGetValue == null) { + fail(); + } + Object attributeInstance = getMovementSpeedAttributeInstance(player); + if (attributeInstance == null) { + fail(); + } + return nmsAttributeInstance_getAttributeModifierMultiplier(attributeInstance); + } + + /** + * (Not an existing method.) + * @param attributeInstance + */ + public double nmsAttributeInstance_getAttributeModifierMultiplier(Object attributeInstance) { + if (this.reflectAttributeInstance.nmsGetAttributeModifier == null) { + fail(); + } + // TODO: Need to fall back in case of errors. + Object mod = ReflectionUtil.invokeMethod(this.reflectAttributeInstance.nmsGetAttributeModifier, attributeInstance, AttribUtil.ID_SPRINT_BOOST); + if (mod == null) { + return 1.0; + } + else { + return AttribUtil.getMultiplier((Integer) ReflectionUtil.invokeMethodNoArgs(this.reflectAttributeModifier.nmsGetOperation, mod), (Double) ReflectionUtil.invokeMethodNoArgs(this.reflectAttributeModifier.nmsGetValue, mod)); + } + } + + public Object getHandle(World world) { + if (this.reflectWorld.obcGetHandle == null) { + fail(); + } + Object handle = ReflectionUtil.invokeMethodNoArgs(this.reflectWorld.obcGetHandle, world); + if (handle == null) { + fail(); + } + return handle; + } + + public Object nmsBlockPosition(int x, int y, int z) { + if (!this.reflectBlock.useBlockPosition || this.reflectBlockPosition.new_nmsBlockPosition == null) { + fail(); + } + Object blockPos = ReflectionUtil.newInstance(this.reflectBlockPosition.new_nmsBlockPosition, x, y, z); + if (blockPos == null) { + fail(); + } + return blockPos; + } + + /** + * + * @param id + * @return Block instance (could be null). + */ + public Object nmsBlock_getById(int id) { + if (this.reflectBlock.nmsGetById == null) { + fail(); + } + return ReflectionUtil.invokeMethod(this.reflectBlock.nmsGetById, null, id); + } + + public Object nmsBlock_getMaterial(Object block) { + if (this.reflectBlock.nmsGetMaterial == null) { + fail(); + } + return ReflectionUtil.invokeMethodNoArgs(this.reflectBlock.nmsGetMaterial, block); + } + + public void nmsBlock_updateShape(Object block, Object iBlockAccess, int x, int y, int z) { + if (this.reflectBlock.nmsUpdateShape == null) { + fail(); + } + if (this.reflectBlock.useBlockPosition) { + ReflectionUtil.invokeMethod(this.reflectBlock.nmsUpdateShape, block, iBlockAccess, nmsBlockPosition(x, y, z)); + } else { + ReflectionUtil.invokeMethod(this.reflectBlock.nmsUpdateShape, block, iBlockAccess, x, y, z); + } + } + + public boolean nmsMaterial_isSolid(Object material) { + if (this.reflectMaterial.nmsIsSolid == null) { + fail(); + } + return (Boolean) ReflectionUtil.invokeMethodNoArgs(this.reflectMaterial.nmsIsSolid, material); + } + + public boolean nmsMaterial_isLiquid(Object material) { + if (this.reflectMaterial.nmsIsLiquid == null) { + fail(); + } + return (Boolean) ReflectionUtil.invokeMethodNoArgs(this.reflectMaterial.nmsIsLiquid, material); + } + + public AlmostBoolean isBlockSolid(int id) { + Object obj = nmsBlock_getById(id); + if (obj == null) { + return AlmostBoolean.MAYBE; + } + obj = nmsBlock_getMaterial(obj); + if (obj == null) { + return AlmostBoolean.MAYBE; + } + return AlmostBoolean.match(nmsMaterial_isSolid(obj)); + } + + public AlmostBoolean isBlockLiquid(int id) { + Object obj = nmsBlock_getById(id); + if (obj == null) { + return AlmostBoolean.MAYBE; + } + obj = nmsBlock_getMaterial(obj); + if (obj == null) { + return AlmostBoolean.MAYBE; + } + return AlmostBoolean.match(nmsMaterial_isLiquid(obj)); + } + + /** + * (Not a method in world types.) + * @param nmsWorld + * @param typeId + * @param x + * @param y + * @param z + * @return double[6] minX, minY, minZ, maxX, maxY, maxZ. Returns null for cases like air/unspecified. + */ + public double[] nmsWorld_fetchBlockShape(Object nmsWorld, int id, int x, int y, int z) { + if (this.reflectBlock.nmsGetMinX == null) { // Use nmsGetMinX as reference for all six methods (!). + fail(); + } + Object block = nmsBlock_getById(id); + if (block == null) { + return null; + } + nmsBlock_updateShape(block, nmsWorld, x, y, z); + // TODO: The methods could return null [better try-catch here]. + return new double[] { + ((Number) ReflectionUtil.invokeMethodNoArgs(this.reflectBlock.nmsGetMinX, block)).doubleValue(), + ((Number) ReflectionUtil.invokeMethodNoArgs(this.reflectBlock.nmsGetMinY, block)).doubleValue(), + ((Number) ReflectionUtil.invokeMethodNoArgs(this.reflectBlock.nmsGetMinZ, block)).doubleValue(), + ((Number) ReflectionUtil.invokeMethodNoArgs(this.reflectBlock.nmsGetMaxX, block)).doubleValue(), + ((Number) ReflectionUtil.invokeMethodNoArgs(this.reflectBlock.nmsGetMaxY, block)).doubleValue(), + ((Number) ReflectionUtil.invokeMethodNoArgs(this.reflectBlock.nmsGetMaxZ, block)).doubleValue(), + }; + } + +} diff --git a/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/cbreflect/reflect/ReflectLivingEntity.java b/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/cbreflect/reflect/ReflectLivingEntity.java new file mode 100644 index 00000000..6057a454 --- /dev/null +++ b/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/cbreflect/reflect/ReflectLivingEntity.java @@ -0,0 +1,20 @@ +package fr.neatmonster.nocheatplus.compat.cbreflect.reflect; + +import java.lang.reflect.Method; + +import fr.neatmonster.nocheatplus.utilities.ReflectionUtil; + +public class ReflectLivingEntity extends ReflectEntity { + + public final Method nmsGetHealth; + + public ReflectLivingEntity(ReflectBase base, ReflectDamageSource damageSource) throws ClassNotFoundException { + this(base, damageSource, Class.forName(base.obcPackageName + ".entity.CraftLivingEntity"), Class.forName(base.nmsPackageName + ".EntityLiving")); + } + + public ReflectLivingEntity(ReflectBase base, ReflectDamageSource damageSource, Class obcClass, Class nmsClass) throws ClassNotFoundException { + super(base, damageSource, obcClass, nmsClass); + this.nmsGetHealth = ReflectionUtil.getMethodNoArgs(nmsClass, "getHealth"); + } + +} diff --git a/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/cbreflect/reflect/ReflectMaterial.java b/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/cbreflect/reflect/ReflectMaterial.java new file mode 100644 index 00000000..a741485c --- /dev/null +++ b/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/cbreflect/reflect/ReflectMaterial.java @@ -0,0 +1,18 @@ +package fr.neatmonster.nocheatplus.compat.cbreflect.reflect; + +import java.lang.reflect.Method; + +import fr.neatmonster.nocheatplus.utilities.ReflectionUtil; + +public class ReflectMaterial { + + public final Method nmsIsLiquid; + public final Method nmsIsSolid; + + public ReflectMaterial(ReflectBase base) throws ClassNotFoundException { + Class nmsClass = Class.forName(base.nmsPackageName + ".Material"); + nmsIsLiquid = ReflectionUtil.getMethodNoArgs(nmsClass, "isLiquid", boolean.class); + nmsIsSolid = ReflectionUtil.getMethodNoArgs(nmsClass, "isSolid", boolean.class); + } + +} diff --git a/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/cbreflect/reflect/ReflectPlayer.java b/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/cbreflect/reflect/ReflectPlayer.java new file mode 100644 index 00000000..868ac47f --- /dev/null +++ b/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/cbreflect/reflect/ReflectPlayer.java @@ -0,0 +1,37 @@ +package fr.neatmonster.nocheatplus.compat.cbreflect.reflect; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +import fr.neatmonster.nocheatplus.utilities.ReflectionUtil; + +public class ReflectPlayer extends ReflectLivingEntity { + + // Not sure: Living/Human entity. + public final Field nmsDeathTicks; + public final Field nmsInvulnerableTicks; + + public final Method nmsGetAttributeInstance; // TODO: LivingEntity + + public ReflectPlayer(ReflectBase base, ReflectDamageSource damageSource) throws ClassNotFoundException { + this(base, damageSource, Class.forName(base.obcPackageName + ".entity.CraftPlayer"), Class.forName(base.nmsPackageName + ".EntityPlayer")); + } + + public ReflectPlayer(ReflectBase base, ReflectDamageSource damageSource, Class obcClass, Class nmsClass) throws ClassNotFoundException { + super(base, damageSource, obcClass, nmsClass); + // TODO: invulnerable etc. + // deathTicks + nmsDeathTicks = ReflectionUtil.getField(nmsClass, "deathTicks", int.class); + nmsInvulnerableTicks = ReflectionUtil.getField(nmsClass, "invulnerableTicks", int.class); + + Method method; + try { + Class clazzIAttribute = Class.forName(base.nmsPackageName + ".IAttribute"); + method = ReflectionUtil.getMethod(nmsClass, "getAttributeInstance", clazzIAttribute); + } catch (ClassNotFoundException e) { + method = null; + } + nmsGetAttributeInstance = method; + } + +} diff --git a/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/cbreflect/reflect/ReflectWorld.java b/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/cbreflect/reflect/ReflectWorld.java new file mode 100644 index 00000000..7ab6945a --- /dev/null +++ b/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/cbreflect/reflect/ReflectWorld.java @@ -0,0 +1,18 @@ +package fr.neatmonster.nocheatplus.compat.cbreflect.reflect; + +import java.lang.reflect.Method; + +import fr.neatmonster.nocheatplus.utilities.ReflectionUtil; + +public class ReflectWorld { + + public final Method obcGetHandle; + + // nms - WorldServer: Used as IBlockAccess as well. + + public ReflectWorld(ReflectBase base) throws ClassNotFoundException { + Class obcClass = Class.forName(base.obcPackageName + ".CraftWorld"); + obcGetHandle = ReflectionUtil.getMethodNoArgs(obcClass, "getHandle"); + } + +} diff --git a/NCPCompatCB2512/src/main/java/fr/neatmonster/nocheatplus/compat/cb2512/MCAccessCB2512.java b/NCPCompatCB2512/src/main/java/fr/neatmonster/nocheatplus/compat/cb2512/MCAccessCB2512.java index 4b968f12..82683b60 100644 --- a/NCPCompatCB2512/src/main/java/fr/neatmonster/nocheatplus/compat/cb2512/MCAccessCB2512.java +++ b/NCPCompatCB2512/src/main/java/fr/neatmonster/nocheatplus/compat/cb2512/MCAccessCB2512.java @@ -168,7 +168,7 @@ public class MCAccessCB2512 implements MCAccess{ @Override public AlmostBoolean dealFallDamageFiresAnEvent() { - return AlmostBoolean.MAYBE; + return AlmostBoolean.NO; } // @Override diff --git a/NCPCompatCB2545/src/main/java/fr/neatmonster/nocheatplus/compat/cb2545/MCAccessCB2545.java b/NCPCompatCB2545/src/main/java/fr/neatmonster/nocheatplus/compat/cb2545/MCAccessCB2545.java index bb1f25cd..f42426cc 100644 --- a/NCPCompatCB2545/src/main/java/fr/neatmonster/nocheatplus/compat/cb2545/MCAccessCB2545.java +++ b/NCPCompatCB2545/src/main/java/fr/neatmonster/nocheatplus/compat/cb2545/MCAccessCB2545.java @@ -168,7 +168,7 @@ public class MCAccessCB2545 implements MCAccess{ @Override public AlmostBoolean dealFallDamageFiresAnEvent() { - return AlmostBoolean.MAYBE; + return AlmostBoolean.NO; } // @Override diff --git a/NCPCompatCB2602/src/main/java/fr/neatmonster/nocheatplus/compat/cb2602/MCAccessCB2602.java b/NCPCompatCB2602/src/main/java/fr/neatmonster/nocheatplus/compat/cb2602/MCAccessCB2602.java index 1c005771..c8bfa73e 100644 --- a/NCPCompatCB2602/src/main/java/fr/neatmonster/nocheatplus/compat/cb2602/MCAccessCB2602.java +++ b/NCPCompatCB2602/src/main/java/fr/neatmonster/nocheatplus/compat/cb2602/MCAccessCB2602.java @@ -169,7 +169,7 @@ public class MCAccessCB2602 implements MCAccess{ @Override public AlmostBoolean dealFallDamageFiresAnEvent() { - return AlmostBoolean.MAYBE; + return AlmostBoolean.NO; } // @Override diff --git a/NCPCompatCB2645/src/main/java/fr/neatmonster/nocheatplus/compat/cb2645/MCAccessCB2645.java b/NCPCompatCB2645/src/main/java/fr/neatmonster/nocheatplus/compat/cb2645/MCAccessCB2645.java index e723ff0d..071fd57d 100644 --- a/NCPCompatCB2645/src/main/java/fr/neatmonster/nocheatplus/compat/cb2645/MCAccessCB2645.java +++ b/NCPCompatCB2645/src/main/java/fr/neatmonster/nocheatplus/compat/cb2645/MCAccessCB2645.java @@ -170,7 +170,7 @@ public class MCAccessCB2645 implements MCAccess{ @Override public AlmostBoolean dealFallDamageFiresAnEvent() { - return AlmostBoolean.MAYBE; + return AlmostBoolean.NO; } // @Override diff --git a/NCPCompatCB2691/src/main/java/fr/neatmonster/nocheatplus/compat/cb2691/MCAccessCB2691.java b/NCPCompatCB2691/src/main/java/fr/neatmonster/nocheatplus/compat/cb2691/MCAccessCB2691.java index 100ed3a0..06863c54 100644 --- a/NCPCompatCB2691/src/main/java/fr/neatmonster/nocheatplus/compat/cb2691/MCAccessCB2691.java +++ b/NCPCompatCB2691/src/main/java/fr/neatmonster/nocheatplus/compat/cb2691/MCAccessCB2691.java @@ -172,7 +172,7 @@ public class MCAccessCB2691 implements MCAccess{ @Override public AlmostBoolean dealFallDamageFiresAnEvent() { - return AlmostBoolean.MAYBE; + return AlmostBoolean.NO; } // @Override diff --git a/NCPCompatCB2763/src/main/java/fr/neatmonster/nocheatplus/compat/cb2763/MCAccessCB2763.java b/NCPCompatCB2763/src/main/java/fr/neatmonster/nocheatplus/compat/cb2763/MCAccessCB2763.java index 42c1bced..fb80ad3b 100644 --- a/NCPCompatCB2763/src/main/java/fr/neatmonster/nocheatplus/compat/cb2763/MCAccessCB2763.java +++ b/NCPCompatCB2763/src/main/java/fr/neatmonster/nocheatplus/compat/cb2763/MCAccessCB2763.java @@ -171,7 +171,7 @@ public class MCAccessCB2763 implements MCAccess{ @Override public AlmostBoolean dealFallDamageFiresAnEvent() { - return AlmostBoolean.MAYBE; + return AlmostBoolean.NO; } // @Override diff --git a/NCPCompatCB2794/src/main/java/fr/neatmonster/nocheatplus/compat/cb2794/MCAccessCB2794.java b/NCPCompatCB2794/src/main/java/fr/neatmonster/nocheatplus/compat/cb2794/MCAccessCB2794.java index e05292c4..d74858b8 100644 --- a/NCPCompatCB2794/src/main/java/fr/neatmonster/nocheatplus/compat/cb2794/MCAccessCB2794.java +++ b/NCPCompatCB2794/src/main/java/fr/neatmonster/nocheatplus/compat/cb2794/MCAccessCB2794.java @@ -122,7 +122,13 @@ public class MCAccessCB2794 implements MCAccess{ @Override public double getSpeedAttributeMultiplier(Player player) { final AttributeInstance attr = ((CraftLivingEntity) player).getHandle().a(GenericAttributes.d); - return attr.e() / attr.b(); + double val = attr.e() / attr.b(); + final AttributeModifier mod = attr.a(AttribUtil.ID_SPRINT_BOOST); + if (mod == null) { + return val; + } else { + return val / AttribUtil.getMultiplier(mod.c(), mod.d()); + } } @Override @@ -176,7 +182,7 @@ public class MCAccessCB2794 implements MCAccess{ @Override public AlmostBoolean dealFallDamageFiresAnEvent() { - return AlmostBoolean.MAYBE; + return AlmostBoolean.NO; } // @Override diff --git a/NCPCompatCB2808/src/main/java/fr/neatmonster/nocheatplus/compat/cb2808/MCAccessCB2808.java b/NCPCompatCB2808/src/main/java/fr/neatmonster/nocheatplus/compat/cb2808/MCAccessCB2808.java index d2d814ca..c26fe7d4 100644 --- a/NCPCompatCB2808/src/main/java/fr/neatmonster/nocheatplus/compat/cb2808/MCAccessCB2808.java +++ b/NCPCompatCB2808/src/main/java/fr/neatmonster/nocheatplus/compat/cb2808/MCAccessCB2808.java @@ -123,7 +123,13 @@ public class MCAccessCB2808 implements MCAccess{ @Override public double getSpeedAttributeMultiplier(Player player) { final AttributeInstance attr = ((CraftLivingEntity) player).getHandle().getAttributeInstance(GenericAttributes.d); - return attr.getValue() / attr.b(); + final double val = attr.getValue() / attr.b(); + final AttributeModifier mod = attr.a(AttribUtil.ID_SPRINT_BOOST); + if (mod == null) { + return val; + } else { + return val / AttribUtil.getMultiplier(mod.c(), mod.d()); + } } @Override @@ -177,7 +183,7 @@ public class MCAccessCB2808 implements MCAccess{ @Override public AlmostBoolean dealFallDamageFiresAnEvent() { - return AlmostBoolean.MAYBE; + return AlmostBoolean.NO; } // @Override diff --git a/NCPCompatCB2882/src/main/java/fr/neatmonster/nocheatplus/compat/cb2882/MCAccessCB2882.java b/NCPCompatCB2882/src/main/java/fr/neatmonster/nocheatplus/compat/cb2882/MCAccessCB2882.java index e2e2941b..802e7aee 100644 --- a/NCPCompatCB2882/src/main/java/fr/neatmonster/nocheatplus/compat/cb2882/MCAccessCB2882.java +++ b/NCPCompatCB2882/src/main/java/fr/neatmonster/nocheatplus/compat/cb2882/MCAccessCB2882.java @@ -124,7 +124,13 @@ public class MCAccessCB2882 implements MCAccess{ @Override public double getSpeedAttributeMultiplier(Player player) { final AttributeInstance attr = ((CraftLivingEntity) player).getHandle().getAttributeInstance(GenericAttributes.d); - return attr.getValue() / attr.b(); + final double val = attr.getValue() / attr.b(); + final AttributeModifier mod = attr.a(AttribUtil.ID_SPRINT_BOOST); + if (mod == null) { + return val; + } else { + return val / AttribUtil.getMultiplier(mod.c(), mod.d()); + } } @Override @@ -178,7 +184,7 @@ public class MCAccessCB2882 implements MCAccess{ @Override public AlmostBoolean dealFallDamageFiresAnEvent() { - return AlmostBoolean.MAYBE; + return AlmostBoolean.NO; } // @Override diff --git a/NCPCompatCB2922/src/main/java/fr/neatmonster/nocheatplus/compat/cb2922/MCAccessCB2922.java b/NCPCompatCB2922/src/main/java/fr/neatmonster/nocheatplus/compat/cb2922/MCAccessCB2922.java index 2a549a7c..b4db1d54 100644 --- a/NCPCompatCB2922/src/main/java/fr/neatmonster/nocheatplus/compat/cb2922/MCAccessCB2922.java +++ b/NCPCompatCB2922/src/main/java/fr/neatmonster/nocheatplus/compat/cb2922/MCAccessCB2922.java @@ -124,7 +124,13 @@ public class MCAccessCB2922 implements MCAccess{ @Override public double getSpeedAttributeMultiplier(Player player) { final AttributeInstance attr = ((CraftLivingEntity) player).getHandle().getAttributeInstance(GenericAttributes.d); - return attr.getValue() / attr.b(); + final double val = attr.getValue() / attr.b(); + final AttributeModifier mod = attr.a(AttribUtil.ID_SPRINT_BOOST); + if (mod == null) { + return val; + } else { + return val / AttribUtil.getMultiplier(mod.c(), mod.d()); + } } @Override @@ -178,7 +184,7 @@ public class MCAccessCB2922 implements MCAccess{ @Override public AlmostBoolean dealFallDamageFiresAnEvent() { - return AlmostBoolean.MAYBE; + return AlmostBoolean.NO; } // @Override diff --git a/NCPCompatCB3026/src/main/java/fr/neatmonster/nocheatplus/compat/cb3026/MCAccessCB3026.java b/NCPCompatCB3026/src/main/java/fr/neatmonster/nocheatplus/compat/cb3026/MCAccessCB3026.java index 5862e0c8..58796bad 100644 --- a/NCPCompatCB3026/src/main/java/fr/neatmonster/nocheatplus/compat/cb3026/MCAccessCB3026.java +++ b/NCPCompatCB3026/src/main/java/fr/neatmonster/nocheatplus/compat/cb3026/MCAccessCB3026.java @@ -1,13 +1,14 @@ package fr.neatmonster.nocheatplus.compat.cb3026; +import net.minecraft.server.v1_7_R2.AttributeInstance; +import net.minecraft.server.v1_7_R2.AttributeModifier; import net.minecraft.server.v1_7_R2.AxisAlignedBB; import net.minecraft.server.v1_7_R2.Block; import net.minecraft.server.v1_7_R2.DamageSource; import net.minecraft.server.v1_7_R2.EntityComplexPart; import net.minecraft.server.v1_7_R2.EntityPlayer; -import net.minecraft.server.v1_7_R2.MobEffectList; -import net.minecraft.server.v1_7_R2.AttributeModifier; import net.minecraft.server.v1_7_R2.GenericAttributes; +import net.minecraft.server.v1_7_R2.MobEffectList; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -15,8 +16,8 @@ import org.bukkit.World; import org.bukkit.command.CommandMap; import org.bukkit.craftbukkit.v1_7_R2.CraftServer; import org.bukkit.craftbukkit.v1_7_R2.entity.CraftEntity; -import org.bukkit.craftbukkit.v1_7_R2.entity.CraftPlayer; import org.bukkit.craftbukkit.v1_7_R2.entity.CraftLivingEntity; +import org.bukkit.craftbukkit.v1_7_R2.entity.CraftPlayer; import org.bukkit.entity.Entity; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; @@ -122,8 +123,14 @@ public class MCAccessCB3026 implements MCAccess{ @Override public double getSpeedAttributeMultiplier(Player player) { - // TODO: Implement. - return 1.0; + final AttributeInstance attr = ((CraftLivingEntity) player).getHandle().getAttributeInstance(GenericAttributes.d); + final double val = attr.getValue() / attr.b(); + final AttributeModifier mod = attr.a(AttribUtil.ID_SPRINT_BOOST); + if (mod == null) { + return val; + } else { + return val / AttribUtil.getMultiplier(mod.c(), mod.d()); + } } @Override diff --git a/NCPCompatCB3043/src/main/java/fr/neatmonster/nocheatplus/compat/cb3043/MCAccessCB3043.java b/NCPCompatCB3043/src/main/java/fr/neatmonster/nocheatplus/compat/cb3043/MCAccessCB3043.java index 3d10ef0f..18739170 100644 --- a/NCPCompatCB3043/src/main/java/fr/neatmonster/nocheatplus/compat/cb3043/MCAccessCB3043.java +++ b/NCPCompatCB3043/src/main/java/fr/neatmonster/nocheatplus/compat/cb3043/MCAccessCB3043.java @@ -143,7 +143,13 @@ public class MCAccessCB3043 implements MCAccess{ @Override public double getSpeedAttributeMultiplier(Player player) { final AttributeInstance attr = ((CraftLivingEntity) player).getHandle().getAttributeInstance(GenericAttributes.d); - return attr.getValue() / attr.b(); + final double val = attr.getValue() / attr.b(); + final AttributeModifier mod = attr.a(AttribUtil.ID_SPRINT_BOOST); + if (mod == null) { + return val; + } else { + return val / AttribUtil.getMultiplier(mod.c(), mod.d()); + } } @Override diff --git a/NCPCompatCB3100/src/main/java/fr/neatmonster/nocheatplus/compat/cb3100/MCAccessCB3100.java b/NCPCompatCB3100/src/main/java/fr/neatmonster/nocheatplus/compat/cb3100/MCAccessCB3100.java index 3d00e3ec..1b0bd2fc 100644 --- a/NCPCompatCB3100/src/main/java/fr/neatmonster/nocheatplus/compat/cb3100/MCAccessCB3100.java +++ b/NCPCompatCB3100/src/main/java/fr/neatmonster/nocheatplus/compat/cb3100/MCAccessCB3100.java @@ -145,7 +145,13 @@ public class MCAccessCB3100 implements MCAccess{ @Override public double getSpeedAttributeMultiplier(final Player player) { final AttributeInstance attr = ((CraftLivingEntity) player).getHandle().getAttributeInstance(GenericAttributes.d); - return attr.getValue() / attr.b(); + final double val = attr.getValue() / attr.b(); + final AttributeModifier mod = attr.a(AttribUtil.ID_SPRINT_BOOST); + if (mod == null) { + return val; + } else { + return val / AttribUtil.getMultiplier(mod.c(), mod.d()); + } } @Override diff --git a/NCPCompatCBDev/src/main/java/fr/neatmonster/nocheatplus/compat/cbdev/BlockCacheCBDev.java b/NCPCompatCBDev/src/main/java/fr/neatmonster/nocheatplus/compat/cbdev/BlockCacheCBDev.java index 40ece9a6..0923d8a0 100644 --- a/NCPCompatCBDev/src/main/java/fr/neatmonster/nocheatplus/compat/cbdev/BlockCacheCBDev.java +++ b/NCPCompatCBDev/src/main/java/fr/neatmonster/nocheatplus/compat/cbdev/BlockCacheCBDev.java @@ -21,7 +21,7 @@ import fr.neatmonster.nocheatplus.utilities.BlockCache; public class BlockCacheCBDev extends BlockCache implements IBlockAccess{ protected net.minecraft.server.v1_8_R3.WorldServer world; - protected World bukkitWorld; // WHACKS + protected World bukkitWorld; public BlockCacheCBDev(World world) { setAccess(world); diff --git a/NCPCompatCBDev/src/main/java/fr/neatmonster/nocheatplus/compat/cbdev/MCAccessCBDev.java b/NCPCompatCBDev/src/main/java/fr/neatmonster/nocheatplus/compat/cbdev/MCAccessCBDev.java index 6c4f23e7..d89bfd61 100644 --- a/NCPCompatCBDev/src/main/java/fr/neatmonster/nocheatplus/compat/cbdev/MCAccessCBDev.java +++ b/NCPCompatCBDev/src/main/java/fr/neatmonster/nocheatplus/compat/cbdev/MCAccessCBDev.java @@ -31,26 +31,52 @@ import fr.neatmonster.nocheatplus.utilities.ReflectionUtil; public class MCAccessCBDev implements MCAccess{ /** - * Constructor to let it fail. + * Test for availability in constructor. */ public MCAccessCBDev() { +// try { getCommandMap(); - ReflectionUtil.checkMembers("net.minecraft.server.v1_8_R3.", new String[] {"Entity" , "dead"}); + ReflectionUtil.checkMembers("net.minecraft.server.v1_8_R3.", + new String[] {"Entity" , "length", "width", "locY"}); + ReflectionUtil.checkMembers("net.minecraft.server.v1_8_R3.", + new String[] {"EntityPlayer" , "dead", "deathTicks", "invulnerableTicks"}); // block bounds, original: minX, maxX, minY, maxY, minZ, maxZ + ReflectionUtil.checkMethodReturnTypesNoArgs(net.minecraft.server.v1_8_R3.EntityLiving.class, + new String[]{"getHeadHeight"}, float.class); + ReflectionUtil.checkMethodReturnTypesNoArgs(net.minecraft.server.v1_8_R3.EntityPlayer.class, + new String[]{"getHealth"}, float.class); ReflectionUtil.checkMethodReturnTypesNoArgs(net.minecraft.server.v1_8_R3.Block.class, new String[]{"B", "C", "D", "E", "F", "G"}, double.class); - // TODO: Nail it down further. + ReflectionUtil.checkMethodReturnTypesNoArgs(net.minecraft.server.v1_8_R3.AttributeInstance.class, + new String[]{"b"}, double.class); + ReflectionUtil.checkMethodReturnTypesNoArgs(net.minecraft.server.v1_8_R3.AttributeModifier.class, + new String[]{"c"}, int.class); + ReflectionUtil.checkMethodReturnTypesNoArgs(net.minecraft.server.v1_8_R3.AttributeModifier.class, + new String[]{"d"}, double.class); + ReflectionUtil.checkMethodReturnTypesNoArgs(net.minecraft.server.v1_8_R3.Material.class, + new String[]{"isSolid", "isLiquid"}, boolean.class); + ReflectionUtil.checkMethodReturnTypesNoArgs(net.minecraft.server.v1_8_R3.Block.class, + new String[]{"getMaterial"}, net.minecraft.server.v1_8_R3.Material.class); + // obc: getHandle() for CraftWorld, CraftPlayer, CraftEntity. + // nms: Several: AxisAlignedBB, WorldServer + // nms: Block.getById(int), BlockPosition(int, int, int), WorldServer.getEntities(Entity, AxisAlignedBB) + // nms: AttributeInstance.a(UUID), EntityComplexPart, EntityPlayer.getAttributeInstance(IAttribute). + +// } catch(Throwable t) { +// NCPAPIProvider.getNoCheatPlusAPI().getLogManager().severe(Streams.INIT, t); +// throw new RuntimeException("NO WERK"); +// } } @Override public String getMCVersion() { // 1.8.4-1.8.6 (1_8_R3) - return "1.8.3"; + return "1.8.4-1.8.7"; } @Override public String getServerVersionTag() { - return "Spigot-CB-DEV"; + return "Spigot-CB-1.8_R3"; } @Override @@ -146,7 +172,13 @@ public class MCAccessCBDev implements MCAccess{ @Override public double getSpeedAttributeMultiplier(Player player) { final AttributeInstance attr = ((CraftLivingEntity) player).getHandle().getAttributeInstance(GenericAttributes.MOVEMENT_SPEED); - return attr.getValue() / attr.b(); + final double val = attr.getValue() / attr.b(); + final AttributeModifier mod = attr.a(AttribUtil.ID_SPRINT_BOOST); + if (mod == null) { + return val; + } else { + return val / AttribUtil.getMultiplier(mod.c(), mod.d()); + } } @Override @@ -202,13 +234,13 @@ public class MCAccessCBDev implements MCAccess{ return AlmostBoolean.YES; } - // @Override - // public void correctDirection(final Player player) { - // final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); - // // Main direction. - // mcPlayer.yaw = LocUtil.correctYaw(mcPlayer.yaw); - // mcPlayer.pitch = LocUtil.correctPitch(mcPlayer.pitch); - // // Consider setting the lastYaw here too. - // } + // @Override + // public void correctDirection(final Player player) { + // final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); + // // Main direction. + // mcPlayer.yaw = LocUtil.correctYaw(mcPlayer.yaw); + // mcPlayer.pitch = LocUtil.correctPitch(mcPlayer.pitch); + // // Consider setting the lastYaw here too. + // } } diff --git a/NCPCompatSpigotCB1_8_R1/src/main/java/fr/neatmonster/nocheatplus/compat/spigotcb1_8_R1/MCAccessSpigotCB1_8_R1.java b/NCPCompatSpigotCB1_8_R1/src/main/java/fr/neatmonster/nocheatplus/compat/spigotcb1_8_R1/MCAccessSpigotCB1_8_R1.java index 34e4756f..a2a96037 100644 --- a/NCPCompatSpigotCB1_8_R1/src/main/java/fr/neatmonster/nocheatplus/compat/spigotcb1_8_R1/MCAccessSpigotCB1_8_R1.java +++ b/NCPCompatSpigotCB1_8_R1/src/main/java/fr/neatmonster/nocheatplus/compat/spigotcb1_8_R1/MCAccessSpigotCB1_8_R1.java @@ -146,7 +146,13 @@ public class MCAccessSpigotCB1_8_R1 implements MCAccess{ @Override public double getSpeedAttributeMultiplier(Player player) { final AttributeInstance attr = ((CraftLivingEntity) player).getHandle().getAttributeInstance(GenericAttributes.d); - return attr.getValue() / attr.b(); + final double val = attr.getValue() / attr.b(); + final AttributeModifier mod = attr.a(AttribUtil.ID_SPRINT_BOOST); + if (mod == null) { + return val; + } else { + return val / AttribUtil.getMultiplier(mod.c(), mod.d()); + } } @Override diff --git a/NCPCompatSpigotCB1_8_R2/src/main/java/fr/neatmonster/nocheatplus/compat/spigotcb1_8_R2/MCAccessSpigotCB1_8_R2.java b/NCPCompatSpigotCB1_8_R2/src/main/java/fr/neatmonster/nocheatplus/compat/spigotcb1_8_R2/MCAccessSpigotCB1_8_R2.java index 801473b7..ab4cab25 100644 --- a/NCPCompatSpigotCB1_8_R2/src/main/java/fr/neatmonster/nocheatplus/compat/spigotcb1_8_R2/MCAccessSpigotCB1_8_R2.java +++ b/NCPCompatSpigotCB1_8_R2/src/main/java/fr/neatmonster/nocheatplus/compat/spigotcb1_8_R2/MCAccessSpigotCB1_8_R2.java @@ -146,7 +146,13 @@ public class MCAccessSpigotCB1_8_R2 implements MCAccess{ @Override public double getSpeedAttributeMultiplier(Player player) { final AttributeInstance attr = ((CraftLivingEntity) player).getHandle().getAttributeInstance(GenericAttributes.d); - return attr.getValue() / attr.b(); + final double val = attr.getValue() / attr.b(); + final AttributeModifier mod = attr.a(AttribUtil.ID_SPRINT_BOOST); + if (mod == null) { + return val; + } else { + return val / AttribUtil.getMultiplier(mod.c(), mod.d()); + } } @Override diff --git a/NCPCompatSpigotCB1_8_R3/src/main/java/fr/neatmonster/nocheatplus/compat/spigotcb1_8_R3/BlockCacheSpigotCB1_8_R3.java b/NCPCompatSpigotCB1_8_R3/src/main/java/fr/neatmonster/nocheatplus/compat/spigotcb1_8_R3/BlockCacheSpigotCB1_8_R3.java index de4be53c..f1b93772 100644 --- a/NCPCompatSpigotCB1_8_R3/src/main/java/fr/neatmonster/nocheatplus/compat/spigotcb1_8_R3/BlockCacheSpigotCB1_8_R3.java +++ b/NCPCompatSpigotCB1_8_R3/src/main/java/fr/neatmonster/nocheatplus/compat/spigotcb1_8_R3/BlockCacheSpigotCB1_8_R3.java @@ -21,7 +21,7 @@ import fr.neatmonster.nocheatplus.utilities.BlockCache; public class BlockCacheSpigotCB1_8_R3 extends BlockCache implements IBlockAccess{ protected net.minecraft.server.v1_8_R3.WorldServer world; - protected World bukkitWorld; // WHACKS + protected World bukkitWorld; public BlockCacheSpigotCB1_8_R3(World world) { setAccess(world); @@ -53,7 +53,7 @@ public class BlockCacheSpigotCB1_8_R3 extends BlockCache implements IBlockAccess @Override public double[] fetchBounds(final int x, final int y, final int z){ - final int id = getTypeId(x, y, z); + final int id = getTypeId(x, y, z); final net.minecraft.server.v1_8_R3.Block block = net.minecraft.server.v1_8_R3.Block.getById(id); if (block == null) { // TODO: Convention for null bounds -> full ? diff --git a/NCPCompatSpigotCB1_8_R3/src/main/java/fr/neatmonster/nocheatplus/compat/spigotcb1_8_R3/MCAccessSpigotCB1_8_R3.java b/NCPCompatSpigotCB1_8_R3/src/main/java/fr/neatmonster/nocheatplus/compat/spigotcb1_8_R3/MCAccessSpigotCB1_8_R3.java index 4aac7166..40cdf1fc 100644 --- a/NCPCompatSpigotCB1_8_R3/src/main/java/fr/neatmonster/nocheatplus/compat/spigotcb1_8_R3/MCAccessSpigotCB1_8_R3.java +++ b/NCPCompatSpigotCB1_8_R3/src/main/java/fr/neatmonster/nocheatplus/compat/spigotcb1_8_R3/MCAccessSpigotCB1_8_R3.java @@ -31,21 +31,41 @@ import fr.neatmonster.nocheatplus.utilities.ReflectionUtil; public class MCAccessSpigotCB1_8_R3 implements MCAccess{ /** - * Constructor to let it fail. + * Test for availability in constructor. */ public MCAccessSpigotCB1_8_R3() { getCommandMap(); - ReflectionUtil.checkMembers("net.minecraft.server.v1_8_R3.", new String[] {"Entity" , "dead"}); + ReflectionUtil.checkMembers("net.minecraft.server.v1_8_R3.", + new String[] {"Entity" , "length", "width", "locY"}); + ReflectionUtil.checkMembers("net.minecraft.server.v1_8_R3.", + new String[] {"EntityPlayer" , "dead", "deathTicks", "invulnerableTicks"}); // block bounds, original: minX, maxX, minY, maxY, minZ, maxZ + ReflectionUtil.checkMethodReturnTypesNoArgs(net.minecraft.server.v1_8_R3.EntityLiving.class, + new String[]{"getHeadHeight"}, float.class); + ReflectionUtil.checkMethodReturnTypesNoArgs(net.minecraft.server.v1_8_R3.EntityPlayer.class, + new String[]{"getHealth"}, float.class); ReflectionUtil.checkMethodReturnTypesNoArgs(net.minecraft.server.v1_8_R3.Block.class, new String[]{"B", "C", "D", "E", "F", "G"}, double.class); - // TODO: Nail it down further. + ReflectionUtil.checkMethodReturnTypesNoArgs(net.minecraft.server.v1_8_R3.AttributeInstance.class, + new String[]{"b"}, double.class); + ReflectionUtil.checkMethodReturnTypesNoArgs(net.minecraft.server.v1_8_R3.AttributeModifier.class, + new String[]{"c"}, int.class); + ReflectionUtil.checkMethodReturnTypesNoArgs(net.minecraft.server.v1_8_R3.AttributeModifier.class, + new String[]{"d"}, double.class); + ReflectionUtil.checkMethodReturnTypesNoArgs(net.minecraft.server.v1_8_R3.Material.class, + new String[]{"isSolid", "isLiquid"}, boolean.class); + ReflectionUtil.checkMethodReturnTypesNoArgs(net.minecraft.server.v1_8_R3.Block.class, + new String[]{"getMaterial"}, net.minecraft.server.v1_8_R3.Material.class); + // obc: getHandle() for CraftWorld, CraftPlayer, CraftEntity. + // nms: Several: AxisAlignedBB, WorldServer + // nms: Block.getById(int), BlockPosition(int, int, int), WorldServer.getEntities(Entity, AxisAlignedBB) + // nms: AttributeInstance.a(UUID), EntityComplexPart, EntityPlayer.getAttributeInstance(IAttribute). } @Override public String getMCVersion() { // 1.8.4-1.8.6 (1_8_R3) - return "1.8.4-1.8.6"; + return "1.8.4-1.8.7"; } @Override @@ -146,7 +166,13 @@ public class MCAccessSpigotCB1_8_R3 implements MCAccess{ @Override public double getSpeedAttributeMultiplier(Player player) { final AttributeInstance attr = ((CraftLivingEntity) player).getHandle().getAttributeInstance(GenericAttributes.MOVEMENT_SPEED); - return attr.getValue() / attr.b(); + final double val = attr.getValue() / attr.b(); + final AttributeModifier mod = attr.a(AttribUtil.ID_SPRINT_BOOST); + if (mod == null) { + return val; + } else { + return val / AttribUtil.getMultiplier(mod.c(), mod.d()); + } } @Override diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/combined/CombinedListener.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/combined/CombinedListener.java index 40f4d0e9..4d1f4175 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/combined/CombinedListener.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/combined/CombinedListener.java @@ -56,9 +56,14 @@ public class CombinedListener extends CheckListener { if (cc.invulnerableCheck && (cc.invulnerableTriggerAlways || cc.invulnerableTriggerFallDistance && player.getFallDistance() > 0)){ // TODO: maybe make a heuristic for small fall distances with ground under feet (prevents future abuse with jumping) ? - final int ticks = cc.invulnerableInitialTicksJoin >= 0 ? cc.invulnerableInitialTicksJoin : mcAccess.getInvulnerableTicks(player); - data.invulnerableTick = TickTask.getTick() + ticks; - mcAccess.setInvulnerableTicks(player, 0); + final int invulnerableTicks = mcAccess.getInvulnerableTicks(player); + if (invulnerableTicks == Integer.MAX_VALUE) { + // TODO: Maybe log a warning. + } else { + final int ticks = cc.invulnerableInitialTicksJoin >= 0 ? cc.invulnerableInitialTicksJoin : invulnerableTicks; + data.invulnerableTick = TickTask.getTick() + ticks; + mcAccess.setInvulnerableTicks(player, 0); + } } } diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/GodMode.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/GodMode.java index 62a1821b..2a624a09 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/GodMode.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/GodMode.java @@ -60,7 +60,7 @@ public class GodMode extends Check { // Invulnerable or inconsistent. // TODO: might check as well if NCP has taken over invulnerable ticks of this player. - if (invulnerabilityTicks > 0 && noDamageTicks != invulnerabilityTicks || tick < data.lastDamageTick){ + if (invulnerabilityTicks != Integer.MAX_VALUE && invulnerabilityTicks > 0 || tick < data.lastDamageTick){ // (Second criteria is for MCAccessBukkit.) legit = set = resetAcc = true; } diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MovingData.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MovingData.java index 59682103..2af795df 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MovingData.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MovingData.java @@ -109,7 +109,7 @@ public class MovingData extends ACheckData { public double jumpAmplifier; /** Last time the player was actually sprinting. */ public long timeSprinting = 0; - public double multSprinting = 1.3; // Multiplier at the last time sprinting. + public double multSprinting = 1.30000002; // Multiplier at the last time sprinting. /** Tick at which walk/fly speeds got changed last time. */ public int speedTick = 0; diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/NoFall.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/NoFall.java index adde02be..7ad04b3d 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/NoFall.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/NoFall.java @@ -103,8 +103,9 @@ public class NoFall extends Check { } } - // TODO: let this be done by the damage event (!). - // data.clearNoFallData(); // -> currently done in the damage eventhandling method. + // Currently resetting is done from within the damage event handler. + // TODO: MUST detect if event fired at all (...) and override, if necessary. Best probe once per class (with YES). + // data.clearNoFallData(); player.setFallDistance(0); } diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/SurvivalFly.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/SurvivalFly.java index 8d0f388b..85d33b7e 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/SurvivalFly.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/SurvivalFly.java @@ -187,7 +187,7 @@ public class SurvivalFly extends Check { // Use the player-specific walk speed. // TODO: Might get from listener. // TODO: Use in lostground? - final double walkSpeed = SurvivalFly.walkSpeed * ((double) data.walkSpeed / 0.2) * mcAccess.getSpeedAttributeMultiplier(player) * (sprinting ? 1.0 / data.multSprinting : 1.0); + final double walkSpeed = SurvivalFly.walkSpeed * ((double) data.walkSpeed / 0.2) * mcAccess.getSpeedAttributeMultiplier(player); setNextFriction(from, to, data, cc); @@ -671,8 +671,6 @@ public class SurvivalFly extends Check { hAllowedDistance *= modIce; } - // TODO: Attributes - // Speed amplifier. final double speedAmplifier = mcAccess.getFasterMovementAmplifier(player); if (speedAmplifier != Double.NEGATIVE_INFINITY) { diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/compat/BridgeHealth.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/compat/BridgeHealth.java index 4746f967..3880a15d 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/compat/BridgeHealth.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/compat/BridgeHealth.java @@ -222,7 +222,7 @@ public class BridgeHealth { private static void checkLogEntry(final String tag) { // New entry. if (ConfigManager.getConfigFile().getBoolean(ConfPaths.LOGGING_EXTENDED_STATUS)){ - StaticLog.logWarning("[NoCheatPlus] API incompatibility detected: " + tag); + StaticLog.logInfo("[NoCheatPlus] Try old health API: " + tag); } } diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/compat/BridgeMisc.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/compat/BridgeMisc.java index f5668716..147c58a1 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/compat/BridgeMisc.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/compat/BridgeMisc.java @@ -1,6 +1,7 @@ package fr.neatmonster.nocheatplus.compat; import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.util.Collection; import org.bukkit.Bukkit; @@ -9,6 +10,8 @@ import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.bukkit.entity.Projectile; +import fr.neatmonster.nocheatplus.utilities.ReflectionUtil; + /** * Various bridge methods not enough for an own class. @@ -24,7 +27,9 @@ public class BridgeMisc { return null; } - public static final GameMode GAME_MODE_SPECTATOR = getSpectatorGameMode(); + public static final GameMode GAME_MODE_SPECTATOR = getSpectatorGameMode(); + + private static final Method Bukkit_getOnlinePlayers = ReflectionUtil.getMethodNoArgs(Bukkit.class, "getOnlinePlayers"); /** * Return a shooter of a projectile if we get an entity, null otherwise. @@ -71,17 +76,18 @@ public class BridgeMisc { * @return */ public static Player[] getOnlinePlayers() { - Object obj = Bukkit.getOnlinePlayers(); - if (obj instanceof Collection) { - @SuppressWarnings("unchecked") - Collection players = (Collection) obj; - return players.toArray(new Player[players.size()]); + try { + Collection players = Bukkit.getOnlinePlayers(); + return players.isEmpty() ? new Player[0] : players.toArray(new Player[players.size()]); } - else if (obj instanceof Player[]) { - return (Player[]) obj; - } else { - return new Player[0]; + catch (NoSuchMethodError e) {} + if (Bukkit_getOnlinePlayers != null) { + Object obj = ReflectionUtil.invokeMethodNoArgs(Bukkit_getOnlinePlayers, null); + if (obj != null && (obj instanceof Player[])) { + return (Player[]) obj; + } } + return new Player[0]; } } diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/compat/MCAccess.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/compat/MCAccess.java index 1fbdd929..544af118 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/compat/MCAccess.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/compat/MCAccess.java @@ -95,7 +95,8 @@ public interface MCAccess { /** * * @param player - * @return A multiplier for the allowed speed, should be 1.0 if not possible to determine. + * @return A multiplier for the allowed speed, excluding the sprint boost + * modifier (!). If not possible to determine, it should be 1.0. */ public double getSpeedAttributeMultiplier(Player player); @@ -106,6 +107,11 @@ public interface MCAccess { */ public double getSprintAttributeMultiplier(Player player); + /** + * + * @param player + * @return Integer.MAX_VALUE if not available (!). + */ public int getInvulnerableTicks(Player player); public void setInvulnerableTicks(Player player, int ticks); diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/players/DataManager.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/players/DataManager.java index ca1ae19d..342055cb 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/players/DataManager.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/players/DataManager.java @@ -13,7 +13,6 @@ import java.util.Map.Entry; import java.util.Set; import java.util.UUID; -import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; @@ -469,7 +468,7 @@ public class DataManager implements Listener, INotifyReload, INeedConfig, Compon * Initializing with online players. */ public void onEnable() { - for (final Player player : Bukkit.getOnlinePlayers()) { + for (final Player player : BridgeMisc.getOnlinePlayers()) { addOnlinePlayer(player); } } diff --git a/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/compat/MCAccessFactory.java b/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/compat/MCAccessFactory.java index 99d05556..df35cab0 100644 --- a/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/compat/MCAccessFactory.java +++ b/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/compat/MCAccessFactory.java @@ -97,9 +97,10 @@ public class MCAccessFactory { final String[] classNames = new String[] { // Current DEV / LATEST: CB (Spigot) - // "fr.neatmonster.nocheatplus.compat.cbdev.MCAccessCBDev", // future / tests. + //"fr.neatmonster.nocheatplus.compat.cbdev.MCAccessCBDev", // future / tests. + //"fr.neatmonster.nocheatplus.compat.cbreflect.MCAccessCBReflect", // TEST // Dedicated: CB (Spigot) - "fr.neatmonster.nocheatplus.compat.spigotcb1_8_R3.MCAccessSpigotCB1_8_R3", // 1.8.4|1.8.5 (1_8_R3) + "fr.neatmonster.nocheatplus.compat.spigotcb1_8_R3.MCAccessSpigotCB1_8_R3", // 1.8.4-1.8.7 (1_8_R3) "fr.neatmonster.nocheatplus.compat.spigotcb1_8_R2.MCAccessSpigotCB1_8_R2", // 1.8.3 (1_8_R2) "fr.neatmonster.nocheatplus.compat.spigotcb1_8_R1.MCAccessSpigotCB1_8_R1", // 1.8 (1_8_R1) // Dedicated CB (original) @@ -116,6 +117,8 @@ public class MCAccessFactory { "fr.neatmonster.nocheatplus.compat.cb2602.MCAccessCB2602", // 1.4.7 "fr.neatmonster.nocheatplus.compat.cb2545.MCAccessCB2545", // 1.4.6 "fr.neatmonster.nocheatplus.compat.cb2512.MCAccessCB2512", // 1.4.5-R1.0 + // Reflection (all of the above) + "fr.neatmonster.nocheatplus.compat.cbreflect.MCAccessCBReflect", // ALL, TODO: Configuration. }; for (String className : classNames) {