mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-07-12 06:55:10 +00:00
Merge pull request #770 from jgillich/unknown30
add Unknown30 decoder, allow non-advanced packets
This commit is contained in:
commit
6fbe35b15f
5 changed files with 146 additions and 92 deletions
|
|
@ -129,7 +129,7 @@ object MiddlewareActor {
|
||||||
* Do nothing.
|
* Do nothing.
|
||||||
* Wait to be told to do something.
|
* Wait to be told to do something.
|
||||||
*/
|
*/
|
||||||
private def doNothing(): Unit = { }
|
private def doNothing(): Unit = {}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -182,7 +182,8 @@ class MiddlewareActor(
|
||||||
val outQueueBundled: mutable.Queue[PlanetSidePacket] = mutable.Queue()
|
val outQueueBundled: mutable.Queue[PlanetSidePacket] = mutable.Queue()
|
||||||
|
|
||||||
/** Latest outbound sequence number;
|
/** Latest outbound sequence number;
|
||||||
* the current sequence is one less than this number */
|
* the current sequence is one less than this number
|
||||||
|
*/
|
||||||
var outSequence = 0
|
var outSequence = 0
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -202,7 +203,8 @@ class MiddlewareActor(
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Latest outbound subslot number;
|
/** Latest outbound subslot number;
|
||||||
* the current subslot is one less than this number */
|
* the current subslot is one less than this number
|
||||||
|
*/
|
||||||
var outSubslot = 0
|
var outSubslot = 0
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -224,12 +226,13 @@ class MiddlewareActor(
|
||||||
/**
|
/**
|
||||||
* Do not bundle these packets together with other packets
|
* Do not bundle these packets together with other packets
|
||||||
*/
|
*/
|
||||||
val packetsBundledByThemselves: List[PlanetSidePacket=>Boolean] = List(
|
val packetsBundledByThemselves: List[PlanetSidePacket => Boolean] = List(
|
||||||
MiddlewareActor.keepAliveMessageGuard,
|
MiddlewareActor.keepAliveMessageGuard,
|
||||||
MiddlewareActor.characterInfoMessageGuard
|
MiddlewareActor.characterInfoMessageGuard
|
||||||
)
|
)
|
||||||
|
|
||||||
val smpHistoryLength: Int = 100
|
val smpHistoryLength: Int = 100
|
||||||
|
|
||||||
/** History of created `SlottedMetaPacket`s.
|
/** History of created `SlottedMetaPacket`s.
|
||||||
* In case the client does not register receiving a packet by checking against packet subslot index numbers,
|
* In case the client does not register receiving a packet by checking against packet subslot index numbers,
|
||||||
* it will dispatch a `RelatedA` packet,
|
* it will dispatch a `RelatedA` packet,
|
||||||
|
|
@ -239,24 +242,32 @@ class MiddlewareActor(
|
||||||
* The client and server supposedly maintain reciprocating mechanisms.
|
* The client and server supposedly maintain reciprocating mechanisms.
|
||||||
*/
|
*/
|
||||||
val preparedSlottedMetaPackets: Array[SlottedMetaPacket] = new Array[SlottedMetaPacket](smpHistoryLength)
|
val preparedSlottedMetaPackets: Array[SlottedMetaPacket] = new Array[SlottedMetaPacket](smpHistoryLength)
|
||||||
var nextSmpIndex: Int = 0
|
var nextSmpIndex: Int = 0
|
||||||
var acceptedSmpSubslot: Int = 0
|
var acceptedSmpSubslot: Int = 0
|
||||||
|
|
||||||
/** end of life stat */
|
/** end of life stat */
|
||||||
var timesInReorderQueue: Int = 0
|
var timesInReorderQueue: Int = 0
|
||||||
|
|
||||||
/** end of life stat */
|
/** end of life stat */
|
||||||
var timesSubslotMissing: Int = 0
|
var timesSubslotMissing: Int = 0
|
||||||
|
|
||||||
/** Delay between runs of the packet bundler/resolver timer (ms);
|
/** Delay between runs of the packet bundler/resolver timer (ms);
|
||||||
* 250ms per network update (client upstream), so 10 runs of this bundling code every update */
|
* 250ms per network update (client upstream), so 10 runs of this bundling code every update
|
||||||
|
*/
|
||||||
val packetProcessorDelay = Config.app.network.middleware.packetBundlingDelay
|
val packetProcessorDelay = Config.app.network.middleware.packetBundlingDelay
|
||||||
|
|
||||||
/** Timer that handles the bundling and throttling of outgoing packets and resolves disorganized inbound packets */
|
/** Timer that handles the bundling and throttling of outgoing packets and resolves disorganized inbound packets */
|
||||||
var packetProcessor: Cancellable = Default.Cancellable
|
var packetProcessor: Cancellable = Default.Cancellable
|
||||||
|
|
||||||
/** how long packets that are out of sequential order wait for the missing sequence before being expedited (ms) */
|
/** how long packets that are out of sequential order wait for the missing sequence before being expedited (ms) */
|
||||||
val inReorderTimeout = Config.app.network.middleware.inReorderTimeout
|
val inReorderTimeout = Config.app.network.middleware.inReorderTimeout
|
||||||
|
|
||||||
/** Timer that handles the bundling and throttling of outgoing packets requesting packets with known subslot numbers */
|
/** Timer that handles the bundling and throttling of outgoing packets requesting packets with known subslot numbers */
|
||||||
var subslotMissingProcessor: Cancellable = Default.Cancellable
|
var subslotMissingProcessor: Cancellable = Default.Cancellable
|
||||||
|
|
||||||
/** how long to wait between repeated requests for packets with known missing subslot numbers (ms) */
|
/** how long to wait between repeated requests for packets with known missing subslot numbers (ms) */
|
||||||
val inSubslotMissingDelay = Config.app.network.middleware.inSubslotMissingDelay
|
val inSubslotMissingDelay = Config.app.network.middleware.inSubslotMissingDelay
|
||||||
|
|
||||||
/** how many time to repeat the request for a packet with a known missing subslot number */
|
/** how many time to repeat the request for a packet with a known missing subslot number */
|
||||||
val inSubslotMissingNumberOfAttempts = Config.app.network.middleware.inSubslotMissingAttempts
|
val inSubslotMissingNumberOfAttempts = Config.app.network.middleware.inSubslotMissingAttempts
|
||||||
|
|
||||||
|
|
@ -274,6 +285,13 @@ class MiddlewareActor(
|
||||||
send(ServerStart(nonce, serverNonce), None, None)
|
send(ServerStart(nonce, serverNonce), None, None)
|
||||||
cryptoSetup()
|
cryptoSetup()
|
||||||
|
|
||||||
|
/** Unknown30 is used to reuse an existing crypto session when switching from login to world
|
||||||
|
* When not handling it, it appears that the client will fall back to using ClientStart
|
||||||
|
* TODO implement this
|
||||||
|
*/
|
||||||
|
case (Unknown30(nonce), _) =>
|
||||||
|
connectionClose()
|
||||||
|
|
||||||
// TODO ResetSequence
|
// TODO ResetSequence
|
||||||
case _ =>
|
case _ =>
|
||||||
log.warn(s"Unexpected packet type $packet in start (before crypto)")
|
log.warn(s"Unexpected packet type $packet in start (before crypto)")
|
||||||
|
|
@ -356,17 +374,17 @@ class MiddlewareActor(
|
||||||
packet match {
|
packet match {
|
||||||
case (ClientFinished(clientPubKey, _), Some(_)) =>
|
case (ClientFinished(clientPubKey, _), Some(_)) =>
|
||||||
serverMACBuffer ++= msg.drop(3)
|
serverMACBuffer ++= msg.drop(3)
|
||||||
val agreedKey = dh.agree(clientPubKey.toArray)
|
val agreedKey = dh.agree(clientPubKey.toArray)
|
||||||
val agreedMessage = ByteVector("master secret".getBytes) ++ clientChallenge ++
|
val agreedMessage = ByteVector("master secret".getBytes) ++ clientChallenge ++
|
||||||
hex"00000000" ++ serverChallenge ++ hex"00000000"
|
hex"00000000" ++ serverChallenge ++ hex"00000000"
|
||||||
val masterSecret = new Md5Mac(ByteVector.view(agreedKey)).updateFinal(agreedMessage)
|
val masterSecret = new Md5Mac(ByteVector.view(agreedKey)).updateFinal(agreedMessage)
|
||||||
val mac = new Md5Mac(masterSecret)
|
val mac = new Md5Mac(masterSecret)
|
||||||
//TODO verify client challenge?
|
//TODO verify client challenge?
|
||||||
val serverChallengeResult = mac
|
val serverChallengeResult = mac
|
||||||
.updateFinal(ByteVector("server finished".getBytes) ++ serverMACBuffer ++ hex"01", 0xc)
|
.updateFinal(ByteVector("server finished".getBytes) ++ serverMACBuffer ++ hex"01", 0xc)
|
||||||
val encExpansion = ByteVector.view("server expansion".getBytes) ++ hex"0000" ++ serverChallenge ++
|
val encExpansion = ByteVector.view("server expansion".getBytes) ++ hex"0000" ++ serverChallenge ++
|
||||||
hex"00000000" ++ clientChallenge ++ hex"00000000"
|
hex"00000000" ++ clientChallenge ++ hex"00000000"
|
||||||
val decExpansion = ByteVector.view("client expansion".getBytes) ++ hex"0000" ++ serverChallenge ++
|
val decExpansion = ByteVector.view("client expansion".getBytes) ++ hex"0000" ++ serverChallenge ++
|
||||||
hex"00000000" ++ clientChallenge ++ hex"00000000"
|
hex"00000000" ++ clientChallenge ++ hex"00000000"
|
||||||
val expandedEncKey = mac.updateFinal(encExpansion, 64)
|
val expandedEncKey = mac.updateFinal(encExpansion, 64)
|
||||||
val expandedDecKey = mac.updateFinal(decExpansion, 64)
|
val expandedDecKey = mac.updateFinal(decExpansion, 64)
|
||||||
|
|
@ -381,13 +399,12 @@ class MiddlewareActor(
|
||||||
)
|
)
|
||||||
send(ServerFinished(serverChallengeResult))
|
send(ServerFinished(serverChallengeResult))
|
||||||
//start the queue processor loop
|
//start the queue processor loop
|
||||||
packetProcessor =
|
packetProcessor = context.system.scheduler.scheduleWithFixedDelay(
|
||||||
context.system.scheduler.scheduleWithFixedDelay(
|
packetProcessorDelay,
|
||||||
packetProcessorDelay,
|
packetProcessorDelay
|
||||||
packetProcessorDelay
|
)(() => {
|
||||||
)(()=> {
|
context.self ! ProcessQueue()
|
||||||
context.self ! ProcessQueue()
|
})
|
||||||
})
|
|
||||||
active()
|
active()
|
||||||
|
|
||||||
case other =>
|
case other =>
|
||||||
|
|
@ -416,7 +433,7 @@ class MiddlewareActor(
|
||||||
activeSequenceFunc(packet, sequence)
|
activeSequenceFunc(packet, sequence)
|
||||||
case Successful((packet, None)) =>
|
case Successful((packet, None)) =>
|
||||||
in(packet)
|
in(packet)
|
||||||
case Failure(e) =>
|
case Failure(e) =>
|
||||||
log.error(s"Could not decode $connectionId's packet: $e")
|
log.error(s"Could not decode $connectionId's packet: $e")
|
||||||
}
|
}
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
|
|
@ -446,7 +463,7 @@ class MiddlewareActor(
|
||||||
val onSignal: PartialFunction[(ActorContext[Command], Signal), Behavior[Command]] = {
|
val onSignal: PartialFunction[(ActorContext[Command], Signal), Behavior[Command]] = {
|
||||||
case (_, PostStop) =>
|
case (_, PostStop) =>
|
||||||
context.stop(nextActor)
|
context.stop(nextActor)
|
||||||
if(timesInReorderQueue > 0 || timesSubslotMissing > 0) {
|
if (timesInReorderQueue > 0 || timesSubslotMissing > 0) {
|
||||||
log.trace(s"out of sequence checks: $timesInReorderQueue, subslot missing checks: $timesSubslotMissing")
|
log.trace(s"out of sequence checks: $timesInReorderQueue, subslot missing checks: $timesSubslotMissing")
|
||||||
}
|
}
|
||||||
packetProcessor.cancel()
|
packetProcessor.cancel()
|
||||||
|
|
@ -575,31 +592,29 @@ class MiddlewareActor(
|
||||||
} else if (outQueue.nonEmpty) {
|
} else if (outQueue.nonEmpty) {
|
||||||
val bundle = {
|
val bundle = {
|
||||||
var length = 0L
|
var length = 0L
|
||||||
val (_, bundle) = outQueue
|
val (_, bundle) = outQueue.dequeueWhile {
|
||||||
.dequeueWhile {
|
case (packet, payload) =>
|
||||||
case (packet, payload) =>
|
// packet length + MultiPacketEx header length
|
||||||
// packet length + MultiPacketEx header length
|
val packetLength = payload.length + (
|
||||||
val packetLength = payload.length + (
|
if (payload.length < 2048) { 8L } //256 * 8; 1L * 8
|
||||||
if (payload.length < 2048) { 8L } //256 * 8; 1L * 8
|
else if (payload.length < 524288) { 16L } //65536 * 8; 2L * 8
|
||||||
else if (payload.length < 524288) { 16L } //65536 * 8; 2L * 8
|
else { 32L } //4L * 8
|
||||||
else { 32L } //4L * 8
|
)
|
||||||
)
|
length += packetLength
|
||||||
length += packetLength
|
|
||||||
|
|
||||||
if (packetsBundledByThemselves.exists { _(packet) }) {
|
if (packetsBundledByThemselves.exists { _(packet) }) {
|
||||||
if (length == packetLength) {
|
if (length == packetLength) {
|
||||||
length += MTU
|
length += MTU
|
||||||
true //dequeue only packet
|
true //dequeue only packet
|
||||||
} else {
|
|
||||||
false //dequeue later
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// Some packets may be larger than the MTU limit, in that case we dequeue anyway and split later
|
false //dequeue later
|
||||||
// We deduct some bytes to leave room for SlottedMetaPacket (4 bytes) and MultiPacketEx (2 bytes + prefix per packet)
|
|
||||||
length == packetLength || length <= (MTU - 6) * 8
|
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
.unzip
|
// Some packets may be larger than the MTU limit, in that case we dequeue anyway and split later
|
||||||
|
// We deduct some bytes to leave room for SlottedMetaPacket (4 bytes) and MultiPacketEx (2 bytes + prefix per packet)
|
||||||
|
length == packetLength || length <= (MTU - 6) * 8
|
||||||
|
}
|
||||||
|
}.unzip
|
||||||
bundle
|
bundle
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -616,7 +631,7 @@ class MiddlewareActor(
|
||||||
case Successful(data) =>
|
case Successful(data) =>
|
||||||
outQueueBundled.enqueue(smp(slot = 0, data.bytes))
|
outQueueBundled.enqueue(smp(slot = 0, data.bytes))
|
||||||
sendFirstBundle()
|
sendFirstBundle()
|
||||||
case Failure(cause) =>
|
case Failure(cause) =>
|
||||||
log.error(s"could not bundle $bundle: ${cause.message}")
|
log.error(s"could not bundle $bundle: ${cause.message}")
|
||||||
//to avoid packets being lost, unwrap bundle and queue the packets individually
|
//to avoid packets being lost, unwrap bundle and queue the packets individually
|
||||||
bundle.foreach { packet =>
|
bundle.foreach { packet =>
|
||||||
|
|
@ -636,7 +651,7 @@ class MiddlewareActor(
|
||||||
* @see `activeNormal`
|
* @see `activeNormal`
|
||||||
* @see `activeWithReordering`
|
* @see `activeWithReordering`
|
||||||
*/
|
*/
|
||||||
private var activeSequenceFunc: (PlanetSidePacket, Int)=>Unit = activeNormal
|
private var activeSequenceFunc: (PlanetSidePacket, Int) => Unit = activeNormal
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Properly handle the newly-arrived packet based on its sequence number.
|
* Properly handle the newly-arrived packet based on its sequence number.
|
||||||
|
|
@ -651,7 +666,7 @@ class MiddlewareActor(
|
||||||
if (sequence == inSequence + 1) {
|
if (sequence == inSequence + 1) {
|
||||||
inSequence = sequence
|
inSequence = sequence
|
||||||
in(packet)
|
in(packet)
|
||||||
} else if(sequence < inSequence) { //expedite this packet
|
} else if (sequence < inSequence) { //expedite this packet
|
||||||
in(packet)
|
in(packet)
|
||||||
} else if (sequence == inSequence) {
|
} else if (sequence == inSequence) {
|
||||||
//do nothing?
|
//do nothing?
|
||||||
|
|
@ -681,7 +696,7 @@ class MiddlewareActor(
|
||||||
inSequence = sequence
|
inSequence = sequence
|
||||||
in(packet)
|
in(packet)
|
||||||
processInReorderQueue()
|
processInReorderQueue()
|
||||||
} else if(sequence < inSequence) { //expedite this packet
|
} else if (sequence < inSequence) { //expedite this packet
|
||||||
inReorderQueue.filterInPlace(_.sequence == sequence)
|
inReorderQueue.filterInPlace(_.sequence == sequence)
|
||||||
in(packet)
|
in(packet)
|
||||||
inReorderQueueFunc = inReorderQueueTest
|
inReorderQueueFunc = inReorderQueueTest
|
||||||
|
|
@ -690,7 +705,7 @@ class MiddlewareActor(
|
||||||
//do nothing?
|
//do nothing?
|
||||||
} else {
|
} else {
|
||||||
var insertAtIndex = 0
|
var insertAtIndex = 0
|
||||||
val length = inReorderQueue.length
|
val length = inReorderQueue.length
|
||||||
while (insertAtIndex < length && sequence >= inReorderQueue(insertAtIndex).sequence) {
|
while (insertAtIndex < length && sequence >= inReorderQueue(insertAtIndex).sequence) {
|
||||||
insertAtIndex += 1
|
insertAtIndex += 1
|
||||||
}
|
}
|
||||||
|
|
@ -704,7 +719,7 @@ class MiddlewareActor(
|
||||||
* @see `inReorderQueueTest`
|
* @see `inReorderQueueTest`
|
||||||
* @see `processInReorderQueueTimeoutOnly`
|
* @see `processInReorderQueueTimeoutOnly`
|
||||||
*/
|
*/
|
||||||
private var inReorderQueueFunc: ()=>Unit = doNothing
|
private var inReorderQueueFunc: () => Unit = doNothing
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Examine inbound packets that need to be reordered by sequence number and
|
* Examine inbound packets that need to be reordered by sequence number and
|
||||||
|
|
@ -715,9 +730,9 @@ class MiddlewareActor(
|
||||||
*/
|
*/
|
||||||
def processInReorderQueue(): Unit = {
|
def processInReorderQueue(): Unit = {
|
||||||
timesInReorderQueue += 1
|
timesInReorderQueue += 1
|
||||||
var currentSequence = inSequence
|
var currentSequence = inSequence
|
||||||
val currentTime = System.currentTimeMillis()
|
val currentTime = System.currentTimeMillis()
|
||||||
val takenPackets = (inReorderQueue.indexWhere { currentTime - _.time > inReorderTimeout.toMillis } match {
|
val takenPackets = (inReorderQueue.indexWhere { currentTime - _.time > inReorderTimeout.toMillis } match {
|
||||||
case -1 =>
|
case -1 =>
|
||||||
inReorderQueue
|
inReorderQueue
|
||||||
.takeWhile { entry =>
|
.takeWhile { entry =>
|
||||||
|
|
@ -731,7 +746,7 @@ class MiddlewareActor(
|
||||||
}
|
}
|
||||||
case index =>
|
case index =>
|
||||||
// Forward all packets ahead of any packet that has been in the queue for 50ms
|
// Forward all packets ahead of any packet that has been in the queue for 50ms
|
||||||
val entries = inReorderQueue.take(index + 1)
|
val entries = inReorderQueue.take(index + 1)
|
||||||
currentSequence = entries.last.sequence
|
currentSequence = entries.last.sequence
|
||||||
entries
|
entries
|
||||||
}).map(_.packet)
|
}).map(_.packet)
|
||||||
|
|
@ -757,7 +772,7 @@ class MiddlewareActor(
|
||||||
inReorderQueue.dropInPlace(takenPackets.length)
|
inReorderQueue.dropInPlace(takenPackets.length)
|
||||||
takenPackets.foreach { p =>
|
takenPackets.foreach { p =>
|
||||||
inReorderQueueFunc = inReorderQueueTest
|
inReorderQueueFunc = inReorderQueueTest
|
||||||
inSequence = p.sequence
|
inSequence = p.sequence
|
||||||
in(p.packet)
|
in(p.packet)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -783,7 +798,7 @@ class MiddlewareActor(
|
||||||
* @see `inSubslotNotMissing`
|
* @see `inSubslotNotMissing`
|
||||||
* @see `inSubslotMissingRequests`
|
* @see `inSubslotMissingRequests`
|
||||||
*/
|
*/
|
||||||
private var activeSubslotsFunc: (Int, Int, ByteVector)=>Unit = inSubslotNotMissing
|
private var activeSubslotsFunc: (Int, Int, ByteVector) => Unit = inSubslotNotMissing
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* What to do with a `SlottedMetaPacket` control packet normally.
|
* What to do with a `SlottedMetaPacket` control packet normally.
|
||||||
|
|
@ -851,7 +866,7 @@ class MiddlewareActor(
|
||||||
* resume normal operations when acting upon inbound `SlottedMetaPacket` packets.
|
* resume normal operations when acting upon inbound `SlottedMetaPacket` packets.
|
||||||
* @param slot the optional slot to report the "first" `RelatedB` in a "while"
|
* @param slot the optional slot to report the "first" `RelatedB` in a "while"
|
||||||
*/
|
*/
|
||||||
def inSubslotsMissingRequestsFinished(slot: Int = 0) : Unit = {
|
def inSubslotsMissingRequestsFinished(slot: Int = 0): Unit = {
|
||||||
if (inSubslotsMissing.isEmpty) {
|
if (inSubslotsMissing.isEmpty) {
|
||||||
subslotMissingProcessor.cancel()
|
subslotMissingProcessor.cancel()
|
||||||
activeSubslotsFunc = inSubslotNotMissing
|
activeSubslotsFunc = inSubslotNotMissing
|
||||||
|
|
@ -870,25 +885,25 @@ class MiddlewareActor(
|
||||||
*/
|
*/
|
||||||
def askForMissingSubslots(): Unit = {
|
def askForMissingSubslots(): Unit = {
|
||||||
if (subslotMissingProcessor.isCancelled) {
|
if (subslotMissingProcessor.isCancelled) {
|
||||||
subslotMissingProcessor =
|
subslotMissingProcessor = context.system.scheduler.scheduleWithFixedDelay(
|
||||||
context.system.scheduler.scheduleWithFixedDelay(
|
initialDelay = 0.milliseconds,
|
||||||
initialDelay = 0.milliseconds,
|
inSubslotMissingDelay
|
||||||
inSubslotMissingDelay
|
)(() => {
|
||||||
)(()=> {
|
inSubslotsMissing.synchronized {
|
||||||
inSubslotsMissing.synchronized {
|
timesSubslotMissing += inSubslotsMissing.size
|
||||||
timesSubslotMissing += inSubslotsMissing.size
|
inSubslotsMissing.foreach {
|
||||||
inSubslotsMissing.foreach { case (subslot, attempt) =>
|
case (subslot, attempt) =>
|
||||||
val value = attempt - 1
|
val value = attempt - 1
|
||||||
if(value > 0) {
|
if (value > 0) {
|
||||||
inSubslotsMissing(subslot) = value
|
inSubslotsMissing(subslot) = value
|
||||||
} else {
|
} else {
|
||||||
inSubslotsMissing.remove(subslot)
|
inSubslotsMissing.remove(subslot)
|
||||||
}
|
}
|
||||||
send(RelatedA(0, subslot))
|
send(RelatedA(0, subslot))
|
||||||
}
|
|
||||||
inSubslotsMissingRequestsFinished()
|
|
||||||
}
|
}
|
||||||
})
|
inSubslotsMissingRequestsFinished()
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,20 +12,41 @@ object ControlPacketOpcode extends Enumeration {
|
||||||
type Type = Value
|
type Type = Value
|
||||||
val
|
val
|
||||||
// OPCODES 0x00-0f
|
// OPCODES 0x00-0f
|
||||||
HandleGamePacket, // a whoopsi case: not actually a control packet, but a game packet
|
HandleGamePacket, // a whoopsi case: not actually a control packet, but a game packet
|
||||||
ClientStart, // first packet ever sent during client connection
|
ClientStart, // first packet ever sent during client connection
|
||||||
ServerStart, // second packet sent in response to ClientStart
|
ServerStart, // second packet sent in response to ClientStart
|
||||||
MultiPacket, // used to send multiple packets with one UDP message (subpackets limited to <= 255)
|
MultiPacket, // used to send multiple packets with one UDP message (subpackets limited to <= 255)
|
||||||
Unknown4, TeardownConnection, Unknown6, ControlSync, // sent to the server from the client
|
Unknown4, //
|
||||||
|
TeardownConnection, //
|
||||||
|
Unknown6, //
|
||||||
|
ControlSync, // sent to the server from the client
|
||||||
// 0x08
|
// 0x08
|
||||||
ControlSyncResp, // the response generated by the server
|
ControlSyncResp, // the response generated by the server
|
||||||
SlottedMetaPacket0, SlottedMetaPacket1, SlottedMetaPacket2, SlottedMetaPacket3, SlottedMetaPacket4,
|
SlottedMetaPacket0, //
|
||||||
SlottedMetaPacket5, SlottedMetaPacket6,
|
SlottedMetaPacket1, //
|
||||||
|
SlottedMetaPacket2, //
|
||||||
|
SlottedMetaPacket3, //
|
||||||
|
SlottedMetaPacket4, //
|
||||||
|
SlottedMetaPacket5, //
|
||||||
|
SlottedMetaPacket6, //
|
||||||
// OPCODES 0x10-1f
|
// OPCODES 0x10-1f
|
||||||
SlottedMetaPacket7, RelatedA0, RelatedA1, RelatedA2, RelatedA3, RelatedB0, RelatedB1, RelatedB2,
|
SlottedMetaPacket7, //
|
||||||
|
RelatedA0, //
|
||||||
|
RelatedA1, //
|
||||||
|
RelatedA2, //
|
||||||
|
RelatedA3, //
|
||||||
|
RelatedB0, //
|
||||||
|
RelatedB1, //
|
||||||
|
RelatedB2, //
|
||||||
// 0x18
|
// 0x18
|
||||||
RelatedB3, MultiPacketEx, // same as MultiPacket, but with the ability to send extended length packets
|
RelatedB3, //
|
||||||
Unknown26, Unknown27, Unknown28, ConnectionClose, Unknown30 = Value
|
MultiPacketEx, // same as MultiPacket, but with the ability to send extended length packets
|
||||||
|
Unknown26, //
|
||||||
|
Unknown27, //
|
||||||
|
Unknown28, //
|
||||||
|
ConnectionClose, //
|
||||||
|
Unknown30 // Probably a more lightweight variant of ClientStart, containing only the client nonce
|
||||||
|
= Value
|
||||||
|
|
||||||
private def noDecoder(opcode: ControlPacketOpcode.Type) =
|
private def noDecoder(opcode: ControlPacketOpcode.Type) =
|
||||||
(bits: BitVector) => Attempt.failure(Err(s"Could not find a marshaller for control packet $opcode (${bits.toHex})"))
|
(bits: BitVector) => Attempt.failure(Err(s"Could not find a marshaller for control packet $opcode (${bits.toHex})"))
|
||||||
|
|
@ -69,7 +90,7 @@ object ControlPacketOpcode extends Enumeration {
|
||||||
case 0x1b => noDecoder(Unknown27)
|
case 0x1b => noDecoder(Unknown27)
|
||||||
case 0x1c => noDecoder(Unknown28)
|
case 0x1c => noDecoder(Unknown28)
|
||||||
case 0x1d => control.ConnectionClose.decode
|
case 0x1d => control.ConnectionClose.decode
|
||||||
case 0x1e => noDecoder(Unknown30)
|
case 0x1e => control.Unknown30.decode
|
||||||
case _ => noDecoder(opcode)
|
case _ => noDecoder(opcode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,7 @@ object PacketType extends Enumeration(1) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** PlanetSide packet flags (beginning of most packets) */
|
/** PlanetSide packet flags (beginning of most packets) */
|
||||||
final case class PlanetSidePacketFlags(packetType: PacketType.Value, secured: Boolean)
|
final case class PlanetSidePacketFlags(packetType: PacketType.Value, secured: Boolean, advanced: Boolean = true)
|
||||||
|
|
||||||
/** Codec for [[PlanetSidePacketFlags]] */
|
/** Codec for [[PlanetSidePacketFlags]] */
|
||||||
object PlanetSidePacketFlags extends Marshallable[PlanetSidePacketFlags] {
|
object PlanetSidePacketFlags extends Marshallable[PlanetSidePacketFlags] {
|
||||||
|
|
@ -71,7 +71,8 @@ object PlanetSidePacketFlags extends Marshallable[PlanetSidePacketFlags] {
|
||||||
("packet_type" | PacketType.codec) :: // first 4-bits
|
("packet_type" | PacketType.codec) :: // first 4-bits
|
||||||
("unused" | constant(bin"0")) ::
|
("unused" | constant(bin"0")) ::
|
||||||
("secured" | bool) ::
|
("secured" | bool) ::
|
||||||
("advanced" | constant(bin"1")) :: // we only support "advanced packets"
|
//("advanced" | constant(bin"1")) :: // we only support "advanced packets"
|
||||||
|
("advanced" | bool) ::
|
||||||
("length_specified" | constant(bin"0")) // we DO NOT support this field
|
("length_specified" | constant(bin"0")) // we DO NOT support this field
|
||||||
).as[PlanetSidePacketFlags]
|
).as[PlanetSidePacketFlags]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ object PacketCoding {
|
||||||
case Some(_sequence) =>
|
case Some(_sequence) =>
|
||||||
uint16L.encode(_sequence) match {
|
uint16L.encode(_sequence) match {
|
||||||
case Successful(_seq) => _seq
|
case Successful(_seq) => _seq
|
||||||
case f @ Failure(_) => return f
|
case f @ Failure(_) => return f
|
||||||
}
|
}
|
||||||
case None =>
|
case None =>
|
||||||
return Failure(Err(s"Missing sequence"))
|
return Failure(Err(s"Missing sequence"))
|
||||||
|
|
@ -125,7 +125,7 @@ object PacketCoding {
|
||||||
case Successful(opcode) => Successful(opcode)
|
case Successful(opcode) => Successful(opcode)
|
||||||
case f @ Failure(_) => f
|
case f @ Failure(_) => f
|
||||||
}
|
}
|
||||||
|
|
||||||
case _ =>
|
case _ =>
|
||||||
Failure(Err("packet not supported"))
|
Failure(Err("packet not supported"))
|
||||||
}
|
}
|
||||||
|
|
@ -174,7 +174,7 @@ object PacketCoding {
|
||||||
): Attempt[(PlanetSidePacket, Int)] = {
|
): Attempt[(PlanetSidePacket, Int)] = {
|
||||||
val (flags, remainder) = Codec.decode[PlanetSidePacketFlags](BitVector(msg)) match {
|
val (flags, remainder) = Codec.decode[PlanetSidePacketFlags](BitVector(msg)) match {
|
||||||
case Successful(DecodeResult(value, _remainder)) => (value, _remainder)
|
case Successful(DecodeResult(value, _remainder)) => (value, _remainder)
|
||||||
case Failure(e) => return Failure(Err(s"Failed to parse packet flags: ${e.message}"))
|
case Failure(e) => return Failure(Err(s"Failed to parse packet flags: ${e.message}"))
|
||||||
}
|
}
|
||||||
|
|
||||||
flags.packetType match {
|
flags.packetType match {
|
||||||
|
|
@ -218,8 +218,8 @@ object PacketCoding {
|
||||||
case (PacketType.ResetSequence, Some(_crypto)) =>
|
case (PacketType.ResetSequence, Some(_crypto)) =>
|
||||||
_crypto.decrypt(payload.drop(1)) match {
|
_crypto.decrypt(payload.drop(1)) match {
|
||||||
case Successful(p) if p == hex"01" => Successful((ResetSequence(), sequence))
|
case Successful(p) if p == hex"01" => Successful((ResetSequence(), sequence))
|
||||||
case Successful(p) => Failure(Err(s"ResetSequence decrypted to unsupported value - $p"))
|
case Successful(p) => Failure(Err(s"ResetSequence decrypted to unsupported value - $p"))
|
||||||
case _ => Failure(Err(s"ResetSequence did not decrypt properly"))
|
case _ => Failure(Err(s"ResetSequence did not decrypt properly"))
|
||||||
}
|
}
|
||||||
case (ptype, _) =>
|
case (ptype, _) =>
|
||||||
Failure(Err(s"Cannot unmarshal $ptype packet at all"))
|
Failure(Err(s"Cannot unmarshal $ptype packet at all"))
|
||||||
|
|
@ -238,8 +238,7 @@ object PacketCoding {
|
||||||
def decodePacket(msg: ByteVector): Attempt[PlanetSidePacket] = {
|
def decodePacket(msg: ByteVector): Attempt[PlanetSidePacket] = {
|
||||||
if (msg.length < PLANETSIDE_MIN_PACKET_SIZE)
|
if (msg.length < PLANETSIDE_MIN_PACKET_SIZE)
|
||||||
return Failure(Err(s"Packet does not meet the minimum length of $PLANETSIDE_MIN_PACKET_SIZE bytes"))
|
return Failure(Err(s"Packet does not meet the minimum length of $PLANETSIDE_MIN_PACKET_SIZE bytes"))
|
||||||
val firstByte = msg { 0 }
|
msg(0) match {
|
||||||
firstByte match {
|
|
||||||
case 0x00 =>
|
case 0x00 =>
|
||||||
// control packets don't need the first byte
|
// control packets don't need the first byte
|
||||||
ControlPacketOpcode.codec.decode(msg.drop(1).bits) match {
|
ControlPacketOpcode.codec.decode(msg.drop(1).bits) match {
|
||||||
|
|
@ -303,7 +302,7 @@ object PacketCoding {
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
case e: Throwable =>
|
case e: Throwable =>
|
||||||
val msg = if(e.getMessage == null) e.getClass.getSimpleName else e.getMessage
|
val msg = if (e.getMessage == null) e.getClass.getSimpleName else e.getMessage
|
||||||
Failure(Err(s"encrypt error: '$msg' data: ${packetWithPadding.toHex}"))
|
Failure(Err(s"encrypt error: '$msg' data: ${packetWithPadding.toHex}"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -321,14 +320,14 @@ object PacketCoding {
|
||||||
// last byte is the padding length
|
// last byte is the padding length
|
||||||
val padding = uint8L.decode(payloadDecrypted.takeRight(1).bits) match {
|
val padding = uint8L.decode(payloadDecrypted.takeRight(1).bits) match {
|
||||||
case Successful(_padding) => _padding.value
|
case Successful(_padding) => _padding.value
|
||||||
case Failure(e) => return Failure(Err(s"Failed to decode the encrypted padding length: ${e.message}"))
|
case Failure(e) => return Failure(Err(s"Failed to decode the encrypted padding length: ${e.message}"))
|
||||||
}
|
}
|
||||||
|
|
||||||
val payloadNoPadding = payloadDecrypted.dropRight(1 + padding)
|
val payloadNoPadding = payloadDecrypted.dropRight(1 + padding)
|
||||||
val payloadMac = payloadNoPadding.takeRight(Md5Mac.MACLENGTH)
|
val payloadMac = payloadNoPadding.takeRight(Md5Mac.MACLENGTH)
|
||||||
|
|
||||||
val mac = bytes(Md5Mac.MACLENGTH).decode(payloadMac.bits) match {
|
val mac = bytes(Md5Mac.MACLENGTH).decode(payloadMac.bits) match {
|
||||||
case Failure(e) => return Failure(Err("Failed to extract the encrypted MAC: " + e.message))
|
case Failure(e) => return Failure(Err("Failed to extract the encrypted MAC: " + e.message))
|
||||||
case Successful(_mac) => _mac.value
|
case Successful(_mac) => _mac.value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
18
src/main/scala/net/psforever/packet/control/Unknown30.scala
Normal file
18
src/main/scala/net/psforever/packet/control/Unknown30.scala
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
package net.psforever.packet.control
|
||||||
|
|
||||||
|
import net.psforever.packet.{ControlPacketOpcode, Marshallable, PlanetSideControlPacket}
|
||||||
|
import scodec.Codec
|
||||||
|
import scodec.bits._
|
||||||
|
import scodec.codecs._
|
||||||
|
|
||||||
|
final case class Unknown30(clientNonce: Long) extends PlanetSideControlPacket {
|
||||||
|
type Packet = Unknown30
|
||||||
|
def opcode = ControlPacketOpcode.Unknown30
|
||||||
|
def encode = Unknown30.encode(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
object Unknown30 extends Marshallable[Unknown30] {
|
||||||
|
implicit val codec: Codec[Unknown30] = (
|
||||||
|
("client_nonce" | uint32L)
|
||||||
|
).as[Unknown30]
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue