diff --git a/common/src/main/scala/psforever/net/ClientChallengeXchg.scala b/common/src/main/scala/psforever/net/ClientChallengeXchg.scala new file mode 100644 index 000000000..584dd14a8 --- /dev/null +++ b/common/src/main/scala/psforever/net/ClientChallengeXchg.scala @@ -0,0 +1,36 @@ +// Copyright (c) 2016 PSForever.net to present +package psforever.net + +import scodec.bits.ByteVector + +import scodec.Codec +import scodec.codecs._ +import scodec.bits._ + +final case class ClientChallengeXchg(time : Long, challenge : ByteVector, p : ByteVector, g : ByteVector) + extends PlanetSideCryptoPacket { + def opcode = CryptoPacketOpcode.ClientChallengeXchg + def encode = ClientChallengeXchg.encode(this) +} + +object ClientChallengeXchg extends Marshallable[ClientChallengeXchg] { + implicit val codec: Codec[ClientChallengeXchg] = ( + ("unknown" | constant(1)) :: + ("unknown" | constant(1)) :: + ("client_time" | uint32L) :: + ("challenge" | bytes(12)) :: + ("end_chal?" | constant(0)) :: + ("objects?" | constant(1)) :: + ("object_type?" | constant(hex"0002".bits)) :: + ("unknown" | constant(hex"ff240000".bits)) :: + ("P_len" | constant(hex"1000".bits)) :: + ("P" | bytes(16)) :: + ("G_len" | constant(hex"1000".bits)) :: + ("G" | bytes(16)) :: + ("end?" | constant(0)) :: + ("end?" | constant(0)) :: + ("objects?" | constant(1)) :: + ("unknown" | constant(hex"03070000".bits)) :: + ("end?" | constant(0)) + ).as[ClientChallengeXchg] +} \ No newline at end of file diff --git a/common/src/main/scala/psforever/net/ClientFinished.scala b/common/src/main/scala/psforever/net/ClientFinished.scala new file mode 100644 index 000000000..d02888602 --- /dev/null +++ b/common/src/main/scala/psforever/net/ClientFinished.scala @@ -0,0 +1,24 @@ +// Copyright (c) 2016 PSForever.net to present +package psforever.net + +import scodec.bits.ByteVector +import scodec.Codec +import scodec.codecs._ +import scodec.bits._ + +final case class ClientFinished(pubKey : ByteVector, challengeResult: ByteVector) + extends PlanetSideCryptoPacket { + type Packet = ClientFinished + def opcode = CryptoPacketOpcode.ClientFinished + def encode = ClientFinished.encode(this) +} + +object ClientFinished extends Marshallable[ClientFinished] { + implicit val codec : Codec[ClientFinished] = ( + ("obj_type?" | constant(hex"10".bits)) :: + ("pub_key_len" | constant(hex"1000")) :: + ("pub_key" | bytes(16)) :: + ("unknown" | constant(hex"0114".bits)) :: + ("challenge_result" | bytes(0xc)) + ).as[ClientFinished] +} \ No newline at end of file diff --git a/common/src/main/scala/psforever/net/ClientStart.scala b/common/src/main/scala/psforever/net/ClientStart.scala new file mode 100644 index 000000000..aac801da1 --- /dev/null +++ b/common/src/main/scala/psforever/net/ClientStart.scala @@ -0,0 +1,21 @@ +// Copyright (c) 2016 PSForever.net to present +package psforever.net + +import scodec.Codec +import scodec.codecs._ +import scodec.bits._ + +final case class ClientStart(clientNonce : Long) + extends PlanetSideControlPacket { + type Packet = ClientStart + def opcode = ControlPacketOpcode.ClientStart + def encode = ClientStart.encode(this) +} + +object ClientStart extends Marshallable[ClientStart] { + implicit val codec : Codec[ClientStart] = ( + ("unknown" | constant(hex"00000002".bits)) :: + ("client_nonce" | uint32L) :: + ("unknown" | constant(hex"000001f0".bits)) + ).as[ClientStart] +} \ No newline at end of file diff --git a/common/src/main/scala/psforever/net/ConnectToWorldMessage.scala b/common/src/main/scala/psforever/net/ConnectToWorldMessage.scala new file mode 100644 index 000000000..84834fbc0 --- /dev/null +++ b/common/src/main/scala/psforever/net/ConnectToWorldMessage.scala @@ -0,0 +1,16 @@ +// Copyright (c) 2016 PSForever.net to present +package psforever.net + +import scodec.Codec +import scodec.codecs._ + +final case class ConnectToWorldMessage(world : String) + extends PlanetSideGamePacket { + type Packet = ConnectToWorldMessage + def opcode = GamePacketOpcode.ConnectToWorldMessage + def encode = ConnectToWorldMessage.encode(this) +} + +object ConnectToWorldMessage extends Marshallable[ConnectToWorldMessage] { + implicit val codec : Codec[ConnectToWorldMessage] = ascii.as[ConnectToWorldMessage] +} \ No newline at end of file diff --git a/common/src/main/scala/psforever/net/ConnectionClose.scala b/common/src/main/scala/psforever/net/ConnectionClose.scala new file mode 100644 index 000000000..9ad4321ea --- /dev/null +++ b/common/src/main/scala/psforever/net/ConnectionClose.scala @@ -0,0 +1,15 @@ +// Copyright (c) 2016 PSForever.net to present +package psforever.net + +import scodec.Codec + +final case class ConnectionClose() + extends PlanetSideControlPacket { + type Packet = ConnectionClose + def opcode = ControlPacketOpcode.ConnectionClose + def encode = ConnectionClose.encode(this) +} + +object ConnectionClose extends Marshallable[ConnectionClose] { + implicit val codec: Codec[ConnectionClose] = PacketHelpers.emptyCodec(ConnectionClose()) +} \ No newline at end of file diff --git a/common/src/main/scala/psforever/net/HandleGamePacket.scala b/common/src/main/scala/psforever/net/HandleGamePacket.scala new file mode 100644 index 000000000..6ba67a258 --- /dev/null +++ b/common/src/main/scala/psforever/net/HandleGamePacket.scala @@ -0,0 +1,16 @@ +// Copyright (c) 2016 PSForever.net to present +package psforever.net + +import scodec.bits.ByteVector +import scodec.Codec +import scodec.codecs._ + +final case class HandleGamePacket(packet : ByteVector) + extends PlanetSideControlPacket { + def opcode = ControlPacketOpcode.HandleGamePacket + def encode = throw new Exception("This packet type should never be encoded") +} + +object HandleGamePacket extends Marshallable[HandleGamePacket] { + implicit val codec : Codec[HandleGamePacket] = bytes.as[HandleGamePacket].decodeOnly +} \ No newline at end of file diff --git a/common/src/main/scala/psforever/net/LoginMessage.scala b/common/src/main/scala/psforever/net/LoginMessage.scala new file mode 100644 index 000000000..69cfd6242 --- /dev/null +++ b/common/src/main/scala/psforever/net/LoginMessage.scala @@ -0,0 +1,72 @@ +// Copyright (c) 2016 PSForever.net to present +package psforever.net + +import scodec.Codec +import scodec.codecs._ +import scodec.bits._ +import shapeless._ + +final case class LoginMessage(majorVersion : Long, + minorVersion : Long, + buildDate : String, + username : String, + password : Option[String], + token : Option[String], + revision : Long) extends PlanetSideGamePacket { + require(majorVersion >= 0) + require(minorVersion >= 0) + require(revision >= 0) + require(password.isDefined ^ token.isDefined, "Either 'username' or 'token' must be set, but not both") + + def opcode = GamePacketOpcode.LoginMessage + def encode = LoginMessage.encode(this) +} + +object LoginMessage extends Marshallable[LoginMessage] { + private def username = PacketHelpers.encodedStringAligned(7) + private def password = PacketHelpers.encodedString + private def tokenPath = fixedSizeBytes(32, ascii) :: username + private def passwordPath = username :: password + + type Struct = String :: Option[String] :: Option[String] :: HNil + + /* Okay, okay, here's what's happening here: + + PlanetSide's *wonderful* packet design reuses packets for different encodings. + What we have here is that depending on a boolean in the LoginPacket, we will either + be decoding a username & password OR a token & username. Yeah...so this doesn't + really fit in to a fixed packet decoding scheme. + + The below code abstracts away from this by using pattern matching. + The scodec specific part is the either(...) Codec, which decodes one bit and chooses + Left or Right depending on it. + */ + implicit val credentialChoice : Codec[Struct] = { + type InStruct = Either[String :: String :: HNil, String :: String :: HNil] + + def from(a : InStruct) : Struct = a match { + case Left(username :: password :: HNil) => username :: Some(password) :: None :: HNil + case Right(token :: username :: HNil) => username :: None :: Some(token) :: HNil + } + + // serialization can fail if the user did not specify a token or password (or both) + def to(a : Struct) : InStruct = a match { + case username :: Some(password) :: None :: HNil => Left(username :: password :: HNil) + case username :: None :: Some(token) :: HNil => Right(token :: username :: HNil) + } + + either(bool, passwordPath, tokenPath).xmap[Struct](from, to) + } + + implicit val codec : Codec[LoginMessage] = ( + ("major_version" | uint32L) :: + ("minor_version" | uint32L) :: + ("build_date" | PacketHelpers.encodedString) :: + ( + // The :+ operator (and the parens) are required because we are adding an HList to an HList, + // not merely a value (like bool). Weird shit, but hey this works. + ("credential_choice" | credentialChoice) :+ + ("revision" | uint32L) + ) + ).as[LoginMessage] +} \ No newline at end of file diff --git a/common/src/main/scala/psforever/net/LoginRespMessage.scala b/common/src/main/scala/psforever/net/LoginRespMessage.scala new file mode 100644 index 000000000..2804cb42d --- /dev/null +++ b/common/src/main/scala/psforever/net/LoginRespMessage.scala @@ -0,0 +1,34 @@ +// Copyright (c) 2016 PSForever.net to present +package psforever.net + +import scodec.bits.ByteVector +import scodec.Codec +import scodec.codecs._ +import scodec.bits._ + +final case class LoginRespMessage(token : String, // printable ascii for 16 + unknown : ByteVector, // hex"00000000 18FABE0C 00000000 00000000" + error : Long, // 0 + stationError : Long, // 1 + subscriptionStatus : Long, // 2 or 5 + someToken : Long, // 685276011 + username : String, // the user + unk5 : Long, // 0 and unset bool + someBit : Boolean) extends PlanetSideGamePacket { + def opcode = GamePacketOpcode.LoginRespMessage + def encode = LoginRespMessage.encode(this) +} + +object LoginRespMessage extends Marshallable[LoginRespMessage] { + implicit val codec : Codec[LoginRespMessage] = ( + ("token" | fixedSizeBytes(16, ascii)) :: + ("unknown" | bytes(16)) :: + ("error" | uint32L) :: + ("station_error" | uint32L) :: + ("subscription_status" | uint32L) :: + ("unknown" | uint32L) :: + ("username" | PacketHelpers.encodedString) :: + ("unknown" | uint32L) :: + ("unknown" | byteAligned(bool)) + ).as[LoginRespMessage] +} \ No newline at end of file diff --git a/common/src/main/scala/psforever/net/MultiPacket.scala b/common/src/main/scala/psforever/net/MultiPacket.scala new file mode 100644 index 000000000..bca35f74e --- /dev/null +++ b/common/src/main/scala/psforever/net/MultiPacket.scala @@ -0,0 +1,18 @@ +// Copyright (c) 2016 PSForever.net to present +package psforever.net + +import scodec.bits.ByteVector +import scodec.Codec +import scodec.codecs._ +import scodec.bits._ + +final case class MultiPacket(packets : Vector[ByteVector]) + extends PlanetSideControlPacket { + type Packet = MultiPacket + def opcode = ControlPacketOpcode.MultiPacket + def encode = MultiPacket.encode(this) +} + +object MultiPacket extends Marshallable[MultiPacket] { + implicit val codec : Codec[MultiPacket] = ("packets" | vector(variableSizeBytes(uint8L, bytes))).as[MultiPacket] +} \ No newline at end of file diff --git a/common/src/main/scala/psforever/net/PSPacket.scala b/common/src/main/scala/psforever/net/PSPacket.scala index f6e6d6209..c540817d4 100644 --- a/common/src/main/scala/psforever/net/PSPacket.scala +++ b/common/src/main/scala/psforever/net/PSPacket.scala @@ -8,7 +8,6 @@ import scodec.bits._ import scodec.codecs._ import scodec._ import shapeless._ -import shapeless.ops.hlist.Prepend // Base packets sealed trait PlanetSidePacket extends Serializable { @@ -36,283 +35,6 @@ trait PlanetSideCryptoPacket extends PlanetSidePacket { def opcode : CryptoPacketOpcode.Type } -// Crypto Packets -final case class ClientChallengeXchg(time : Long, challenge : ByteVector, p : ByteVector, g : ByteVector) - extends PlanetSideCryptoPacket { - def opcode = CryptoPacketOpcode.ClientChallengeXchg - def encode = ClientChallengeXchg.encode(this) -} - -object ClientChallengeXchg extends Marshallable[ClientChallengeXchg] { - implicit val codec: Codec[ClientChallengeXchg] = ( - ("unknown" | constant(1)) :: - ("unknown" | constant(1)) :: - ("client_time" | uint32L) :: - ("challenge" | bytes(12)) :: - ("end_chal?" | constant(0)) :: - ("objects?" | constant(1)) :: - ("object_type?" | constant(hex"0002".bits)) :: - ("unknown" | constant(hex"ff240000".bits)) :: - ("P_len" | constant(hex"1000".bits)) :: - ("P" | bytes(16)) :: - ("G_len" | constant(hex"1000".bits)) :: - ("G" | bytes(16)) :: - ("end?" | constant(0)) :: - ("end?" | constant(0)) :: - ("objects?" | constant(1)) :: - ("unknown" | constant(hex"03070000".bits)) :: - ("end?" | constant(0)) - ).as[ClientChallengeXchg] -} - -final case class ServerChallengeXchg(time : Long, challenge : ByteVector, pubKey : ByteVector) - extends PlanetSideCryptoPacket { - type Packet = ServerChallengeXchg - def opcode = CryptoPacketOpcode.ServerChallengeXchg - def encode = ServerChallengeXchg.encode(this) -} - -object ServerChallengeXchg extends Marshallable[ServerChallengeXchg] { - def getCompleteChallenge(time : Long, rest : ByteVector): ByteVector = - uint32L.encode(time).require.toByteVector ++ rest - - implicit val codec: Codec[ServerChallengeXchg] = ( - ("unknown" | constant(2)) :: - ("unknown" | constant(1)) :: - ("server_time" | uint32L) :: - ("challenge" | bytes(0xC)) :: - ("end?" | constant(0)) :: - ("objects" | constant(1)) :: - ("unknown" | constant(hex"03070000000c00".bits)) :: - ("pub_key_len" | constant(hex"1000")) :: - ("pub_key" | bytes(16)) :: - ("unknown" | constant(0x0e)) - ).as[ServerChallengeXchg] -} - -final case class ClientFinished(pubKey : ByteVector, challengeResult: ByteVector) - extends PlanetSideCryptoPacket { - type Packet = ClientFinished - def opcode = CryptoPacketOpcode.ClientFinished - def encode = ClientFinished.encode(this) -} - -object ClientFinished extends Marshallable[ClientFinished] { - implicit val codec : Codec[ClientFinished] = ( - ("obj_type?" | constant(hex"10".bits)) :: - ("pub_key_len" | constant(hex"1000")) :: - ("pub_key" | bytes(16)) :: - ("unknown" | constant(hex"0114".bits)) :: - ("challenge_result" | bytes(0xc)) - ).as[ClientFinished] -} - -final case class ServerFinished(challengeResult : ByteVector) - extends PlanetSideCryptoPacket { - type Packet = ServerFinished - def opcode = CryptoPacketOpcode.ServerFinished - def encode = ServerFinished.encode(this) -} - -object ServerFinished extends Marshallable[ServerFinished] { - implicit val codec : Codec[ServerFinished] = ( - ("unknown" | constant(hex"0114".bits)) :: - ("challenge_result" | bytes(0xc)) - ).as[ServerFinished] -} - -// Game Packets -final case class LoginMessage(majorVersion : Long, - minorVersion : Long, - buildDate : String, - username : String, - password : Option[String], - token : Option[String], - revision : Long) extends PlanetSideGamePacket { - require(majorVersion >= 0) - require(minorVersion >= 0) - require(revision >= 0) - require(password.isDefined ^ token.isDefined, "Either 'username' or 'token' must be set, but not both") - - def opcode = GamePacketOpcode.LoginMessage - def encode = LoginMessage.encode(this) -} - -object LoginMessage extends Marshallable[LoginMessage] { - private def username = PacketHelpers.encodedStringAligned(7) - private def password = PacketHelpers.encodedString - private def tokenPath = fixedSizeBytes(32, ascii) :: username - private def passwordPath = username :: password - - type Struct = String :: Option[String] :: Option[String] :: HNil - - /* Okay, okay, here's what's happening here: - - PlanetSide's *wonderful* packet design reuses packets for different encodings. - What we have here is that depending on a boolean in the LoginPacket, we will either - be decoding a username & password OR a token & username. Yeah...so this doesn't - really fit in to a fixed packet decoding scheme. - - The below code abstracts away from this by using pattern matching. - The scodec specific part is the either(...) Codec, which decodes one bit and chooses - Left or Right depending on it. - */ - implicit val credentialChoice : Codec[Struct] = { - type InStruct = Either[String :: String :: HNil, String :: String :: HNil] - - def from(a : InStruct) : Struct = a match { - case Left(username :: password :: HNil) => username :: Some(password) :: None :: HNil - case Right(token :: username :: HNil) => username :: None :: Some(token) :: HNil - } - - // serialization can fail if the user did not specify a token or password (or both) - def to(a : Struct) : InStruct = a match { - case username :: Some(password) :: None :: HNil => Left(username :: password :: HNil) - case username :: None :: Some(token) :: HNil => Right(token :: username :: HNil) - } - - either(bool, passwordPath, tokenPath).xmap[Struct](from, to) - } - - implicit val codec : Codec[LoginMessage] = ( - ("major_version" | uint32L) :: - ("minor_version" | uint32L) :: - ("build_date" | PacketHelpers.encodedString) :: - ( - // The :+ operator (and the parens) are required because we are adding an HList to an HList, - // not merely a value (like bool). Weird shit, but hey this works. - ("credential_choice" | credentialChoice) :+ - ("revision" | uint32L) - ) - ).as[LoginMessage] -} - -final case class LoginRespMessage(token : String, // printable ascii for 16 - unknown : ByteVector, // hex"00000000 18FABE0C 00000000 00000000" - error : Long, // 0 - stationError : Long, // 1 - subscriptionStatus : Long, // 2 or 5 - someToken : Long, // 685276011 - username : String, // the user - unk5 : Long, // 0 and unset bool - someBit : Boolean) extends PlanetSideGamePacket { - def opcode = GamePacketOpcode.LoginRespMessage - def encode = LoginRespMessage.encode(this) -} - -object LoginRespMessage extends Marshallable[LoginRespMessage] { - implicit val codec : Codec[LoginRespMessage] = ( - ("token" | fixedSizeBytes(16, ascii)) :: - ("unknown" | bytes(16)) :: - ("error" | uint32L) :: - ("station_error" | uint32L) :: - ("subscription_status" | uint32L) :: - ("unknown" | uint32L) :: - ("username" | PacketHelpers.encodedString) :: - ("unknown" | uint32L) :: - ("unknown" | byteAligned(bool)) - ).as[LoginRespMessage] -} - -final case class ConnectToWorldMessage(world : String) - extends PlanetSideGamePacket { - type Packet = ConnectToWorldMessage - def opcode = GamePacketOpcode.ConnectToWorldMessage - def encode = ConnectToWorldMessage.encode(this) -} - -object ConnectToWorldMessage extends Marshallable[ConnectToWorldMessage] { - implicit val codec : Codec[ConnectToWorldMessage] = ascii.as[ConnectToWorldMessage] -} - -// Control Packets -final case class HandleGamePacket(packet : ByteVector) - extends PlanetSideControlPacket { - def opcode = ControlPacketOpcode.HandleGamePacket - def encode = throw new Exception("This packet type should never be encoded") -} - -object HandleGamePacket extends Marshallable[HandleGamePacket] { - implicit val codec : Codec[HandleGamePacket] = bytes.as[HandleGamePacket].decodeOnly -} - -final case class ClientStart(clientNonce : Long) - extends PlanetSideControlPacket { - type Packet = ClientStart - def opcode = ControlPacketOpcode.ClientStart - def encode = ClientStart.encode(this) -} - -object ClientStart extends Marshallable[ClientStart] { - implicit val codec : Codec[ClientStart] = ( - ("unknown" | constant(hex"00000002".bits)) :: - ("client_nonce" | uint32L) :: - ("unknown" | constant(hex"000001f0".bits)) - ).as[ClientStart] -} - -final case class ServerStart(clientNonce : Long, serverNonce : Long) - extends PlanetSideControlPacket { - type Packet = ServerStart - def opcode = ControlPacketOpcode.ServerStart - def encode = ServerStart.encode(this) -} - -object ServerStart extends Marshallable[ServerStart] { - implicit val codec : Codec[ServerStart] = ( - ("client_nonce" | uint32L) :: - ("server_nonce" | uint32L) :: - ("unknown" | constant(hex"000000000001d300000002".bits)) - ).as[ServerStart] -} - -final case class MultiPacket(packets : Vector[ByteVector]) - extends PlanetSideControlPacket { - type Packet = MultiPacket - def opcode = ControlPacketOpcode.MultiPacket - def encode = MultiPacket.encode(this) -} - -object MultiPacket extends Marshallable[MultiPacket] { - implicit val codec : Codec[MultiPacket] = ("packets" | vector(variableSizeBytes(uint8L, bytes))).as[MultiPacket] -} - -final case class SlottedMetaPacket(/*slot : Int,*/ packet : ByteVector) - extends PlanetSideControlPacket { - type Packet = SlottedMetaPacket - - //assert(slot >= 0 && slot <= 7, "Slot number is out of range") - - def opcode = { - val base = ControlPacketOpcode.SlottedMetaPacket0.id - ControlPacketOpcode(base/* + slot*/) - } - - def encode = SlottedMetaPacket.encode(this) -} - -object SlottedMetaPacket extends Marshallable[SlottedMetaPacket] { - implicit val codec : Codec[SlottedMetaPacket] = ( - ("unknown" | constant(0)) :: - ("unknown" | constant(0)) :: - ("rest" | bytes) - ).as[SlottedMetaPacket] -} - -final case class ConnectionClose() - extends PlanetSideControlPacket { - type Packet = ConnectionClose - def opcode = ControlPacketOpcode.ConnectionClose - def encode = ConnectionClose.encode(this) -} - -object ConnectionClose extends Marshallable[ConnectionClose] { - implicit val codec: Codec[ConnectionClose] = PacketHelpers.emptyCodec(ConnectionClose()) -} - - -///////////////////////////////////////////////////////////////// - // Packet typing final case class PlanetSidePacketFlags(packetType : PacketType.Value, secured : Boolean) diff --git a/common/src/main/scala/psforever/net/ServerChallengeXchg.scala b/common/src/main/scala/psforever/net/ServerChallengeXchg.scala new file mode 100644 index 000000000..c77b96f81 --- /dev/null +++ b/common/src/main/scala/psforever/net/ServerChallengeXchg.scala @@ -0,0 +1,32 @@ +// Copyright (c) 2016 PSForever.net to present +package psforever.net + +import scodec.bits.ByteVector +import scodec.Codec +import scodec.codecs._ +import scodec.bits._ + +final case class ServerChallengeXchg(time : Long, challenge : ByteVector, pubKey : ByteVector) + extends PlanetSideCryptoPacket { + type Packet = ServerChallengeXchg + def opcode = CryptoPacketOpcode.ServerChallengeXchg + def encode = ServerChallengeXchg.encode(this) +} + +object ServerChallengeXchg extends Marshallable[ServerChallengeXchg] { + def getCompleteChallenge(time : Long, rest : ByteVector): ByteVector = + uint32L.encode(time).require.toByteVector ++ rest + + implicit val codec: Codec[ServerChallengeXchg] = ( + ("unknown" | constant(2)) :: + ("unknown" | constant(1)) :: + ("server_time" | uint32L) :: + ("challenge" | bytes(0xC)) :: + ("end?" | constant(0)) :: + ("objects" | constant(1)) :: + ("unknown" | constant(hex"03070000000c00".bits)) :: + ("pub_key_len" | constant(hex"1000")) :: + ("pub_key" | bytes(16)) :: + ("unknown" | constant(0x0e)) + ).as[ServerChallengeXchg] +} \ No newline at end of file diff --git a/common/src/main/scala/psforever/net/ServerFinished.scala b/common/src/main/scala/psforever/net/ServerFinished.scala new file mode 100644 index 000000000..2e4641193 --- /dev/null +++ b/common/src/main/scala/psforever/net/ServerFinished.scala @@ -0,0 +1,21 @@ +// Copyright (c) 2016 PSForever.net to present +package psforever.net + +import scodec.bits.ByteVector +import scodec.Codec +import scodec.codecs._ +import scodec.bits._ + +final case class ServerFinished(challengeResult : ByteVector) + extends PlanetSideCryptoPacket { + type Packet = ServerFinished + def opcode = CryptoPacketOpcode.ServerFinished + def encode = ServerFinished.encode(this) +} + +object ServerFinished extends Marshallable[ServerFinished] { + implicit val codec : Codec[ServerFinished] = ( + ("unknown" | constant(hex"0114".bits)) :: + ("challenge_result" | bytes(0xc)) + ).as[ServerFinished] +} \ No newline at end of file diff --git a/common/src/main/scala/psforever/net/ServerStart.scala b/common/src/main/scala/psforever/net/ServerStart.scala new file mode 100644 index 000000000..1b6b0bdfc --- /dev/null +++ b/common/src/main/scala/psforever/net/ServerStart.scala @@ -0,0 +1,21 @@ +// Copyright (c) 2016 PSForever.net to present +package psforever.net + +import scodec.Codec +import scodec.codecs._ +import scodec.bits._ + +final case class ServerStart(clientNonce : Long, serverNonce : Long) + extends PlanetSideControlPacket { + type Packet = ServerStart + def opcode = ControlPacketOpcode.ServerStart + def encode = ServerStart.encode(this) +} + +object ServerStart extends Marshallable[ServerStart] { + implicit val codec : Codec[ServerStart] = ( + ("client_nonce" | uint32L) :: + ("server_nonce" | uint32L) :: + ("unknown" | constant(hex"000000000001d300000002".bits)) + ).as[ServerStart] +} \ No newline at end of file diff --git a/common/src/main/scala/psforever/net/SlottedMetaPacket.scala b/common/src/main/scala/psforever/net/SlottedMetaPacket.scala new file mode 100644 index 000000000..ccb1de920 --- /dev/null +++ b/common/src/main/scala/psforever/net/SlottedMetaPacket.scala @@ -0,0 +1,28 @@ +// Copyright (c) 2016 PSForever.net to present +package psforever.net + +import scodec.bits.ByteVector +import scodec.Codec +import scodec.codecs._ + +final case class SlottedMetaPacket(/*slot : Int,*/ packet : ByteVector) + extends PlanetSideControlPacket { + type Packet = SlottedMetaPacket + + //assert(slot >= 0 && slot <= 7, "Slot number is out of range") + + def opcode = { + val base = ControlPacketOpcode.SlottedMetaPacket0.id + ControlPacketOpcode(base/* + slot*/) + } + + def encode = SlottedMetaPacket.encode(this) +} + +object SlottedMetaPacket extends Marshallable[SlottedMetaPacket] { + implicit val codec : Codec[SlottedMetaPacket] = ( + ("unknown" | constant(0)) :: + ("unknown" | constant(0)) :: + ("rest" | bytes) + ).as[SlottedMetaPacket] +} \ No newline at end of file