[Util] Beacon BlockState API (Sort of)

Discussion in 'Resources' started by foodyling, Aug 1, 2013.

Thread Status:
Not open for further replies.
  1. Offline

    foodyling

    As far as I know, there is currently no way to set what potion effects a beacon can give off (since the interface of the Beacon blockstate is empty). I've created this quick class to essentially be able to set the effects of a Beacon and get the data for the beacon. (I'm going to cry if there already was a way I missed)
    Code:
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
     
    import org.bukkit.Bukkit;
    import org.bukkit.block.Block;
    import org.bukkit.block.BlockState;
    import org.bukkit.entity.LivingEntity;
    import org.bukkit.inventory.ItemStack;
    import org.bukkit.potion.PotionEffectType;
     
    /**
    *
    * @author Foodyling
    */
    public class BeaconState {
        public static class ReflectionUtil {
            private static final String MC_VERSION = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3];
     
            public static Class<?> forClassName(String name) {
                try {
                    return Class.forName(name.replace("%MC_VERSION%", MC_VERSION));
                } catch (Throwable error) {
                    return null;
                }
            }
     
            public static Method getMethod(Class<?> clazz, String methodName, Class<?>... params) {
                try {
                    return clazz.getMethod(methodName, params);
                } catch (Throwable error) {
                    return null;
                }
            }
     
            public static Field getField(Class<?> clazz, String fieldName) {
                try {
                    return clazz.getDeclaredField(fieldName);
                } catch (Throwable error) {
                    return null;
                }
            }
        }
       
        private static final Class<?> beaconState = ReflectionUtil.forClassName("net.minecraft.server.%MC_VERSION%.TileEntityBeacon"),
                                      craftBeacon = ReflectionUtil.forClassName("org.bukkit.craftbukkit.%MC_VERSION%.block.CraftBeacon"),
                                      entityHuman = ReflectionUtil.forClassName("net.minecraft.server.%MC_VERSION%.EntityHuman");
       
        private static final Field tileEntityBeacon = ReflectionUtil.getField(craftBeacon, "beacon"),
                                  primary = ReflectionUtil.getField(beaconState, "f"),
                                  secondary = ReflectionUtil.getField(beaconState, "g");
       
        private static final Method getName = ReflectionUtil.getMethod(beaconState, "getName"),
                                    setName = ReflectionUtil.getMethod(beaconState, "a", String.class),
                                    tier = ReflectionUtil.getMethod(beaconState, "l"),
                                    getItem = ReflectionUtil.getMethod(beaconState, "getItem", int.class),
                                    setItem = ReflectionUtil.getMethod(beaconState, "setItem", int.class, ItemStack.class);
       
        static {
            tileEntityBeacon.setAccessible(true);
            primary.setAccessible(true);
            secondary.setAccessible(true);
        }
       
        private final Block block;
        private final Object blockState;
       
        public BeaconState(Block block) {
            this(block.getState());
        }
       
        public BeaconState(BlockState state) {
            if (craftBeacon.isInstance(state)) {
                try {
                    this.block = state.getBlock();
                    this.blockState = tileEntityBeacon.get(craftBeacon.cast(state));
                } catch (Throwable error) {
                    throw new IllegalArgumentException("BlockState must be a instance of org.bukkit.craftbukkit.block.CraftBeacon");               
                }
            } else {
                throw new IllegalArgumentException("BlockState must be a instance of org.bukkit.craftbukkit.block.CraftBeacon");
            }
        }
       
        public static BeaconState asBeacon(Block block) {
            return new BeaconState(block);
        }
       
        public static BeaconState asBeacon(BlockState state) {
            return new BeaconState(state);
        }
       
        public Block getBlock() {
            return block;
        }
       
        public String getName() {
            try {
                return (String) getName.invoke(blockState);
            } catch (Throwable error) {
                return null;
            }
        }
       
        public void setName(String name) {
            try {
                setName.invoke(blockState, name);
            } catch (Throwable error) {
                error.printStackTrace();
            }
        }
       
        public PotionEffectType getPrimary() {
            try {
                return PotionEffectType.getById(primary.getInt(blockState));
            } catch (Throwable error) {
                return null;
            }
        }
       
        public PotionEffectType getSecondary() {
            try {
                return PotionEffectType.getById(secondary.getInt(blockState));
            } catch (Throwable error) {
                return null;
            }
        }
       
        public void setPrimary(PotionEffectType type) {
            try {
                primary.setInt(blockState, type.getId());
            } catch (Throwable error) {
                error.printStackTrace();
            }
        }
       
        public void setSecondary(PotionEffectType type) {
            try {
                secondary.setInt(blockState, type.getId());
            } catch (Throwable error) {
                error.printStackTrace();
            }
        }
       
        public void setBoth(PotionEffectType type) {
            setPrimary(type);
            setSecondary(type);
        }
       
        public int getTier() {
            try {
                return ((Integer) tier.invoke(blockState)).intValue();
            } catch (Throwable error) {
                error.printStackTrace();
                return 0;
            }
        }
       
        public ItemStack getItem() {
            try {
                return (ItemStack) getItem.invoke(blockState, 0);
            } catch (Throwable error) {
                return null;
            }
        }
       
        public void setItem(ItemStack stack) {
            try {
                setItem.invoke(blockState, 0, stack);
            } catch (Throwable error) {
           
            }
        }
       
        public boolean applicableFor(LivingEntity entity) {
            if (entityHuman.isInstance(entity)) {
                return entity.getLocation().distanceSquared(block.getLocation()) <= 4096;
            }
            return false;
        }
    }
    To use this, you must either have a Block or a BlockState (it will throw a IllegalArgumentException if it's not a Beacon). Here's an example of how to get a Beacon BlockState
    Code:
    // There are two ways to get the 'BlockState' either using BeaconState.asBeacon(block) or create a new instance new BeaconState(block)
    BeaconState state = new BeaconState(block);
    // Set the effect for both, which makes it a level 2 effect
    state.setBoth(PotionEffectType.SLOW);
     
    // Set the primary effect
    state.setPrimary(PotionEffectType.HEALTH_BOOST);
    // Set a secondary
    state.setSecondary(PotionEffectType.ABSORPTION);
     
    // Get an effect, returns null if empty or an error occures
    state.getPrimary();
     
    // Checks if a living entity is elgible for a effect
    state.applicableFor(entity);
     
    // Get the tier of the beacon
    state.getTier();
    
     
  2. Offline

    Ultimate_n00b

    I believe that the tuxtwolib has this ability (from the prettyscarylib).
     
  3. Offline

    foodyling

    Ultimate_n00b *Well* you wouldn't need another dependency if you only wanted to change the beacon effects.
     
  4. Offline

    Ultimate_n00b

    No, I understand the reasoning for you posting here. I just thought I would mention it for the people who already have the dependency.
     
    foodyling likes this.
  5. Offline

    JPG2000

Thread Status:
Not open for further replies.

Share This Page