1. Этот сайт использует файлы cookie. Продолжая пользоваться данным сайтом, Вы соглашаетесь на использование нами Ваших файлов cookie. Узнать больше.
  2. Вы находитесь в сообществе Rubukkit. Мы - администраторы серверов Minecraft, разрабатываем собственные плагины и переводим на различные языки плагины наших коллег из других стран.
    Скрыть объявление
Скрыть объявление
В преддверии глобального обновления, мы проводим исследования, которые помогут нам сделать опыт пользования форумом ещё удобнее. Помогите нам, примите участие!

Стартап Создаем кастомный PermissibleBase и делаем кастомную проверку permissions.

Тема в разделе "Разработка плагинов для новичков", создана пользователем alexandrage, 9 апр 2017.

  1. Автор темы
    alexandrage

    alexandrage Старожил Пользователь

    Баллы:
    173
    Многие задавались вопросом, как же можно сделать проверку группы permissions в своем плагине прав через обычный player().hasPermission("permissions.args.*"));. Я опишу как такое можно сделать используя магию рефлексии.
    Будет полезно тем, кто пишет свой плагин permissions.

    Создаем класс. PermissibleBaseEx
    Код:
    package Example;
    
    import java.lang.reflect.Method;
    import java.util.LinkedHashMap;
    import java.util.List;
    import java.util.Locale;
    import java.util.Map;
    import java.util.concurrent.atomic.AtomicBoolean;
    
    import org.bukkit.Bukkit;
    import org.bukkit.permissions.PermissibleBase;
    import org.bukkit.permissions.Permission;
    import org.bukkit.permissions.PermissionAttachment;
    import org.bukkit.permissions.PermissionAttachmentInfo;
    import org.bukkit.permissions.ServerOperator;;
    
    public class PermissibleBaseEx extends PermissibleBase {
        private static FieldReplacer<PermissibleBase, Map> PERMISSIONS_FIELD;
        private static FieldReplacer<PermissibleBase, List> ATTACHMENTS_FIELD;
        private static Method CALC_CHILD_PERMS_METH;
        private static final AtomicBoolean LAST_CALL_ERRORED;
        private List<PermissionAttachment> attachments;
        private Map<String, PermissionAttachmentInfo> permissions;
     
        public PermissibleBaseEx(ServerOperator opable) {
            super(opable);
            this.permissions = new LinkedHashMap<String, PermissionAttachmentInfo>() {
                private static final long serialVersionUID = 1L;
                @Override
                public PermissionAttachmentInfo put(String k, PermissionAttachmentInfo v) {
                    PermissionAttachmentInfo existing = this.get(k);
                    if (existing != null) {
                        return existing;
                    }
                    return super.put(k, v);
                }
            };
            PERMISSIONS_FIELD.set(this, permissions);
            attachments = (List)ATTACHMENTS_FIELD.get(this);
            recalculatePermissions();
        }
     
        @Override
        public boolean hasPermission(String inName) {
            if (inName == null) {
                throw new IllegalArgumentException("Permission name cannot be null");
            }
            String name = inName.toLowerCase(Locale.ENGLISH);
         
            /**
             * Тут пример кастомной проверки со звездочкой.
             */
            if(name.endsWith("*") && name.length()>1) {
                for(String list : this.permissions.keySet()) {
                    if(list.startsWith(name.toLowerCase(Locale.ENGLISH).replace("*", ""))) {
                        return list.replace("*", "").contains(name.replace("*", ""));
                    }
                }
            }
            if (isPermissionSet(name)) {
                return ((PermissionAttachmentInfo)this.permissions.get(name)).getValue();
            }
            Permission perm = Bukkit.getServer().getPluginManager().getPermission(name);
            if (perm != null) {
                return perm.getDefault().getValue(isOp());
            }
            return Permission.DEFAULT_PERMISSION.getValue(isOp());
        }
    
        @Override
        public boolean hasPermission(Permission perm) {
            if (perm == null) {
                throw new IllegalArgumentException("Permission cannot be null");
            }
            String name = perm.getName().toLowerCase(Locale.ENGLISH);
            if (isPermissionSet(name)) {
                return ((PermissionAttachmentInfo)this.permissions.get(name)).getValue();
            }
            return perm.getDefault().getValue(isOp());
        }
    
        @Override
        public boolean isPermissionSet(String name) {
            if (name == null) {
                throw new IllegalArgumentException("Permission name cannot be null");
            }
            return this.permissions.containsKey(name.toLowerCase(Locale.ENGLISH));
        }
     
        @Override
        public boolean isPermissionSet(Permission perm) {
            if (perm == null) {
                throw new IllegalArgumentException("Permission cannot be null");
            }
            return isPermissionSet(perm.getName());
        }
       
        static {
            PERMISSIONS_FIELD = new FieldReplacer<PermissibleBase, Map>(PermissibleBase.class, "permissions", Map.class);
            ATTACHMENTS_FIELD = new FieldReplacer<PermissibleBase, List>(PermissibleBase.class, "attachments", List.class);
            try {
                CALC_CHILD_PERMS_METH = PermissibleBase.class.getDeclaredMethod("calculateChildPermissions", Map.class, Boolean.TYPE, PermissionAttachment.class);
            }
            catch (NoSuchMethodException e) {
                throw new ExceptionInInitializerError(e);
            }
            PermissibleBaseEx.CALC_CHILD_PERMS_METH.setAccessible(true);
            LAST_CALL_ERRORED = new AtomicBoolean(false);
        }
    }

    Создаем класс. PermissibleInjector
    Код:
    package Example;
    
    import java.lang.reflect.Field;
    import java.util.List;
    import org.bukkit.entity.Player;
    import org.bukkit.permissions.Permissible;
    import org.bukkit.permissions.PermissibleBase;
    
    public class PermissibleInjector {
        static Class<?> humanEntity;
    
        public Permissible inject(Player player, Permissible permissible) throws Exception {
            Field permField = getPermissibleField(player);
            if (permField == null) {
                return null;
            }
            Permissible oldPerm = (Permissible)permField.get(player);
            PermissibleBase newBase = (PermissibleBase)permissible;
            PermissibleBase oldBase = (PermissibleBase)oldPerm;
            copyValues(oldBase, newBase);
            permField.set(player, permissible);
            return oldPerm;
        }
    
        public Permissible getPermissible(Player player) throws Exception {
            return (Permissible)getPermissibleField(player).get(player);
        }
    
        private Field getPermissibleField(Player player) throws Exception {
            if(humanEntity==null) {
                return null;
            }
            if (!humanEntity.isAssignableFrom(player.getClass())) {
                return null;
            }
            Field permField = humanEntity.getDeclaredField("perm");
            permField.setAccessible(true);
            return permField;
        }
    
        private void copyValues(PermissibleBase old, PermissibleBase newPerm) throws Exception {
            Field attachmentField = PermissibleBase.class.getDeclaredField("attachments");
            attachmentField.setAccessible(true);
            List<Object> attachmentPerms = (List)attachmentField.get(newPerm);
            attachmentPerms.clear();
            attachmentPerms.addAll((List)attachmentField.get(old));
            newPerm.recalculatePermissions();
        }
    
        static {
            String version = org.bukkit.Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3];
            try {
                humanEntity = Class.forName("org.bukkit.craftbukkit." + version + ".entity.CraftHumanEntity");
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    }

    Создаем класс. FieldReplacer
    Код:
    package Example;
    
    import java.lang.reflect.Field;
    
    public class FieldReplacer<Instance, Type> {
        private final Class<Type> requiredType;
        private final Field field;
    
        public FieldReplacer(Class<? extends Instance> clazz, String fieldName, Class<Type> requiredType) {
            this.requiredType = requiredType;
            this.field = getField(clazz, fieldName);
            if (this.field == null) {
                throw new ExceptionInInitializerError("No such field " + fieldName + " in class " + clazz);
            }
            this.field.setAccessible(true);
            if (!requiredType.isAssignableFrom(this.field.getType())) {
                throw new ExceptionInInitializerError("Field of wrong type");
            }
        }
    
        public Type get(Instance instance) {
            try {
                return (Type)this.requiredType.cast(this.field.get(instance));
            }
            catch (IllegalAccessException e) {
                throw new Error(e);
            }
        }
    
        public void set(Instance instance, Type newValue) {
            try {
                this.field.set(instance, newValue);
            }
            catch (IllegalAccessException e) {
                throw new Error(e);
            }
        }
    
        private static Field getField(Class<?> clazz, String fieldName) {
            while ((clazz != null) && (clazz != Object.class)) {
                try
                {
                    return clazz.getDeclaredField(fieldName);
                }
                catch (NoSuchFieldException e) {
                    clazz = clazz.getSuperclass();
                }
            }
            return null;
        }
    }

    Остается нам добавить игроков в наш PermissibleBaseEx, используем к примеру PlayerJoinEvent.
    Код:
        @EventHandler
        public void on(PlayerJoinEvent e) throws Exception {
            PermissibleInjector inj = new PermissibleInjector();
            inj.inject(e.getPlayer(), new PermissibleBaseEx(e.getPlayer()));
        }
     
    Последнее редактирование: 11 апр 2017
  2. DonDays

    DonDays Активный участник Пользователь

    Баллы:
    96
    Имя в Minecraft:
    DonDays
    к слову о магии, инжектора тут нет.

    пример

    Код:
    package ru.dondays.permsystem.bukkit.utils;
    
    import org.bukkit.entity.Player;
    import ru.dondays.permsystem.bukkit.Perms;
    import ru.dondays.permsystem.bukkit.service.DPermissibleBase;
    
    import java.lang.reflect.Field;
    
    public class ReflectionUtils {
    
        private static Class<?> typeHumanEntity;
        private static Field permissibleField;
    
        public static void initialize(final Player player, final DPermissibleBase base) {
            try {
                permissibleField.setAccessible(true);
                permissibleField.set(typeHumanEntity.cast(player), base);
            } catch(final Exception ex) {
                Pexer.getInstance().getLogger().info("Failed reflection task: " + ex.getMessage());
                ex.printStackTrace();
            }
        }
    
        static {
            final String version = org.bukkit.Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3];
            try {
                typeHumanEntity = Class.forName("org.bukkit.craftbukkit." + version + ".entity.CraftHumanEntity");
                permissibleField = typeHumanEntity.getDeclaredField("perm");
            } catch(final Exception ex) {
                Perms.getInstance().getLogger().info("Failed reflection initialize: " + ex.getMessage());
                ex.printStackTrace();
            }
        }
    }
    
     
  3. Автор темы
    alexandrage

    alexandrage Старожил Пользователь

    Баллы:
    173
    Не успел дописать.
    .
     
  4. Автор темы
    alexandrage

    alexandrage Старожил Пользователь

    Баллы:
    173
  5. Exception_Prototype

    Exception_Prototype Активный участник Пользователь

    Баллы:
    96

Поделиться этой страницей