diff options
Diffstat (limited to 'src/scalevalapokalypsi/Server')
-rw-r--r-- | src/scalevalapokalypsi/Server/Client.scala | 73 | ||||
-rw-r--r-- | src/scalevalapokalypsi/Server/Server.scala | 14 |
2 files changed, 63 insertions, 24 deletions
diff --git a/src/scalevalapokalypsi/Server/Client.scala b/src/scalevalapokalypsi/Server/Client.scala index 5727fc6..940888b 100644 --- a/src/scalevalapokalypsi/Server/Client.scala +++ b/src/scalevalapokalypsi/Server/Client.scala @@ -8,6 +8,22 @@ import ServerProtocolState.* import scalevalapokalypsi.Model.Action import scalevalapokalypsi.Model.Entities.Player import java.lang.System.currentTimeMillis +import scala.collection.mutable.Buffer + + +// Note to graders etc +// This class has an interesting design choice: +// It does not write data directly to the client. +// This is because with some multithreaded implementation of +// Server.scala it could lead to race conditions etc where +// several writes happened at once. +// +// Thus, this class never writes to the client anything, and +// all TCP communication is the responsibility of the server. +// Because of lack of time though, I had to rely on a very dirty +// trick - the client has a workaround way to actually write data +// to the client by queuing it to the server via the method +// `dataToThisClient`. class Client(val socket: Socket): private var incompleteMessage: Array[Byte] = @@ -21,12 +37,31 @@ class Client(val socket: Socket): private var nextAction: Option[Action] = None private var turnUsed = false private var singStartTime: Option[Long] = None - private var verseToSing: String = "" + private var versesToSing: Vector[String] = Vector.empty + private val versesSung: Buffer[String] = Buffer.empty + private var verseIndex = 0 def clientHasSong = this.singStartTime.isDefined - def startSong(verse: String): Unit = - this.verseToSing = verse - this.singStartTime = Some(currentTimeMillis() / 1000) + def startSongIfNeeded(): Unit = + if this.player.exists(_.isSinging) && !this.clientHasSong then + val verses = this.player.flatMap(_.getVerses) + verses.foreach(v => + this.versesToSing = v + this.verseIndex = 0 + this.singStartTime = Some(currentTimeMillis() / 1000) + this.startVerse() + ) + + /** Starts the next verse for the remote client, + * use only when you have checked that there are verses left to sing! + */ + def startVerse(): Unit = + val verse = this.versesToSing.lift(this.verseIndex) + verse.foreach(v => + this.addDataToSend(s"${SING_INDICATOR}$v") + ) + this.verseIndex += 1 + /** Calculates the amount of bytes available for future incoming messages */ def spaceAvailable: Int = this.incompleteMessage.size - incompleteMessageIndex - 1 @@ -166,17 +201,27 @@ class Client(val socket: Socket): if !this.turnUsed then this.singStartTime match case Some(t) => - - val timePassed = currentTimeMillis()/1000 - t - - val quality = if line == this.verseToSing then - 5.0 / max(5.0, timePassed.toDouble) + if this.verseIndex == this.versesToSing.length then + val timeQuality = + 5.0*this.versesToSing.length / + max(5.0, currentTimeMillis()/1000 - t) + + val songToSing = this.versesToSing.mkString("") + .toLowerCase + val songSung = this.versesSung.mkString("").toLowerCase + val quality = + timeQuality * ( + 1.0 - hammingDistance(songToSing, songSung) + .toFloat + / songToSing.length.toFloat + ) + + this.player.foreach(_.applySingEffect(quality.toFloat)) + + this.singStartTime = None else - 0.0 - - this.player.foreach(_.applySingEffect(quality.toFloat)) - - this.singStartTime = None + this.versesSung += line + this.startVerse() case None if isPrintable(line) => diff --git a/src/scalevalapokalypsi/Server/Server.scala b/src/scalevalapokalypsi/Server/Server.scala index 16a2128..6bf21d4 100644 --- a/src/scalevalapokalypsi/Server/Server.scala +++ b/src/scalevalapokalypsi/Server/Server.scala @@ -51,8 +51,8 @@ class Server( this.receiveNewClient() this.readFromAll() this.clients.foreach(_.interpretData()) - this.writeClientDataToClients() this.makeClientsSing() + this.writeClientDataToClients() this.writeObservations() this.endGameForDeadClients() if this.canExecuteTurns then @@ -61,7 +61,7 @@ class Server( this.clients.foreach(c => this.writeToClient(this.turnStartInfo(c), c) ) - this.previousTurn = currentTimeMillis() / 1000 + this.previousTurn = currentTimeMillis().toDouble / 1000.0 if this.adventure.isDefined && this.joinAfterStart then this.clients.foreach( c => if c.isReadyForGameStart then this.adventure.foreach(a => @@ -76,7 +76,7 @@ class Server( then this.adventure = Some(Adventure(this.clients.names)) this.clients.foreach(startGameForClient(_)) - this.previousTurn = currentTimeMillis() / 1000 + this.previousTurn = currentTimeMillis().toDouble / 1000.0 /** Helper function to start the game for the specified client c. * MAY ONLY BE USED IF `this.adventure` is Some! @@ -120,13 +120,7 @@ class Server( private def makeClientsSing(): Unit = this.clients.foreach(c => - val target = c.player.flatMap(_.getSingEffectTarget) - target.foreach(t => - if c.player.exists(_.isSinging) && !c.clientHasSong then - val verse = t.getVerseAgainst - this.writeToClient(s"${SING_INDICATOR}$verse\r\n", c) - c.startSong(verse) - ) + c.startSongIfNeeded() ) /** Helper function to determine if the next turn can be taken */ |