walfie / pogoprotos.scala

Abandoned ScalaPB fork of POGOProtos

Website GitHub

pogoprotos.scala

Scala fork of AeonLucid/POGOProtos using ScalaPB.

Installation

Add to build.sbt (sbt version >=0.13.6):

resolvers += Resolver.bintrayRepo("walfie", "maven")

libraryDependencies ++= Seq(
  "com.github.walfie" %% "pogoprotos" % "1.0.0"
)

Differences

  • Packages are lowercase, e.g., pogoprotos.networking.requests.messages.GetPlayerMessage instead of POGOProtos.Networking.Requests.Messages.GetPlayerMessage
  • Response messages extend pogoprotos.networking.ResponseMessage
  • Request messages extend pogoprotos.networking.RequestMessage[ResponseT] (where ResponseT is the corresponding ResponseMessage)

Usage

Classes are generated by ScalaPB, so everything in the ScalaPB docs applies here as well.

Serializing

Below is an example of constructing a RequestEnvelope with several requests. You can call .toByteArray on any object to convert it to an Array[Byte] that can be sent over the wire.

import pogoprotos.networking.envelopes.RequestEnvelope
import pogoprotos.networking.requests.{Request, RequestType}
import pogoprotos.networking.requests.messages._

// ScalaPB-generated objects have a `.toByteArray` method, but the `requests`
// field in `RequestEnvelope` expects a protobuf `ByteString`.
import com.google.protobuf.ByteString
implicit def byteArrayToByteString(byteArray: Array[Byte]): ByteString =
  ByteString.copyFrom(byteArray)

val request = RequestEnvelope().update(
  _.statusCode := 2,
  _.requestId := 25252,
  _.latitude := 38.7330,
  _.longitude := -121.8073,
  _.altitude := 42.069,
  _.unknown12 := 123,
  _.requests :++= Seq(
    Request().update(_.requestType := RequestType.GET_HATCHED_EGGS),
    Request().update(
      _.requestType := RequestType.GET_INVENTORY,
      _.requestMessage := GetInventoryMessage().update(
        _.lastTimestampMs := 123456789
      ).toByteArray
    ),
    Request().update(_.requestType := RequestType.CHECK_AWARDED_BADGES),
    Request().update(
      _.requestType := RequestType.DOWNLOAD_SETTINGS,
      _.requestMessage := DownloadSettingsMessage().update(
        _.hash := "abcdef01234567890abcdef0123456789abcdef0"
      ).toByteArray
    )
  )
)

val requestAsBytes: Array[Byte] = request.toByteArray

As with ScalaPB, unspecified fields will be set to some default value.

Deserializing

The generated companion objects of each message have .parseFrom (returns T) and .validate (returns Try[T]) methods.

import pogoprotos.networking.envelopes.ResponseEnvelope

// Pretend this is a response we received after posting the `RequestEnvelope`
val bytes: Array[Byte] = ResponseEnvelope().update(
  _.statusCode := 53,
  _.requestId := 315,
  _.apiUrl := "http://example.com/"
).toByteArray

// `parseFrom` can throw an exception if the bytes could not be deserialized
val response = ResponseEnvelope.parseFrom(bytes)

// If we're concerned with possible failure, `validate` returns a `Try[ResponseEnvelope]`
val tryResponse = ResponseEnvelope.validate(bytes)

Assuming parsing succeeded, we can check the result's fields:

response.statusCode
// res11: Int = 53

response.requestId
// res12: Long = 315

response.apiUrl
// res13: String = http://example.com/

response.authTicket // This is unspecified in the message, so we expect `None`
// res14: Option[pogoprotos.networking.envelopes.AuthTicket] = None

On the other hand, if we try to parse an invalid message, we get:

ResponseEnvelope.validate(Array[Byte](1, 2, 3, 4, 5))
// res15: scala.util.Try[pogoprotos.networking.envelopes.ResponseEnvelope] = Failure(com.google.protobuf.InvalidProtocolBufferException: Protocol message contained an invalid tag (zero).)