Util Mob Spawner - Reflection Class

Discussion in 'Resources' started by PhantomUnicorns, Apr 24, 2018.

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

    PhantomUnicorns

    This is a class that allows you to customize the range, maximum mobs, entity type, their armor, and their weapons. Usually it takes NMS, so I made it in to reflection. The only reason this is possible (this post, not the code) is because of this thread and its owner. If you guys want/add any features comment it so I can add it to the main post, I'm unsure what version TileEntityMobSpawner#load(NBTTagCompound) gets supported in, all I know in his post he used a method labeled a; which, someone told him to correct in the comments for future versions. This takes care of that, assuming that it's below 1.9 that a is used instead of load.

    Class (open)

    Code:
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Method;
    import java.util.Map;
    
    import org.bukkit.Bukkit;
    import org.bukkit.Location;
    import org.bukkit.enchantments.Enchantment;
    import org.bukkit.entity.EntityType;
    import org.bukkit.inventory.ItemStack;
    
    public class CSpawner {
    
        private static Class<?> _CRAFT_WORLD_CLASS, _TILE_ENTITY_MOB_SPAWNER_CLASS, _NBT_TAG_COMPOUND_CLASS,
                _NBT_TAG_LIST_CLASS, _NBT_BASE_CLASS;
        private static Constructor<?> _BLOCK_POSITION_CONSTRUCTOR, _NBT_TAG_LIST_CONSTRUCTOR, _NBT_TAG_COMPOUND_CONSTRUCTOR;
        private static Method _GET_HANDLE_METHOD, _GET_TILE_ENTITY_METHOD, _D_METHOD, _SET_SHORT_METHOD, _SET_STRING_METHOD,
                _SET_METHOD, _ADD_METHOD, _LOAD_METHOD;
    
        static {
            String name = Bukkit.getServer().getClass().getName();
            name = name.substring(name.indexOf("craftbukkit.") + "craftbukkit.".length());
            final String _VERSION = name.substring(0, name.indexOf("."));
            try {
                Class<?> _BLOCK_POSITION_CLASS = Class.forName("net.minecraft.server." + _VERSION + ".BlockPosition");
                _CRAFT_WORLD_CLASS = Class.forName("org.bukkit.craftbukkit." + _VERSION + ".CraftWorld");
                _TILE_ENTITY_MOB_SPAWNER_CLASS = Class
                        .forName("net.minecraft.server." + _VERSION + ".TileEntityMobSpawner");
                _NBT_TAG_COMPOUND_CLASS = Class.forName("net.minecraft.server." + _VERSION + ".NBTTagCompound");
                _NBT_TAG_LIST_CLASS = Class.forName("net.minecraft.server." + _VERSION + ".NBTTagList");
                _NBT_BASE_CLASS = Class.forName("net.minecraft.server." + _VERSION + ".NBTBase");
    
                _BLOCK_POSITION_CONSTRUCTOR = _BLOCK_POSITION_CLASS.getConstructor(double.class, double.class,
                        double.class);
                _NBT_TAG_LIST_CONSTRUCTOR = _NBT_TAG_LIST_CLASS.getConstructor();
                _NBT_TAG_COMPOUND_CONSTRUCTOR = _NBT_TAG_COMPOUND_CLASS.getConstructor();
    
                _GET_HANDLE_METHOD = _CRAFT_WORLD_CLASS.getMethod("getHandle");
                _GET_TILE_ENTITY_METHOD = Class.forName("net.minecraft.server." + _VERSION + ".WorldServer")
                        .getMethod("getTileEntity", _BLOCK_POSITION_CLASS);
                _D_METHOD = _TILE_ENTITY_MOB_SPAWNER_CLASS.getMethod("d");
                _SET_SHORT_METHOD = _NBT_TAG_COMPOUND_CLASS.getMethod("setShort", String.class, short.class);
                _SET_STRING_METHOD = _NBT_TAG_COMPOUND_CLASS.getMethod("setString", String.class, String.class);
                _SET_METHOD = _NBT_TAG_COMPOUND_CLASS.getMethod("set", String.class, _NBT_BASE_CLASS);
                _ADD_METHOD = _NBT_TAG_LIST_CLASS.getMethod("add", _NBT_BASE_CLASS);
                String[] split = _VERSION.split("_");
                if (Double.valueOf(split[1]) < 1.9) {
                    _LOAD_METHOD = _TILE_ENTITY_MOB_SPAWNER_CLASS.getMethod("a", _NBT_TAG_COMPOUND_CLASS);
                } else {
                    _LOAD_METHOD = _TILE_ENTITY_MOB_SPAWNER_CLASS.getMethod("load", _NBT_TAG_COMPOUND_CLASS);
                }
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    
        private Object _spawner;
        private Object _hands, _armor;
        private String _entityType;
    
        private int _maxRange, _maxMobs;
    
        public CSpawner(Location location) {
            try {
                Object blockPosition = _BLOCK_POSITION_CONSTRUCTOR.newInstance(location.getBlockX(), location.getBlockY(),
                        location.getBlockZ());
                Object craftWorld = _CRAFT_WORLD_CLASS.cast(location.getWorld());
                Object serverWorld = _GET_HANDLE_METHOD.invoke(craftWorld);
                this._spawner = _GET_TILE_ENTITY_METHOD.invoke(serverWorld, blockPosition);
                this._entityType = "zombie";
                this._maxMobs = 20;
                this._maxRange = 20;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        public void setEntityType(EntityType entityType) {
            this._entityType = entityType.name().toLowerCase();
        }
    
        public void setSpawnRange(int range) {
            this._maxRange = range;
        }
    
        public void setMaxNearbyEntities(int max) {
            this._maxMobs = max;
        }
    
        public void setArmor(ItemStack helmetItem, ItemStack chestplateItem, ItemStack leggingsItem, ItemStack bootsItem) {
            try {
                this._armor = _NBT_TAG_LIST_CONSTRUCTOR.newInstance();
    
                Object helmet = _NBT_TAG_COMPOUND_CONSTRUCTOR.newInstance();
                Object chestplate = _NBT_TAG_COMPOUND_CONSTRUCTOR.newInstance();
                Object leggings = _NBT_TAG_COMPOUND_CONSTRUCTOR.newInstance();
                Object boots = _NBT_TAG_COMPOUND_CONSTRUCTOR.newInstance();
    
                setItemOnTag(helmet, helmetItem);
                setItemOnTag(chestplate, chestplateItem);
                setItemOnTag(leggings, leggingsItem);
                setItemOnTag(boots, bootsItem);
    
                _ADD_METHOD.invoke(this._armor, boots);
                _ADD_METHOD.invoke(this._armor, leggings);
                _ADD_METHOD.invoke(this._armor, chestplate);
                _ADD_METHOD.invoke(this._armor, helmet);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        public void setHands(ItemStack mainHandItem, ItemStack offHandItem) {
            try {
                this._hands = _NBT_TAG_LIST_CONSTRUCTOR.newInstance();
    
                Object mainHand = _NBT_TAG_COMPOUND_CONSTRUCTOR.newInstance();
                Object offHand = _NBT_TAG_COMPOUND_CONSTRUCTOR.newInstance();
    
                setItemOnTag(mainHand, mainHandItem);
                setItemOnTag(offHand, offHandItem);
    
                _ADD_METHOD.invoke(this._hands, mainHand);
                _ADD_METHOD.invoke(this._hands, offHand);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        @SuppressWarnings("deprecation")
        private void setItemOnTag(Object tag, ItemStack item) {
            try {
                if (item != null) {
                    _SET_STRING_METHOD.invoke(tag, "id", "minecraft:" + item.getType().toString().toLowerCase());
                    _SET_SHORT_METHOD.invoke(tag, "Count", (short) item.getAmount());
                    Map<Enchantment, Integer> enchants = item.getEnchantments();
                    if (!enchants.isEmpty()) {
                        Object enchantments = _NBT_TAG_LIST_CONSTRUCTOR.newInstance();
                        for (Enchantment enchant : enchants.keySet()) {
                            Object e = _NBT_TAG_COMPOUND_CONSTRUCTOR.newInstance();
                            _SET_SHORT_METHOD.invoke(e, "id", (short) enchant.getId());
                            _SET_SHORT_METHOD.invoke(e, "lvl", (short) enchants.get(enchant).intValue());
                        }
                        Object ench = _NBT_TAG_COMPOUND_CONSTRUCTOR.newInstance();
                        _SET_METHOD.invoke(ench, "ench", enchantments);
                        _SET_METHOD.invoke(tag, "tag", ench);
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        public void load() {
            try {
                Object spawnerTag = _D_METHOD.invoke(this._spawner);
                Object spawnData = _NBT_TAG_COMPOUND_CONSTRUCTOR.newInstance();
                _SET_STRING_METHOD.invoke(spawnData, "id", this._entityType);
                _SET_SHORT_METHOD.invoke(spawnerTag, "SpawnRange", (short) _maxRange);
                _SET_SHORT_METHOD.invoke(spawnerTag, "MaxNearbyEntities", (short) _maxMobs);
                if (this._hands != null) {
                    _SET_METHOD.invoke(spawnData, "HandItems", this._hands);
                }
                if (this._armor != null) {
                    _SET_METHOD.invoke(spawnData, "ArmorItems", this._armor);
                }
                _SET_METHOD.invoke(spawnerTag, "SpawnData", spawnData);
                _LOAD_METHOD.invoke(this._spawner, spawnerTag);
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    }
    

    Class Information:
    CSpawner(Location) : The location of the mob spawner
    setEntityType(EntityType entityType): sets the entity type
    setSpawnRange(int range): sets the maximum range
    setMaxNearbyEntities(int max) : sets the max nearby entities in range
    setArmor(ItemStack, ItemStack, ItemStack, ItemStack): The Helmet, Chestplate, Leggings, and Boots of the mob
    setHands(ItemStack, ItemStack): The mainhand and offhand of the mob
    load(): Call this after all the sets above, this sets the data to the spawner.

    I don't think it needs documentation, it's straight forward and if you need help; read above. If above doesn't help, comment below.

    UPDATE: I changed the some of the fields in the class so it doesn't waste as much ram.
     
    Last edited: May 3, 2018
    Zombie_Striker likes this.
Thread Status:
Not open for further replies.

Share This Page