diff options
author | Joel Kronqvist <joel.kronqvist@iki.fi> | 2024-11-05 00:18:36 +0200 |
---|---|---|
committer | Joel Kronqvist <joel.kronqvist@iki.fi> | 2024-11-05 00:18:36 +0200 |
commit | 12cbf4d451d1002b8872b0028acbe6bd886ca9bd (patch) | |
tree | c8d8ed236cc7ae907f52209cb0dd1f7d40ebf346 /src | |
parent | ae82027a9bd4e75582f9499d4006b18c29a4129c (diff) | |
download | scalevalapokalypsi-12cbf4d451d1002b8872b0028acbe6bd886ca9bd.tar.gz scalevalapokalypsi-12cbf4d451d1002b8872b0028acbe6bd886ca9bd.zip |
Small refactoring
* Moved `ConnectionGetter` to its own file
* Added method `mapAndRemove` to Server/Client.scala
* Replaced usages of `removeNonSatisfying` with the above
* Added some documentation
Diffstat (limited to 'src')
-rw-r--r-- | src/main/scala/Server/Client.scala | 27 | ||||
-rw-r--r-- | src/main/scala/Server/ConnectionGetter.scala | 25 | ||||
-rw-r--r-- | src/main/scala/Server/Server.scala | 49 |
3 files changed, 62 insertions, 39 deletions
diff --git a/src/main/scala/Server/Client.scala b/src/main/scala/Server/Client.scala index c7ff075..3bc4012 100644 --- a/src/main/scala/Server/Client.scala +++ b/src/main/scala/Server/Client.scala @@ -1,6 +1,7 @@ package o1game.Server import java.net.Socket +import scala.util.Try import scala.math.min import o1game.constants.* @@ -69,12 +70,36 @@ class Clients(maxClients: Int): this.clients(i) = Some(client) true + /** Returns all the clients. + * + * @return an iterable of all the clients + */ def allClients: Iterable[Client] = clients.toVector.flatten + + /** Applies the function `f` to all the clients for its side effects. */ def foreach(f: Client => Any): Unit = this.clients.flatten.foreach(f) + + /** Applies the function `f` to all the clients for its side effects + * and removes all the clients for which `f([client])` returns false. + * This is useful for doing IO with the client and removing clients + * with stale sockets. + * + * @param f the function to apply to all the clients and filter them with + */ def removeNonSatisfying(f: Client => Boolean): Unit = for i <- this.clients.indices do this.clients(i) match case Some(c) => if !f(c) then this.clients(i) = None - case None =>
\ No newline at end of file + case None => + + /** Applies the function f to all clients for its side effects. + * If the function throws an exception, the client is removed. + * Probably a more concise alternative to `removeNonSatisfying`, + * but might catch exceptions unintentionally. + * + * @param f the function to apply for its side effects to each client + */ + def mapAndRemove(f: Client => Unit): Unit = + this.removeNonSatisfying(c => Try(f(c)).isSuccess)
\ No newline at end of file diff --git a/src/main/scala/Server/ConnectionGetter.scala b/src/main/scala/Server/ConnectionGetter.scala new file mode 100644 index 0000000..b3246a7 --- /dev/null +++ b/src/main/scala/Server/ConnectionGetter.scala @@ -0,0 +1,25 @@ +package o1game.Server + +import java.io.IOException +import java.net.{ServerSocket, Socket} +import scala.concurrent.Future +import scala.util.{Failure, Success} +import scala.concurrent.ExecutionContext.Implicits.global + +/** Small helper class for getting new connections using futures */ +class ConnectionGetter(val socket: ServerSocket): + + private var nextClient: Future[Socket] = Future.failed(IOException()) + + /** Returns a new socket to a client if there is any new connections. */ + def newClient(): Option[Socket] = + this.nextClient.value match + case Some(Success(s)) => + nextClient = Future(socket.accept()) + Some(s) + case Some(Failure(e)) => + nextClient = Future(socket.accept()) + None + case None => None + +end ConnectionGetter diff --git a/src/main/scala/Server/Server.scala b/src/main/scala/Server/Server.scala index 829010c..1f0cbfc 100644 --- a/src/main/scala/Server/Server.scala +++ b/src/main/scala/Server/Server.scala @@ -6,9 +6,6 @@ package o1game.Server import java.io.IOException import java.lang.Thread.sleep import java.net.{ServerSocket, Socket} -import scala.concurrent.ExecutionContext.Implicits.global -import scala.concurrent.Future -import scala.util.{Failure, Success, Try} import o1game.constants.* /** `Server` exists to initialize a server for the game @@ -47,44 +44,20 @@ class Server(port: Int, maxClients: Int): * @param message the message to send */ private def writeToAll(message: String): Unit = - clients.removeNonSatisfying((c: Client) => - try - val output = c.socket.getOutputStream - output.write(message.toVector.map(_.toByte).toArray) - output.flush() - true - catch - case e: IOException => false + clients.mapAndRemove(c => + val output = c.socket.getOutputStream + output.write(message.toVector.map(_.toByte).toArray) + output.flush() ) /** Reads data sent by clients and stores it in the `Client`s of `clients` */ private def readFromAll(): Unit = - clients.removeNonSatisfying((c: Client) => - try - val input = c.socket.getInputStream - while input.available() != 0 do - val bytesRead = input.read(buffer) - if bytesRead != -1 then - c.receiveData(buffer.take(bytesRead).toVector) - true - catch - case e: IOException => false + clients.mapAndRemove(c => + val input = c.socket.getInputStream + while input.available() != 0 do + val bytesRead = input.read(buffer) + if bytesRead != -1 then + c.receiveData(buffer.take(bytesRead).toVector) ) -end Server - -class ConnectionGetter(val socket: ServerSocket): - - private var nextClient: Future[Socket] = Future.failed(IOException()) - - def newClient(): Option[Socket] = - this.nextClient.value match - case Some(Success(s)) => - nextClient = Future(socket.accept()) - Some(s) - case Some(Failure(e)) => - nextClient = Future(socket.accept()) - None - case None => None - -end ConnectionGetter +end Server
\ No newline at end of file |