-
Notifications
You must be signed in to change notification settings - Fork 1
Packets Generator
The packets generator is activated with the --packets option. It generates Scala classes that contain, parse and write packet data. The generated source files are located in the packets folder.
Example: packets/login/serverbound/EncryptionResponsePacket.scala for Minecraft 1.12.2
import com.electronwill.niol.{NiolInput, NiolOutput}
/** Packet class auto-generated by DataTractor */
final class EncryptionResponsePacket(var sharedSecretLength: Int, var sharedSecret: Array[Byte], var verifyTokenLength: Int, var verifyToken: Array[Byte]) extends Packet {
override def write(out: NiolOutput): Unit {
out.putVarint(sharedSecretLength)
out.putBytes(sharedSecret)
out.putVarint(verifyTokenLength)
out.putBytes(verifyToken)
}
override def id = EncryptionResponsePacket.id
}
object EncryptionResponsePacket extends PacketObj {
override val id = 1
override def read(in: NiolOutput): EncryptionResponsePacket {
val sharedSecretLength = in.getVarint()
// TODO read sharedSecret
val verifyTokenLength = in.getVarint()
// TODO read verifyToken
new EncryptionResponsePacket(sharedSecretLength, sharedSecret, verifyTokenLength, verifyToken)
}
}
/** Packet builder auto-generated by DataTractor */
final class EncryptionResponsePacketBuilder[P <: EncryptionResponsePacket] extends PacketBuilder[EncryptionResponsePacket] {
private[this] var sharedSecretLength: Int = _
private[this] var sharedSecret: Array[Byte] = _
private[this] var verifyTokenLength: Int = _
private[this] var verifyToken: Array[Byte] = _
def withSharedSecretLength(sharedSecretLength: Int): EncryptionResponsePacketBuilder[P with SharedSecretLength] {
this.sharedSecretLength = sharedSecretLength
this.asInstanceOf[EncryptionResponsePacketBuilder[P with SharedSecretLength]]
}
def withSharedSecret(sharedSecret: Array[Byte]): EncryptionResponsePacketBuilder[P with SharedSecret] {
this.sharedSecret = sharedSecret
this.asInstanceOf[EncryptionResponsePacketBuilder[P with SharedSecret]]
}
def withVerifyTokenLength(verifyTokenLength: Int): EncryptionResponsePacketBuilder[P with VerifyTokenLength] {
this.verifyTokenLength = verifyTokenLength
this.asInstanceOf[EncryptionResponsePacketBuilder[P with VerifyTokenLength]]
}
def withVerifyToken(verifyToken: Array[Byte]): EncryptionResponsePacketBuilder[P with VerifyToken] {
this.verifyToken = verifyToken
this.asInstanceOf[EncryptionResponsePacketBuilder[P with VerifyToken]]
}
override def build()(implicit evidence: P =:= Complete) {
new EncryptionResponsePacket(sharedSecretLength, sharedSecret, verifyTokenLength, verifyToken)
}
}
object EncryptionResponsePacketBuilder {
sealed trait SharedSecretLength
sealed trait SharedSecret
sealed trait VerifyTokenLength
sealed trait VerifyToken
type Complete = EncryptionResponsePacketBuilder with SharedSecretLength with SharedSecret with VerifyTokenLength with VerifyToken
}Most of the code works as it is, but as you can see, there are some TODOs in the generated file. They mean that the packet generator wasn't sure of how to read or write a value. In that case, it is up to the developer to read the protocol documentation and implement the missing parts.
This often occurs when some fields have a variable length, like arrays and strings.
Note: DataTractor is always improving and could handle these variable-length fields in the future.
On top of the packet class, a packet builder is generated.
This builder is type-safe, which means that your code won't compile if you forget a parameter. You can still configure the parameters in the order you want, the Scala type system just ensures that all the parameters have been set before you call the build() method.