[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

    572

    How can I trigger an event when the server receive
    a "BOOK_AND_QUILL" content change?
     
  2. Offline

    Comphenix

    Intercept the internal plugin channel MC|BEdit (MC|BSign for when a book is signed):
    Code:java
    1. public class ExampleMod extends JavaPlugin implements Listener {
    2. @Override
    3. public void onEnable() {
    4. ProtocolLibrary.getProtocolManager().addPacketListener(
    5. new PacketAdapter(this, PacketType.Play.Client.CUSTOM_PAYLOAD) {
    6. @Override
    7. public void onPacketReceiving(PacketEvent event) {
    8. // PLEASE KEEP IN MIND THAT THIS IS EXECUTED ASYNCHRONOUSLY
    9. PacketContainer packet = event.getPacket();
    10. String packetName = packet.getStrings().read(0);
    11.  
    12. // Make sure this is a book edit
    13. if (packetName.equals("MC|BEdit")) {
    14. handleBookEdit(event, packet);
    15. }
    16. }
    17. });
    18. }
    19.  
    20. private void handleBookEdit(PacketEvent event, PacketContainer packet) {
    21. try {
    22. byte[] data = packet.getByteArrays().read(0);
    23. ItemStack stack = StreamSerializer.getDefault().deserializeItemStack(input);
    24.  
    25. System.out.println(String.format("Player %s is signing %s.", event.getPlayer(), stack));
    26. } catch (IOException e) {
    27. throw new RuntimeException("Cannot deserialize item stack.", e);
    28. }
    29. }
    30. }


    Or, just use PlayerEditBookEvent.
     
  3. Offline

    572

    Thanks for your solution! ^^^^^^^^^^^^^ Comphenix

    But, I think
    Code:
    new PacketAdapter(this, PacketType.Play.Client.CUSTOM_PAYLOAD)
    Should be replaced by
    Code:
    new PacketAdapter(this,ConnectionSide.CLIENT_SIDE,ListenerPriority.NORMAL,Packets.Client.CUSTOM_PAYLOAD)
    instead in order to make it works! (Because I am in 1.6.4 not 1.7)
     
  4. Offline

    desht

    572 as Comphenix pointed out, you don't even need ProtocolLib for this - there's a Bukkit event for detecting (and modifying/stopping) book editing & signing.
     
  5. Offline

    Comphenix

    That's not necessary, though - PacketType.Play.Client will automatically use the old packet ID in 1.6.4, as long as you're using ProtocolLib 3.0.0 or newer in Minecraft 1.6.4.

    Ah, desht is actually the author of PlayerEditBookEvent, which was added in Bukkit 1.5.2. Well, you should definitely use a Bukkit event in favor of ProtocolLib, if you have a choice. It's easier, faster and much more likely to be forwards compatible.
     
    desht likes this.
  6. Offline

    Cybermaxke

    Nvm fixed it. ;)
     
  7. Offline

    Comphenix

    Improvements to TinyProtocol

    TinyProtocol is my (relatively) small alternative to ProtocolLib. It is a simple drop-in class (with a helper class) that can be included and used in any 1.7.2+ CraftBukkit plugin, without conflicting with ProtocolLib. :)

    Much has changed since I last wrote about it, though, it is now fully compatible with any 1.7.2+ servers (MCPC+ not supported). In addition, it can also intercept packets that have been sent and received before the player has logged in:
    Code:java
    1. public class ExamplePlugin extends JavaPlugin {
    2. // Server info packet
    3. private Class<?> serverInfoClass = Reflection.getClass("{nms}.PacketStatusOutServerInfo");
    4. private Class<Object> serverPingClass = Reflection.getUntypedClass("{nms}.ServerPing");
    5. private Class<Object> playerSampleClass = Reflection.getUntypedClass("{nms}.ServerPingPlayerSample");
    6. private FieldAccessor<Object> serverPing = Reflection.getField(serverInfoClass, serverPingClass, 0);
    7. private FieldAccessor<Object> playerSample = Reflection.getField(serverPingClass, playerSampleClass, 0);
    8. private ConstructorInvoker playerSampleInvoker = Reflection.getConstructor(playerSampleClass, int.class, int.class);
    9.  
    10. private TinyProtocol protocol;
    11.  
    12. @Override
    13. public void onEnable() {
    14. protocol = new TinyProtocol(this) {
    15. @Override
    16. public Object onPacketOutAsync(Player reciever, Channel channel, Object packet) {
    17. if (serverInfoClass.isInstance(packet)) {
    18. Object ping = serverPing.get(packet);
    19. playerSample.set(ping, playerSampleInvoker.invoke(1000, 0));
    20.  
    21. // Which is equivalent to:
    22. //serverPing.get(packet).setPlayerSample(new ServerPingPlayerSample(1000, 0));
    23. return packet;
    24. }
    25. return super.onPacketOutAsync(reciever, channel, packet);
    26. }
    27. };
    28. }
    29. }

    Which changes the apparent player count on a server. Note that I had to change the method signature of onPacketOutAsync() and onPacketInAsync() to include a Channel object, as the Player object may not even be constructed when a packet is intercepted. The channel object is guaranteed to be the same throughout a single player session, and can be a target for a packet transmission:
    Code:java
    1. protocol.sendPacket(channel, testPacket);

    You can also simulate receiving a packet.
     
    bigteddy98 likes this.
  8. Offline

    Cybermaxke

    Is there a easy way to intercept itemstack encoding/decoding? Maybe a custom stream serializer?
     
  9. Offline

    Comphenix

    In ProtocolLib? Sure - you can use StreamSerializer and decode the raw ItemStack before Bukkit intercepted it. Then, replace the Bukkit-decoded version with yours in a packet listener.

    This is basically what I do in ItemRenamer to read custom NBT tags stored by the client. I presume you're doing something similar, so you can probably reuse most of that code.
     
  10. Offline

    rtc5200

    I would like to get any messages client recieves ,so I used the same code(http://forums.bukkit.org/threads/li...d-recieved-packets.101035/page-7#post-1718617) but it doesn't work.

    In the code,
    Code:java
    1. String text = event.getPacket().getStrings().read(0);//occuered Null PointerException every message


    error log:
    Code:
    [21:38:31] [Server thread/ERROR]: [MessageReplacer] Unhandled exception occured in onPacketSending(PacketEvent) for MyPlugin
    java.lang.NullPointerException
        at jp.rtc5200.MyPlugin.Main$1.onPacketSending(Main.java:56) ~[MyPlugin-0.0.1-SNAPSHOT.jar:?]
        at com.comphenix.protocol.injector.SortedPacketListenerList.invokeSendingListener(SortedPacketListenerList.java:195) [ProtocolLib-3.2.0.jar:?]
        at com.comphenix.protocol.injector.SortedPacketListenerList.invokePacketSending(SortedPacketListenerList.java:149) [ProtocolLib-3.2.0.jar:?]
        at com.comphenix.protocol.injector.PacketFilterManager.handlePacket(PacketFilterManager.java:597) [ProtocolLib-3.2.0.jar:?]
        at com.comphenix.protocol.injector.PacketFilterManager.invokePacketSending(PacketFilterManager.java:573) [ProtocolLib-3.2.0.jar:?]
        at com.comphenix.protocol.injector.netty.NettyProtocolInjector.packetQueued(NettyProtocolInjector.java:286) [ProtocolLib-3.2.0.jar:?]
        at com.comphenix.protocol.injector.netty.NettyProtocolInjector.onPacketSending(NettyProtocolInjector.java:237) [ProtocolLib-3.2.0.jar:?]
        at com.comphenix.protocol.injector.netty.ChannelInjector.processSending(ChannelInjector.java:199) [ProtocolLib-3.2.0.jar:?]
        at com.comphenix.protocol.injector.netty.ChannelInjector.access$000(ChannelInjector.java:52) [ProtocolLib-3.2.0.jar:?]
        at com.comphenix.protocol.injector.netty.ChannelInjector$2.onMessageScheduled(ChannelInjector.java:178) [ProtocolLib-3.2.0.jar:?]
        at com.comphenix.protocol.injector.netty.ChannelProxy$2.schedulingRunnable(ChannelProxy.java:91) [ProtocolLib-3.2.0.jar:?]
        at com.comphenix.protocol.injector.netty.EventLoopProxy.execute(EventLoopProxy.java:76) [ProtocolLib-3.2.0.jar:?]
        at net.minecraft.server.v1_7_R1.NetworkManager.b(NetworkManager.java:110) [craftbukkit-1.7.2-R0.3.jar:git-Bukkit-1.7.2-R0.3-b3020jnks]
        at net.minecraft.server.v1_7_R1.NetworkManager.handle(NetworkManager.java:88) [craftbukkit-1.7.2-R0.3.jar:git-Bukkit-1.7.2-R0.3-b3020jnks]
        at net.minecraft.server.v1_7_R1.PlayerConnection.sendPacket(PlayerConnection.java:737) [craftbukkit-1.7.2-R0.3.jar:git-Bukkit-1.7.2-R0.3-b3020jnks]
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.7.0_51]
        at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) ~[?:1.7.0_51]
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) ~[?:1.7.0_51]
        at java.lang.reflect.Method.invoke(Unknown Source) ~[?:1.7.0_51]
        at com.comphenix.protocol.injector.netty.ChannelInjector.invokeSendPacket(ChannelInjector.java:403) [ProtocolLib-3.2.0.jar:?]
        at com.comphenix.protocol.injector.netty.ChannelInjector.access$200(ChannelInjector.java:52) [ProtocolLib-3.2.0.jar:?]
        at com.comphenix.protocol.injector.netty.ChannelInjector$3.run(ChannelInjector.java:280) [ProtocolLib-3.2.0.jar:?]
        at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source) [?:1.7.0_51]
        at com.comphenix.executors.CallableTask.compute(CallableTask.java:93) [ProtocolLib-3.2.0.jar:?]
        at com.comphenix.executors.CallableTask.run(CallableTask.java:102) [ProtocolLib-3.2.0.jar:?]
        at org.bukkit.craftbukkit.v1_7_R1.scheduler.CraftTask.run(CraftTask.java:53) [craftbukkit-1.7.2-R0.3.jar:git-Bukkit-1.7.2-R0.3-b3020jnks]
        at org.bukkit.craftbukkit.v1_7_R1.scheduler.CraftScheduler.mainThreadHeartbeat(CraftScheduler.java:345) [craftbukkit-1.7.2-R0.3.jar:git-Bukkit-1.7.2-R0.3-b3020jnks]
        at net.minecraft.server.v1_7_R1.MinecraftServer.u(MinecraftServer.java:587) [craftbukkit-1.7.2-R0.3.jar:git-Bukkit-1.7.2-R0.3-b3020jnks]
        at net.minecraft.server.v1_7_R1.DedicatedServer.u(DedicatedServer.java:250) [craftbukkit-1.7.2-R0.3.jar:git-Bukkit-1.7.2-R0.3-b3020jnks]
        at net.minecraft.server.v1_7_R1.MinecraftServer.t(MinecraftServer.java:545) [craftbukkit-1.7.2-R0.3.jar:git-Bukkit-1.7.2-R0.3-b3020jnks]
        at net.minecraft.server.v1_7_R1.MinecraftServer.run(MinecraftServer.java:457) [craftbukkit-1.7.2-R0.3.jar:git-Bukkit-1.7.2-R0.3-b3020jnks]
        at net.minecraft.server.v1_7_R1.ThreadServerApplication.run(SourceFile:617) [craftbukkit-1.7.2-R0.3.jar:git-Bukkit-1.7.2-R0.3-b3020jnks]
    [21:38:31] [Server thread/ERROR]: Parameters:
      { a = (("TextComponent{text='', siblings=[TextComponent{text='<_RailgunS_> a', siblings=[], style=Style{hasParent=true, color=null, bold=null, italic=null, underlined=null, obfuscated=null, clickEvent=null, hoverEvent=null}}], style=Style{hasParent=false, color=null, bold=null, italic=null, underlined=null, obfuscated=null, clickEvent=null, hoverEvent=null}}", "TextComponent{text='<_RailgunS_> a', siblings=[], style=Style{hasParent=false, color=null, bold=false, italic=false, underlined=false, obfuscated=false, clickEvent=null, hoverEvent=null}}"), ("TextComponent{text='<_RailgunS_> a', siblings=[], style=Style{hasParent=false, color=null, bold=false, italic=false, underlined=false, obfuscated=false, clickEvent=null, hoverEvent=null}}")), b = true, timestamp = 1397565511903 }
    
     
  11. Offline

    Comphenix

    That's because the example was written for Minecraft 1.5.2, while 1.6.4 introduced a new chat system based on JSON that broke it.

    You have to do something like this to intercept server packets nowadays (1.6.4+). Though, you can still intercept client-packets as normal Java strings.
     
  12. Offline

    rtc5200

    It helped me very much.Thank you.
    I want to get whole text that includes ChatColor,however it returns a short text that is devided by color.
    How can I get whole text?
     
  13. Offline

    Comphenix

    The packet doesn't contain any ChatColor characters - they're converted to key-values in a special JSON format documented here.

    If you read through my example code, you'll notice that I iterate through all the key-values in the JSON object, searching for the exact text I want to modify. Once I've found it, I replace it AND make it read by adding a "color" tag to the parent JSON object. So - if you want to change the color (or make text italicized/bold), that's how you have to go about it.

    ProtocolLib 3.3.1
    Build: #241

    This is a major release with a number of smaller bug fixes and improvements, along with a compatibility fix for CraftBukkit 1.7.8. The latter mainly affects plugins that modify the server ping packet, and plugins that read/write NBT tags using the built-in NBT library. But even if you're not affected, I'd recommend upgrading for the improved stability and performance.

    Minecraft 1.7.8
    Features
    Fixes
    Warnings
    API

    Improvements to the API. Developers rejoice!
    Compatibility
    Small fixes
    Performance
    Concurrency

    These issues are related to concurrency, and are often very rare and difficult to trigger. But it's best to fix them regardless.

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

    572

    If I want to send some message from a Android phone to the server and
    at the same time my phone is able to view the chats from the server or specific player,
    what do I do on both the Android project and Minecraft plugin?
     
  15. Offline

    Comphenix

    It depends, what kind of features do you need? Do you need to support older server software? Is a simple and minimal server installation (or none at all) important?

    I ask, because I can see many different solutions here - each with their own strong points and drawbacks.

    Rcon
    There's actually built-in support for limited server administration in both the vanilla server (since 1.9pre4) and CraftBukkit, allowing execution of server commands and retrieving their output. This is a very general approach that should work across many different server implementation and versions, and you should be able to perform most administrative tasks and plugin configurations.

    It's commonly used (another popular Android app is using it), and most importantly - it's very simple to implement and setup on the server. An administrator merely have to enable it in server.properties, and forward the default port number if that has not already been done. And of course, since the port number is well known, it might already have been forwarded along with the standard Minecraft port (on a game server).

    In addition, as a developer, you might be able to find an open source implementation of a Rcon client, and import it directly into your Android app. Just be mindful of any licensing issues - you cannot use GPL code in your app without also open sourcing it.

    The downside is that this protocol is a bit more difficult to customize - each command is limited to 1446 bytes, along with the response (as far as I know). You can customize the protocol through custom plugin commands - for instance, to get the current global chat log, I recommend adding a "get-chat" command that simply returns a list of messages seen in the global (or specific) chat since the last call to that command.

    That way, your Android client would be able to periodically poll the server (every 5 seconds, say), and see the chat. Unfortunately, RCON doesn't really have a concept continuously sending data to the client (or at least, the server doesn't support it), so you can't get a live view of the chat. But the polling method is probably good enough.

    HTTP requests
    Another method similar to Rcon would be to use HTTP request to an embedded webserver, like JSON API. Then you can even send the requests to the same port as the server (25565).

    Of course, this requires you to add a plugin to the server, but this might be okay if RCON (with no plugins) is insufficient in the first place.

    Custom Protocol
    Yet another method would be to setup a custom server, either through a plugin or a server wrapper, and connect through some other port. This would allow you to design the protocol to your hearts desire, for instance allowing the server to notify the client. That way, it would be very possible to support a live view of the chat, or the entire game world for that matter.

    This does require more work, especially on the server side. To speed things up, I recommend using Netty and Protocol buffers. It's incredibly performant, especially on the server side, and not too difficult to implement.

    Connect as a client
    You could even connect to the server as a fully-fledged Minecraft client. Of course, then the server would also send you a section of the game world (MAP_CHUNK and MAP_CHUNK_BULK), which you could ignore, but would also burn up bandwidth - not ideal in an App. It's also much more complex to implement, especially since you probably have to support encryption.

    There might be more alternatives, but I'll wait and see what your requirements are first before attempting to list them all. Though - I suspect you can probably do just fine with RCON.
     
    572 and Hoolean like this.
  16. Offline

    Comphenix

    ProtocolLib 3.4.0
    This is a major release with a couple of bug fixes and new features that may be useful for disguise plugins, among other things altering properties associated with game profiles, and the ability to schedule a packet transmission after a given packet event has successfully concluded.
    In addition, there is an unconfirmed fix for a memory leak that often occurs on servers that generate and destroy worlds. I recommend upgrading if you're running such a server, just in case.

    Features
    Fixes
    API
    Compatibility
    Memory leaks

    These bugs are only triggered under certain conditions, such as creating and destroying worlds multiple times in a session.
    Concurrency

    These issues are related to concurrency, and are often very rare and difficult to trigger.
     
    glen3b, xTigerRebornx and Garris0n like this.
  17. Offline

    tekreme73

    I would like to detect on server-side when a player is writing in chat.
    I tried with the bukkit API but i realized that the chat is client-side so there is no way to do this with bukkit.

    So, if it's possible, how can I create an event (or something else) like "PlayerChatWritingEvent" with ProtocolLib API ?
     
  18. Offline

    bars96

    How to send Team packet to all players once? I recoding NametagEdit for Cauldron 1.7.10 with ProtocolLib.
     
Thread Status:
Not open for further replies.

Share This Page