Thursday 11 February 2010

Commands

It's been a week or so since I have worked on my MUD codebase. The event system is out of the way, although I have to admit to myself I do not have any pressing use for it, now that it works. However, I did read an interesting post today about how the commands worked in some other MUD where a player would use an adverb to indicate their motive. This led me to write up a few thoughts on how I might go about implementing a command system in my codebase.

Brainstorming

I started off by just listing the possible commands that might exist. "Ask, tell, take, offer.." Then I wondered how "offer" might be used to indicate the reason an action is carried out. OFFER ITEM TO RECIPIENT <adverb>. The adverb "happily" might, as might "willingly", indicate a willing act. "unhappily", and accordingly "unwillingly", might indicate an unwilling act.

"Demand, request, push, pull, drag, throw, open, close, put, place.."

Side note: The system should track entered commands and identify verbs or adverbs that people try to use, which do not actually exist. At the simplest level, this data might be used to add these omissions. And at a more complex level, it might be used to track typos followed by correct spelling, and use it to autocorrect. In this Google day and age, that seems like a reasonable thing to do.

A scenario comes to mind:

> give 5 gold to bob
Bob needs to request or demand the 5 gold coins before
you can give them to him. You could also offer him
the coins.
It's definitely an idea, but it is too much output to throw at the user. A better idea would be to prompt the user with hints, like Midkemia Online does.


Item exchange between characters

What I have sort of become focused on at this point, is a protocol for the exchange of items between characters, using at least the give, take, demand, offer and request commands.

Demand would be seen as imposing on the other person to give whatever the item might be. Request would be seen as asking the other person for the item. You could only give an item to another person, if there was a recent demand or request for it. Offer would be seen as an opportunity for the other person to take the item from you. Take would only acquire an item from another person if the item had been offered recently.

At this point, I can go ahead and implement this. But more important would be to flesh out the use of commands for normal item handling.

Basic item manipulation commands

Should putting an item in a closed chest implicitly open the chest in order to accomplish the action? Should putting an item in a locked chest implicitly unlock the chest before it is implicitly opened? This is getting too complicated, so fancy functionalities like these are something I am going to put on the "cool and interesting things to do some other day" pile.

I felt like writing a transcript for some reason:
> put knife in chest
The chest is closed.
> open chest
You open the chest.
> put knife in chest
You put the knife in the chest.
> get knife
Where is this knife?
> get knife from chest
You take the knife from the chest.
> drop knife
You drop the knife.
> put knife in chest
Where is this knife?
Writing that felt like writing a Python doctest. Given that my framework has unit testing, it should be worth testing transcripts like these.

This gives me a defined behaviour for a simple set of commands. Get, put, drop, open and close. Worth putting on my "do it" list.

Summing it up

I've fleshed out a range of basic functionality to the point I can implement it. There's also some underlying functionality like a simple parser, containers and descriptions that I need to work on as part of this.

Reflecting on the protocol for character item exchange, for some reason I seem to be reminded of a Skotos article which I cannot locate at this time. Back in the heyday of MUD development, when the MUD-Dev mailing list was at its peak, Skotos was publishing interesting articles like nobody's business. On one hand I am tempted to track some of them down and give them a re-read, but on the other, there is value in just seeing where this "write stuff down and see where it goes" approach takes me.

Tuesday 9 February 2010

Mailman-style mailing list archives

I have the posts made to several mailing lists in a variety of non-standard formats. Converting them to a standard mbox file is a matter of parsing and is a different process for each. Once I have each parsed, what I would like to do is generate Mailman-style list archives.

I've downloaded Mailman and tried to get it to take my mbox file, and output the list archives. But the process is to some degree tied to Unix-style platforms, relying on functionality that is not supported on Windows. But to a larger degree, it is tied into the quality of being a proper Mailman hosted mailing list. Even changing the code to address or work around these things is not the cleanest of processes. There must be a better way.

Any suggestions?

Have some hacky code while I am at it:


WORKING_PATH = r"D:\MailingList"
MAILMAN_PATH = os.path.join(WORKING_PATH, "mailman-2.1.13")

class MailList:
def __init__(self, basePath, fileName):
self.basePath = basePath
self.fileName = fileName
self.SetVars()

def SetVars(self):
self._internal_name = "mud-dev"
self._fullpath = "/resource/MUD-Dev/"
self.host_name = "localhost"
self.subject_prefix = "[MUD-Dev] "
self.real_name = "MUD-Dev"

def fullpath(self):
return self._fullpath

def archive_dir(self):
return self.basePath

def internal_name(self):
return self.fileName

def ArchiveFileName(self):
return os.path.join(self.basePath, self.internal_name() + ".mbox")

def GetScriptURL(self, *args, **kwargs):
return args[0]

def GetListEmail(self):
return "no-list-email"

class SuperDuperArchive(HyperArchive):
def GetArchLock(self): return 1

def DropArchLock(self): pass

def fake_symlink(src, dst):
if os.path.exists(src):
open(dst, "w").write(open(src, "r").read())

os.symlink = fake_symlink
os.link = fake_symlink
Mailman.mm_cfg.TEMPLATE_DIR = os.path.join(MAILMAN_PATH, "templates")
Mailman.mm_cfg.LIST_DATA_DIR = WORKING_PATH
Mailman.mm_cfg.PUBLIC_ARCHIVE_FILE_DIR = WORKING_PATH
Mailman.mm_cfg.PRIVATE_ARCHIVE_FILE_DIR = WORKING_PATH

mlist = MailList(os.path.join(filePath, "archives"), "mud-dev")
mlist.preferred_language = 'en'

listPath = os.path.join(Mailman.mm_cfg.LIST_DATA_DIR, mlist.internal_name())
if not os.path.exists(listPath):
os.makedirs(listPath)

class DummyClass:
def internal_name(self):
return "mud-dev"

def archive_dir(self):
return Site.get_archpath(self.internal_name())

def GetScriptURL(self, *args, **kwargs):
return args[0]

def GetListEmail(self):
return "no-list-email"

instance = DummyClass()

Mailman.MailList.MailList.InitVars.im_func(instance, "mud-dev")
for baseclass in Mailman.MailList.MailList.__bases__:
if hasattr(baseclass, 'InitVars'):
baseclass.InitVars.im_func(instance)

MailList.SetVars.im_func(instance)

listConfigPath = os.path.join(listPath, "config.pck")
if not os.path.exists(listConfigPath):
cPickle.dump(instance.__dict__, open(listConfigPath, "wb"))

archive = SuperDuperArchive(mlist)
archive.processListArch()
archive.close()
Next post: Abusing mailman on Windows