jump to navigation

Python MUD Reborn April 15, 2008

Posted by PythonGuy in Advanced Python, MUD, Python, Web Technologies.
Tags: , ,

I was prodded by one Jose from Spain who wanted to learn Python to revisit SimPyMUD. Of course, that project is old and dead, but I had learned enough about how to code up servers in Python in the meantime to write a new MUD from scratch in a matter of hours.

The model I used is a threaded one. Every connection gets its own thread. It is based on the SocketServer. Here’s some code to poke at.

Let me explain a little about how it works.

First, I use a TCP threaded socket server–ThreadingTCPServer. This server will spawn a new thread which calls the request handler you set with the parameters socket, (client address, client port) and the server instance.

The request handler requires a bit of magic to read and write at the same time. That magic is provided by an asyncore dispatcher. Each thread gets its own dispatch map to manage that dispatcher.

Around the dispatcher is a session object. This manages all the state of the current session. It starts off by writing out a welcome message and prompting for the login name.

The dispatcher handles writes by accumulating them in a buffer. When the dispatcher’s handle_write method is called, that’s a sign that the socket is ready to write to. The dispatcher writes as much data as it can, pulling it off of the buffer.

When an incoming bit of text arrives, the handle_read() method of the dispatcher is called. This will read as many bytes as it can, storing them in the incoming buffer. Then the buffer is split by lines and passed off to handle_read_line method. This is conveniently overridden with an attribute assignment to the dispatcher instance. Depending on the state of the session, that input will be rerouted to one of different places.

(An alternate implementation could keep track of the state and then have methods for all of the states. I would rather take advantage of Python’s dynamic environment than keep the rules of C.)

In the login state, input is read and when it is non-blank, it is stored as the login name. Then the state moves to the password input state. There, input is read that represents the password. The corresponding player is looked up in the database (really, a dict) and if one matches with the login and password, then the state moves to the playing state with the player assigned accordingly. Otherwise, an error message is shown and the state transfers back to the login mode.

In the play mode, commands are parsed. The commands dispatch to one of the command methods, which are supposed to do further parsing of the input. Based on the command, objects in the universe are modified, messages are sent out to different connections, and basically, stuff happens.

Thus, you have a MUD.

Now, in building a MUD, there are two bits you have to work on: (1) The functionality, (2) the world. The functionality, for the most part, is going to be implemented in python. New commands are new methods in the play state. Objects also have some methods that are invoked by the commands. If you want a new feature, you’ll have to change existing commands or add new ones.

The world, however, is represented with data. These are the object instances that have been created and linked to each other.

Python doesn’t do a tremendous job of allowing functionality to be represented with data. Ideally, I can imagine a mud universe where everything is code or everything is data because data and code are the same thing. (I guess I have been reading too many Lisp books.) You could log into this world and, as an administrator, change the very rules of nature. But with Python, I don’t quite see a way to do that without either implementing a new language inside of Python, or finding some way to record the Python program state. When I figure it out, I am sure there is some Python award or something.

An alternative method of implementing a MUD, one that I would like to explore, involves using greenlets to simulate threads while truly doing asynchronous socket programming. (the eventlet module is very interesting.) This is an interesting idea because the functionality doesn’t have to be implemented with states. It can be implemented with pure functions that are interrupted and resumed appropriately. It is kind of like the anti-Twisted environment.