diff options
author | Joel Kronqvist <joel.kronqvist@iki.fi> | 2024-11-04 21:38:50 +0200 |
---|---|---|
committer | Joel Kronqvist <joel.kronqvist@iki.fi> | 2024-11-04 22:03:40 +0200 |
commit | ae82027a9bd4e75582f9499d4006b18c29a4129c (patch) | |
tree | 92d23012158647d31fbdc0e6cdae7854ee773cbc /src/main/scala/Server/Server.scala | |
download | scalevalapokalypsi-ae82027a9bd4e75582f9499d4006b18c29a4129c.tar.gz scalevalapokalypsi-ae82027a9bd4e75582f9499d4006b18c29a4129c.zip |
Added basic TCP networking for the server.
The client's networking is still very experimental and
the actual protocol is not yet specified for either side.
Diffstat (limited to 'src/main/scala/Server/Server.scala')
-rw-r--r-- | src/main/scala/Server/Server.scala | 90 |
1 files changed, 90 insertions, 0 deletions
diff --git a/src/main/scala/Server/Server.scala b/src/main/scala/Server/Server.scala new file mode 100644 index 0000000..829010c --- /dev/null +++ b/src/main/scala/Server/Server.scala @@ -0,0 +1,90 @@ +package o1game.Server + + +// TODO: TLS/SSL / import javax.net.ssl.SSLServerSocketFactory + +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 + * and run it with its method `startServer`. + */ +class Server(port: Int, maxClients: Int): + private val socket = ServerSocket(port) + private val clientGetter = ConnectionGetter(socket) + private val clients: Clients = Clients(maxClients) + private val buffer: Array[Byte] = Array.ofDim(1024) + private var bufferIndex = 0 + + /** Starts the server. Won't terminate under normal circumstances. */ + def startServer(): Unit = + while true do + sleep(POLL_INTERVAL) + this.receiveNewClient() + this.writeToAll("Test message.") + this.readFromAll() + clients.foreach(_.executeActions()) + + /** Receives a new client and stores it in `clients`. + * + * @return describes if a client was added + */ + private def receiveNewClient(): Boolean = + clientGetter.newClient() match + case Some(c) => + clients.addClient(Client(c, None)) + true + case None => + false + + /** Sends `message` to all clients + * + * @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 + ) + + /** 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 + ) + +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 |