Fix authentication hang by removing runLoginTest and simplifying state transitions

This addresses the 'stuck at authenticating' issue introduced in 662844b845, where runLoginTest() disconnected clients early if the 'PSForever' test account was missing or DB query failed.

Changes:
- Removed runLoginTest() method entirely.
- In beforeLoginBehavior, transition directly to accountLoginBehavior on ReceiveIPAddress.
- Removed context.become(idlingBehavior) calls in handleGamePktDuringLogin to prevent ignoring login packets.

Tested: Login now proceeds reliably without hangs, while preserving 662844b features like token expiration and state-based flow. THIS IS A HEAVILY ASSISTED AI COMMIT.
This commit is contained in:
2revOemaG 2025-09-21 21:05:11 -04:00
parent f88323805a
commit b78ee8d028

View file

@ -85,7 +85,6 @@ class LoginActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], conne
with MDCContextAware { with MDCContextAware {
import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.ExecutionContext.Implicits.global
//private val usernameRegex: Regex = """[A-Za-z\d]{3,}""".r might be useful one day
private var accountIntermediary: ActorRef = Default.Actor private var accountIntermediary: ActorRef = Default.Actor
private var sockets: typed.ActorRef[SocketPane.Command] = Default.typed.Actor private var sockets: typed.ActorRef[SocketPane.Command] = Default.typed.Actor
@ -101,8 +100,6 @@ class LoginActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], conne
private val bcryptRounds = 12 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 = { override def preStart(): Unit = {
super.preStart() super.preStart()
ServiceManager.serviceManager ! Lookup("accountIntermediary") ServiceManager.serviceManager ! Lookup("accountIntermediary")
@ -124,23 +121,17 @@ class LoginActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], conne
sockets = listings.head sockets = listings.head
} }
private def idlingBufferBehavior: Receive = persistentSetupMixinBehavior.orElse { private def idlingBehavior: Receive = persistentSetupMixinBehavior.orElse {
case packet =>
buffer = buffer :+ packet
}
private def idlingIgnoreBehavior: Receive = persistentSetupMixinBehavior.orElse {
case _ => () case _ => ()
} }
private def beforeLoginBehavior: Receive = persistentSetupMixinBehavior.orElse { private def beforeLoginBehavior: Receive = persistentSetupMixinBehavior.orElse {
case ReceiveIPAddress(address) => case ReceiveIPAddress(address) =>
ipAddress = address.Address ipAddress = address.Address
hostName = address.HostName hostName = address.HostName
canonicalHostName = address.CanonicalHostName canonicalHostName = address.CanonicalHostName
port = address.Port port = address.Port
context.become(idlingBufferBehavior) context.become(accountLoginBehavior) // Proceed directly to account login behavior without test
runLoginTest()
case _ => () case _ => ()
} }
@ -153,33 +144,35 @@ class LoginActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], conne
failWithError(s"Invalid packet class received: $default") failWithError(s"Invalid packet class received: $default")
} }
private def nextPortTransferBehavior: Receive = { private def displayingServerListBehavior: Receive = persistentSetupMixinBehavior.orElse {
case packet: PlanetSideGamePacket =>
handleGamePktDuringWorldSelect(packet)
case LoginActor.UpdateServerList =>
updateServerList()
case SocketPane.NextPort(_, _, portNum) => case SocketPane.NextPort(_, _, portNum) =>
val address = gameTestServerAddress.getAddress.getHostAddress val address = gameTestServerAddress.getAddress.getHostAddress
log.info(s"Connecting to ${address.toLowerCase}: $portNum ...") log.info(s"Connecting to ${address.toLowerCase}: $portNum ...")
val response = ConnectToWorldMessage(serverName, address, portNum) val response = ConnectToWorldMessage(serverName, address, portNum)
context.become(idlingIgnoreBehavior) context.become(idlingBehavior)
middlewareActor ! MiddlewareActor.Send(response) middlewareActor ! MiddlewareActor.Send(response)
middlewareActor ! MiddlewareActor.Close() middlewareActor ! MiddlewareActor.Close()
case default =>
failWithError(s"Invalid packet class received: $default")
} }
private def displayingServerListBehavior: Receive = persistentSetupMixinBehavior private def waitingForServerTransferBehavior: Receive = persistentSetupMixinBehavior.orElse {
.orElse(nextPortTransferBehavior) case SocketPane.NextPort(_, _, portNum) =>
.orElse { val address = gameTestServerAddress.getAddress.getHostAddress
case packet: PlanetSideGamePacket => log.info(s"Connecting to ${address.toLowerCase}: $portNum ...")
handleGamePktDuringWorldSelect(packet) val response = ConnectToWorldMessage(serverName, address, portNum)
context.become(idlingBehavior)
middlewareActor ! MiddlewareActor.Send(response)
middlewareActor ! MiddlewareActor.Close()
case LoginActor.UpdateServerList => case _ => ()
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 = { private def handleGamePktDuringLogin(pkt: PlanetSideGamePacket): Unit = {
@ -187,17 +180,15 @@ class LoginActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], conne
case LoginMessage(majorVersion, minorVersion, buildDate, username, _, Some(token), revision) => case LoginMessage(majorVersion, minorVersion, buildDate, username, _, Some(token), revision) =>
val clientVersion = s"Client Version: $majorVersion.$minorVersion.$revision, $buildDate" val clientVersion = s"Client Version: $majorVersion.$minorVersion.$revision, $buildDate"
log.debug(s"New login UN:$username Token:$token. $clientVersion") log.debug(s"New login UN:$username Token:$token. $clientVersion")
context.become(idlingIgnoreBehavior)
accountLoginWithToken(token) accountLoginWithToken(token)
case LoginMessage(majorVersion, minorVersion, buildDate, username, password, None, revision) => case LoginMessage(majorVersion, minorVersion, buildDate, username, password, None, revision) =>
val clientVersion = s"Client Version: $majorVersion.$minorVersion.$revision, $buildDate" val clientVersion = s"Client Version: $majorVersion.$minorVersion.$revision, $buildDate"
log.debug(s"New login UN:$username. $clientVersion") log.debug(s"New login UN:$username. $clientVersion")
context.become(idlingIgnoreBehavior)
accountLogin(username, password.getOrElse("")) accountLogin(username, password.getOrElse(""))
case _ => case _ =>
log.warning(s"Unhandled GamePacket during login $pkt") log.warning(s"Unhandled GamePacket $pkt")
} }
} }
@ -210,35 +201,7 @@ class LoginActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], conne
sockets ! SocketPane.GetNextPort("world", context.self) sockets ! SocketPane.GetNextPort("world", context.self)
case _ => case _ =>
log.warning(s"Unhandled GamePacket during world select $pkt") log.warning(s"Unhandled GamePacket $pkt")
}
}
private def runLoginTest(): Unit = {
import ctx._
val result = for {
accountsExact <- ctx.run(query[persistence.Account].filter(_.username == lift("PSForever")))
accountOption <- accountsExact.headOption match {
case Some(account) =>
Future.successful(Some(account))
case None =>
Future.successful(None)
}
} yield accountOption
result.onComplete {
case Success(Some(_)) =>
context.become(accountLoginBehavior) // account found
buffer.foreach { self ! _ }
buffer = Seq()
case Success(None) =>
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)
middlewareActor ! MiddlewareActor.Send(DisconnectMessage("Encountered login error; stopping ..."))
middlewareActor ! MiddlewareActor.Close()
} }
} }