Shadow

Warning

This feature of LabyMod is still in development and may not be entirely stable.

Overview

By using this protocol called SHADOW you can access the client's movement input keys. This protocol provides you the ability to improve the server side anticheat!

Enabling the protocol

The client will send you this information on join in the LMC channel

1
2
3
4
5
6
7
8
9
{  
   "version": "3.4.2",
   "ccp": {},
   "shadow": {  
      "enabled": true,
      "version": 1
   },
   "addons": []
}

The server should answer on the SHADOW channel just an id that is an integer.

There are following valid id's:

1
2
3
0: Reset the packet counter to zero.
1: Enable the protocol for this client
2: Disable the protocol for this client

Now you can setup the pipeline transformer for the new protocol:

1
2
3
4
EntityPlayer entityPlayer = ( ( CraftPlayer ) player ).getHandle();
Channel channel = entityPlayer.playerConnection.networkManager.channel;
ChannelPipeline pipeline = channel.pipeline();
pipeline.addAfter( "decoder", "shadowpacketin", new PacketPlayerIn( shadow ) );

Example code of server

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import net.minecraft.server.v1_8_R3.PacketDataSerializer;
import net.minecraft.server.v1_8_R3.PacketPlayInCustomPayload;
import net.minecraft.server.v1_8_R3.PacketPlayInFlying;
import net.minecraft.server.v1_8_R3.PacketPlayInFlying.PacketPlayInLook;
import net.minecraft.server.v1_8_R3.PacketPlayInFlying.PacketPlayInPosition;
import net.minecraft.server.v1_8_R3.PacketPlayInFlying.PacketPlayInPositionLook;

public class PacketPlayerIn extends ChannelInboundHandlerAdapter {

    @Override
    public void channelRead( ChannelHandlerContext ctx, Object msg ) throws Exception {
         // Is incoming packet a custom payload?
        if ( msg instanceof PacketPlayInCustomPayload ) {
            PacketPlayInCustomPayload packet = ( PacketPlayInCustomPayload ) msg;

            // Is channel name SHADOW?
            String channel = packet.a();
            if ( channel.equals( "SHADOW" ) ) {
                PacketDataSerializer data = packet.b();

                // First byte is the shadow packet id
                byte id = data.readByte();

                switch ( id ) {
                    case 0x00: // PacketPlayInPosition
                        double x = data.readDouble();
                        double y = data.readDouble();
                        double z = data.readDouble();
                        boolean onGround = data.readBoolean();

                        PacketPlayInPosition packetPlayInPosition = new PacketPlayInPosition();
                        setValue( PacketPlayInFlying.class, packetPlayInPosition, "x", x );
                        setValue( PacketPlayInFlying.class, packetPlayInPosition, "y", y );
                        setValue( PacketPlayInFlying.class, packetPlayInPosition, "z", z );
                        setValue( PacketPlayInFlying.class, packetPlayInPosition, "f", onGround );
                        super.channelRead( ctx, packetPlayInPosition );

                        break;
                    case 0x01: // PacketPlayInLook
                        float yaw = data.readFloat();
                        float pitch = data.readFloat();
                        onGround = data.readBoolean();

                        PacketPlayInLook packetPlayInLook = new PacketPlayInLook();
                        setValue( PacketPlayInFlying.class, packetPlayInLook, "yaw", yaw );
                        setValue( PacketPlayInFlying.class, packetPlayInLook, "pitch", pitch );
                        setValue( PacketPlayInFlying.class, packetPlayInLook, "f", onGround );
                        super.channelRead( ctx, packetPlayInLook );

                        break;
                    case 0x02: // PacketPlayInPositionLook
                        x = data.readDouble();
                        y = data.readDouble();
                        z = data.readDouble();
                        yaw = data.readFloat();
                        pitch = data.readFloat();
                        onGround = data.readBoolean();

                        PacketPlayInPositionLook packetPlayInPositionLook = new PacketPlayInPositionLook();
                        setValue( PacketPlayInFlying.class, packetPlayInPositionLook, "x", x );
                        setValue( PacketPlayInFlying.class, packetPlayInPositionLook, "y", y );
                        setValue( PacketPlayInFlying.class, packetPlayInPositionLook, "z", z );
                        setValue( PacketPlayInFlying.class, packetPlayInPositionLook, "yaw", yaw );
                        setValue( PacketPlayInFlying.class, packetPlayInPositionLook, "pitch", pitch );
                        setValue( PacketPlayInFlying.class, packetPlayInPositionLook, "f", onGround );
                        super.channelRead( ctx, packetPlayInPositionLook );

                        break;
                    case 0x03: // PacketPlayInFlying
                        onGround = data.readBoolean();

                        PacketPlayInFlying packetPlayInFlying = new PacketPlayInFlying();
                        setValue( PacketPlayInFlying.class, packetPlayInFlying, "f", onGround );
                        super.channelRead( ctx, packetPlayInFlying );

                        break;
                }

                  // Timestamp of client at this packet
                long time = data.readLong();

                // Read all movement inputs
                double moveForward = data.readDouble();
                double moveStrafe = data.readDouble();
                boolean jump = data.readBoolean();
                boolean sneak = data.readBoolean();
                double x = data.readDouble();
                double y = data.readDouble();
                double z = data.readDouble();
                float yaw = data.readFloat();
                float pitch = data.readFloat();
                boolean sprinting = data.readBoolean();

                // Packet counter since join
                int packetCounter = data.readInt();

                  // Handle data
                handleInput( time, moveForward, moveStrafe, jump, sneak, x, y, z, yaw, pitch, sprinting, packetCounter );

                  // Cancel this packet
                return;
            }
        }

        // This packet can pass
        super.channelRead( ctx, msg );
    }


    private void setValue( Class clazz, Object instance, String field, Object value ) throws Exception {
        Field f = clazz.getDeclaredField( field );
        f.setAccessible( true );
        f.set( instance, value );
    }
}