1.15.2 How to change a final field using Reflection

Discussion in 'Spigot Plugin Development' started by _xXProDudeXx_, Mar 3, 2020.

  1. Hello,

    I have been trying to inject the 'perm' field (type=PermissibleBase) with my own Permissible implementation of the CraftHumanEntity class based on what I read in this thread

    I couldn't remember the name of the field anymore, so happy I finally got it again now I am stuck at the point where I cannot change the particular field, because it has the final modifier.

    Leaves me with the question if it is actually possible to change the field. If not, would that mean that permissible-injection is no longer allowed?

    Thanks in advance,

    Wouter
     
    #1 _xXProDudeXx_, Mar 3, 2020
    Last edited: Mar 3, 2020
  2. What code did you try? It should be working fine
    http://java-performance.info/updating-final-and-static-final-fields/
     
  3. Here's the code I've been using for setting final fields, just a warning though, it raises an illegal reflective access error on systems that have those disabled

    Code (Java):

    public static void setFinal(Class<?> clazz, Object instance, String fieldName, Object toSet) {
      try {
        Field f = clazz.getDeclaredField(fieldName);
        AccessController.doPrivileged((PrivilegedAction) () -> {
          f.setAccessible(true);
          return null;
        });
        Objects.requireNonNull(modF()).setInt(f, f.getModifiers() & ~Modifier.FINAL);
        f.set(instance, toSet);
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
    Code (Java):

    private static Field modF() {
      try {
        Field f = Field.class.getDeclaredField("modifiers");
        AccessController.doPrivileged((PrivilegedAction) () -> {
          f.setAccessible(true);
          return null;
        });
        return f;
      } catch (Exception e) {
        e.printStackTrace();
        return null;
      }
    }
     
     
  4. Code (Java):
    private void replacePermissibleBase(Player p, Permissible permissible) {
            Class<?> craftHumanEntityClass = ReflectionUtils.getClass(ReflectionUtils.Type.CRAFTBUKKIT, "entity.CraftHumanEntity");
            Field permField = ReflectionUtils.getField(craftHumanEntityClass, true, "perm");
            // equals craftHumanEntityClass.getDeclaredField("perm");
            permField.setAccessible(true);

            ReflectionUtils.setFieldValue(permField, p, permissible);
            // equals permField.set(p, permissible);
        }
    I have tried removing the final modifier, using the information I retrieved from this thread at StackOverflow, but no matter what, I kept getting this error:

    Code (Java):
    [17:27:01] [Server thread/WARN]: java.lang.IllegalArgumentException: Can not set final org.bukkit.permissions.PermissibleBase field org.bukkit.craftbukkit.v1_15_R1.entity.CraftHumanEntity.perm to {my permissble here}
    At some point, I even got this warning in the console:
    upload_2020-3-3_18-27-5.png

    Edit: I see you mentioned the illegal reflective access error but how do I get rid of it. I have used a similar method that did the same as you did with this,
    Code (Java):
    Objects.requireNonNull(modF()).setInt(f, f.getModifiers() & ~Modifier.FINAL);
    but it just gives me that error and doesn't work furthermore.

    I honestly have no idea how I can achieve this, though I will try your code!
     
    #4 _xXProDudeXx_, Mar 3, 2020
    Last edited: Mar 3, 2020
  5. You need to set accessible to false after the change
     
  6. No you don’t
     
  7. Agree with @xTrollxDudex here, don't think that would make any difference.