From 88b6974edc33c54c1d66655347e2497bb5021f2e Mon Sep 17 00:00:00 2001 From: L-11 Date: Fri, 26 Aug 2016 22:46:57 -0500 Subject: [PATCH] Initial support for server REPL (#68) * Initial support for server console * Expand README * Fix unmanagedClasspath for `sbt pslogin/console` --- README.md | 34 ++++++++++++++++++++++++++++ build.sbt | 3 ++- pslogin/src/main/scala/PsLogin.scala | 29 +++++++++++++++++------- 3 files changed, 57 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index ce01b8d2a..2bb4a0a3d 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,40 @@ sbt pslogin/run ``` This will clone the repository and SBT will compile and run the login server. Note: SBT is quite slow at starting up. It's recommended you have an open SBT console in order to avoid this startup time. +## Running the Server + +To run a headless, non-interactive server, run + +``` +sbt pslogin/run +``` + +PlanetSide can now connect to your server. + +To run your custom server with an interactive `scala>` REPL, run + +``` +sbt pslogin/console +``` + +![image](https://cloud.githubusercontent.com/assets/16912082/18024110/7b48dba8-6bc8-11e6-81d8-4692bc9d48a8.png) + +To start the server and begin listening for connections, enter the following expression into the REPL: + +``` +PsLogin.run +``` + +![image](https://cloud.githubusercontent.com/assets/16912082/18024137/1167452a-6bc9-11e6-8765-a86fb465de61.png) + +This process is identical to running the headless, non-interactive server: PlanetSide clients can connect, logging output will be printed to the screen, etc. The advantage is that you now have an interactive REPL that will evaluate any Scala expression you type into it. + +![image](https://cloud.githubusercontent.com/assets/16912082/18024339/62197f66-6bcd-11e6-90f7-5569d33472a7.png) + +The REPL supports various useful commands. For example, to see the type of an arbitrary expression `foo`, run `:type foo`. To print all members of a type, run `:javap -p some-type`. You can run `:help` to see a full list of commands. + +![image](https://cloud.githubusercontent.com/assets/16912082/18024371/e0b72f9e-6bcd-11e6-9de5-421ec3eff994.png) + ## Connecting to the Server through the Client To get PlanetSide to connect to your custom server, you will have to navigate to the `client.ini` file (located within the PlanetSide game directory) and modify the IP addresses. diff --git a/build.sbt b/build.sbt index 6e0fbc38e..f81f6627c 100644 --- a/build.sbt +++ b/build.sbt @@ -22,7 +22,8 @@ lazy val commonSettings = Seq( lazy val pscryptoSettings = Seq( unmanagedClasspath in Test += (baseDirectory in ThisBuild).value / "pscrypto-lib", - unmanagedClasspath in Runtime += (baseDirectory in ThisBuild).value / "pscrypto-lib" + unmanagedClasspath in Runtime += (baseDirectory in ThisBuild).value / "pscrypto-lib", + unmanagedClasspath in Compile += (baseDirectory in ThisBuild).value / "pscrypto-lib" ) lazy val psloginPackSettings = packAutoSettings ++ Seq( diff --git a/pslogin/src/main/scala/PsLogin.scala b/pslogin/src/main/scala/PsLogin.scala index 0eeec39db..9895045e2 100644 --- a/pslogin/src/main/scala/PsLogin.scala +++ b/pslogin/src/main/scala/PsLogin.scala @@ -21,6 +21,14 @@ import scala.concurrent.duration._ object PsLogin { private val logger = org.log4s.getLogger + var args : Array[String] = Array() + var config : java.util.Map[String,Object] = null + var system : akka.actor.ActorSystem = null + var loginRouter : akka.actor.Props = null + var worldRouter : akka.actor.Props = null + var loginListener : akka.actor.ActorRef = null + var worldListener : akka.actor.ActorRef = null + def banner() : Unit = { println(ansi().fgBright(BLUE).a(""" ___ ________""")) println(ansi().fgBright(BLUE).a(""" / _ \/ __/ __/__ _______ _ _____ ____""")) @@ -94,7 +102,7 @@ object PsLogin { } } - def main(args : Array[String]) : Unit = { + def run() : Unit = { // Early start up banner() println(systemInformation) @@ -109,7 +117,7 @@ object PsLogin { } initializeLogging(configDirectory + File.separator + "logback.xml") - parseArgs(args) + parseArgs(this.args) /** Initialize the PSCrypto native library * @@ -143,14 +151,14 @@ object PsLogin { * This same config can be specified in a configuration file, but that's more work at this point. * In the future we will have a unified configuration file specific to this server */ - val config = Map( + config = Map( "akka.loggers" -> List("akka.event.slf4j.Slf4jLogger").asJava, "akka.loglevel" -> "INFO", "akka.logging-filter" -> "akka.event.slf4j.Slf4jLoggingFilter" ).asJava /** Start up the main actor system. This "system" is the home for all actors running on this server */ - val system = ActorSystem("PsLogin", ConfigFactory.parseMap(config)) + system = ActorSystem("PsLogin", ConfigFactory.parseMap(config)) /** Create pipelines for the login and world servers * @@ -186,10 +194,10 @@ object PsLogin { */ /** Create two actors for handling the login and world server endpoints */ - val listener = system.actorOf(Props(new UdpListener(Props(new SessionRouter("Login", loginTemplate)), "login-session-router", - LoginConfig.serverIpAddress, loginServerPort, None)), "login-udp-endpoint") - val worldListener = system.actorOf(Props(new UdpListener(Props(new SessionRouter("World", worldTemplate)), "world-session-router", - LoginConfig.serverIpAddress, worldServerPort, None)), "world-udp-endpoint") + loginRouter = Props(new SessionRouter("Login", loginTemplate)) + worldRouter = Props(new SessionRouter("World", worldTemplate)) + loginListener = system.actorOf(Props(new UdpListener(loginRouter, "login-session-router", LoginConfig.serverIpAddress, loginServerPort, None)), "login-udp-endpoint") + worldListener = system.actorOf(Props(new UdpListener(worldRouter, "world-session-router", LoginConfig.serverIpAddress, worldServerPort, None)), "world-udp-endpoint") logger.info(s"NOTE: Set client.ini to point to ${LoginConfig.serverIpAddress.getHostAddress}:$loginServerPort") @@ -198,6 +206,11 @@ object PsLogin { // TODO: clean up active sessions and close resources safely logger.info("Login server now shutting down...") } + } + + def main(args : Array[String]) : Unit = { + this.args = args + run() // Wait forever until the actor system shuts down Await.result(system.whenTerminated, Duration.Inf)