aboutsummaryrefslogtreecommitdiff
path: root/src/main/scala/Server
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/scala/Server')
-rw-r--r--src/main/scala/Server/Client.scala19
-rw-r--r--src/main/scala/Server/Server.scala56
-rw-r--r--src/main/scala/Server/constants.scala10
3 files changed, 51 insertions, 34 deletions
diff --git a/src/main/scala/Server/Client.scala b/src/main/scala/Server/Client.scala
index e557c22..cd557c6 100644
--- a/src/main/scala/Server/Client.scala
+++ b/src/main/scala/Server/Client.scala
@@ -53,7 +53,6 @@ class Client(val socket: Socket):
* @param entity the entity this client is to control
*/
def giveEntity(entity: Entity): Unit =
- println(entity)
this.character = Some(entity)
/** Gets the name of this client, which should match the name of the entity
@@ -90,18 +89,19 @@ class Client(val socket: Socket):
* @param data data to buffer for sending
*/
private def addDataToSend(data: String): Unit =
- this.outData += s"$data\n"
+ this.outData += s"$data\r\n"
/** Returns one line of data if there are any line breaks.
* Removes the parsed data from the message buffering area.
*/
private def nextLine(): Option[String] =
- val nextLF = this.incompleteMessage.indexOf(LF)
- if nextLF != -1 then
- val message = this.incompleteMessage.take(nextLF)
- val rest = this.incompleteMessage.drop(nextLF + 1)
- this.incompleteMessage = rest ++ Array.fill(nextLF + 1)(0.toByte)
+ var nextCRLF = this.incompleteMessage.indexOf(CRLF(0))
+ if this.incompleteMessage(nextCRLF + 1) != CRLF(1) then nextCRLF = -1
+ if nextCRLF != -1 then
+ val message = this.incompleteMessage.take(nextCRLF)
+ val rest = this.incompleteMessage.drop(nextCRLF + 1)
+ this.incompleteMessage = rest ++ Array.fill(nextCRLF + 1)(0.toByte)
// TODO: the conversion may probably be exploited to crash the server
Some(String(message))
else
@@ -157,17 +157,14 @@ class Client(val socket: Socket):
this.entity.exists(action.takesATurnFor(_))
) then
this.nextAction = Some(action)
- this.addDataToSend("Waiting for everyone to end their turns...")
else if this.nextAction.isEmpty then
executeAction(action)
- /** Executes the specified action */
+ /** Executes the specified action and buffers its description for sending */
private def executeAction(action: Action) =
this.character.flatMap(action.execute(_)) match
case Some(s) => this.addDataToSend((s))
case None => this.addDataToSend("You can't do that")
- this.character.map(_.location.fullDescription)
- .foreach(this.addDataToSend(_))
end Client
diff --git a/src/main/scala/Server/Server.scala b/src/main/scala/Server/Server.scala
index faf82e1..a03bc53 100644
--- a/src/main/scala/Server/Server.scala
+++ b/src/main/scala/Server/Server.scala
@@ -4,21 +4,16 @@ package o1game.Server
// TODO: TLS/SSL / import javax.net.ssl.SSLServerSocketFactory
import java.lang.Thread.{currentThread, sleep}
+import java.io.IOException
import java.net.{ServerSocket, Socket}
import o1game.constants.*
import o1game.Model.Adventure
import o1game.Model.Entity
+import o1game.utils.stringToByteArray
import java.lang.System.currentTimeMillis
import scala.util.Try
-/** Converts this string to an array of bytes (probably for transmission).
- *
- * @param str the string to convert
- * @return an array of bytes representing the string in UTF8.
- */
-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`.
@@ -52,17 +47,19 @@ class Server(
sleep(POLL_INTERVAL)
private def serverStep(): Unit =
+ this.clients.removeNonCompliant()
if this.adventure.isEmpty || this.joinAfterStart then
this.receiveNewClient()
this.readFromAll()
- this.writeClientDataToClients()
- this.clients.removeNonCompliant()
this.clients.foreach(_.interpretData())
+ this.writeClientDataToClients()
if this.canExecuteTurns then
- println("taking turns")
- this.clients.inRandomOrder(_.act())
- this.writeToAll("next turn!")
- this.previousTurn = currentTimeMillis() / 1000
+ this.clients.inRandomOrder(_.act())
+ this.writeClientDataToClients()
+ this.clients.foreach(c =>
+ this.writeToClient(this.turnStartInfo(c), c)
+ )
+ this.previousTurn = currentTimeMillis() / 1000
if this.adventure.isDefined && this.joinAfterStart then
this.clients.foreach( c => if c.isReadyForGameStart then
this.adventure.foreach(a =>
@@ -91,7 +88,7 @@ class Server(
case None => None
case None => None
entity.foreach(c.giveEntity(_))
- this.writeToClient(s"$timeLimit", c)
+ this.writeToClient(s"$timeLimit\r\n", c)
/** Helper function to determine if the next turn can be taken */
@@ -119,6 +116,25 @@ class Server(
case None =>
false
+ private def turnStartInfo(client: Client): String =
+ val clientArea = client.entity.map(_.location)
+ val areaDesc = clientArea
+ .map(_.description)
+ .getOrElse("You are floating in the middle of a soothing void.")
+ val directions = clientArea
+ .map(_.getNeighborNames.mkString(LIST_SEPARATOR))
+ .getOrElse("")
+ val items = clientArea
+ .map(_.getItemNames.mkString(LIST_SEPARATOR))
+ .getOrElse("")
+ val entities = client.entity.map(c =>
+ c.location
+ .getEntityNames
+ .filter(c.name != _)
+ .mkString(LIST_SEPARATOR)
+ ).getOrElse("")
+ s"$TURN_INDICATOR\r\n$areaDesc\r\n$directions\r\n$items\r\n$entities\r\n"
+
/** Sends `message` to all clients
*
* @param message the message to send
@@ -131,13 +147,13 @@ class Server(
)
private def writeToClient(message: String, client: Client): Unit =
- val success = Try( () =>
+ try {
val output = client.socket.getOutputStream
- output.write(stringToByteArray(message))
+ output.write(message.toVector.map(_.toByte).toArray)
output.flush()
- )
- if success.isFailure then
- client.failedProtocol()
+ } catch {
+ case e: IOException => client.failedProtocol()
+ }
/** Sends every client's `dataToThisClient` to the client */
private def writeClientDataToClients(): Unit =
@@ -158,4 +174,4 @@ class Server(
c.receiveData(buffer.take(bytesRead).toVector)
)
-end Server \ No newline at end of file
+end Server
diff --git a/src/main/scala/Server/constants.scala b/src/main/scala/Server/constants.scala
index ddaab00..a9e4502 100644
--- a/src/main/scala/Server/constants.scala
+++ b/src/main/scala/Server/constants.scala
@@ -2,12 +2,16 @@
package o1game.constants
val MAX_MSG_SIZE = 1024 // bytes
-val LF: Byte = 10
+val CRLF: Vector[Byte] = Vector(13.toByte, 10.toByte)
val POLL_INTERVAL = 100 // millisec.
val GAME_VERSION = "0.1.0"
+val TURN_INDICATOR = ">"
-val PROTOCOL_VERSION_GOOD = "version ok"
-val PROTOCOL_VERSION_BAD = "version bad"
+val LIST_SEPARATOR=";"
+
+val PROTOCOL_VERSION_GOOD = "1"
+val PROTOCOL_VERSION_BAD = "0"
+//assert(PROTOCOL_VERSION_BAD.length <= PROTOCOL_VERSION_GOOD.length)
enum ServerProtocolState:
case WaitingForVersion, WaitingForClientName, WaitingForGameStart, InGame