aboutsummaryrefslogtreecommitdiff
path: root/src/scalevalapokalypsi/Server/Client.scala
diff options
context:
space:
mode:
Diffstat (limited to 'src/scalevalapokalypsi/Server/Client.scala')
-rw-r--r--src/scalevalapokalypsi/Server/Client.scala73
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) =>