diff options
Diffstat (limited to 'src/scalevalapokalypsi/Server/Client.scala')
-rw-r--r-- | src/scalevalapokalypsi/Server/Client.scala | 73 |
1 files changed, 59 insertions, 14 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) => |