aboutsummaryrefslogtreecommitdiff
path: root/src/main/scala/Client
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/scala/Client')
-rw-r--r--src/main/scala/Client/Client.scala178
-rw-r--r--src/main/scala/Client/ReceivedLineParser.scala27
-rw-r--r--src/main/scala/Client/StdinLineReader.scala31
-rw-r--r--src/main/scala/Client/Turn.scala32
4 files changed, 0 insertions, 268 deletions
diff --git a/src/main/scala/Client/Client.scala b/src/main/scala/Client/Client.scala
deleted file mode 100644
index fc3e6b8..0000000
--- a/src/main/scala/Client/Client.scala
+++ /dev/null
@@ -1,178 +0,0 @@
-package o1game.Client
-
-import java.lang.Thread.sleep
-import java.net.Socket
-import scala.io.Source
-import scala.sys.process.stdout
-import o1game.constants.*
-import o1game.utils.{stringToByteArray,getNCharsFromSocket}
-import o1game.Client.{ReceivedLineParser,StdinLineReader,Turn}
-import scala.concurrent.Future
-import scala.concurrent.ExecutionContext.Implicits.global
-import scala.util.{Try, Success, Failure}
-import scala.collection.mutable.Buffer
-import java.lang.System.currentTimeMillis
-
-
-/** A helper enum for `Client` to keep track of communications with the server
- */
-enum ServerLineState:
- case WaitingForTimeLimit,
- ActionDescription,
- TurnIndicator,
- AreaDescription,
- Directions,
- Items,
- Entities
-
-
-/** Creates a new client.
- *
- * @param name the name the client and its player should have
- * @ip the ip of the server to connect to
- * @port the port of the server to connect to
- * @return the client created, if all was successful
- */
-def newClient(name: String, ip: String, port: Int): Option[Client] =
- val socket = Socket(ip, port)
- val output = socket.getOutputStream
- val input = socket.getInputStream
- val initMsg = s"$GAME_VERSION\r\n$name\r\n"
- output.write(stringToByteArray(initMsg))
- val msgLen = (PROTOCOL_VERSION_GOOD + "\r\n").length
- val versionResponse = getNCharsFromSocket(input, msgLen)
- if versionResponse == Some(s"$PROTOCOL_VERSION_GOOD\r\n") then
- Some(Client(socket))
- else
- None
-
-
-
-/** Main class for the client: handles communication with the server
- * and the player. Should be initialized with `newClient`.
- *
- * @param socket the socket the client uses
- */
-class Client(socket: Socket):
-
- /** Essential IO variables */
- private val input = socket.getInputStream
- private val output = socket.getOutputStream
- private val buffer: Array[Byte] = Array.ofDim(MAX_MSG_SIZE)
- private var bufferIndex = 0
- private val serverLineParser = ReceivedLineParser()
- private val stdinReader = StdinLineReader()
-
- private var serverLineState = ServerLineState.WaitingForTimeLimit
-
- /** Variables about the status of the current turn for the client */
- private var canAct = false
- private var timeLimit: Long = 0
- private var lastTurnStart: Long = 0
- private var lastExecutedTurn: Long = 0
- assert(
- lastTurnStart <= lastExecutedTurn,
- "don't initialize with unexecuted turn"
- )
- private val turnInfo = Turn()
-
-
- /** Starts the client. This shouldn't terminate. */
- def startClient(): Unit =
-
- stdinReader.startReading()
-
- while true do
- sleep(POLL_INTERVAL)
-
- this.readAndParseDataFromServer()
-
- if this.lastExecutedTurn < this.lastTurnStart then
- print(this.giveTurn())
-
- stdinReader.newLine().foreach((s: String) =>
- output.write(stringToByteArray(s+"\r\n"))
- )
-
- end startClient
-
-
- private def readAndParseDataFromServer(): Unit =
- var availableBytes = input.available()
- while availableBytes != 0 do
- val bytesRead = input.read(buffer, 0, availableBytes)
- if bytesRead != -1 then
- // TODO: unsafe conversion
- parseDataFromServer(buffer.take(bytesRead))
- availableBytes = input.available()
-
- private def giveTurn(): String =
- this.canAct = true
- this.lastExecutedTurn = currentTimeMillis / 1000
- s"\n\n${this.turnInfo}\n${this.actionGetterIndicator}"
-
- private def displayAction(action: String): Unit =
- println(s"$action")
- if this.canAct then
- print(this.actionGetterIndicator)
-
- private def actionGetterIndicator =
- val timeOfTurnEnd = this.lastTurnStart + this.timeLimit
- val timeToTurnEnd = -currentTimeMillis()/1000 + timeOfTurnEnd
- s"[$timeToTurnEnd]> "
-
-
-
- private def parseDataFromServer(data: Array[Byte]): Unit =
- this.serverLineParser.in(data)
- var nextLine: Option[String] = Some("")
- while nextLine.isDefined do
- nextLine = this.serverLineParser
- .nextLine()
- nextLine
- .foreach(this.parseLineFromServer(_))
-
-
- private def parseLineFromServer(line: String) =
-
- if line == TURN_INDICATOR then
- this.serverLineState = ServerLineState.TurnIndicator
-
- serverLineState match
-
- case ServerLineState.WaitingForTimeLimit =>
- val time = line.toLongOption
- time match
- case Some(t) => this.timeLimit = t
- case None => print("Invalid time limit, oh no!!!")
- this.serverLineState = ServerLineState.TurnIndicator
- this.lastTurnStart = currentTimeMillis / 1000
-
- case ServerLineState.ActionDescription =>
- if line.nonEmpty && line.head == ACTION_BLOCKING_INDICATOR then
- this.canAct = false
- this.displayAction(line.tail)
-
- case ServerLineState.TurnIndicator =>
- this.serverLineState = ServerLineState.AreaDescription
-
- case ServerLineState.AreaDescription =>
- this.turnInfo.areaDescription = line
- this.serverLineState = ServerLineState.Directions
-
- case ServerLineState.Directions =>
- this.turnInfo.possibleDirections = line.split(LIST_SEPARATOR)
- this.serverLineState = ServerLineState.Items // TODO: maybe use a list instead?
-
- case ServerLineState.Items =>
- this.turnInfo.visibleItems = line.split(LIST_SEPARATOR)
- this.serverLineState = ServerLineState.Entities
-
- case ServerLineState.Entities =>
- this.turnInfo.visibleEntities = line.split(LIST_SEPARATOR)
- this.serverLineState = ServerLineState.ActionDescription
- this.lastTurnStart = currentTimeMillis() / 1000
-
- end parseLineFromServer
-
-end Client
diff --git a/src/main/scala/Client/ReceivedLineParser.scala b/src/main/scala/Client/ReceivedLineParser.scala
deleted file mode 100644
index 7cbf935..0000000
--- a/src/main/scala/Client/ReceivedLineParser.scala
+++ /dev/null
@@ -1,27 +0,0 @@
-package o1game.Client
-
-import scala.collection.mutable.Buffer
-import o1game.constants.*
-
-/** A class for checking asynchronously for received lines */
-class ReceivedLineParser:
-
- private var serverLineState = ServerLineState.ActionDescription
-
- private var bufferedData: Buffer[Byte] = Buffer.empty // TODO: suboptimal DS
-
- /** Add received data */
- def in(data: Array[Byte]): Unit =
- this.bufferedData ++= data
-
- /** Read a line from the received data */
- def nextLine(): Option[String] =
- val indexOfCRLF = this.bufferedData.indexOfSlice(CRLF)
- if indexOfCRLF == -1 then
- None
- else
- val splitData = this.bufferedData.splitAt(indexOfCRLF)
- this.bufferedData = Buffer.from(splitData(1).drop(CRLF.length))
- Some(String(splitData(0).toArray))
-
-end ReceivedLineParser
diff --git a/src/main/scala/Client/StdinLineReader.scala b/src/main/scala/Client/StdinLineReader.scala
deleted file mode 100644
index 42a1f40..0000000
--- a/src/main/scala/Client/StdinLineReader.scala
+++ /dev/null
@@ -1,31 +0,0 @@
-package o1game.Client
-
-import scala.concurrent.Future
-import scala.concurrent.ExecutionContext.Implicits.global
-import scala.io.StdIn.readLine
-import scala.util.{Try, Success, Failure}
-
-/** This class is for taking new lines from stdin when they are available.
- * reading starts when either newLine or clear or startReading are called.
- */
-class StdinLineReader:
-
- private var nextLine: Future[String] = Future.failed(Exception())
-
- /** Returns a new line of input if there are any. */
- def newLine(): Option[String] =
- this.nextLine.value match
- case Some(Success(s)) =>
- this.startReading()
- Some(s)
- case Some(Failure(e)) =>
- this.startReading()
- None
- case None => None
-
- /** Discards the line that is currently being read and restarts reading */
- def startReading(): Unit =
- this.nextLine = Future(readLine())
-
-
-end StdinLineReader
diff --git a/src/main/scala/Client/Turn.scala b/src/main/scala/Client/Turn.scala
deleted file mode 100644
index 6b78811..0000000
--- a/src/main/scala/Client/Turn.scala
+++ /dev/null
@@ -1,32 +0,0 @@
-package o1game.Client
-
-/** `Turn`s represent information the client has got about a turn.
- * This class exists essentially so that the client has somewhere
- * to store data about turns and something to format that data with.
- */
-class Turn:
-
- /** Description of the area the player controlled by the client is in
- * at the end of the turn. */
- var areaDescription: String = ""
-
- /** Directions the player controlled by the client can go to. */
- var possibleDirections: Array[String] = Array.empty
-
- /** Items the player controlled by the client can see. */
- var visibleItems: Array[String] = Array.empty
-
- /** Entities the player controlled by the client can see. */
- var visibleEntities: Array[String] = Array.empty
-
- override def toString: String =
- val itemDesc = "You can see the following items: " +
- this.visibleItems.mkString(", ")
- val entityDesc = "The following entities reside in the room: " +
- this.visibleEntities.mkString(", ")
- val directionDesc = "There are exits to " +
- this.possibleDirections.mkString(", ")
- (s"$areaDescription\n$directionDesc\n" +
- s"\n$itemDesc\n$entityDesc")
-
-end Turn