Package fr.neatmonster.nocheatplus.compat

Source Code of fr.neatmonster.nocheatplus.compat.BridgeHealth

package fr.neatmonster.nocheatplus.compat;

import java.util.HashSet;
import java.util.Set;

import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.event.entity.EntityDamageEvent;
import org.bukkit.event.entity.EntityDamageEvent.DamageCause;
import org.bukkit.event.entity.EntityRegainHealthEvent;

import fr.neatmonster.nocheatplus.config.ConfPaths;
import fr.neatmonster.nocheatplus.config.ConfigManager;
import fr.neatmonster.nocheatplus.logging.LogUtil;
import fr.neatmonster.nocheatplus.utilities.ReflectionUtil;

/**
* Utility class with static access methods to bridge compatibility issues, such as arising from changes in Bukkit from MC 1.5.2 to 1.6.1.
* <br>NOTES:
* <li>To simplify code IncompatibleClassChangeError is caught instead of AbstractMethodError and NoSuchMethodError etc.</li>
* <li>TODO: Since API dependency is now 1.6.1+, some things can be simplified to just call the "int-methods".</li>
* @author mc_dev
*
*/
public class BridgeHealth {
 
  /** For debugging purposes. TODO: Reset on shutdown !? */
  private static Set<String> failures = new HashSet<String>();
 
  /**
   * This method is meant to be called on API that changed from int to double.<br>
   * NOTE: Might get changed to return a Number instance.
   * @param obj Object to call the method on.
   * @param methodName
   * @param RuntimeException with reason Will be thrown if no recovery from present methods is possible. If not null the first call gets logged as "API incompatibility".
   * @return
   */
  public static final double getDoubleOrInt(final Object obj, final String methodName, final Throwable reason){
    if (reason != null){
      final String tag = obj.getClass().getName() + "." + methodName;
      if (failures.add(tag)){
        // New entry.
        checkLogEntry(tag);
      }
    }
    final Object o1 = ReflectionUtil.invokeMethodNoArgs(obj, methodName, double.class, int.class);
    if (o1 instanceof Number){
      return ((Number) o1).doubleValue();
    }
    else{
      String message = "Expect method " + methodName + " in " + obj.getClass() + " with return type double or int, returned instead: " + ((o1 == null ? "null" : o1.getClass().getName()));
      if (reason == null){
        throw new RuntimeException(message);
      }
      else{
        throw new RuntimeException(message, reason);
      }
    }
  }
 
  /**
   * Get the amount of health added with the event.
   * @param event
   * @return
   * @throws RuntimeException, in case of an IncompatibleClassChangeError without success on recovery attempts.
   */
  public static double getAmount(final EntityRegainHealthEvent event){
    try{
      return event.getAmount();
    }
    catch(IncompatibleClassChangeError e){
      return getDoubleOrInt(event, "getAmount", e);
    }
  }
 
  /**
   * Get the damage from an EntityDamageEvent.
   * @param event
   * @return
   * @throws RuntimeException, in case of an IncompatibleClassChangeError without success on recovery attempts.
   */
  public static double getDamage(final EntityDamageEvent event){
    try{
      return event.getDamage();
    }
    catch(IncompatibleClassChangeError e){
      return getDoubleOrInt(event, "getDamage", e);
    }
  }
 
  /**
   * Set the damage from an EntityDamageEvent.
   * @param event
   * @param damage
   * @return
   * @throws RuntimeException, in case of an IncompatibleClassChangeError without success on recovery attempts.
   */
  public static void setDamage(final EntityDamageEvent event, final double damage){
    try{
      event.setDamage(damage);
    }
    catch(IncompatibleClassChangeError e){
      invokeVoid(event, "setDamage", (int) Math.round(damage), e);
    }
  }
 
  /**
   * Get the health for an entity (LivingEntity).
   * @param entity
   * @return
   * @throws RuntimeException, in case of an IncompatibleClassChangeError without success on recovery attempts.
   */
  public static double getHealth(final LivingEntity entity){
    try{
      return entity.getHealth();
    }
    catch(IncompatibleClassChangeError e){
      return getDoubleOrInt(entity, "getHealth", e);
    }
  }
 
  /**
   * Get the maximum health for an entity (LivingEntity).
   * @param entity
   * @return
   * @throws RuntimeException, in case of an IncompatibleClassChangeError without success on recovery attempts.
   */
  public static double getMaxHealth(final LivingEntity entity){
    try{
      return entity.getMaxHealth();
    }
    catch(IncompatibleClassChangeError e){
      return getDoubleOrInt(entity, "getMaxHealth", e);
    }
  }
 
  /**
   * Get the last damage for an entity (LivingEntity).
   * @param entity
   * @return
   * @throws RuntimeException, in case of an IncompatibleClassChangeError without success on recovery attempts.
   */
  public static double getLastDamage(final LivingEntity entity){
    try{
      return entity.getLastDamage();
    }
    catch(IncompatibleClassChangeError e){
      return getDoubleOrInt(entity, "getLastDamage", e);
    }
  }
 
  /**
   * Set the health for an entity (LivingEntity).
   * @param entity
   * @param health
   * @return
   * @throws RuntimeException, in case of an IncompatibleClassChangeError without success on recovery attempts.
   */
  public static void setHealth(final LivingEntity entity, final double health){
    try{
      entity.setHealth(health);
    }
    catch(IncompatibleClassChangeError e){
      invokeVoid(entity, "setHealth", (int) Math.round(health), e);
    }
  }
 
  /**
   * Damage an entity (LivingEntity).
   * @param entity
   * @param damage
   * @throws RuntimeException, in case of an IncompatibleClassChangeError without success on recovery attempts.
   */
  public static void damage(final LivingEntity entity, final double damage){
    try{
      entity.damage(damage);
    }
    catch(IncompatibleClassChangeError e){
      invokeVoid(entity, "damage", (int) Math.round(damage), e);
    }
  }
 
  @SuppressWarnings("deprecation")
  public static EntityDamageEvent getEntityDamageEvent(final Entity entity, final DamageCause damageCause, final double damage){
    try{
      return new EntityDamageEvent(entity, damageCause, damage);
    }
    catch(IncompatibleClassChangeError e){
      return new EntityDamageEvent(entity, damageCause, (int) Math.round(damage));
    }
  }
 
  /**
   * Intended for faster compatibility methods for defined scenarios. Transforms any exception to a RuntimeException.
   * @param obj
   * @param methodName
   * @param value
   */
  public static void invokeVoid(final Object obj, final String methodName, final int value, final Throwable reason){
    if (reason != null){
      final String tag = obj.getClass().getName() + "." + methodName;
      if (failures.add(tag)){
        checkLogEntry(tag);
      }
    }
    try {
      obj.getClass().getMethod(methodName, int.class).invoke(obj, value);
    } catch (Throwable t) {
      throw new RuntimeException("Could not invoke " + methodName + " with one argument (int) on: " + obj.getClass().getName(), reason);
    }
  }

  private static void checkLogEntry(final String tag) {
    // New entry.
    if (ConfigManager.getConfigFile().getBoolean(ConfPaths.LOGGING_DEBUG)){
      LogUtil.logWarning("[NoCheatPlus] API incompatibility detected: " + tag);
    }
  }
 
}
TOP

Related Classes of fr.neatmonster.nocheatplus.compat.BridgeHealth

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.