Войти или зарегистрироваться
  • Вы находитесь в сообществе Rubukkit. Мы - администраторы серверов Minecraft, разрабатываем собственные плагины и переводим на различные языки плагины наших коллег из других стран.

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

alexandrage

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

Создаем класс. PermissibleBaseEx
Java:
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
Java:
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
Java:
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.
Java:
    @EventHandler
    public void on(PlayerJoinEvent e) throws Exception {
        PermissibleInjector inj = new PermissibleInjector();
        inj.inject(e.getPlayer(), new PermissibleBaseEx(e.getPlayer()));
    }
 
Последнее редактирование:
к слову о магии, инжектора тут нет.

пример

Код:
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();
        }
    }
}
 
к слову о магии, инжектора тут нет.

пример

Код:
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();
        }
    }
}
Не успел дописать.
.
 

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

Назад
Верх