Skip to content
This repository was archived by the owner on May 15, 2024. It is now read-only.

Packets Generator

Guillaume R edited this page Apr 21, 2018 · 6 revisions

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
}

TODOs

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.

Builder

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.

Clone this wiki locally