SmartMonsters

Saturday, December 14, 2013

Why Not Telnet? 'Cos grid computing.

TriadCity being text-based, it would have been possible to design its server to communicate with clients via the standard telnet protocol. This is the strategy taken by classical MUDs including DikuMUD and CircleMUD, and by many popular proprietary MUDs today. It allows players to use their choice of free telnet clients, including ones optimized for the most common MUD experiences such as command aliasing and so on.

Our choice to write proprietary clients for TriadCity is determined by TC's scale and complexity. Unlike conventional MUDs, TC's server has many hundreds of thousands of AI decisions to compute from moment to moment, determining NPC actions and generating the often complex subjectivities displayed to human players. We use proprietary clients to distribute some of the processing. TriadCity is less like old-school client-server with a smart server and dumb clients, and more like a distributed computational grid capitalizing on the horsepower made available by players. These are some of the tasks performed by clients:

  • "Natural language" processing. We use a two-stage parser, where the first and more computationally demanding step lives in the clients. Clients send only perfectly-formed, canonical commands over the wire, performing the heavy lifting so the server doesn't have to. While the processing requirement for a single user command is not burdensome, offloading that requirement allows the server to scale more easily to MMORPG levels.
  • Distributed arithmetic. Wherever sustained computation is required — for example, 2D vector arithmetic for chariot races — portions of those computations can be parallelized and distributed. Think SETI@Home. In that regard the TriadCity client/server architecture is something like a single distributed computer.
  • Character state management. Housekeeping such as hunger and thirst calculations, attribute computations, and consciousness state can live happily in the clients. Much of the arithmetic driving character subjectivity and situational capability can be offloaded.
  • Network encryption. Telnet's not secure. TC's clients encrypt network I/O, so your password is safe at Starbucks.
  • Firewall immunity. TC's clients allow connection through company firewalls, so we can happily diminish players' productivity at work. Telnet usually doesn't.
  • Bandwidth optimizing. TC's clients transmit only when the communication is guaranteed to be correctly formed. The server doesn't have to invest cycles processing garbage.
  • Integrated support for assistive technologies. Our HTML5 client implements WEI-ARIA Live Regions; the Java applet has an integrated screen reader.
  • Additional security measures. Because we control the clients, it's easy to know when Bad Guys On The Net are trying to poke the server. Swish-and-flick! — into the blacklist bin with them. Easy-peasy.

Most of this architectural logic is about helping the server scale to MMORPG levels without having to shard the game world or add other complexity. We routinely test with 10k simulated players, and despite its significant AI computational load the server does just fine.

Of course, we don't have 10k simultaneous players in real life. Invite your friends.

Neo4j Graph 3

Friday, December 13, 2013

Scalable Command Routing Via the Strategy and HashedAdaptor OO Design Patterns

TriadCity's command vocabulary is far larger than the 400 or so commands typed by players.  Every Admin and Builder action is implemented as a command — there are tens of thousands altogether.

In C, you'd probably route user input through a simple switch() statement. CircleMUD does this.  The parser and command router are tightly coupled inside one master controlling statement which switch()es on the user's first word of input. Logic can be parceled out to functions, or handled right there inside the switch() statement itself. This is fine for a couple of hundred commands, but growing it to tens of thousands would be a performance and maintenance disaster.

We ensure infinite command routing scalability for TriadCity by combining the Strategy and Hashed Adapter OO patterns.

Strategy encapsulates an algorithm as an object, allowing algorithms to be selected and substituted at runtime. In Java you'll typically implement Strategy via an Interface defining the algorithm's contract. Classes implementing that Interface define the available algorithms. In TriadCity we have a Command Interface defining our Strategy contract:

public Interface Command {
          public abstract boolean doCommand(<multile-args-go-here>);
}

And we have many thousands of classes implementing that Interface, for example, Chat, which implements the doCommand() method to iterate all NPCs and logged-in Players, sending the arguments to the chat channel.

The Hashed Adapter pattern achieves scalable routing by stashing these concrete Command instances in a HashMap keyed by command name. Each concrete Command is a Singleton; we populate the map with command names as keys, instantiated Command Singletons as values. The resulting routing algorithm is super-simple:

Command command = this.commands.get(commandName);
command.doCommand(<multiple-args-passed-here>);

There's other logic wrapped around these lines, for example authorization checking for security. But that's the idea.

By using Hashed Adapter this way, routing scales infinitely without performance degradation. Server processing is very efficient, and for developers there's no nightmare switch() statement of ten million lines to maintain. By using Strategy, it's very easy to do the file housekeeping necessary to keep tens of thousands of Command implementations easy to navigate.