In the previous post, I explained a little bit about my reasoning behind experimenting with a MUD as a way of learning a new language. As a result, I’ve been poking around a little with Scala. Whenever you’re learning a new language it helps to have a goal in mind rather than building a bunch of disparate “hello world” samples. My goal is a MUD.

Last time I build a telnet server that prompts the user for their name and then acts as an echo server thereafter. In this post, I’m going to experiment with how I can add some global state. I want to be able to keep track of who is currently logged into the MUD. In addition, I want to be able to send messages to all connected players and let them know that another player has connected.

To do this, I’m going to modify my original Scala script from the previous post to make it look like the following:

/*
 * Pathetically simple Scala echo server
 * Step 2 on the road to making a MUD - maintaining player list
 */
import java.net._
import java.io._

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

case class Echo(socket: Socket)

class Player extends Actor {
 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 echo(in: BufferedReader, out: PrintWriter) {
   inReader = in
   outWriter = out
   out.println("Who be ye?")
   out.flush()
   name = in.readLine();
   out.println("Welcome to the Echo Server, " + name)
   out.println("Other people currently playing:")
   PlayerRegistry.allPlayers.foreach( p=>
	{
		out.println(p.name)
		if (p.name != name)
			p.outWriter.println(name + " just logged in.")
		p.outWriter.flush()
	})
   out.flush()
   while (true) {
       val line = in.readLine()
       out.println(name + ": " + line)
       out.flush()
  }
 }

 def act() {
  loop {
   receive {
    case Echo(socket) =>
     actor {
      echo(socket.getInputStream(), socket.getOutputStream())
     }
   }
  }
 }

}

object PlayerRegistry
{
	var allPlayers = List[Player]()
	def getPlayer() : Player = {
		val newPlayer = new Player()
		allPlayers ::= newPlayer
		newPlayer
	}
}
// Singleton object for the echo server.
object EchoServer {
 val serverSocket = new ServerSocket(8888)

 def start() {
  while(true) {
   println("Awaiting connection...")
   val clientSocket = serverSocket.accept()
//   val player = new Player()
   val player = PlayerRegistry.getPlayer()
   player.start
   player ! Echo(clientSocket)
   println("Spun off echo handler for player")
  }
 }
}

EchoServer.start

The new code here is the PlayerRegistry object. Object’s in Scala are a first-class implementation of the singleton pattern. Simply by declaring something an ‘object’ rather than a ‘class’ Scala automatically takes care of the plumbing involved in setting that up as a singleton. The PlayerRegistry singleton contains a method called getPlayer, which is responsible for instantiating a new player as well as adding that new player object to a list. Note that as we change properties on the player object, that will be reflected in the global player list because we’re making changes to the same object (aren’t pointers awesome?).

The logic behind what I’ve done is pretty simple. The PlayerRegistry singleton object keeps track of all players currently in the game. When someone logs in, I iterate through the list of players. If the player object in the current iteration is not the player doing the logging in, I send the “(player) logged in.” message.

Veteran programmers and programmers with some measure of good taste will realize that I’m violating some patterns here. When a player logs in, I shouldn’t be looping through the player list and manually doing the notifications. What I should be doing is sending messages and letting the player object handle the message accordingly. I’ll take care of refactoring this in the next blog post. For now, take a look at this screenshot from a terminal session logged into my ‘game’ server:

Using My Scala MUD

Using My Scala MUD