From ae82027a9bd4e75582f9499d4006b18c29a4129c Mon Sep 17 00:00:00 2001 From: Joel Kronqvist Date: Mon, 4 Nov 2024 21:38:50 +0200 Subject: 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. --- src/main/scala/Server/Server.scala | 90 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 src/main/scala/Server/Server.scala (limited to 'src/main/scala/Server/Server.scala') 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 -- cgit v1.2.3