aboutsummaryrefslogtreecommitdiff
path: root/src/main/scala/Server/Client.scala
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/scala/Server/Client.scala')
-rw-r--r--src/main/scala/Server/Client.scala173
1 files changed, 0 insertions, 173 deletions
diff --git a/src/main/scala/Server/Client.scala b/src/main/scala/Server/Client.scala
deleted file mode 100644
index 3cd2b36..0000000
--- a/src/main/scala/Server/Client.scala
+++ /dev/null
@@ -1,173 +0,0 @@
-package o1game.Server
-
-import java.net.Socket
-import scala.math.min
-import o1game.constants.*
-import ServerProtocolState.*
-import o1game.Model.{Action,Player,Entity}
-
-class Client(val socket: Socket):
- private var incompleteMessage: Array[Byte] =
- Array.fill(MAX_MSG_SIZE)(0.toByte)
- private var incompleteMessageIndex = 0
- private var protocolState = WaitingForVersion
- private var outData: String = ""
- private var character: Option[Player] = None
- private var protocolIsIntact = true
- private var name: Option[String] = None
- private var nextAction: Option[Action] = None
-
- /** Calculates the amount of bytes available for future incoming messages */
- def spaceAvailable: Int = MAX_MSG_SIZE - incompleteMessageIndex
-
- /** Tests whether the client has behaved according to protocol.
- *
- * @return false if there has been a protocol violation, true otherwise
- */
- def isIntactProtocolWise: Boolean = this.protocolIsIntact
-
- /** Marks that this client misbehaved in eyes of the protocol */
- def failedProtocol(): Unit = this.protocolIsIntact = false
-
- /** Tests whether this client is initialized and ready to start the game
- *
- * @return true if the client is ready to join the game
- */
- def isReadyForGameStart: Boolean =
- this.protocolState == WaitingForGameStart
-
- /** Signals this client that it's joining the game. This is important so
- * that this object knows to update its protocol state.
- */
- def gameStart(): Unit = this.protocolState = InGame
-
- /** Returns the player this client controls in the model.
- *
- * @return an option containing the player
- */
- def player: Option[Player] = this.character
-
- /** Tells this client object that it controls the specified player.
- *
- * @param player the player this client is to control
- */
- def givePlayer(player: Player): Unit =
- this.character = Some(player)
-
- /** Gets the name of this client, which should match the name of the player
- * that is given to this client. Not very useful if the client hasn't yet
- * received the name or if it already has an player.
- *
- * @return the name of this client
- */
- def getName: Option[String] = this.name
-
- /** Sets `data` as received for the client.
- *
- * @return false means there was not enough space to receive the message
- */
- def receiveData(data: Vector[Byte]): Boolean =
- for i <- 0 until min(data.length, spaceAvailable) do
- this.incompleteMessage(this.incompleteMessageIndex + i) = data(i)
- this.incompleteMessageIndex += data.length
- this.incompleteMessageIndex =
- min(this.incompleteMessageIndex, MAX_MSG_SIZE)
- data.length < spaceAvailable
-
- /** Returns data that should be sent to this client.
- * The data is cleared when calling.
- */
- def dataToThisClient(): String =
- val a = this.outData
- this.outData = ""
- a
-
- /** Specifies that the data should be buffered for
- * sending to this client
- *
- * @param data data to buffer for sending
- */
- private def addDataToSend(data: String): Unit =
- 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] =
- 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 + 2)
- this.incompleteMessage = rest ++ Array.fill(nextCRLF + 1)(0.toByte)
- // TODO: the conversion may probably be exploited to crash the server
- Some(String(message))
- else
- None
-
- /** Makes the client play its turn */
- def act(): Unit =
- this.addDataToSend(ACTION_BLOCKING_INDICATOR.toString)
- this.nextAction.foreach(a => this.addDataToSend(
- s"$ACTION_BLOCKING_INDICATOR${this.executeAction(a)}"
- ))
- this.nextAction = None
-
- /** Checks whether the client has chosen its next action
- *
- * @return whether the client is ready to act */
- def isReadyToAct: Boolean = this.nextAction.isDefined
-
- /** Causes the client to interpret the data it has received */
- def interpretData(): Unit =
- LazyList.continually(this.nextLine())
- .takeWhile(_.isDefined)
- .flatten
- .foreach(s => interpretLine(s))
-
- /** Makes the client execute the action specified by `line`.
- * If there is a protocol error, the function changes
- * the variable `protocolIsIntact` to false.
- *
- * @param line the line to interpret
- */
- private def interpretLine(line: String): Unit =
- this.protocolIsIntact = this.protocolState match
- case WaitingForVersion =>
- if line == GAME_VERSION then
- addDataToSend(s"$PROTOCOL_VERSION_GOOD")
- this.protocolState = WaitingForClientName
- true
- else
- addDataToSend(s"$PROTOCOL_VERSION_BAD")
- false
- case WaitingForClientName =>
- this.name = Some(line)
- this.protocolState = WaitingForGameStart
- true
- case WaitingForGameStart => true
- case InGame =>
- this.bufferAction(Action(line))
- true
-
- /** Buffers the action for execution or executes it immediately if it
- * doesn't take a turn */
- private def bufferAction(action: Action) =
- if (
- this.nextAction.isEmpty &&
- this.player.exists(action.takesATurnFor(_))
- ) then
- this.nextAction = Some(action)
- else if this.nextAction.isEmpty then
- this.addDataToSend(s"$ACTION_NONBLOCKING_INDICATOR${this.executeAction(action)}")
-
- /** Executes the specified action and returns its description */
- private def executeAction(action: Action): String =
- this.character.flatMap(action.execute(_)) match
- case Some(s) => s
- case None => "You can't do that"
-
-
-end Client
-