Intercept sending of Packets

Discussion in 'Plugin Development' started by autoit4you, Aug 25, 2013.

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



    I want to intercept the PlayerConnection.sendPacket(Packet packet) function.
    I so far have a class which extends PlayerConnection and overrides sendPacket, but I don't know how to tell CraftBukkit to use my PlayerConnection class instead of the default one.
    Some help would be appreciated.

  2. Offline


    Check out ProtocolLib.
  3. Offline


    I would like to do it without a library.
  4. Offline


  5. Offline


    Yeah, I know, but I'm not sure in which class.
  6. Offline


    Sendpacket class?
  7. Offline


    There's a potential for many subtle and tricky bugs when injecting custom proxy classes, so be prepared to spend some time screaming in anguish. Though it does depend on which method you use to intercept packets - there's a number of distinct approaches here:

    Just remember to always clean up after yourself. Never leave one of your own classes behind after your plugin has been reloaded - this is a sure-fire way to cause memory leaks or crashes, and confuse you when you try to proxy your own proxy classes from the previous round. You can't extend classes with reflection, only interfaces, so your plugin will be tied to a specific CraftBukkit version if you use method #1 (unless you use CGLib).

    And please, try to fall back on ProtocolLib if its present by using soft-depend. Otherwise, your plugin will conflict with every plugin on this list.
  8. Offline


    Thanks for your help. I just don't understand how to insert the Proxy PlayerConnection class into DedicatedServerConnection. Would be nice if you explain that bit to me.
  9. Offline


    Notice that DedicatedServerConnection extends ServerConnection, and within that class you have the following:
    1. public void b() {
    2. for (int i = 0; i < this.c.size(); ++i) {
    3. PlayerConnection playerconnection = (PlayerConnection) this.c.get(i);

    This b() method (or NetworkListenThread.handleNetworkListenThread() in MCP) is regularly called to process packets, send the keep alive packet and prevent chat spam. If you don't exchange it for your proxy class, players will eventually (usually after five minutes) be kicked for spamming the server, even if they don't send more than a couple of messages in total.

    In order to insert your version, one solution is to proxy the List called "c" in the same class, and exchange the PlayerConnection inserted with your version. This is what I do in ProtocolLib.

    And of course, remember to swap the old list back when your plugin is unloading, together with the real PlayerConnections.

    Alternatively, you can use the scheduler and replace the PlayerConnection instance with your own version after the player has logged in (delay it for a couple of ticks). I believe BkCommonLib uses this method.
    autoit4you likes this.
  10. Offline


    Hm, I get a NoSuchFieldException, when I try to get the List.
    1. try {
    2. DedicatedServer server = ((DedicatedServer)((CraftServer)getServer()).getServer());
    3. Field f0 = server.getClass().getDeclaredField("s");
    4. f0.setAccessible(true);
    5. DedicatedServerConnection conn = (DedicatedServerConnection)f0.get(server);
    6. Field f = conn.getClass().getDeclaredField("c");//This is where the Exception occurs
    7. f.setAccessible(true);
    8. List org = (List)f.get(conn);
    9. List c = (List) Proxy.newProxyInstance(List.class.getClassLoader(), new Class[]{ List.class }, new ProxyPlayerConnectionList(org));
    10. f.set(conn, c);
    11. } catch (Throwable t) {
    12. t.printStackTrace();
    13. getServer().getPluginManager().disablePlugin(this);
    14. return;
    15. }
  11. Offline


    That's because "c" is not declared in DedicatedServerConnection, it's declared in ServerConnection. You have to call ServerConnection.getDeclaredField("c"). But keep in mind that these letters can change at any time - in fact, in 1.3.1 this field was called "d". So it's probably a good idea to check the field type before you start using it.

    You could also search through each super class directly, like FieldUtils in Apache Commons. Then you wouldn't have to care about where the field is declared:
    1. Object list = FieldUtils.readField(conn, "c", true);

    Also, why are you using a Java proxy to intercept calls to a normal array list? Why not implement the List directly (extend an ArrayList) and just override each method like any other normal subclass?
  12. Offline


    It still cannot field:
    1. DedicatedServer server = ((DedicatedServer)((CraftServer)getServer()).getServer());
    2. Field f0 = server.getClass().getDeclaredField("s");
    3. f0.setAccessible(true);
    4. ServerConnection conn = (ServerConnection)f0.get(server);
    5. Field f = conn.getClass().getDeclaredField("c");
  13. Offline


    By ServerConnection.getDeclaredField(), I meant ServerConnection.class.getDeclaredField(), not (ServerConnection) f0.getClass(). The latter will only return a Class for DedicatedServerConnection.

    But seriously, why not use ProtocolLib?
Thread Status:
Not open for further replies.

Share This Page