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

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

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


    That's not going to help you - it will only modify up to 64 blocks on the client-side.

    Maybe there's a simpler way. Have you thought about kicking all the players, restoring the region files and (if necessary) restart? It might also be possible to do this without kicking any players (NMS).

    You might also be able to reload the world by using the API of plugins like Prism, CoreProtect, LogBlock or Hawkeye. That might be simpler than figuring how to store the old world state, and restore it yourself.
  2. Offline


    I keep getting the error "com.comphenix.protocol.reflect.FieldAccessException: Field index must be within 0 - count".

    I've read your post about internal packet fields, but what I could apply from that didn't seem to work. Some help?
    My code is:
    1. float exp = bars * ((float) 1/ 18);
    2. PacketContainer setXP = protocolManager.createPacket(Packets.Server.SET_EXPERIENCE);
    4. setXP.getFloat().write(0, exp);
    5. setXP.getShorts().write(0, (short) player.getLevel());
    6. try {
    7. protocolManager.sendServerPacket(player, setXP);
    8. e.printStackTrace();
    9. }

    The error is thrown on line 5.
  3. Offline


    As I explained in my tutorial, ProtocolLib modifies the content of the packets in memory, not what is transmitted across the network.

    Take a look at packet 43. It does have a float field, but it stores the short values in two integer fields. Thus you have to use getIntegers() instead of getShorts(). Take a look at PacketWrapper for an example.
  4. Offline


    Comphenix Thanks for that. I didn't realise that it could be using different base types (you explained it with entities in your tutorial).
  5. Offline


    Looking at using ProtocolLib for a plugin I'm planning on making. The thing I want to achieve is complete anonymity for players: everyone should have "stranger" as nametag and show up as "stranger" in the scoreboard. Every client should not be aware of the identities of other players. Is this possible to achieve with packet edits, and without creating problems (would this affect skins?)?

    TagAPI seems to be able to change nametags but I doesn't seem to mention scoreboard names (although, are those the same thing for the client?).

    If anyone has some quick information on these problems before I dive in, it'd be appreciated.
    Comphenix likes this.
  6. Offline


    You don't necessarily have to use ProtocolLib directly - you can use the latest developer build of CraftBukkit and its Scoreboard API, together with TagAPI:
    1. public class ExampleMod extends JavaPlugin implements Listener {
    2. private int TICKS_PER_SECOND = 20;
    4. // Prefix used to hide a player's name
    5. private String prefixObfuscate = ChatColor.MAGIC.toString();
    7. @Override
    8. public void onEnable() {
    9. getServer().getPluginManager().registerEvents(this, this);
    11. // Repeatedly obfuscate the player scoreboard
    12. getServer().getScheduler().scheduleSyncRepeatingTask(this, new Runnable() {
    13. @Override
    14. public void run() {
    15. Set<Scoreboard> processedBoards = new HashSet<Scoreboard>();
    17. for (Player player : getServer().getOnlinePlayers()) {
    18. Scoreboard board = player.getScoreboard();
    20. // Don't process the same scoreboard twice
    21. if (!processedBoards.add(board)) {
    22. obfuscateScoreboard(board);
    23. }
    24. }
    25. }
    26. }, 0, TICKS_PER_SECOND * 1);
    27. }
    29. private void obfuscateScoreboard(Scoreboard scoreboard) {
    30. for (Team team : scoreboard.getTeams()) {
    31. // I wouldn't recommend displaying the score in the sidebar ... too distracting.
    32. team.setPrefix(prefixObfuscate);
    33. }
    34. }
    36. @EventHandler
    37. public void onPlayerLogin(PlayerJoinEvent e) {
    38. e.getPlayer().setDisplayName("Somebody");
    39. }
    41. @EventHandler
    42. public void onPlayerTag(PlayerReceiveNameTagEvent e) {
    43. // Anonymize player tags
    44. e.setTag("Somebody");
    45. }
    46. }

    You could skip TagAPI entirely, but then you would have a randomizing character effect on each player, which could become really distracting over time.

    It's also possible to intercept the scoreboard packet directly, but trouble is - you can't set a different display name as it's used by the client internally to determine which score to update. So there's little point in using ProtocolLib to intercept the packet.
  7. Offline


    Thanks for the info! One more thing: this completely removes the possibility to identify other players even if you'd have a modded client right? The client application has not a single shred of information of another client's real identity? It's kind of vital for my project.
  8. Offline


    Unfortunately, I don't think the native Scoreboard API can conceal the actual player names - I merely use the Team Prefix feature to scramble the visible name on screen. A scrupilous client could still aquire these names by reading the data in memory or intercepting the packets, though there wouldn't be any immediate connection to the actual players in the game as they're all named Somebody in the perspective of the client.

    It depends on how much anonymity you need. If you want to prevent ANY identity information from leaking, you could randomize the name on player login and replace it whenever you need to send a unique name to a client (for the scoreboard). This random name could even be unique per receieved client. The other packets (displayed name tag, tab list, etc.) doesn't necessarily require a unique name, so you can simply supply "Somebody" in those cases.

    It looks like you will have to use ProtocolLib extensively. I recommend taking a look at all the tutorials/examples and PacketWrapper in particular. Using JavaBeans is much easier than having to look up the field indices and types for each packet. There's also this list of all the packets in Minecraft. You probably have to intercept packet 0x14, 0xC9, 0xD1 and 0xCF (packet 0x3 is not necessary as you can use setDisplayName() for that).

    Let me know if you have any further questions. :)
  9. Offline


    ProtocolLib 2.4.1

    This is a minor update that addresses a couple of compatibility issues - particularly with CraftBukkit forks such as Spigot - along with improvement in performance and features (a filter system and better fuzzy reflection). I've also marked Minecraft 1.5.1 as compatible, removing the warning message during startup.

    Among the more interesting new features is the filter system - it uses the built in JavaScript interpreter in JVM 6 (Rhino) to extend the packet command with filtering capabilities - it is now possible to, say, only print entity metadata packet events (packet add server 40) for a given entity ID:
    > packet add server 40 true
    Added listener ListeningWhitelist{priority=MONITOR, packets=[40]}
    > filter add entity_filter 40
    Enter filter program ('}' to complete or CANCEL):
    function(event, packet) {
    > return packet.a == 1000;
    Added filter entity_filter.
    This should be much more convenient than having to compile a test plugin and reload the whole server. Note that this feature is disabled by default for security reasons. To enable it, add "debug: true" to config.yml.

    For more information about all the other changes, consult the change log.

    API changes
    Stability and performance
    Bug fixes
    Small fixes
  10. Offline


    Are you able to make external connections with this?

    ie; I would possibly be interested in using this for creating an interserver/cross-server teleporting (something a lot better than the oddly done but infamous BungeeCoord)

    However, it would be easier for me to create everything as opposed to rely on this as an external dependency just for packets - Unless of course this contains the ability to create connections (A socket system if you like)
  11. Offline


    Unfortunately, ProtocolLib can only intercept packets in existing connections - it cannot create a new client connection itself and act as a bridge. You'll have to initiate a connection with the other server yourself, and read/write packets using the packet's read and write methods.

    Looking at BungeeCoord, it appears that it does some complicated packet translation when forwarding packets in each direction. This could be implemented in ProtocolLib, though it would probably be less efficient than the method in BC that process a byte array. Another limitation is the Minecraft system itself - it creates up to three threads per player, unlike Netty in BC, which could eat up a lot of memory.
  12. Offline


    Just curious, does this make it possible to cancel the firework explosion sound?
  13. Offline


    I don't think so - the explosion effect isn't transmitted using packet 61 or 62 at the very least:
    It's probably client side, I'm afraid. But you can cancel the "launch" sound effect.
  14. Offline


    Thanks. I couldn't find any packets for it, so I was not sure. Well, thanks anyways.
  15. Offline


    ProtocolLib 2.4.3

    This is another minor update that addresses a few bugs and compatibility issues, primarily with BkCommobLib.
    The only additional features this time is an internal "report" system that identifies the different warnings and error reports ProtocolLib may print to the console. ProtocolLib will use this to suppress certain warnings on different implementations of Bukkit. In addition, I've also added a couple of warnings that detects if a plugin developer has forgotten to depend on ProtocolLib.

    API changes
    Stability and performance
    Bug fixes
    Small fixes
  16. Offline


    ProtocolLib 2.4.5

    This update was mainly released to address a very serious memory leak in conjunction with Spigot. If you're using Spigot or one of its derivatives, I urge you to update as soon as possible. When loading and unloading worlds in succession, ProtocolLib would incorrectly retain a reference to each of these worlds, which would eventually exhaust all available memory to the JVM (and server). This has now been fixed.

    It's now also possible to suppress warnings and errors by editing the configuration, should the need arise. It's probably most useful for people that are running a modded CraftBukkit server, and occasionally get irrelevant warnings and errors in the console.

    There's also been a couple of other very minor bug fixes. See the change log for more details.

    Bug fixes
    API changes
    Small fixes
  17. Offline



    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 :(
  18. Offline


    You register a server-side packet listener for the CHAT packet (ID 3);
    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);
    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.
  19. Offline


  20. Offline


    ProtocolLib 2.4.7

    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
    Small fixes
    chasechocolate likes this.
  21. Offline


    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:
    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:
    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();
    7. // Can occur if the packet is "sent" by a plugin using recieveClientPacket
    8. if (input == null)
    9. return;
    11. try {
    12. // Read slot
    13. int slot = input.readShort();
    14. ItemStack stack = event.getNetworkMarker().getSerializer().deserializeItemStack(input);
    16. // Do something
    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:
    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
    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).
    Minecrell likes this.
  22. Offline


  23. Offline


    Just a heads up - I've added support for reading and modifying Packet44UpdateAttributes, using a standard range of "wrappers" and builder classes (download):
    2. final UUID SPRITING_SPEED = UUID.fromString("662a6b8d-da3e-4c1c-8813-96ea6097278d");
    3. final UUID SUPER_SPRINTING = UUID.fromString("d64c79c2-a459-446c-9308-409e1b6b3340");
    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);
    10. for (int i = 0; i < list.size(); i++) {
    11. WrappedAttribute attribute = list.get(i);
    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());
    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());
    24. list.set(i, attribute.withModifiers(modifiers));
    25. }
    26. }
    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.
  24. Offline


    ProtocolLib 2.6.0

    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

    Bug fixes
    Small fixes
  25. Offline


    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.

    public class AdapterPressButton extends PacketAdapter {
        public AdapterPressButton(Plugin plugin) {
            super(plugin, ConnectionSide.CLIENT_SIDE, ListenerPriority.HIGH, Client.WINDOW_CLICK);
        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);
  26. Offline


    This works fine on CraftBukkit 1.6.2 #2850 and ProtocolLib 2.6.1.

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


    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
  28. Offline


    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?
  29. Offline


    Only ProtocolLib and my plugin.
  30. Offline


    Hm, try adding something like this into your plugin. I'd like to see what the packet class list looks like:
    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.
Thread Status:
Not open for further replies.

Share This Page