mirror of
https://github.com/psforever/PSF-LoginServer.git
synced 2026-01-19 18:44:45 +00:00
Increase SessionReaper timeouts and add to config file
This should fix issues disconnecting at loading screens/zone changes as no packets are being transmitted during this window. If the WorldSessionsActor is also slightly overloaded, the session reaper can drop the session mistakenly due to no outbound traffic. Also fix-up WorldConfig.Get with better error messages along with more tests.
This commit is contained in:
parent
d2732550e8
commit
83ac66a3bf
|
|
@ -105,7 +105,17 @@ trait ConfigParser {
|
|||
protected val config_template : Seq[ConfigSection]
|
||||
|
||||
// Misuse of this function can lead to run time exceptions when the types don't match
|
||||
def Get[T : ConfigTypeRequired](key : String) : T = config_map(key).asInstanceOf[T]
|
||||
// ClassTag is needed due to type erasure on T
|
||||
// https://dzone.com/articles/scala-classtag-a-simple-use-case
|
||||
def Get[T : ConfigTypeRequired](key : String)(implicit m: ClassTag[T]) : T = {
|
||||
config_map.get(key) match {
|
||||
case Some(value : T) => value
|
||||
case None =>
|
||||
throw new NoSuchElementException(s"Config key '${key}' not found")
|
||||
case Some(value : Any) =>
|
||||
throw new ClassCastException(s"Incorrect type T = ${m.runtimeClass.getSimpleName} passed to Get[T]: needed ${value.getClass.getSimpleName}")
|
||||
}
|
||||
}
|
||||
|
||||
def Load(filename : String) : ValidationResult = {
|
||||
val ini = new org.ini4j.Ini()
|
||||
|
|
|
|||
|
|
@ -48,6 +48,35 @@ ListeningPort = 51001
|
|||
|
||||
ListeningPort = 51000
|
||||
|
||||
###################################################################################################
|
||||
# NETWORK SETTINGS
|
||||
###################################################################################################
|
||||
|
||||
[network]
|
||||
|
||||
# Session.InboundGraceTime (time)
|
||||
# Description: The maximum amount of time since the last inbound packet from a UDP session
|
||||
# before it is dropped.
|
||||
# Important: Lower values will cause legitimate clients to be dropped during loading
|
||||
# screens, but higher values will make the server be more susceptible to
|
||||
# denial of service attacks and running out of memory.
|
||||
# Range: [10 seconds, 10 minutes] - (10 second grace, 10 minute grace)
|
||||
# Default: 1 minute - (Clients sending a packet at least
|
||||
# once a minute stay alive)
|
||||
|
||||
Session.InboundGraceTime = 1 minute
|
||||
|
||||
# Session.OutboundGraceTime (time)
|
||||
# Description: The maximum amount of time since the last outbound packet for a UDP session
|
||||
# before it is dropped. Can be used as a watchdog for hung server sessions.
|
||||
# Important: Lower values will cause legitimate clients to be dropped during server
|
||||
# lag spikes or Zone transitions.
|
||||
# Range: [10 seconds, 10 minutes] - (10 second grace, 10 minute grace)
|
||||
# Default: 1 minute - (Clients receiving a packet at least
|
||||
# once a minute stay alive)
|
||||
|
||||
Session.OutboundGraceTime = 1 minute
|
||||
|
||||
###################################################################################################
|
||||
# DEVELOPER SETTINGS
|
||||
# - NETWORK SIMULATOR
|
||||
|
|
|
|||
|
|
@ -111,6 +111,9 @@ class SessionRouter(role : String, pipeline : List[SessionPipeline]) extends Act
|
|||
log.error(s"Requested to drop non-existent session ID=$id from ${sender()}")
|
||||
}
|
||||
case SessionReaper() =>
|
||||
val inboundGrace = WorldConfig.Get[Duration]("network.Session.InboundGraceTime").toMillis
|
||||
val outboundGrace = WorldConfig.Get[Duration]("network.Session.OutboundGraceTime").toMillis
|
||||
|
||||
sessionById.foreach { case (id, session) =>
|
||||
log.trace(session.toString)
|
||||
if(session.getState == Closed()) {
|
||||
|
|
@ -119,9 +122,9 @@ class SessionRouter(role : String, pipeline : List[SessionPipeline]) extends Act
|
|||
sessionById.remove(id)
|
||||
idBySocket.remove(session.socketAddress)
|
||||
log.debug(s"Reaped session ID=$id")
|
||||
} else if(session.timeSinceLastInboundEvent > 10000) {
|
||||
} else if(session.timeSinceLastInboundEvent > inboundGrace) {
|
||||
removeSessionById(id, "session timed out (inbound)", graceful = false)
|
||||
} else if(session.timeSinceLastOutboundEvent > 4000) {
|
||||
} else if(session.timeSinceLastOutboundEvent > outboundGrace) {
|
||||
removeSessionById(id, "session timed out (outbound)", graceful = true) // tell client to STFU
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,10 @@ object WorldConfig extends ConfigParser {
|
|||
ConfigSection("worldserver",
|
||||
ConfigEntryInt("ListeningPort", 51001, Constraints.min(1), Constraints.max(65535))
|
||||
),
|
||||
ConfigSection("network",
|
||||
ConfigEntryTime("Session.InboundGraceTime", 1 minute, Constraints.min(10 seconds)),
|
||||
ConfigEntryTime("Session.OutboundGraceTime", 1 minute, Constraints.min(10 seconds))
|
||||
),
|
||||
ConfigSection("developer",
|
||||
ConfigEntryBool ("NetSim.Active", false),
|
||||
ConfigEntryFloat("NetSim.Loss", 0.02f, Constraints.min(0.0f), Constraints.max(1.0f)),
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
// Copyright (c) 2019 PSForever
|
||||
import java.io._
|
||||
import scala.io.Source
|
||||
import org.specs2.mutable._
|
||||
import net.psforever.config._
|
||||
|
|
@ -11,6 +12,25 @@ class ConfigTest extends Specification {
|
|||
"have no errors" in {
|
||||
WorldConfig.Load("config/worldserver.ini.dist") mustEqual Valid
|
||||
}
|
||||
|
||||
"be formatted correctly" in {
|
||||
var lineno = 1
|
||||
for (line <- Source.fromFile("config/worldserver.ini.dist").getLines) {
|
||||
val linee :String = line
|
||||
val ctx = s"worldserver.ini.dist:${lineno}"
|
||||
val maxLen = 100
|
||||
val lineLen = line.length
|
||||
|
||||
lineLen aka s"${ctx} - line length" must beLessThan(maxLen)
|
||||
line.slice(0, 1) aka s"${ctx} - leading whitespace found" mustNotEqual " "
|
||||
line.slice(line.length-1, line.length) aka s"${ctx} - trailing whitespace found" mustNotEqual " "
|
||||
|
||||
lineno += 1
|
||||
}
|
||||
|
||||
ok
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
"TestConfig" should {
|
||||
|
|
@ -26,6 +46,19 @@ class ConfigTest extends Specification {
|
|||
TestConfig.Get[Boolean]("default.bool_false") mustEqual false
|
||||
TestConfig.Get[Int]("default.missing") mustEqual 1337
|
||||
}
|
||||
|
||||
"throw when getting non-existant keys" in {
|
||||
TestConfig.Load(testConfig) mustEqual Valid
|
||||
TestConfig.Get[Int]("missing.key") must throwA[NoSuchElementException](message = "Config key 'missing.key' not found")
|
||||
TestConfig.Get[String]("missing.key") must throwA[NoSuchElementException](message = "Config key 'missing.key' not found")
|
||||
}
|
||||
|
||||
"throw when Get is not passed the right type parameter" in {
|
||||
TestConfig.Load(testConfig) mustEqual Valid
|
||||
TestConfig.Get[Duration]("default.string") must throwA[ClassCastException](message = "Incorrect type T = Duration passed to Get\\[T\\]: needed String")
|
||||
TestConfig.Get[String]("default.int") must throwA[ClassCastException](message = "Incorrect type T = String passed to Get\\[T\\]: needed Int")
|
||||
ok
|
||||
}
|
||||
}
|
||||
|
||||
"TestBadConfig" should {
|
||||
|
|
|
|||
Loading…
Reference in a new issue