[Lib] [1.7.9] ProtocolLib 3.4.0 - Safely and easily modify sent and recieved packets

Discussion in 'Resources' started by Comphenix, Sep 16, 2012.

  1. Offline

    Hoolean BukkitDev Staff

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Minecraft account:
    MCUSERNAME
    @Comphenix

    How can one catch and cancel outgoing chat messages from the server?

    I have got to the point where I have the PacketContainer but am unsure how to continue :(
  2. Offline

    Comphenix

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Minecraft account:
    MCUSERNAME
    You register a server-side packet listener for the CHAT packet (ID 3);
    Code:java
    1. public class ExampleMod extends JavaPlugin {
    2. public void onEnable() {
    3. ProtocolLibrary.getProtocolManager().addPacketListener(
    4. new PacketAdapter(this, ConnectionSide.SERVER_SIDE, ListenerPriority.HIGHEST, Packets.Server.CHAT) {
    5. public void onPacketSending(PacketEvent event) {
    6. Player reciever = event.getPlayer();
    7. String text = event.getPacket().getStrings().read(0);
    8.  
    9. if (text.contains("hello")) {
    10. // Prevent the packet from being sent
    11. event.setCancelled(true);
    12. }
    13. }
    14. });
    15. }
    16. }

    If you want to know how to read and write data to any given packet, take a look at PacketWrapper. It contains simplified classes for manipulating any given packet.
  3. Offline

    Hoolean BukkitDev Staff

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Minecraft account:
    MCUSERNAME
  4. Offline

    Comphenix

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Minecraft account:
    MCUSERNAME
    ProtocolLib 2.4.7

    Download:
    I've had to expedite my usual monthly release schedule this time, as Minecraft 1.6.1 broke ProtocolLib 2.4.5 and earlier. This version adds 1.6.1 support (specifically, support for client packet interception).

    In addition, ProtocolLib now supports the Lilypad server cluster software.

    Bug fixes
    Performance
    Small fixes

    This post has been edited 1 time. It was last edited by Comphenix Jul 5, 2013.
    chasechocolate likes this.
  5. Offline

    Comphenix

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Minecraft account:
    MCUSERNAME
    I've been working on a couple of new features and API improvements in ProtocolLib lately, and I'd love to get some feedback before I push it out on BukkitDev.


    API Improvement

    Personally, the mess that is constructing a PacketAdapter has always bugged me, and with some new features it has gotten even worse than before. The number of constructors is especially a problem in Eclipse, which always seems to select the wrong constructor when I try filling out the parameters.

    Now, one obvious solution to this is to switch to a Annotation-based listener system, akin to Bukkit, but since Java doesn't support function pointers/delegates this brings up quite a lot of overhead. That may be fine for infrequent Bukkit events, but ProtocolLib has to scale to tens of hundreds of packets per second, for every connected player.

    Instead, I've opted for a hybrid between a builder pattern and constructors. I couldn't go pure builder, as you typically extend PacketAdapter with an anonymous class:
    Code:java
    1. ProtocolLibrary.getProtocolManager().addPacketListener(
    2. new PacketAdapter(PacketAdapter.params(this, Packets.Client.CHAT).clientSide()) {
    3. @Override
    4. public void onPacketReceiving(PacketEvent event) {
    5. System.out.println("Intercepted message: " + event.getPacket().getStrings().read(0));
    6. }
    7. });

    It's much shorter and easier to read than the current syntax, but the biggest advantage is how this plays out in an IDE. This approach feels very fluid and quick, everything is more discoverable, and it can be extended indefinitely without an issue. This is definitely up there with annotations, in my opinion.

    You can try this yourself by downloading the latest developer version.


    Read/Write Network Data

    API improvements may be well and good, but what about new features?

    A lot of new ProtocolLib users naturally assume that the API exposes the exact same data format as defined in the protocol specification, but as you know, it is first parsed by Minecraft itself (or written, at the end) and stored in fields that may differ quite a lot with the network protocol itself. Typically, fields use a larger data type (integer) than the raw network packet, and may store data in lists or custom classes instead of primitive values. But they will nearly always contain the same value semantically.

    Unfortunately, this is no longer the case. In 1.6.1, Bukkit started removing custom NBT tags from ItemStacks when they're read from the network stream, which is the reason ItemStack attributes currently doesn't work in creative mode. This occurs before packet is passed to each packet listener, so there is no easy way to recover these lost attributes (or other tags), even with the aid of ProtocolLib.

    But, with the new "intercept input buffer" option it is now possible to access the raw packet data read directly from the network stream, provided the packet was transmitted by a client:
    Code:java
    1. ProtocolLibrary.getProtocolManager().addPacketListener(
    2. new PacketAdapter(PacketAdapter.params(this, Packets.Client.SET_CREATIVE_SLOT).clientSide().optionIntercept()) {
    3. @Override
    4. public void onPacketReceiving(PacketEvent event) {
    5. DataInputStream input = event.getNetworkMarker().getInputStream();
    6.  
    7. // Can occur if the packet is "sent" by a plugin using recieveClientPacket
    8. if (input == null)
    9. return;
    10.  
    11. try {
    12. // Read slot
    13. int slot = input.readShort();
    14. ItemStack stack = event.getNetworkMarker().getSerializer().deserializeItemStack(input);
    15.  
    16. // Do something
    17.  
    18. } catch (IOException e) {
    19. e.printStackTrace();
    20. }
    21. }
    22. });

    I plan on using this in ItemRenamer to store custom NBT tags, among other things. It also made it possible to use attributes in creative mode. :)

    In addition to reading data, you can also customize the raw packet output:
    Code:java
    1. ProtocolLibrary.getProtocolManager().addPacketListener(
    2. new PacketAdapter(PacketAdapter.params(this, Packets.Client.CUSTOM_PAYLOAD).serverSide()) {
    3. @Override
    4. public void onPacketReceiving(PacketEvent event) {
    5. event.getNetworkMarker().addOutputHandler(
    6. new PacketOutputAdapter(ExampleMod.this, ListenerPriority.NORMAL) {
    7. @Override
    8. public byte[] handle(PacketEvent event, byte[] buffer) {
    9. // Change the byte buffer here
    10.  
    11. return buffer;
    12. }
    13. });
    14. }
    15. });

    Obviously, this only works for packets that are about to be sent to the client (server-side packets).

    This post has been edited 4 times. It was last edited by Comphenix Jul 19, 2013.
    Minecrell likes this.
  6. Offline

    Comphenix

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Minecraft account:
    MCUSERNAME
  7. Offline

    Comphenix

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Minecraft account:
    MCUSERNAME
    Just a heads up - I've added support for reading and modifying Packet44UpdateAttributes, using a standard range of "wrappers" and builder classes (download):
    Code:java
    1.  
    2. final UUID SPRITING_SPEED = UUID.fromString("662a6b8d-da3e-4c1c-8813-96ea6097278d");
    3. final UUID SUPER_SPRINTING = UUID.fromString("d64c79c2-a459-446c-9308-409e1b6b3340");
    4.  
    5. ProtocolLibrary.getProtocolManager().addPacketListener(
    6. new PacketAdapter(this, ConnectionSide.SERVER_SIDE, Packets.Server.UPDATE_ATTRIBUTES) {
    7. public void onPacketSending(PacketEvent event) {
    8. List<WrappedAttribute> list = event.getPacket().getAttributeCollectionModifier().read(0);
    9.  
    10. for (int i = 0; i < list.size(); i++) {
    11. WrappedAttribute attribute = list.get(i);
    12.  
    13. // See if we should add the super sprinting attribute
    14. if (attribute.hasModifier(SPRITING_SPEED) && !attribute.hasModifier(SUPER_SPRINTING)) {
    15. Set<WrappedAttributeModifier> modifiers = Sets.newHashSet(attribute.getModifiers());
    16.  
    17. // Add the new super sprinting too
    18. modifiers.add(WrappedAttributeModifier.newBuilder().
    19. name("Super Sprinting").
    20. uuid(SUPER_SPRINTING).
    21. amount(2).
    22. operation(Operation.ADD_PERCENTAGE).build());
    23.  
    24. list.set(i, attribute.withModifiers(modifiers));
    25. }
    26. }
    27.  
    28. event.getPacket().getAttributeCollectionModifier().write(0, list);
    29. }
    30. }
    31. );

    This causes sprinting to be 200% faster.

    You need the latest developer build for this to work.
  8. Offline

    Comphenix

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Minecraft account:
    MCUSERNAME
    ProtocolLib 2.6.0

    Download:
    The previous release (2.5.0) contained a potentially game breaking bug (ticket), so I've opted to expedite the usual monthly release schedule to get it corrected as soon as possible. Plugins affected by this bug may begin spamming the console, though the error message rate limiter should kick in and prevent the server from crashing. I recommend either staying on 2.4.7, or upgrading immediately.

    Still, I did manage to cram in a new feature, without having to touch the rest of the code base. That will hopefully prevent a repeat from last time.

    PacketContainer now allows you to read and modify the UPDATE_ATTRIBUTE (44) packet, using getAttributeCollectionModifier(). See post #187 for more information.

    Change log

    Features
    Bug fixes
    Small fixes

    This post has been edited 1 time. It was last edited by Comphenix Jul 31, 2013.
  9. Offline

    Cybermaxke

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Minecraft account:
    MCUSERNAME
    I am getting for all my registered packet listeners: "Unsupported server packet ID in current Minecraft version:" or "Unsupported client packet ID in current Minecraft version:" And the window click packet isn't doing so much.

    Code:
    public class AdapterPressButton extends PacketAdapter {
     
        public AdapterPressButton(Plugin plugin) {
            super(plugin, ConnectionSide.CLIENT_SIDE, ListenerPriority.HIGH, Client.WINDOW_CLICK);
            ProtocolLibrary.getProtocolManager().addPacketListener(this);
        }
     
        @Override
        public void onPacketReceiving(PacketEvent e) {
            PacketContainer packet = e.getPacket();
     
            int button = packet.getIntegers().read(2);
            int mode = packet.getIntegers().read(3);
     
            System.out.print("Mode: " + mode + ", Button: " + button);
        }
    }

    This post has been edited 1 time. It was last edited by Cybermaxke Aug 9, 2013.
  10. Offline

    Comphenix

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Minecraft account:
    MCUSERNAME
    This works fine on CraftBukkit 1.6.2 #2850 and ProtocolLib 2.6.1.

    What versions of CB and PL are you using?
  11. Offline

    Cybermaxke

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Minecraft account:
    MCUSERNAME
    I just tested it with the lastest versions: ProtocolLib-2.6.1-SNAPSHOT Build 118 and CraftBukkit-1.6.2-R0.2-SNAPSHOT Build 2850
  12. Offline

    Comphenix

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Minecraft account:
    MCUSERNAME
    Really? Strange.

    Perhaps some other plugin might be interfering with the internal packet id-to-class map in Minecraft. What kind of plugins are you using beside ProtocolLib?
  13. Offline

    Cybermaxke

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Minecraft account:
    MCUSERNAME
    Only ProtocolLib and my plugin.
  14. Offline

    Comphenix

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Minecraft account:
    MCUSERNAME
    Hm, try adding something like this into your plugin. I'd like to see what the packet class list looks like:
    Code:java
    1. public class ExampleMod extends JavaPlugin implements Listener {
    2. // Remember to depend on ProtocolLib
    3. @Override
    4. public void onEnable() {
    5. System.out.println(PacketRegistry.getPacketToID());
    6. }
    7. }

    The output will be somewhat big, so it's probably best to post it using pastebin.
  15. Offline

    Cybermaxke

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Minecraft account:
    MCUSERNAME
  16. Offline

    Comphenix

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Minecraft account:
    MCUSERNAME
    So they at least show up there. Okay, what about the server and client packet list:
    Code:java
    1. public class ExampleMod extends JavaPlugin implements Listener {
    2. // Remember to depend on ProtocolLib
    3. @Override
    4. public void onEnable() {
    5. System.out.println("Server: " + PacketRegistry.getServerPackets());
    6. System.out.println("Client: " + PacketRegistry.getClientPackets());
    7. }
    8. }
  17. Offline

    Cybermaxke

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Minecraft account:
    MCUSERNAME
    http://pastie.org/8223059
    Looks like client and server are switched:
    Code:
    Server: [103, ...]
    Code:
    Unsupported client packet ID in current Minecraft version: 103
    EDIT: It is working in the normal inventory, but not in thr creative one. I still don't know why its throwing the unsupported exceptions.

    This post has been edited 2 times. It was last edited by Cybermaxke Aug 10, 2013.
  18. Offline

    Comphenix

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Minecraft account:
    MCUSERNAME
    Ah, now I see - you're not just registering a WINDOW_CLICK listener, you're also registering listeners for a range of other packets. Next time, remember to post all the relevant code, not just a snippet that happens to work fine by itself ...

    The problem here is that you can only register listeners for packet IDs that actually exist. I suggest registering two separate packet listeners, one for every server packet your interested in, and one for every client packet. Then use the correct ConnectionSide (not ConnectionSide.BOTH - which really should be depreciated), and things should work again.

    This post has been edited 1 time. It was last edited by Comphenix Aug 10, 2013.
  19. Offline

    Cybermaxke

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Minecraft account:
    MCUSERNAME
  20. Offline

    Comphenix

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Minecraft account:
    MCUSERNAME
  21. Offline

    Cybermaxke

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Minecraft account:
    MCUSERNAME
  22. Offline

    Ultimate_n00b

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Minecraft account:
    MCUSERNAME
    I don't suppose it is possible to send a packet that changes how far a bow is drawn?
  23. Offline

    Cybermaxke

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Minecraft account:
    MCUSERNAME
    Sadly, that is client side. I also tried to make the bow charging slower/faster but it isn't possible. :/
  24. Offline

    Ultimate_n00b

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Minecraft account:
    MCUSERNAME
    :/ I kinda figured, but there are quite a few things we used to think were only client side.
  25. Offline

    desht

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Minecraft account:
    MCUSERNAME
    The basic rule of thumb for knowing if something is purely client side or not is to look at the packet reference here: http://www.wiki.vg/Protocol - if there's a packet for what you want to do, then you can control it from the server, either via Bukkit, direct NMS calls, or via ProtocolLib. It's well worth reading that page and getting at least somewhat familiar with all the packets and what they can do.
  26. Offline

    Ultimate_n00b

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Minecraft account:
    MCUSERNAME
    Forgot that link, thank you.
  27. Offline

    Comphenix

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Minecraft account:
    MCUSERNAME
    ProtocolLib 2.7.0

    Download:
    It's been a month, and it's time for yet another bug fix release - this time to address a couple of issues with the INTERCEPT_BUFFER feature introduced in 2.5.0 regarding certain plugins and Spigot, along with some API improvements. Spigot users are advised to upgrade.

    An interesting bug was reported in issue 118 - asynchronous listeners, which were introduced way back in 1.2.0, can actually player actions to be performed twice. If you have noticed problems such as doors closing and opening, blocks being double-placed, etc., I urge you to upgrade.

    Finally, I've added a new convenience method to PacketManager - broadcastServerPacket(), with three different overloads that allow you to broadcast a packet to every player on the server, every player tracking a given entity or every player within a certain radius.

    Change log

    Features
    Bug fixes
    API changes

    This post has been edited 2 times. It was last edited by Comphenix Sep 2, 2013.
  28. Offline

    BorisTheTerrible

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Minecraft account:
    MCUSERNAME
  29. Offline

    Comphenix

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Minecraft account:
    MCUSERNAME
  30. Offline

    bsymon

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Minecraft account:
    MCUSERNAME
    Hi, I'm using ProtoLib to make a plugin that mask the messages listed in a file. If the server send a message, and if this message is contained in the file, the message is not sended. This work pretty well :)

    BUT (yeah, is not fun when everything work for the first try), all the messages sended from PlayerJoinEvent are not intercepted ... And it's very annoying :/ Is not normal ?

    If yes, there is a way to still get the messages ?


    Thank you, and sorry if my english is not perfect, I'm french :)

    This post has been edited 1 time. It was last edited by bsymon Sep 9, 2013.

Share This Page