diff options
Diffstat (limited to 'src/main/scala/Server/Client.scala')
-rw-r--r-- | src/main/scala/Server/Client.scala | 173 |
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 - |