mercredi 31 août 2016

Spigot/Bukkit 1.10.2 Field Reflection returns NullPointerException without clear reason

  • Spigot: v1_10_R1
  • Java: 1.8.0_25 x64

Hello Guys, First I hope I dont get trouble because I post original code from Spigots intern copy of Minecraft-Server.

Ok said that now to the problem: I'm writing a plugin that hooks into the network packet system of mc-server to overwatch some packet traffic for learning reasons. Now is the problem that mojang declares all the fields inside a outgoing packet to private, so the only way to access it is via reflection. It works mostly fine but especially in the PacketPlayOutScoreboardScore class there are 2 fields that should be setted but reflection dont like them it returns 2 times a NullPointerException. here the fields out of the class PacketPlayOutScoreboardScore.

   private int c;

   private EnumScoreboardAction d;

I traced the call hirachy down to the function handleScoreChanged in ScoreboardServer where we cleary see the used constructor.

   public void handleScoreChanged(ScoreboardScore scoreboardscore)
   {
    super.handleScoreChanged(scoreboardscore);
    if (this.b.contains(scoreboardscore.getObjective())) {
      sendAll(new PacketPlayOutScoreboardScore(scoreboardscore));
    }
    b();
   }

   private void sendAll(Packet packet)
   {
     for (EntityPlayer entityplayer : this.a.getPlayerList().players) {
       if (entityplayer.getBukkitEntity().getScoreboard().getHandle() == this) {
         entityplayer.playerConnection.sendPacket(packet);
       }
     }
   }

If we now look at the constructor in PacketPlayOutScoreboardScore we see that the both variables should be setted. we assume that the getScore() method should return 10 because minecraft client shows the score 10 correctly so the value has to be there.

  public PacketPlayOutScoreboardScore(ScoreboardScore paramScoreboardScore)
  {
    this.a = paramScoreboardScore.getPlayerName();
    this.b = paramScoreboardScore.getObjective().getName();
    this.c = paramScoreboardScore.getScore();
    this.d = EnumScoreboardAction.CHANGE;
  }

    public static enum EnumScoreboardAction {

        CHANGE, REMOVE;

        private EnumScoreboardAction() {}
    }

to hook in I use a Class that extends PlayerConnection and replace both via reflection on PlayerJoinEvent

Listener

@EventHandler(priority=EventPriority.NORMAL,ignoreCancelled=false)
public void onJoin(PlayerJoinEvent event)
{
    EntityPlayer nmsplayer = ((CraftPlayer)event.getPlayer()).getHandle();

    FieldReflection f = new FieldReflection();
    try
    {
        f.hook("playerConnection", nmsplayer.getClass(), nmsplayer);
        f.set(new ConnectionWatchdog(nmsplayer.playerConnection));          
    } catch (Exception e)
    {
        e.printStackTrace();
    }



    event.getPlayer().setScoreboard(Bukkit.getScoreboardManager().getNewScoreboard());
    event.getPlayer().getScoreboard().registerNewObjective("test", "dummy");
    Objective o = event.getPlayer().getScoreboard().getObjective("test");
    o.setDisplaySlot(DisplaySlot.SIDEBAR);
    o.setDisplayName("TEST");
    o.getScore("test1").setScore(10);
    o.getScore(" ").setScore(9);
    o.getScore("  ").setScore(8);

}

FieldReflection

public class FieldReflection
{
    public Field f;
    public Object object;

    public void  hook (String name, Class<?> c, Object obj) throws Exception
    {
        f = c.getDeclaredField(name);
        f.setAccessible(true);
        this.object = obj;
    }

    public void set(Object val) throws Exception
    {
        f.set(object, val);
    }

    public Object get() throws Exception
    {
        return f.get(object);
    }

    public int getInt() throws Exception
    {
        return f.getInt(object);
    }

}

ConnectionWatchdog

public class ConnectionWatchdog extends PlayerConnection
{

    public PlayerConnection old;

    public ConnectionWatchdog(PlayerConnection pc)
    {
        super(((CraftServer)Bukkit.getServer()).getServer(), pc.networkManager, pc.player);
    }

    public void sendPacket(Packet<?> p)
    {
        try
        {
            if(p instanceof PacketPlayOutScoreboardDisplayObjective)
            {
                System.out.println("\n \n \n");
                System.out.println("DisplayObjective");
                FieldReflection f1 = new FieldReflection();
                f1.hook("a", p.getClass(), p);
                System.out.println(f1.get());
                FieldReflection f2 = new FieldReflection();
                f2.hook("b", p.getClass(), p);
                System.out.println(f2.get());
            }

            if(p instanceof PacketPlayOutScoreboardObjective)
            {
                System.out.println("\n \n \n");
                System.out.println("Objective");
                FieldReflection f1 = new FieldReflection();
                f1.hook("a", PacketPlayOutScoreboardObjective.class, p);
                System.out.println(f1.get());
                FieldReflection f2 = new FieldReflection();
                f2.hook("b", PacketPlayOutScoreboardObjective.class, p);
                System.out.println(f2.get());
                FieldReflection f3 = new FieldReflection();
                f2.hook("c", PacketPlayOutScoreboardObjective.class, p);
                System.out.println(f3.get());
                FieldReflection f4 = new FieldReflection();
                f2.hook("d", PacketPlayOutScoreboardObjective.class, p);
                System.out.println(f4.get());
            }

            if(p instanceof PacketPlayOutScoreboardScore)
            {
                System.out.println("\n \n \n");
                System.out.println("Score");

                FieldReflection f1 = new FieldReflection();
                f1.hook("a", PacketPlayOutScoreboardScore.class, p);
                System.out.println(f1.get());
                FieldReflection f2 = new FieldReflection();
                f2.hook("b", PacketPlayOutScoreboardScore.class, p);
                System.out.println(f2.get());
                FieldReflection f3 = new FieldReflection();
                f2.hook("c", PacketPlayOutScoreboardScore.class, p);
                System.out.println(f3.getInt());
                Field f = PacketPlayOutScoreboardScore.getDeclaredField("d");
                f.setAccessible(true);
                System.out.println(f.get(p));
            }
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }

        super.sendPacket(p);
    }

}

If anyone has an Idea what I do wrong(except of not simply using ProtocolLib or the stupid normal bukkit-api)please tell me! If you need any more information please tell me.

Thanks in advance,

picatrix1899





Aucun commentaire:

Enregistrer un commentaire