Browse Source

Redo chat protocol

Thomas Dy 11 years ago
parent
commit
7f1ebef0a1
4 changed files with 190 additions and 84 deletions
  1. 15 39
      app/models/ChatRoom.scala
  2. 87 39
      app/models/Taboo.scala
  3. 75 3
      public/javascripts/chatRoomNg.js
  4. 13 3
      public/partials/chatRoom.html

+ 15 - 39
app/models/ChatRoom.scala

@@ -70,67 +70,43 @@ class ChatRoom extends Actor {
         val (personalEnumerator, personalChannel) = Concurrent.broadcast[JsValue]
         members = members + (username -> personalChannel)
         sender ! Connected(chatEnumerator.interleave(personalEnumerator))
-        self ! NotifyJoin(username)
         tabooGame ! Join(username)
       }
     }
 
-    case NotifyJoin(username) => {
-      notifyAll("join", username, "has entered the room")
-    }
-
     case Talk(username, text) => {
-      notifyAll("talk", username, text)
+      if(!text.startsWith("/")) {
+        self ! Announce(Json.obj(
+          "kind" -> "talk",
+          "user" -> username,
+          "message" -> text
+        ))
+      }
       tabooGame ! Talk(username, text)
     }
 
-    case Tell(username, text, to) => {
-      notifyOne(to, "talk", username, text)
+    case Announce(message) => {
+      chatChannel.push(message)
+    }
+
+    case Tell(username, message) => {
+      members(username).push(message)
     }
 
     case Quit(username) => {
       members = members - username
-      notifyAll("quit", username, "has left the room")
       tabooGame ! Quit(username)
     }
 
   }
 
-  def notifyOne(to: String, kind: String, user: String, text: String) {
-    val msg = JsObject(
-      Seq(
-        "kind" -> JsString(kind),
-        "user" -> JsString(user),
-        "message" -> JsString(text),
-        "members" -> JsArray(
-          members.keys.toList.map(JsString)
-        )
-      )
-    )
-    members(to).push(msg)
-  }
-
-  def notifyAll(kind: String, user: String, text: String) {
-    val msg = JsObject(
-      Seq(
-        "kind" -> JsString(kind),
-        "user" -> JsString(user),
-        "message" -> JsString(text),
-        "members" -> JsArray(
-          members.keys.toList.map(JsString)
-        )
-      )
-    )
-    chatChannel.push(msg)
-  }
-
 }
 
 case class Join(username: String)
 case class Quit(username: String)
 case class Talk(username: String, text: String)
-case class Tell(username: String, text: String, to: String)
-case class NotifyJoin(username: String)
+case class Announce(value: JsValue)
+case class Tell(username: String, value: JsValue)
 
 case class Connected(enumerator:Enumerator[JsValue])
 case class CannotConnect(msg: String)

+ 87 - 39
app/models/Taboo.scala

@@ -3,9 +3,19 @@ package models
 import akka.actor._
 import scala.concurrent.duration._
 import play.api.libs.concurrent._
+import play.api.libs.json._
 import play.api.Play.current
 import play.api.libs.concurrent.Execution.Implicits._
 
+object Card {
+  implicit val writes = new Writes[Card] {
+    def writes(o: Card) = Json.obj(
+      "word" -> o.word,
+      "taboo" -> o.taboo
+    )
+  }
+}
+
 case class Card(word: String, taboo: Set[String]) {
 
   def isTaboo(text: String) = {
@@ -24,6 +34,16 @@ case class Card(word: String, taboo: Set[String]) {
 
 }
 
+object Team {
+  implicit val writes = new Writes[Team] {
+    def writes(o: Team) = Json.obj(
+      "members" -> o.members,
+      "player" -> o.player,
+      "guessers" -> o.guessers
+    )
+  }
+}
+
 class Team {
   var members = List.empty[String]
   var player = ""
@@ -53,23 +73,29 @@ class Team {
   def size = members.size
 }
 
+object Round {
+  implicit val writes = new Writes[Round] {
+    def writes(o: Round) = Json.obj(
+      "team" -> o.team,
+      "monitors" -> o.monitors
+    )
+  }
+}
+
 case class Round(team: Team, monitors: Set[String])
 
 case class Information(text: String)
 case class Guess(username: String, text: String)
 case object Pass
 case class Taboo(username: String)
+case object End
 
-case class Correct(username: String, card: Card)
-case class Invalid(card: Card)
-case class Passed(card: Card)
-case class Tabooed(username: String, card: Card)
+case class Score(kind: String, points: Int, card: Card, username: String = "")
 
 case object NextCard
 case object PrepRound
 case object StartRound
-case object End
-case class Points(points: Int)
+case class EndRound(points: Int, card: Option[Card])
 
 class TabooGame(val chatActor: ActorRef) extends Actor {
   var ready = false
@@ -90,6 +116,7 @@ class TabooGame(val chatActor: ActorRef) extends Actor {
       else {
         teamB.join(username)
       }
+      announceStatus("join", username)
       if(round.isEmpty) {
         self ! PrepRound
       }
@@ -97,10 +124,18 @@ class TabooGame(val chatActor: ActorRef) extends Actor {
     case Quit(username) =>
       teamA.leave(username)
       teamB.leave(username)
-      if(!round.isEmpty && (teamA.size < 2 || (currentTeam == teamB && teamB.size < 2))) {
-        roundActor ! End
+      announceStatus("quit", username)
+      if(teamA.size < 2 || currentTeam == teamB && teamB.size < 2) {
+        if(ready) {
+          ready = false
+        }
+        if(!round.isEmpty) {
+          roundActor ! End
+        }
       }
 
+    case Talk(username, "/status") => announceStatus()
+
     case Talk(username, text) => round match {
       case Some(round) =>
         if(username == round.team.player) {
@@ -124,33 +159,26 @@ class TabooGame(val chatActor: ActorRef) extends Actor {
         }
     }
 
-    case Correct(username, card) =>
-      nextCard(username+" got it right!", card)
-
-    case Invalid(card) =>
-      nextCard("Uh-uh! "+player+" said a taboo word. Sorry.", card)
-
-    case Passed(card) =>
-      nextCard(player+" has passed.", card)
-
-    case Tabooed(username, card) =>
-      nextCard("Oh no! "+player+" apparently said a taboo word. :(", card)
-
-    case Points(points) =>
-      chatActor ! Talk("*GM", "The round is over. The team got "+points)
-      round = None
-      ready = false
-      self ! PrepRound
+    case Score(kind, points, card, user) =>
+      chatActor ! Announce(Json.obj(
+        "kind" -> "point",
+        "action" -> kind,
+        "card" -> card,
+        "user" -> user
+      ))
+      self ! NextCard
 
     case NextCard =>
       val card = randomCard()
       roundActor ! card
 
+      val message = Json.obj(
+        "kind" -> "card",
+        "card" -> card
+      )
+
       (round.get.monitors + player).foreach { user =>
-        chatActor ! Tell(
-          "*GM",
-          "The word is "+card.word+". The taboo words are: "+card.taboo.reduceLeft(_+" "+_),
-          user)
+        chatActor ! Tell(user, message)
       }
 
     case PrepRound =>
@@ -168,24 +196,44 @@ class TabooGame(val chatActor: ActorRef) extends Actor {
         }
         currentTeam.nextPlayer()
 
-        chatActor ! Talk("*GM", "Next round, the player will be "+currentTeam.player)
-        chatActor ! Tell("*GM", "Type /start to start the round", currentTeam.player)
+        chatActor ! Announce(Json.obj(
+          "kind" -> "roundReady",
+          "player" -> currentTeam.player
+        ))
       }
 
     case StartRound =>
       round = Some(Round(currentTeam, opposingTeam.members.toSet))
       roundActor = context.actorOf(Props[TabooRound])
       Akka.system.scheduler.scheduleOnce(1 minute, roundActor, End)
+      chatActor ! Announce(Json.obj(
+        "kind" -> "roundStart"
+      ))
       self ! NextCard
+
+    case EndRound(points, card) =>
+      chatActor ! Announce(Json.obj(
+        "kind" -> "roundEnd",
+        "points" -> points,
+        "card" -> card
+      ))
+      round = None
+      ready = false
+      self ! PrepRound
+
   }
 
   def player = round.get.team.player
 
   def randomCard() = Card("test", Set("a", "b", "c", "d", "e"))
 
-  def nextCard(message: String, oldCard: Card) = {
-    chatActor ! Talk("*GM", message+" The word was "+oldCard.word+".")
-    self ! NextCard
+  def announceStatus(kind: String = "status", user: String = "*GM") {
+    chatActor ! Announce(Json.obj(
+      "kind" -> kind,
+      "user" -> user,
+      "teamA" -> teamA,
+      "teamB" -> teamB
+    ))
   }
 
   self ! PrepRound
@@ -202,7 +250,7 @@ class TabooRound extends Actor {
     case Guess(username, text) => card.map { card =>
       if(card.isCorrect(text)) {
         points += 1
-        sender ! Correct(username, card)
+        sender ! Score("correct", points, card, username)
         this.card = None
       }
     }
@@ -210,25 +258,25 @@ class TabooRound extends Actor {
     case Information(text) => card.map { card =>
       if(card.isTaboo(text)) {
         points -= 1
-        sender ! Invalid(card)
+        sender ! Score("invalid", points, card)
         this.card = None
       }
     }
 
     case Pass => card.map { card =>
       points -= 1
-      sender ! Passed(card)
+      sender ! Score("pass", points, card)
       this.card = None
     }
 
     case Taboo(username) => card.map { card =>
       points -= 1
-      sender ! Tabooed(username, card)
+      sender ! Score("taboo", points, card)
       this.card = None
     }
 
     case End =>
-      sender ! Points(points)
+      sender ! EndRound(points, card)
       context.stop(self)
 
   }

+ 75 - 3
public/javascripts/chatRoomNg.js

@@ -6,7 +6,8 @@ angular.module('tabooServices', [])
   var service = {
     username: '',
     messages: [],
-    members: [],
+    teamA: {},
+    teamB: {},
     error: null,
     isConnected: function() {
       return this.username != '';
@@ -16,6 +17,9 @@ angular.module('tabooServices', [])
   service.connect = function(username) {
     chatSocket = new WS(jsRoutes.controllers.Application.chat(username).webSocketURL());
     chatSocket.onmessage = onEvent;
+    chatSocket.onopen = function() {
+      service.send("/status");
+    }
     service.username = username;
   }
 
@@ -33,16 +37,84 @@ angular.module('tabooServices', [])
 
   function onEvent(event) {
     var message = JSON.parse(event.data);
+    console.log(message);
     if(message.error) {
       service.error = message;
     }
-    else {
+    else if(message.kind == "talk") {
       service.messages.push(message);
-      service.members = message.members;
+    }
+    else if(message.kind == "join") {
+      addMessage("join", message.user, " has joined.");
+    }
+    else if(message.kind == "quit") {
+      addMessage("quit", message.user, " has left.");
+    }
+    else if(message.kind == "point") {
+      var text = "";
+
+      if(message.action == "correct") {
+        text = message.user + " got it!";
+      }
+      else if(message.action == "invalid") {
+        text = "Uh-uh! You said a taboo word.";
+      }
+      else if(message.action == "pass") {
+        text = "Tsk tsk. You passed.";
+      }
+      else if(message.action == "taboo") {
+        text = "Oh no! "+message.user+" has called you out.";
+      }
+
+      text += " The last word was "+message.card.word+".";
+      gmMessage(text);
+      service.points = message.points;
+    }
+    else if(message.kind == "roundReady") {
+      gmMessage("Next round, the player will be "+message.player);
+      if(message.player == service.username) {
+        service.startReady = true;
+      }
+    }
+    else if(message.kind == "roundStart") {
+      gmMessage("Start game!");
+      service.startReady = false;
+      service.startTime = new Date();
+      service.points = 0;
+    }
+    else if(message.kind == "roundEnd") {
+      if(message.card) {
+        gmMessage("Time's up! The last word was "+message.card.word+".");
+      }
+      gmMessage("The round has ended. The team got "+message.points+".");
+      service.startReady = false;
+    }
+    else if(message.kind == "card") {
+      service.card = message.card;
+    }
+    else if(message.kind == "status") {
+      updateStatus(message);
     }
     $rootScope.$apply();
   }
 
+  function updateStatus(message) {
+    service.teamA = message.teamA;
+    service.teamB = message.teamB;
+  }
+
+  function addMessage(kind, user, message) {
+    service.messages.push({
+      kind: kind,
+      user: user,
+      message: message
+    });
+  }
+
+  function gmMessage(message) {
+    addMessage("talk", "*GM", message);
+  }
+
   return service;
 });
 

+ 13 - 3
public/partials/chatRoom.html

@@ -20,9 +20,19 @@
       <textarea ng-model="text" id="talk" ng-keypress="onType($event)"></textarea>
     </div>
     <div class="span4">
-      <h2>Members</h2>
-      <ul id="members">
-        <li ng-repeat="member in service.members">{{member}}</li>
+      <button class="btn" ng-show="service.startReady" ng-click="service.send('/start')">Start</button>
+      <h2>Card</h2>
+      <h3>{{service.card.word}}</h3>
+      <ul class="taboo">
+        <li ng-repeat="word in service.card.taboo">{{word}}</li>
+      </ul>
+      <h2>Team A</h2>
+      <ul class="members">
+        <li ng-repeat="member in service.teamA.members">{{member}}</li>
+      </ul>
+      <h2>Team B</h2>
+      <ul class="members">
+        <li ng-repeat="member in service.teamB.members">{{member}}</li>
       </ul>
     </div>
   </div>