From f88323805a72756ce054da68e409270bc3cd501c Mon Sep 17 00:00:00 2001 From: Fate-JH Date: Mon, 15 Sep 2025 19:36:54 -0400 Subject: [PATCH] Login Safety (#1299) * differentiated between login test idling and post-message-received idling; added a messaging buffer that gets emptied during the transition to normal login behavior * isolated behavior --- .../net/psforever/actors/net/LoginActor.scala | 62 +++++++++++-------- 1 file changed, 35 insertions(+), 27 deletions(-) diff --git a/src/main/scala/net/psforever/actors/net/LoginActor.scala b/src/main/scala/net/psforever/actors/net/LoginActor.scala index 5eb8bb14..fbe04adb 100644 --- a/src/main/scala/net/psforever/actors/net/LoginActor.scala +++ b/src/main/scala/net/psforever/actors/net/LoginActor.scala @@ -101,6 +101,8 @@ class LoginActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], conne private val bcryptRounds = 12 + private var buffer: Seq[Any] = Seq() //for typed actors, this becomes an akka.actor.typed.scaladsl.StashBuffer (size 10?) + override def preStart(): Unit = { super.preStart() ServiceManager.serviceManager ! Lookup("accountIntermediary") @@ -122,7 +124,12 @@ class LoginActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], conne sockets = listings.head } - private def idlingBehavior: Receive = persistentSetupMixinBehavior.orElse { + private def idlingBufferBehavior: Receive = persistentSetupMixinBehavior.orElse { + case packet => + buffer = buffer :+ packet + } + + private def idlingIgnoreBehavior: Receive = persistentSetupMixinBehavior.orElse { case _ => () } @@ -132,7 +139,7 @@ class LoginActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], conne hostName = address.HostName canonicalHostName = address.CanonicalHostName port = address.Port - context.become(idlingBehavior) + context.become(idlingBufferBehavior) runLoginTest() case _ => () @@ -146,35 +153,33 @@ class LoginActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], conne failWithError(s"Invalid packet class received: $default") } - private def displayingServerListBehavior: Receive = persistentSetupMixinBehavior.orElse { - case packet: PlanetSideGamePacket => - handleGamePktDuringWorldSelect(packet) - - case LoginActor.UpdateServerList => - updateServerList() - + private def nextPortTransferBehavior: Receive = { case SocketPane.NextPort(_, _, portNum) => val address = gameTestServerAddress.getAddress.getHostAddress log.info(s"Connecting to ${address.toLowerCase}: $portNum ...") val response = ConnectToWorldMessage(serverName, address, portNum) - context.become(idlingBehavior) + context.become(idlingIgnoreBehavior) middlewareActor ! MiddlewareActor.Send(response) middlewareActor ! MiddlewareActor.Close() - - case default => - failWithError(s"Invalid packet class received: $default") } - private def waitingForServerTransferBehavior: Receive = persistentSetupMixinBehavior.orElse { - case SocketPane.NextPort(_, _, portNum) => - val address = gameTestServerAddress.getAddress.getHostAddress - log.info(s"Connecting to ${address.toLowerCase}: $portNum ...") - val response = ConnectToWorldMessage(serverName, address, portNum) - context.become(idlingBehavior) - middlewareActor ! MiddlewareActor.Send(response) - middlewareActor ! MiddlewareActor.Close() + private def displayingServerListBehavior: Receive = persistentSetupMixinBehavior + .orElse(nextPortTransferBehavior) + .orElse { + case packet: PlanetSideGamePacket => + handleGamePktDuringWorldSelect(packet) - case _ => () + case LoginActor.UpdateServerList => + updateServerList() + + case default => + failWithError(s"Invalid packet class received: $default") + } + + private def waitingForServerTransferBehavior: Receive = persistentSetupMixinBehavior + .orElse(nextPortTransferBehavior) + .orElse { + case _ => () } private def handleGamePktDuringLogin(pkt: PlanetSideGamePacket): Unit = { @@ -182,17 +187,17 @@ class LoginActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], conne case LoginMessage(majorVersion, minorVersion, buildDate, username, _, Some(token), revision) => val clientVersion = s"Client Version: $majorVersion.$minorVersion.$revision, $buildDate" log.debug(s"New login UN:$username Token:$token. $clientVersion") - context.become(idlingBehavior) + context.become(idlingIgnoreBehavior) accountLoginWithToken(token) case LoginMessage(majorVersion, minorVersion, buildDate, username, password, None, revision) => val clientVersion = s"Client Version: $majorVersion.$minorVersion.$revision, $buildDate" log.debug(s"New login UN:$username. $clientVersion") - context.become(idlingBehavior) + context.become(idlingIgnoreBehavior) accountLogin(username, password.getOrElse("")) case _ => - log.warning(s"Unhandled GamePacket $pkt") + log.warning(s"Unhandled GamePacket during login $pkt") } } @@ -205,7 +210,7 @@ class LoginActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], conne sockets ! SocketPane.GetNextPort("world", context.self) case _ => - log.warning(s"Unhandled GamePacket $pkt") + log.warning(s"Unhandled GamePacket during world select $pkt") } } @@ -224,8 +229,11 @@ class LoginActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], conne result.onComplete { case Success(Some(_)) => context.become(accountLoginBehavior) // account found + buffer.foreach { self ! _ } + buffer = Seq() case Success(None) => - middlewareActor ! MiddlewareActor.Send(DisconnectMessage("Character database not found; stopping ...")) + log.error("account database not found") + middlewareActor ! MiddlewareActor.Send(DisconnectMessage("Account database not found; stopping ...")) middlewareActor ! MiddlewareActor.Close() case Failure(e) => log.error(e.getMessage)