[plt-scheme] How to use Eval for a MUD

From: Synx (plt at synx.us.to)
Date: Tue Jan 26 00:30:28 EST 2010

I am working on a text based interactive world in scheme. One important
aspect of that for me is to enable dynamic scripting. I want to make it
so people (perhaps only privileged users) can make objects, rooms and
environments from within the game itself, such that when the game
restarts, the programming they have done with these things remains
intact. I'd also like it sandboxed, so one person's broken program
doesn't take down the whole world, obviously.

I have a persistent database of some form, of "objects" that make up
this world, each object has "properties", and some of those properties
must somehow be programs. The obvious way to do that is to store the
s-expression of the program into the property. I figured out how to make
an evaluator, but populating its namespace is something of a puzzle to
me. I'm not sure what strategy to take, since the concept of a game like
this has a rather unusual way of handling context.

The idea is this: players control a player object that is treated as
"them" for purposes of user interaction. That object carries with it a
certain context or namespace, that relates what actions are valid while
being that player. For instance a player might have a procedure that
runs when they type "scan Zymorgian". That procedure only runs for that
player, when they type that syntax in. Other players would not see
anything special happen with the syntax "scan ____" when they type it,
unless they also had a similar procedure in their own personal context.

Rooms can have a context as well though, rooms being not much more than
a collection of objects that are all considered to be in the same
general in-game vicinity. Any player whose player object is in a given
room must be able to access the room's procedures, such as "pull the red
lever" becoming (pull 'red) where pull is a procedure unique to that
room. So there needs to be some context that not only many players share
for having been in the same room, but a context that changes when a
player object moves from one room to another. Dare I say mutates?

There's also the notion of a general environmental context, like "swim"
might be a valid procedure for objects in all rooms that are categorized
as being flooded, while it would not work, or have different
functionality on rooms designated as on a cruise ship. And of course a
global environmental context that applies everywhere like "quit" or
"home" or "reset".

I've gotten as far at studying scheme/sandbox to figure out how to make
a module evaluator, that I can load up with identifiers and modules, and
 evaluate stuff with it. But some of the subtleties of the above system
I'm still puzzling about. When going from room to room, do I somehow
"delete" all the identifiers only relevant to the previous room, while
"defining" the ones relevant to the next room? Do I just set! the old
identifiers to #f or something, and wouldn't that eventually accumulate
every possible identifier in the game into one namespace? Should I have
a namespace for every room, environment, and player object, and somehow
recombine those into one namespace every time either the player or the
room changes? When a player defines some new procedure, how could I make
that procedure persistent? Do I have to have a set of properties in each
environment, room and player that get evaluated every time the player
changes rooms? What if the player changes rooms, but remains in the same
environment? Couldn't the properties on the player object, that govern
the player's personal actions, not be re-evaluated every time they move?

I'm really puzzled how to handle this. Something tells me that searching
through the player, room, and all environments for the name of every
procedure on the left of an s-expression, is a bad idea. Yet that's how
say FuzzballMUCK does it, or TinyMUSH. They just do a recursive search
through all the properties and objects relevant to the current player,
on up to the root object #0. Would it be better to do that search
pre-emptively, and have s-expression evaluation be as simple as an
(eval)? Is there some way to compile these procedures such that they'd
be noticeably quicker than evaluating the source expression of the
procedure?


Posted on the users mailing list.