mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-03-04 04:30:21 +00:00
Create PSForever config framework (#283)
We can now parse arbitrary INI configuration entries. This will allow server customization and testing without recompiling the server.
This commit is contained in:
parent
4b71d76cb2
commit
c3d19b5377
8 changed files with 647 additions and 5 deletions
|
|
@ -11,6 +11,7 @@ import ch.qos.logback.core.joran.spi.JoranException
|
|||
import ch.qos.logback.core.status._
|
||||
import ch.qos.logback.core.util.StatusPrinter
|
||||
import com.typesafe.config.ConfigFactory
|
||||
import net.psforever.config.{Valid, Invalid}
|
||||
import net.psforever.crypto.CryptoInterface
|
||||
import net.psforever.objects.zones._
|
||||
import net.psforever.objects.guid.TaskResolver
|
||||
|
|
@ -102,6 +103,33 @@ object PsLogin {
|
|||
}
|
||||
}
|
||||
|
||||
def loadConfig(configDirectory : String) = {
|
||||
val worldConfigFile = configDirectory + File.separator + "worldserver.ini"
|
||||
// For fallback when no user-specific config file has been created
|
||||
val worldDefaultConfigFile = configDirectory + File.separator + "worldserver.ini.dist"
|
||||
|
||||
val worldConfigToLoad = if ((new File(worldConfigFile)).exists()) {
|
||||
worldConfigFile
|
||||
} else if ((new File(worldDefaultConfigFile)).exists()) {
|
||||
println("WARNING: loading the default worldserver.ini.dist config file")
|
||||
println("WARNING: Please create a worldserver.ini file to override server defaults")
|
||||
|
||||
worldDefaultConfigFile
|
||||
} else {
|
||||
println("FATAL: unable to load any worldserver.ini file")
|
||||
sys.exit(1)
|
||||
}
|
||||
|
||||
WorldConfig.Load(worldConfigToLoad) match {
|
||||
case Valid =>
|
||||
println("Loaded world config from " + worldConfigFile)
|
||||
case i : Invalid =>
|
||||
println("FATAL: Error loading config from " + worldConfigFile)
|
||||
println(WorldConfig.FormatErrors(i).mkString("\n"))
|
||||
sys.exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
def parseArgs(args : Array[String]) : Unit = {
|
||||
if(args.length == 1) {
|
||||
LoginConfig.serverIpAddress = InetAddress.getByName(args{0})
|
||||
|
|
@ -125,9 +153,15 @@ object PsLogin {
|
|||
configDirectory = System.getProperty("prog.home") + File.separator + "config"
|
||||
}
|
||||
|
||||
initializeLogging(configDirectory + File.separator + "logback.xml")
|
||||
parseArgs(this.args)
|
||||
|
||||
val loggingConfigFile = configDirectory + File.separator + "logback.xml"
|
||||
|
||||
loadConfig(configDirectory)
|
||||
|
||||
println(s"Initializing logging from ${loggingConfigFile}...")
|
||||
initializeLogging(loggingConfigFile)
|
||||
|
||||
/** Initialize the PSCrypto native library
|
||||
*
|
||||
* PSCrypto provides PlanetSide specific crypto that is required to communicate with it.
|
||||
|
|
@ -194,9 +228,8 @@ object PsLogin {
|
|||
SessionPipeline("world-session-", Props[WorldSessionActor])
|
||||
)
|
||||
|
||||
val loginServerPort = 51000
|
||||
val worldServerPort = 51001
|
||||
|
||||
val loginServerPort = WorldConfig.Get[Int]("loginserver.ListeningPort")
|
||||
val worldServerPort = WorldConfig.Get[Int]("worldserver.ListeningPort")
|
||||
|
||||
// Uncomment for network simulation
|
||||
// TODO: make this config or command flag
|
||||
|
|
|
|||
28
pslogin/src/main/scala/WorldConfig.scala
Normal file
28
pslogin/src/main/scala/WorldConfig.scala
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
// Copyright (c) 2019 PSForever
|
||||
import net.psforever.config._
|
||||
|
||||
object WorldConfig extends ConfigParser {
|
||||
protected var config_map : Map[String, Any] = Map()
|
||||
|
||||
protected val config_template = Seq(
|
||||
ConfigSection("loginserver",
|
||||
ConfigEntryInt("ListeningPort", 51000, Constraints.min(1), Constraints.max(65535))
|
||||
),
|
||||
ConfigSection("worldserver",
|
||||
ConfigEntryInt("ListeningPort", 51001, Constraints.min(1), Constraints.max(65535))
|
||||
)
|
||||
)
|
||||
|
||||
override def postParseChecks : ValidationResult = {
|
||||
var errors : Invalid = Invalid("")
|
||||
|
||||
if (Get[Int]("worldserver.ListeningPort") == Get[Int]("loginserver.ListeningPort"))
|
||||
errors = errors ++ Invalid("worldserver.ListeningPort must be different from loginserver.ListeningPort")
|
||||
|
||||
if (errors.errors.length > 1)
|
||||
// drop the first error using tail (it was a placeholder)
|
||||
Invalid(errors.errors.tail)
|
||||
else
|
||||
Valid
|
||||
}
|
||||
}
|
||||
19
pslogin/src/test/resources/testconfig.ini
Normal file
19
pslogin/src/test/resources/testconfig.ini
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
# This is a comment
|
||||
[default]
|
||||
string = a string
|
||||
string_quoted = "a string"
|
||||
int = 31
|
||||
time = 1 second
|
||||
time2 = 100 milliseconds
|
||||
float = 0.1
|
||||
bool_true = yes
|
||||
bool_false = no
|
||||
# missing
|
||||
|
||||
[bad]
|
||||
bad_int = not a number
|
||||
bad_time = 10
|
||||
bad_float = A
|
||||
bad_bool = dunno
|
||||
bad_int_range = -1
|
||||
bad_int_range2 = 3
|
||||
79
pslogin/src/test/scala/ConfigTest.scala
Normal file
79
pslogin/src/test/scala/ConfigTest.scala
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
// Copyright (c) 2019 PSForever
|
||||
import scala.io.Source
|
||||
import org.specs2.mutable._
|
||||
import net.psforever.config._
|
||||
import scala.concurrent.duration._
|
||||
|
||||
class ConfigTest extends Specification {
|
||||
val testConfig = getClass.getResource("/testconfig.ini").getPath
|
||||
|
||||
"WorldConfig" should {
|
||||
"have no errors" in {
|
||||
WorldConfig.Load("config/worldserver.ini.dist") mustEqual Valid
|
||||
}
|
||||
}
|
||||
|
||||
"TestConfig" should {
|
||||
"parse" in {
|
||||
TestConfig.Load(testConfig) mustEqual Valid
|
||||
TestConfig.Get[String]("default.string") mustEqual "a string"
|
||||
TestConfig.Get[String]("default.string_quoted") mustEqual "a string"
|
||||
TestConfig.Get[Int]("default.int") mustEqual 31
|
||||
TestConfig.Get[Duration]("default.time") mustEqual (1 second)
|
||||
TestConfig.Get[Duration]("default.time2") mustEqual (100 milliseconds)
|
||||
TestConfig.Get[Float]("default.float") mustEqual 0.1f
|
||||
TestConfig.Get[Boolean]("default.bool_true") mustEqual true
|
||||
TestConfig.Get[Boolean]("default.bool_false") mustEqual false
|
||||
TestConfig.Get[Int]("default.missing") mustEqual 1337
|
||||
}
|
||||
}
|
||||
|
||||
"TestBadConfig" should {
|
||||
"not parse" in {
|
||||
val error = TestBadConfig.Load(testConfig).asInstanceOf[Invalid]
|
||||
val check_errors = List(
|
||||
ValidationError("bad.bad_int: value format error (expected: Int)"),
|
||||
ValidationError("bad.bad_time: value format error (expected: Time)"),
|
||||
ValidationError("bad.bad_float: value format error (expected: Float)"),
|
||||
ValidationError("bad.bad_bool: value format error (expected: Bool)"),
|
||||
ValidationError("bad.bad_int_range: error.min", 0),
|
||||
ValidationError("bad.bad_int_range2: error.max", 2)
|
||||
)
|
||||
|
||||
error.errors mustEqual check_errors
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object TestConfig extends ConfigParser {
|
||||
protected var config_map : Map[String, Any] = Map()
|
||||
|
||||
protected val config_template = Seq(
|
||||
ConfigSection("default",
|
||||
ConfigEntryString("string", ""),
|
||||
ConfigEntryString("string_quoted", ""),
|
||||
ConfigEntryInt("int", 0),
|
||||
ConfigEntryTime("time", 0 seconds),
|
||||
ConfigEntryTime("time2", 0 seconds),
|
||||
ConfigEntryFloat("float", 0.0f),
|
||||
ConfigEntryBool("bool_true", false),
|
||||
ConfigEntryBool("bool_false", true),
|
||||
ConfigEntryInt("missing", 1337)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
object TestBadConfig extends ConfigParser {
|
||||
protected var config_map : Map[String, Any] = Map()
|
||||
|
||||
protected val config_template = Seq(
|
||||
ConfigSection("bad",
|
||||
ConfigEntryInt("bad_int", 0),
|
||||
ConfigEntryTime("bad_time", 0 seconds),
|
||||
ConfigEntryFloat("bad_float", 0.0f),
|
||||
ConfigEntryBool("bad_bool", false),
|
||||
ConfigEntryInt("bad_int_range", 0, Constraints.min(0)),
|
||||
ConfigEntryInt("bad_int_range2", 0, Constraints.min(0), Constraints.max(2))
|
||||
)
|
||||
)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue