As I mentioned in the previous blog post, I left some ugliness lying around in the code last time. The biggest problem in the last version of the code was that every time a player logged in, the Player actor iterated through the connected player list, accessed a variable specific to networking (the socket writer), and then sent text directly to that player. Not only did that violate encapsulation, but it violated the laws of good taste. It wasn’t elegant, simple, or clean enough for me. I had to fix this and I had to fix it now!

First, before I did the coupling fix, I extracted my code into multiple files. I now have an engine directory which contains the Player.scala and GameServer.scala files. I put them both in the com.kotancode.mud.engine package. This made me feel a little better – at least I was getting some proper organization out of this and I was no longer cramming everything into a single file.

Next, to address the encapsulation problem. After thinking about the problem for a little while I realized that the issue was one of pub/sub (publish/subscribe). I wanted the Player actors to simply publish the fact that they have logged in and then any interested party could then respond to that. Great, I’ll make the PlayerRegistry subscribe to a player login event and then publish that same event. The reason why I can’t have players subscribing to that event on other player objects is that players don’t know about other players (at least until after they’ve logged in) – it’s a chicken and the egg problem that only the singleton PlayerRegistry object can fix.

To give my Actors the ability to publish and subscribe, I used a Trait as a mixin called ActorPublisher (which you can find at this great blog post here by Jim McBeath). So now my engine directory has the following class files:

  • Player.scala
  • ActorPublisher.scala
  • GameServer.scala

GameServer has now become ridiculously small and the only change I’ve made is that instead of calling the message sent to the Player actor echo, I now call it StartTextProcessing. I love the fact that the act of reading commands from the player over a socket in a multi-threaded fashion is just taken care of me by the very nature of actors in Scala.

Let’s take a look at Player.scala, which now contains a revised Player actor that publishes notification of logins and subscribes to notifications of other players logging in from the PlayerRegistry object, which is acting as a multiplexer for this type of notification.

/*
 * com.kotancode.mud.engine
 *
 * Player class and related goodies
 */
package com.kotancode.mud.engine

import java.net._
import java.io._

import scala.actors._
import scala.actors.Actor._

case class StartTextProcessing(socket: Socket)

class Player extends Actor with Subscriber[PlayerMessage] with ActorPublisher[PlayerMessage] {
 var name = "";
 var inReader: BufferedReader = null
 var outWriter: PrintWriter = null

 implicit def inputStreamWrapper(in: InputStream) =
  new BufferedReader(new InputStreamReader(in))

 implicit def outputStreamWrapper(out: OutputStream) =
  new PrintWriter(new OutputStreamWriter(out))

 def processText(in: BufferedReader, out: PrintWriter) {
   inReader = in
   outWriter = out
   PlayerRegistry ! Subscribe(this.asInstanceOf[Subscriber[PlayerMessage]])
   out.println("\nWho be ye?")
   out.flush()
   name = in.readLine();
   out.println("\nWelcome to the KotanMUD, " + name)
   publish(PlayerLoggedInMessage(this.asInstanceOf[Player]))
   out.println("Other people currently playing:")

   PlayerRegistry.allPlayers.foreach( p => out.println(p.name) )
   out.flush()
   while (true) {
       val line = in.readLine()
       out.println(name + ": " + line)
       out.flush()
  }
 }

private val handleOther : PartialFunction[Any,Unit] = {
	case StartTextProcessing(socket) => actor { processText(socket.getInputStream(), socket.getOutputStream()) }
	case PlayerLoggedInMessage(player) => {
		if (this != player) {
			outWriter.println(player.name + " logged in.")
			outWriter.flush()
		}
	}
}

def act() {
	loop {
		react (handleSubscribe orElse handleOther)
	}
}
}

sealed abstract class PlayerMessage
case class PlayerLoggedInMessage(source:Player) extends PlayerMessage

object PlayerRegistry extends Actor with Subscriber[PlayerMessage] with ActorPublisher[PlayerMessage]
{
	start
	var allPlayers = List[Player]()
	def getPlayer() : Player = {
		val newPlayer = new Player()
		allPlayers ::= newPlayer
		newPlayer ! Subscribe(this.asInstanceOf[Subscriber[PlayerMessage]])
		newPlayer
	}

	private val handleMessage : PartialFunction[Any, Unit] = {
		case m:PlayerLoggedInMessage => {
			println(m.source.name + " logged in.") // output to console
   			publish(PlayerLoggedInMessage(m.source)) // republish the message
			}
	}

	def act() {
		loop {
			react (handleSubscribe orElse handleMessage)
		}
	}
}

So now I will compile my new MUD with the following command (as I add files it adds a bit more complexity to my ability to rapidly run my MUD… at some point I’m going to have to upgrade to a build tool to automate this… but that’s a topic for another post):

scalac engine/ActorPublisher.scala engine/Player.scala engine/GameServer.scala

And then I run my MUD with:

scala com.kotancode.mud.engine.GameServer

I can now verify that I am able to handle incoming connections the same way I used to, the multi-threading and thread-local storage is all working properly and most importantly, I am now able to let other players know that a player has logged in using a more elegant publish/subscribe approach.