[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"!]
This commit is contained in:
asofold 2015-06-06 16:14:36 +02:00
parent 431099cd3c
commit 2f50cca03d
48 changed files with 2070 additions and 575 deletions

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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.)
// }
}

View File

@ -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.)
// }
}

View File

@ -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;
}
}

View File

@ -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.).
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}
}

View File

@ -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<String> possibleNames = new ArrayList<String>();
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<String> names = new ArrayList<String>();
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<String>() {
@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;
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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.
}
}
}

View File

@ -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;
}
}
}

View File

@ -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<String> parts = new LinkedList<String>();
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(),
};
}
}

View File

@ -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");
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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");
}
}

View File

@ -168,7 +168,7 @@ public class MCAccessCB2512 implements MCAccess{
@Override
public AlmostBoolean dealFallDamageFiresAnEvent() {
return AlmostBoolean.MAYBE;
return AlmostBoolean.NO;
}
// @Override

View File

@ -168,7 +168,7 @@ public class MCAccessCB2545 implements MCAccess{
@Override
public AlmostBoolean dealFallDamageFiresAnEvent() {
return AlmostBoolean.MAYBE;
return AlmostBoolean.NO;
}
// @Override

View File

@ -169,7 +169,7 @@ public class MCAccessCB2602 implements MCAccess{
@Override
public AlmostBoolean dealFallDamageFiresAnEvent() {
return AlmostBoolean.MAYBE;
return AlmostBoolean.NO;
}
// @Override

View File

@ -170,7 +170,7 @@ public class MCAccessCB2645 implements MCAccess{
@Override
public AlmostBoolean dealFallDamageFiresAnEvent() {
return AlmostBoolean.MAYBE;
return AlmostBoolean.NO;
}
// @Override

View File

@ -172,7 +172,7 @@ public class MCAccessCB2691 implements MCAccess{
@Override
public AlmostBoolean dealFallDamageFiresAnEvent() {
return AlmostBoolean.MAYBE;
return AlmostBoolean.NO;
}
// @Override

View File

@ -171,7 +171,7 @@ public class MCAccessCB2763 implements MCAccess{
@Override
public AlmostBoolean dealFallDamageFiresAnEvent() {
return AlmostBoolean.MAYBE;
return AlmostBoolean.NO;
}
// @Override

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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.
// }
}

View File

@ -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

View File

@ -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

View File

@ -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 ?

View File

@ -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

View File

@ -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);
}
}
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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);
}

View File

@ -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) {

View File

@ -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);
}
}

View File

@ -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<? extends Player> players = (Collection<? extends Player>) obj;
return players.toArray(new Player[players.size()]);
try {
Collection<? extends Player> 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];
}
}

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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) {