/Paklet

Annotation based library for easy packet crafting

Primary LanguageJavaMIT LicenseMIT

banner banner

Paklet is annotation based Java library for simple and fast packet crafting.

LICENSE RELEASE


Table of contents

Features

  • Simplicity – The annotation system is straightforward and easy to use
  • Flexibility – You can customize the serialization of each packet field or even the whole packet
  • Expandable – Paklet can be easily extended to automate the serialization of custom types.
  • Speed – Bytecode manipulation in the background ensures the packet serialization is fast

Usage

Creating a packet class

All classes annotated with @Packet are considered packets. All their fields, if they are not static, transient, or annotated with @Ignore are automatically serialized by default serializers if not specified otherwise.

All field values are expected to be not null by default, but this behaviour can be changed with the use of @Optional annotation.

@Packet(id = 0x00, group = "ClientBoundStatus", catalogue = ClientBoundStatusPackets.class)
public class StatusResponsePacket {

    private String jsonResponse;
    
    // getters and setters

}

Each packet needs to specify its numeric ID, catalogue class, and can specify its group in case the packet IDs overlap, this is later used for deserialization.

Packet IDs can be alternatively resolved using @PacketID annotation for packets that require dynamic IDs.

@Packet(id = DYNAMIC_PACKET, group = "ClientBoundStatus", catalogue = ClientBoundStatusPackets.class)
public class StatusResponsePacket {

    @PacketID
    public static final int ID = PacketUtil.getID(StatusResponsePacket.class);
    
    private String jsonResponse;
    
    // getters and setters

}

To specify which serializer to use for each packet field serialization, Paklet offers @SerializeWith annotation and serializer annotation aliases.

@Packet(id = 0x01, catalogue = TestPackets.class)
public class PingPacket {

    // with the usage of SerializeWith
    private @SerializeWith(VarIntSerializer.class) int value;

    // with the alias
    private @VarInt int alias;

}

Some types can be annotated with additional metadata. Default set of supported metadata by Paklet can be found in metadata package.

Both modifiers and metadata can be used for parameters, e.g. List<@VarLong Long>.

For packets that require fully custom serialization, Paklet offers the CustomPacket interface. This is meant to be used for packets that require more complicated serialization that can not be resolved with automatic serialization.

Note

Paklet also offers VarIntSerializer and VarLongSerializer compatible with the Minecraft Java Protocol.

For more examples see tests of the paklet-core module.

Custom Serializer

Paklet allows simple way of creating custom serializers. To create a custom serializer, implement Serializer interface.

@Supports({Integer.class, int.class})
public class MyCustomSerializer implements Serializer<Integer> {
    
    @Override
    public void serialize(SerializerContext context, DataVisitor visitor, Integer value) {
        // serialization
    }

    @Override
    public Integer deserialize(SerializerContext context, DataVisitor visitor) {
        // deserialization
    }

}

@Supports annotation specifies which types the serializer supports, if more complex rule for choosing the types is needed (e.g. array types), the array can stay empty and custom SerializationRule needs to be implemented.

All serializers annotated with @DefaultSerializer are automatically registered with given catalogue.

SerializerContext provided for each serialization action gives access to currently registered serializers and type of field that is currently being serialized.

Provided serializers by Paklet
Serializer Supported types Catalogue
Boolean Boolean, boolean DefaultSerializers
Byte Byte, byte DefaultSerializers
Short Short, short DefaultSerializers
Integer Integer, int DefaultSerializers
Long Long, long DefaultSerializers
Float Float, float DefaultSerializers
Double Double, double DefaultSerializers
Character Character, char DefaultSerializers
Number Number, BigDecimal, BigInteger DefaultSerializers
String String DefaultSerializers
Collection Collection, SequencedCollection, List, LinkedList, ArrayList, Set, LinkedHashSet, HashSet DefaultSerializers
Map Map, SequencedMap, HashMap, LinkedHashMap, TreeMap DefaultSerializers
UUID UUID DefaultSerializers
Instant Instant DefaultSerializers
BitSet BitSet DefaultSerializers
Enum none, has to be specified DefaultSerializers
Array none, has to be specified DefaultSerializers
Serializable none, has to be specified DefaultSerializers
VarIntSerializer Integer, int none
VarLongSerializer Long, long none

For more examples of serializer implementation, see Serializers.

Packet Crafting

To read and write packets, instance of PacketFactory is required. The default implementation is provided in paklet-core module as PacketFactoryImpl.

public static PacketFactory createFactory() {
    // Creates new serializer provider
    SerializerProvider serializerProvider = new SerializerProviderImpl();
    
    // Registers default serializers and serialization rules provided by Paklet
    serializerProvider.addSerializers(DefaultSerializers.class);
    serializerProvider.addSerializationRules(DefaultSerializationRules.class);

    // Creates new packet factory
    PacketFactory packetFactory = new PacketFactoryImpl(PacketEncoder.varInt(), serializerProvider);
    
    // Registers custom packets
    packetFactory.addPackets(MyPacketsCatalogue.class);

    return packetFactory;
}

Packets can be then read and written as such:

// implementation of data visitor backed by netty's byte buffer provided by Paklet.
DataVisitor visitor = new NettyDataVisitor(Unpooled.buffer());

MyPacket packet = new MyPacket();
packet.setContent("Hello World");

// Serialization
factory.write(packet, visitor);

// Deserialization
MyPacket packetClone = factory.create(packetGroup, visitor);

Paklet has much more tools to offer than shown in this README, developers are encouraged to explore the source code and discover additional APIs! :)

For more examples see tests of the paklet-core module.

Importing

API and Annotation Processor

repositories {
    maven {
        name = "machinemcRepositoryReleases"
        url = uri("https://repo.machinemc.org/releases")
    }
}

dependencies {
    implementation("org.machinemc:paklet-api:VERSION")
    annotationProcessor("org.machinemc:paklet-processor:VERSION")
}

Implementation

repositories {
    maven {
        name = "machinemcRepositoryReleases"
        url = uri("https://repo.machinemc.org/releases")
    }
}

dependencies {
    implementation("org.machinemc:paklet-core:VERSION")
}

Gradle Plugin

Note

Gradle plugin is a key feature of Paklet. It modifies bytecode of compiled packet classes later used by generated packet readers and writers to achieve higher speeds.

buildscript {
    repositories {
        maven {
            url = uri("https://repo.machinemc.org/releases")
        }
    }
    dependencies {
        classpath("org.machinemc:paklet-plugin:VERSION")
    }
}

apply<PakletPlugin>()

License

Paklet is free software licensed under the MIT license.