aboutsummaryrefslogtreecommitdiff
path: root/src/main/scala/Server/Server.scala
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/scala/Server/Server.scala')
-rw-r--r--src/main/scala/Server/Server.scala83
1 files changed, 64 insertions, 19 deletions
diff --git a/src/main/scala/Server/Server.scala b/src/main/scala/Server/Server.scala
index 6fa284c..2f78e6b 100644
--- a/src/main/scala/Server/Server.scala
+++ b/src/main/scala/Server/Server.scala
@@ -8,6 +8,9 @@ import java.net.{ServerSocket, Socket}
import o1game.constants.*
import o1game.Model.Adventure
import o1game.Model.Entity
+import scala.util.Try
+
+import scala.util.Try
/** Converts this string to an array of bytes (probably for transmission).
*
@@ -18,9 +21,22 @@ def stringToByteArray(str: String): Array[Byte] =
str.toVector.map(_.toByte).toArray
/** `Server` exists to initialize a server for the game
- * and run it with its method `startServer`.
- */
-class Server(port: Int, maxClients: Int, val timeLimit: Int):
+ * and run it with its method `startServer`.
+ *
+ * @param port the TCP port the server should listen on
+ * @param maxClients the maximum number of clients that may be in the game
+ * simultaneously.
+ * @param timeLimit the time limit clients should have to execute their turns.
+ * @param joinAfterStart whether new clients are accepted after the game has
+ * been started
+ */
+class Server(
+ port: Int,
+ maxClients: Int,
+ val timeLimit: Int,
+ val joinAfterStart: Boolean
+):
+
private val socket = ServerSocket(port)
private val clientGetter = ConnectionGetter(socket)
private val clients: Clients = Clients(maxClients)
@@ -31,25 +47,45 @@ class Server(port: Int, maxClients: Int, val timeLimit: Int):
/** Starts the server. Won't terminate under normal circumstances. */
def startServer(): Unit =
while true do
+ this.serverStep()
sleep(POLL_INTERVAL)
+
+ private def serverStep(): Unit =
+ if this.adventure.isEmpty || this.joinAfterStart then
this.receiveNewClient()
- this.readFromAll()
- this.writeClientDataToClients()
- this.clients.removeNonCompliant()
- this.clients.foreach(_.interpretData())
- if this.adventure.isEmpty && !this.clients.isEmpty && this.clients.forall(_.isReadyForGameStart) then
- this.adventure = Some(Adventure(this.clients.names))
- this.clients.foreach( c =>
- c.gameStart()
- val name = c.getName
- val entity: Option[Entity] = name match
- case Some(n) => this.adventure match
- case Some(a) => a.getPlayer(n)
- case None => None
- case None => None
- entity.map(c.giveEntity(_))
+ this.readFromAll()
+ this.writeClientDataToClients()
+ this.clients.removeNonCompliant()
+ this.clients.foreach(_.interpretData())
+ if this.adventure.isDefined && this.joinAfterStart then
+ this.clients.foreach( c => if c.isReadyForGameStart then
+ this.adventure.foreach(a =>
+ c.getName.foreach(n => a.addPlayer(n))
)
- this.writeToAll(s"$timeLimit")
+ startGameForClient(c)
+ )
+ else if this.adventure.isEmpty && !this.clients.isEmpty && this.clients.forall(_.isReadyForGameStart) then
+ this.adventure = Some(Adventure(this.clients.names))
+ this.clients.foreach(startGameForClient(_))
+
+ /** Helper function to start the game for the specified client c.
+ * MAY ONLY BE USED IF `this.adventure` is Some!
+ * Apparently guard clauses are bad because they use return or something,
+ * but assertions should be fine, as long as they enforce the function
+ * contract?
+ */
+ private def startGameForClient(c: Client): Unit =
+ assert(this.adventure.isDefined)
+ c.gameStart()
+ val name = c.getName
+ val entity: Option[Entity] = name match
+ case Some(n) => this.adventure match
+ case Some(a) => a.getPlayer(n)
+ case None => None
+ case None => None
+ entity.map(c.giveEntity(_))
+ this.writeToClient(s"$timeLimit", c)
+
/** Receives a new client and stores it in `clients`.
@@ -75,6 +111,15 @@ class Server(port: Int, maxClients: Int, val timeLimit: Int):
output.flush()
)
+ private def writeToClient(message: String, client: Client): Unit =
+ val success = Try( () =>
+ val output = client.socket.getOutputStream
+ output.write(stringToByteArray(message))
+ output.flush()
+ )
+ if success.isFailure then
+ client.failedProtocol()
+
/** Sends every client's `dataToThisClient` to the client */
private def writeClientDataToClients(): Unit =
this.clients.mapAndRemove(c =>