[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


    It's an easy mistake to make, but ProtocolLib uses the internal packet fields to read and write the data, not the data that's transmitted over the wire.

    I've written a tutorial for this. Essentially, you use JD-GUI to decompile the source and check which fields to modify:
    1. public class Packet130UpdateSign extends Packet {
    2. public int x;
    3. public int y;
    4. public int z;
    5. public String[] lines;
    6. // ect.
    7. }

    There's a developer build out already with support for 1.4.6.

    But I might be able to release a version on BukkitDev in a few days. It's Christmas though, so don't expect too much. :p
    puyttre likes this.
  2. Offline


  3. Offline


    Comphenix I am developing my own version of Craftbukkit combined with a proxy server for clients. I want to be able to send a custom packet through player connections from the proxy to the server and have the server interpret it through a plugin that acts upon the packet. My problem however is how would I define a custom packet and from there, get it properly read by the plugin acting through your lib (hopefully).

    Would your lib be able to accept my custom packet (say id 0xCF) and be able to act upon it? Or would I need to do some sneaky inserts into Craftbukkit to even accept it and handle it there?
  4. Offline


    Sure, you can define your own custom packets, but you will have to modify the packet registry in "Packet.class" to avoid CraftBukkit throwing an exception. It's also possible to do this on runtime, but I don't expose that capability in ProtocolLib.

    ProtocolLib should work fine as long as you extend the same registry with your custom packets, and don't modify the internals too much. It was designed to accommodate future packets without having to be updated, after all.

    Still, you could accomplish the same thing using the plugin channel system without having to touch the registry at all. I'd probably go that route instead.
  5. Offline


    ProtocolLib 1.9.0

    This is a required update for CraftBukkit 1.4.6-R0.1 Beta, as Bukkit made numerous changes to parts of the internal class structure ProtocolLib uses to inject its custom code.

    In addition, there's a number of small bug fixes - a fix for ticket-24 addresses an oversight causing written ItemStack arrays not being properly converted. I also updated the conversion of CraftItemStacks to net.minecraft.server.ItemStack in 1.4.6.

    Finally, I've completely redone the packet cloning system. It should now work properly, regardless of packet ID (unlike the old system that couldn't handle ID 24, 51, 53 and 56), and is also approximately 2-3x faster.

    New features
    Bug fixes
    Small fixes
    phondeux likes this.
  6. Offline


    1. 2012-12-27 20:01:01 [WARNING] [SearchAndDestroy] Task #13 for SearchAndDestroy v1.7 generated an exception
    2. at sun.reflect.UnsafeFieldAccessorImpl.ensureObj(UnsafeFieldAccessorImpl.java:54)
    3. at sun.reflect.UnsafeObjectFieldAccessorImpl.get(UnsafeObjectFieldAccessorImpl.java:36)
    4. at java.lang.reflect.Field.get(Field.java:372)
    5. at com.comphenix.protocol.reflect.FieldUtils.readField(FieldUtils.java:281)
    6. at com.comphenix.protocol.injector.EntityUtilities.updateEntity(EntityUtilities.java:98)
    7. at com.comphenix.protocol.injector.PacketFilterManager.updateEntity(PacketFilterManager.java:566)
    8. at me.libraryaddict.SearchAndDestroy.SearchAndDestroy.startgame(SearchAndDestroy.java:710)
    9. at me.libraryaddict.SearchAndDestroy.SearchAndDestroy.onSecond(SearchAndDestroy.java:665)
    10. at me.libraryaddict.SearchAndDestroy.SearchAndDestroy$1.run(SearchAndDestroy.java:115)
    11. at org.bukkit.craftbukkit.scheduler.CraftTask.run(CraftTask.java:53)
    12. at org.bukkit.craftbukkit.scheduler.CraftScheduler.mainThreadHeartbeat(CraftScheduler.java:345)
    13. at net.minecraft.server.MinecraftServer.r(MinecraftServer.java:530)
    14. at net.minecraft.server.DedicatedServer.r(DedicatedServer.java:224)
    15. at net.minecraft.server.MinecraftServer.q(MinecraftServer.java:494)
    16. at net.minecraft.server.MinecraftServer.run(MinecraftServer.java:427)
    17. at net.minecraft.server.ThreadServerApplication.run(SourceFile:849)

    1. ProtocolLibrary.getProtocolManager().updateEntity(gamer.getPlayer(),
    2. Arrays.asList(Bukkit.getOnlinePlayers()));

    This has been happening for a while now. Roughly 1-2 weeks.
    Thought there was no way it could be you but..
  7. Offline


    What version of ProtocolLib are you running, by the way?

    I can't reproduce the problem on my small 2 player test server:
    public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
        // Get the target argument
        if (args.length == 1) {
            Player target = getServer().getPlayerExact(args[0]);
            protocolManager.updateEntity(target, Arrays.asList(Bukkit.getOnlinePlayers()));
            return true;
        return false;
    But could it be that the player you're trying to update has just logged out? I suspect that might be the case.

    I'll definitely fix the NullPointerException, but trying to update a logged out player is a bit of an error. I could thrown a PlayerLoggedOutException, or something like that. What do you think?

    A return value (TRUE/FALSE) would probably have been better, but it's a bit late to chance the API now ...
  8. Offline


    I think a little more information on it could be pretty handy.
    I would likely have fixed it myself but without looking into your source (Easy enough I know) just the stacktrace isn't enough.

    I'll change it so it does only online players. (Even through it should be already) with Bukkit.getOnlinePlayers() and if this STILL happens I'll inform you.

    But changing the void returns. Would this create problems for only code that's already compiled?

    Seems to work fine now :eek:

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
    Last edited by a moderator: May 28, 2016
  9. Offline


    ProtocolLib 2.0.0

    This marks the release of the next major version ProtocolLib, with a fix for a potential bug and support for NBT tags.

    Although this particular update is not breaking nor significant enough to warrant it, I've nevertheless decided to increment the major version. It's long overdue - I should have made this jump before releasing 1.8.0 to emphasize the breaking change in the API (getHandle() now returns Object), but it's too late to change it now.

    The main new feature of this update is the ability to modify NBT tags in packets and item stacks, along with reading and writing them to different output formats. For instance, you can now store auxiliary data in an ItemStack very simply:
    2. ItemStack counter = target.getItemInHand();
    3. NbtCompound tag = NbtFactory.asCompound(NbtFactory.fromItemTag(counter));
    4. NbtCompound data = tag.getCompoundOrDefault("com.comphenix.example");
    6. // Increment the count
    7. data.put("count", data.getIntegerOrDefault("count") + 1);
    8. sender.sendMessage("Current count: " + data.getInteger("count"));

    I've also added an utility class that can serialize and deserialize ItemStacks from byte arrays and strings.

    Of course, you can also access and modify the NBT data in packet 132 (Update Tile Entity):
    1. manager.addPacketListener(new PacketAdapter(this, ConnectionSide.SERVER_SIDE, Packets.Server.TILE_ENTITY_DATA) {
    2. @Override
    3. public void onPacketSending(PacketEvent event) {
    4. NbtBase<?> nbt = event.getPacket().getNbtModifier().read(0);
    6. System.out.println("Sent " + nbt.toString());
    7. }
    8. });

    New features
    Bug fixes
  10. Offline



    When reading and writing packets in ProtocolLib, it is necessary to know the order at which the fields are stored in memory. This requires you to decompile the Minecraft source code with JD Gui, while looking up the meaning of a particular field online using a wiki resource (tutorial). It would be much easier if these packets could be accessed as any other normal Java bean.

    Enter PacketWrapper. It contains wrapper classes for all known packets in 1.4.6, providing you with access to the fields by name, along with automatic conversion to existing Bukkit enumerations and classes. It also includes a number of custom enumerations when appropriate.

    You can use PacketWrapper as a dependency if you wish, though the intent is for plugin authors to simply copy-and-paste the classes they need into their project.

    Here's an example of what you can do with these new wrappers:
    1. final Random rnd = new Random();
    3. ProtocolLibrary.getProtocolManager().addPacketListener(
    4. new PacketAdapter(this, ConnectionSide.SERVER_SIDE,
    5. Packets.Server.NAMED_SOUND_EFFECT) {
    6. @Override
    7. public void onPacketSending(PacketEvent event) {
    8. Packet3ENamedSoundEffect named =
    9. new Packet3ENamedSoundEffect(event.getPacket());
    11. // Random sound name
    12. String[] choices = Packet3ENamedSoundEffect.
    13. NamedSoundEffects.values();
    14. named.setSoundName(choices[rnd.nextInt(choices.length)]);
    15. }
    16. });
  11. Offline


    ProtocolLib 2.1.0

    This update primarily contains bug fixes for the functionality introduced in the previous version, along with full support for 1.4.7.

    If you are a developer, remember to take a look at the new NBT wrapper library. See the release notes for the previous version for more information. Also note that a couple of the packets in the packet registry has been depreciated, as they're no longer sent by either the client nor the server.

    There's also a new packet wrapper library that can come in handy.
    New features

    API changes

    Bug fixes

    Small fixes

  12. Offline


    Thanks for all the nice work on this API. I'm just beginning to grasp a few concepts and I started by looking at the BlockPatcher. Everything makes sense except the fact that in the BlockTest, the chunk the player is moving on is manually forced to be re-send to the client. Shouldn't this be already triggered automatically by the server to bring more chunks into view, at which stage the packets get trapped? The code is indeed correct, as without the "resendChunk", the client shows the blocks, when I move further from the login location and new chunks get served from the server, as if the packets were never trapped and modified, but I simply don't understand why.
  13. Offline


    Yes, this is done automatically by the server - however, if you want to modify chunks that have already been sent, you will have to resend them.

    The point here is that BlockTester only affects the chunk underneath the current player. It's basically a kind of window, a glass chunk, that I imagine a operator might use as a kind of server-side x-ray. Of course, BlockPatcher can just as easily do this for every single chunk instead - it's just a demonstration for how to modify individual chunks temporarily.
  14. Offline


    Yep I got it now. Thanks. I have now a soft dependency on your plugin. When installed, the players can view the effect of config changes for my plugin.

    I do get a strange behavior from it though:
    If I modify a large set of chunks using the unofficial "fast" method, i.e. using reflection and manually notifying the changes to the players via chunkCoordIntPairQueue, your plugin intercepts the packets properly and "replaces" non-ore blocks with glass.
    However, if the same code changes blocks this time via the classic Block.setTypeId, the plugin fails to intercept the changes that go to the client and the world slowly comes back to normal.

    Would have expected to have problems when using reflection, and not the other way around :)
    The plugin does change a lot of chunks, and the area that I'm intercepting is far wider than the single chunk you have in the demo. So maybe I'm missing an event? I switched your plugin to output the trace, couldn't see any other events though.
  15. Offline


    Yeah, remember, those changes aren't propagated through map chunk packets, they're sent using Multi Block Change (52) and Block Change (53).

    You should verify that you're code is actually touching those packets. For instance, are you sure the following in translateBlockChange and translateMultiBlockChange won't cause the processing to be interrupted:
    2. if (!isImportantChunk(x >> 4, z >> 4, playerReg)) {
    3. return;
    4. }

    I'd recommend systematically adding debug messages all over to determine if there are any problems. It's saved me countless times, at least (though, often I have to add them to CraftBukkit itself ...).
  16. Offline


    The same validation check goes for every packet, for both methods of updating the world (fast and fail-safe), so if others work, these two should work as well, unless the chunk coordinates are not picked up properly from the packet. I'll follow your advice and add more tracing messages, maybe I can figure it out what the problem is.
  17. Offline


    1. ProtocolLibrary.getProtocolManager().updateEntity(p,
    2. Arrays.asList(Bukkit.getOnlinePlayers()));

    I'm calling this approx for 40 players all who should be next to each other.

    But I'm getting issues ever since the latest update, Namely it was a few invisible players.

    Currently I'm using this code and sometimes there is players sitting still, As if I'm no longer being sent packets concerning them.

    Also, If the client sends a unsolicited login packet. (Forge mod loader)
    Would protocol lib still catch it?

    I tried listening for it and I don't think it caught it. Only one login packet received.
  18. Offline


    This began to occur after CraftBukkit updated? Have you tried updating the entity after a few seconds? I know the EntityTracker system which ProtocolLib depends on may be lagging at times.

    I don't know. It depends on how the Forge server reads this unsolicited packet. Seeing that it's not intercepted by ProtocolLib, it's probably done by constructing the Login packet directly, instead of reading from the generic packet-ID-to-class registry in Packet.

    If that's the case, then you're out of luck. I would have to use a different injection method to intercept the packet in that case, and depending on how Forge operates, that may be impossible too.

    If you're using a normal CraftBukkit server, I'd take a look at how and when the client sends this extra login packet. I'd use this Minecraft parsing proxy to figure that out.

    In general, ProtocolLib is mostly only made to work on CraftBukkit servers, and then only for packets during normal game play.
  19. Offline


    ProtocolLib 2.2.0

    This update adds support for the upcoming improved network connection system (Netty) in Spigot, along with experimental projects such as MCPC-Plus 1.4.7 that can run normal Minecraft mods and Bukkit plugins together on the same server.

    Support for Spigot Netty was added through a direct collaboration with md-5 (the maintainer), using a clean and officially sanctioned API. This makes it very stable and efficient, especially in comparison to the existing method. However, it's not yet included in the main branch of Spigot, but it will probably be out in the latest snapshot of Spigot soon.

    Compatibility with MCPC-Plus has been included, though through the usual means of reflection and code generation. The biggest hurdle here is the fact that most classes has been re-obfuscated in order to support Minecraft mods, so ProtocolLib will now attempt to probe known classes for the changed name of each NMS class it uses. This is a fairly generic method that will also make ProtocolLib resistant against future possible changes in CraftBukkit.

    This probing technique is implemented in the form of a reusable "fuzzy reflection" library (under com.comphenix.protocol.reflection.fuzzy) that can be accessed by other plugins if they so chooses. Plugins authors should also consider depending on MinecraftReflection, as it gives access to NMS classes regardless of their name thanks to the compatibility of MCPC.

    Finally, I've also fixed a number of bugs, including one that would prevent plugins from sending and simulating "receiving" packets during player login.

    New features
    API changes
    Bug fixes
    Small fixes
  20. Offline


    Can you plz make a sampel for a Block Break Animation with higher BlockResistance?

    Perhaps this code with ProtocolLib support, so the Block Break Animation are equal like the longer block break?

          public void setBlockResistance(Block b, Float f)
            Method method = null;
            try {
              method = Block.class.getDeclaredMethod("c", new Class[] { Float.TYPE });
            catch (NoSuchMethodException e) {
            catch (SecurityException e) {
            try {
              method.invoke(b, new Object[] { f });
            catch (IllegalAccessException e) {
            catch (IllegalArgumentException e) {
            catch (InvocationTargetException e) {
  21. Offline


    You should give it a try yourself first.

    You need to start a repeating synchronized task that sends block break animation packets to that player using a different entity ID. Then you also need to cancel the block break on the client side - try setting the block to its same block ID each time you send the animation.

    I haven't tested it though - the usual block break animation is client-side, so that might completely override the packet sent by the server. If so, then this is probably impossible unless you're using Spout.
  22. Offline


    Comphenix I'm fairly sure its started happening after the update.

    It could also be optifine bugging out however. Some players seem not to notice it.

    Just trying to work around it atm.

    Edit: I think I removed the delay as I thought it was fixed. And it was. The issue seemed to have come back so yes. You were right.
  23. Offline


    I believe this comes down to an optimization in Minecraft/CraftBukkit - the entity tracker is not always up to date.

    But I'm glad adding a delay seems to work though.

    On an unrelated note, I've created a discussion/support forum on BukkitDev - I think it's time I try to centralize everything to a single location. One huge thread here, along with the comment section on BukkitDev, really isn't that easy to sift through ...
  24. Offline


    ProtocolLib 2.3.0

    This is the last major update before Minecraft 1.5 hits, so I decided to mainly focus on improving performance and stability.

    A number of users were having problems with the latest developer builds (1.45+) of BkCommonLib, which all came down to the fact that BkCommonLib for some time registered a listener for every server and client packet known. While this has been fixed in the latest developer version of BkCommonLib, it nevertheless prompted me to correct a number of inefficiencies and bugs in ProtocolLib.

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


    Hey comphenix, with spigot's new netty support. Does protocollib have a chance to be compatible with it?
    1. 2013-03-12 21:49:46 [INFO] Error Player hook NETWORK_SERVER_OBJECT failed. (java.lang.IllegalArgumentException: Unable to find a field with the type java\.io\.DataInputStream in org.spigotmc.netty.NettyNetworkManager) occured in com.comphenix.protocol.injector.player.PlayerInjectionHandler@43d3774.
    2. 2013-03-12 21:49:46 [SEVERE] [ProtocolLib] INTERNAL ERROR: Player hook NETWORK_SERVER_OBJECT failed.
    3. If this problem hasn't already been reported, please open a ticket
    4. at [url]http://dev.bukkit.org/server-mods/protocollib/[/url] with the following data:
    5. ===== STACK TRACE =====
    6. java.lang.IllegalArgumentException: Unable to find a field with the type java\.io\.DataInputStream in org.spigotmc.netty.NettyNetworkManager
    7. at com.comphenix.protocol.reflect.FuzzyReflection.getFieldByType(FuzzyReflection.java:303)
    8. at com.comphenix.protocol.injector.player.PlayerInjector.initializeNetworkManager(PlayerInjector.java:212)
    9. at com.comphenix.protocol.injector.player.PlayerInjector.initializePlayer(PlayerInjector.java:172)
    10. at com.comphenix.protocol.injector.player.NetworkServerInjector.initializePlayer(NetworkServerInjector.java:52)
    11. at com.comphenix.protocol.injector.player.PlayerInjector.initialize(PlayerInjector.java:138)
    12. at com.comphenix.protocol.injector.player.NetworkServerInjector.initialize(NetworkServerInjector.java:87)
    13. at com.comphenix.protocol.injector.player.PlayerInjectionHandler.injectPlayerInternal(PlayerInjectionHandler.java:302)
    14. at com.comphenix.protocol.injector.player.PlayerInjectionHandler.injectPlayer(PlayerInjectionHandler.java:272)
    15. at com.comphenix.protocol.injector.player.PlayerInjectionHandler.injectPlayer(PlayerInjectionHandler.java:246)
    16. at com.comphenix.protocol.injector.PacketFilterManager.onPlayerJoin(PacketFilterManager.java:643)
    17. at com.comphenix.protocol.injector.PacketFilterManager.access$400(PacketFilterManager.java:62)
    18. at com.comphenix.protocol.injector.PacketFilterManager$3.onPlayerJoin(PacketFilterManager.java:612)
    19. at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    20. at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    21. at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    22. at java.lang.reflect.Method.invoke(Unknown Source)
    23. at org.bukkit.plugin.java.JavaPluginLoader$1.execute(JavaPluginLoader.java:425)
    24. at org.bukkit.plugin.RegisteredListener.callEvent(RegisteredListener.java:62)
    25. at org.bukkit.plugin.TimedRegisteredListener.callEvent(TimedRegisteredListener.java:26)
    26. at org.bukkit.plugin.SimplePluginManager.fireEvent(SimplePluginManager.java:479)
    27. at org.bukkit.plugin.SimplePluginManager.callEvent(SimplePluginManager.java:464)
    28. at net.minecraft.server.v1_4_R1.PlayerList.c(PlayerList.java:159)
    29. at net.minecraft.server.v1_4_R1.PlayerList.a(PlayerList.java:98)
    30. at net.minecraft.server.v1_4_R1.PendingConnection.d(PendingConnection.java:135)
    31. at net.minecraft.server.v1_4_R1.PendingConnection.c(PendingConnection.java:50)
    32. at org.spigotmc.netty.NettyServerConnection.b(NettyServerConnection.java:72)
    33. at net.minecraft.server.v1_4_R1.MinecraftServer.r(MinecraftServer.java:600)
    34. at net.minecraft.server.v1_4_R1.DedicatedServer.r(DedicatedServer.java:228)
    35. at net.minecraft.server.v1_4_R1.MinecraftServer.q(MinecraftServer.java:489)
    36. at net.minecraft.server.v1_4_R1.MinecraftServer.run(MinecraftServer.java:421)
    37. at net.minecraft.server.v1_4_R1.ThreadServerApplication.run(SourceFile:849)
  26. Offline


    Sure. Just upgrade to ProtocolLib 2.2.0 or later.
  27. Offline


    This really explains a lot. I never quite knew what ProtocolLib was for until now. I'm trying to make something that would roll back everything after the game is over in a certain arena. Is there any kind of wiki that has all of the packets that I can use and all other kind of useful information?

  28. Offline


    Didn't realise that...Thanks!
  29. Offline


    Of course - I'd recommend the Minecraft Coalition wiki. You should also take a look at the PacketWrapper project of mine, which saves you from having to remember the field indexes of everything.

    There's also a bunch of example code here.

    As for rolling back the world - are you sure you want to use ProtocolLib for that? You generally use it to modify the stream of packets such that the client(s) and server disagree on the game state, without being aware of it; like DisguiseCraft where your player character is represented as a mob from the perspective of all the other players,

    A world rollback would require you to update the game state for both the server and the clients, so I'm not sure if you really gain anything by modifying the protocol. I'd probably take a look at this method instead.
  30. Offline


    Yes, I see. Is there any specific way you would recommend making arenas? It's proving to be quite hard for me to do. I did see this:
    Multi Block Change (0x34)

    Server to Client
    Packet ID Field Name Field Type Example Notes
    0x34 Chunk X int -9 Chunk X Coordinate
    Chunk Z int 12 Chunk Z Coordinate
    Record count short The number of blocks affected
    Data size int The total size of the data, in bytes. Should always be 4*record count - please confirm.
    Data Coordinates, type, and metadata of blocks to change (see below table).
    Total Size: 15 bytes + Arrays

    Each record is four bytes.
    Bit mask Width Meaning
    00 00 00 0F 4 bits Block metadata
    00 00 FF F0 12 bits Block ID
    00 FF 00 00 8 bits Y co-ordinate
    0F 00 00 00 4 bits Z co-ordinate, relative to chunk
    F0 00 00 00 4 bits X co-ordinate, relative to chunk

Thread Status:
Not open for further replies.

Share This Page