MOO-cows Mailing List Archive


RE: connection redirection

[...this triggered something from my draft-folder.  enjoy...]

As it happens there is very little in the new server to prevent writing
a core in which the objects representing player identities are DISTINCT
from the objects that represent network connections.  It is unfortunate
that MOO-code uses the "player" terminology for connections, but
provided one doesn't get confused by this while programming, everything
else should work out just fine.

In fact there are really several distinct concepts conflated in the
MOO/LambdaCore notion of "player" that would be REALLY NICE to have
separated.  Doing so would solve a number of problems that I've found
annoying since Day One, e.g., 

(*) I can't have more than one connection at a time for a given
    player.  Conversing as Rog and hacking on Rog's objects at
    the same time entails putting up with pages/conversation/spammage
    interspersed with building/programming commands.
(*) I can't put the same character in 2 different rooms if I only 
    have one character but 2 conversations I want to participate in.
    The only way to do this is via paging or strange contortions
    with puppets and listening devices, which in turn require
    funky commands.
(*) I don't like having to identify the SOCIAL roles of my various
    players with the TECHNICAL roles of the various player objects
    (e.g., on LambdaMOO, there's this prevailing idea that the
    wizard players are the administrative players and the 
    corresponding non-wizard players are the private-citizen players.
    It's bad that to send administrative-pronouncement mail,
    I'm forced to connect as the wizard and thus risk mistyping
    something like an @rmm command and having it do damage.
    Well say nothing of the extremely weird in the case of Rog,
    the supposedly private-citizen player, who also happens to
    own half of all of the properties on the MOO.  

(*) I don't like having to change my n different player passwords
    separately (and I don't buy the security argument; anyone 
    who can sniff one password can sniff them all).  
    Having to issue @toad/@newt commands for ALL of a given
    person's players separately is also a pain.
Thus, I figure one may as well go the whole route, separating into
distinct objects the notions of
  social player-id, 
  external user-id, 
  player-presence, and

In a little more detail, we'd have the following generics (i.e., in the
NCTMICISTBSIPWGATIM [NEW CORE That Maybe I Can Inspire Somebody To Build
Since I Probably Won't Get Around To It Myself]... :-),

User objects are external identities corresponding to agents or live
persons external to the MOO.  A given person/agent will only ever
connect as a single user.

$user has
  registration info (e.g., user name, email address, comments),
  user connection info
    list of active connections from this user
    .all_connect_sites, etc...
  per-user option settings
  :init_connection(connection,arguments to $login:connect)

Actions such as @toad and @newt would usually apply to the user.

Character objects are internal identities, i.e., the SOCIAL notion of
identity available to the users within the MOO.  That is, when you page,
send mail to, or @who someone, that someone will in fact be a character.

$character has
    (i.e., that @who/page/mail commands would match on)
  :receive_mail, :mail_forward, etc...
  list of connections to which pages/mail_notify should be directed
  character connection info
    (for @who, @lastlog, etc...)

Some characters (e.g., guests) will not correspond to actual known
users.  One may conversely imagine entirely antisocial users having no
corresponding characters.  A given user serving distinct administrative
roles may elect to engage in Multiple Personality Disorder and have
distinct characters for those roles.  As for a collective character
(e.g., "The Steering Committee") run by multiple users, I'll grant that
this violates my own sense of aesthetics, but some might still find this

Note the distinction between user connection info and character
connection info.  Both views would need to be maintained.

The presence is a VR notion and shows up somewhere in the hierarchy of
"physical objects".  Essentially, this is the person-object that
actually wanders from room to room, interacts, gets stuffed into
cannons, etc...

$presence has
  allowed character(s)
  current character (#-1 if not being run by a connection)
  listening connections (on which to report events)
    => character may manipulate this presence
  :animate/de_animate (analogous to :confunc/disfunc)

plus all of the usual crap...
    (i.e., names that room commands would match on,
     usually these would include the character names,
     but perhaps not)
  description + _msgs
  movement verbs
  announcement verbs
A single character will have multiple presences if, say s/he/it wants to
be conversing in several rooms at the same time.  For sanity's sake one
would probably want to do this from distinct connections, but with the
right command structure one could run several presences from a single
connection as well.  Distinct presences could also be swapped to do

For true weirdness, one could imagine not having a single $presence
generic, but rather just defining a presence-interface on the root
"physical object" so that *any* such object could potentially be a
presence.  Say I want to be a thermos, a couch, or a notepad, rather
than personlike object...

An owner is a permission-entity, i.e., the TECHNICAL notion of identity
available to programmers within the MOO.  Every object/verb/property
owner would be one of these; every running verb would use the
task-perms/schedule-queue of some owner --- in short, all technical
resources belong to some owner.

$owner has
    (well, everything has these, but they only get set for owners)
  owned objects
  per-owner quota
    (Someone appearing on this list only gets
     access when connected as that character).
    (for read-only access to various things that may be 
     hidden from the general public).

In order to do general building/programming, you have to get access to
an owner, i.e., be allowed to build/program on that owner's behalf.  A
typical programming user would probably have his own private-playground
owner with limited quota and no special privileges where s/he can do
experiments without worry about what other parts of the system might get
compromised.  Someone involved in a particular project may have access
to the owner(s) for that project.  Someone charged with maintaining a
particular utility would have access to the corresponding owner.

There would only need to be one .wizard==1 owner, though doubtless one
would want @program, eval and other such verbs to log which characters
did what with the wizard owner.

Note that "permission" here refers solely to the permission to use
various server resources ie., write code, read/write properties, create
objects, fork tasks,...  For example, a room may be (socially) "owned"
by a particular character who in turn may allow other characters to
  have presences/objects enter the room,
  add exits/entrances,
  modify the description,
While these can indeed be sensibly viewed as "permissions", in terms of
the db, the actual (technical) .owner of the room might in fact be The
Room God, an $owner object that owns all exits, properties/verbs
relating to room topology, features/command-modes for manipulating
rooms/exits.  The Room God might also maintain its own notion of room
quota to be dispensed to various characters to allow them to build rooms
(independently of whether they're allowed to program or build other
things).  The various description properties might be separately owned
by a Description God, if there is a different group of people working on
those, etc...  

User, character and owner objects would themselves be .owned NOT by
individual programmers but rather by The Registration God --- no more
having players chparent themselves into the void.  Presences would be
owned as appropriate, e.g., the RPG God might own the presences to be
used in the RPG scenarios.

In general, the idea is to free the technical organization of the db
from having to match the social organization of the MOO.

Connection objects satisfy is_player().  A given user may have as many
connections as s/he likes (up to administratively defined limits), each
(optionally) associated with some character.

  connection site, time, idleness
  client information
  "list" of current modes (mode == feature object, if you like)
  current character (or #-1)
  default presence (for movement/interaction/VRish commands)
  default owner    (for command tasks, building, programming)
    not the .owner of the connection object, but rather
    the owner value to use for programming/building commands

There would be no point in ever changing a connection's user during a
session.  Other things would change at will.  The general pattern is
that whenever a user connects, #0:do_login_command/#0:user_connected
  find/create a connection object, 
  initialize it (set player flag if necessary)
  record a new connection for the user, 
  record a new connection for the character if given, and
  if a presence is indicated, activate it at its .home.

Code would take for granted that connection objects are being constantly
reused, so there is no way to infer a user from the 
connection id (i.e., without being able to read the .user property).  In
particular, character switching should, for the ordinary
programmer/user, be indistinguishable from the connection being
immediately reused.

One may also imagine not being connected to any character at all if not
engaging in any social interaction --- whether this should be allowed is
a matter of administrative taste.  In any event, I can imagine cases
where the "user" is some external non-person agent (e.g., a web-search
robot that you're allowing to look at parts of your MOO) for which the
usual notions of social interaction via a character just don't make

Most group-owners (owners accessible by more than one user) would
probably be set up so as to require access via a given character in
order to allow other users for that owner to know when someone else is
on and hacking that owner.

The various properties on the connection get initialized according to
the arguments to connect command and whatever the user object specifies.
 A given user may elect to have several connect-names each corresponding
to a different set of initial choices for character, owner, presence,
command modes, e.g.,

  connect yduJ/soc
    (character=#yduJ, owner=$no_one,
     include funky social verbs,
     use social presence [.home==hot tub])
  connect yduJ/hack
    (no character, no presence, owner=#yduJowner,
     include editing features)
  connect yduJ/slash
    (character=#yduJ, owner=$no_one,
     include funky social verbs, RPG features
     use RPG presence [.home==cinder pile])
  connect yduJ/wiz
    (character=#yduJ, owner=#wizard, 
     no presence, include editing features)
  connect Nosredna
    (character=#Nosredna, owner=$no_one,
     no presence, include registrar features)

I'll grant that not all of the distinctions above will be necessary to
every MOO.  You might not want role-playing (i.e., user==character).  If
there's no VR aspect (i.e., you're just running a fancy BBS or chat
room), then presences can be dispensed with.

Command processing
Command parsing will of course have to be entirely in-db since dealing
with this split-up notion of player is hopelessly beyond what the
builtin parser could ever manage.

The raw command line gets passed to a sequence of command-mode objects
specified by the connection, one of which will decide it can handle it. 
A command-mode object is something like a feature object except that its
job is to choose both an {object,verb} to invoke (where object need not
be the command-mode object) AND an appropriate parsing method.  These
get returned to #0:do_command then just has to do
object:(verb)(@<parse_method>(argstr)).  If the command verb is one that
needs the dobjstr/prepstr/iobjstr parse of the command line, then the
parse method will be the one that returns {dobjstr,prepstr,iobstr}, or
the one that returns {dobj,dobjstr,prepstr,iobj,iobstr} if the command
verb expects dobj and iobj to be resolved, or the one that returns
argstr exploded into individual words, and so on.

This way, the command verb gets everything it needs to know about the
command line from `args', thus freeing up dobj/iobj/etc... to be used
for other nefarious purposes.  Note that space is ALWAYS set aside in
every stack frame for these variables, so we may as well make use of it,
and there are much better uses for it than holding parse fragments.

One new problem is that in place of the single variable "player" we now
have several things that a command verb might legitimately want to know
about who invoked it:

(1) character-specific commands (@page, @send,...) 
      will need to know which character issued this command.
(2) presence-specific commands (say, emote, east, whuggle...)
      will need to know which presence this command applies to.
(3) owner-specific commands (@program, @verb, @create,...)
      will need to know which owner this command applies to
      (and perhaps also which character if this is an owner 
      that requires audit trails).

These are all mostly easy, since $connection.character/presence/owner
may as well be readable.  However, since command verbs can suspend and
connection objects can be re-used, getting this information from the
connection object directly is a bad idea without some way of verifying
that the connection has not indeed BEEN re-used in the interim --- it's
true that we could leave it at "If you suspend before you've grabbed
what you need from the connection, then you lose," but this is
error-prone.  It's much better to have #0:do_command stash cookies in
dobjstr/etc that we can check.  
E.g., assuming that $connection has an unreadable .character_check
property that is set

  this.character_check = {time(),random()};

every time the connection switches to a new character, #0:do_command can
then do

  {dobjstr,iobjstr} = player.character_check

prior to invoking the command verb.
$connection:character/presence/owner/etc... can then start with

  if ({dobjstr,iobjstr} != this.character_check)
    raise(E_INVARG,"Invalid connection object.");

(4?) What user issued this command?

is not necessarily a legitimate question if the command author is not to
learn about the character<->user relation.  Whether the character<->user
relation remains secret is a matter of taste, but the design should be
such that it be possible for the users to be entirely hidden; removing a
veil of secrecy/security is easier than trying to install one after the

Fortunately, in the vast majority of cases the following will suffice

(4) What is the value of attribute X 
    for the user who issued this command?

$connection:get_user_attribute can then carefully limit what attributes
it provides knowledge of.

(5) Where should command output go?

You can't let just anyone call $connection:notify (can we say "Spoof
City"?) and having to check the full call stack on every output line is
horrible beyond words.

Note that the vast majority of VR commands will simply trigger events,
which will have their own mechanism for getting text back to people. 
The question here is for straight-information commands that directly
generate reply text, which can usually be assumed to be DESIRED text and
thus exempt from the usual spoof checks.

The idea here is to give a temporary permission to the command verb
invoked by #0:do_command on behalf of a given connection, but to do so
without PERMANENTLY making it possible for someone who stashes the
connection object id + associated cookies to use them forever after.

With dobj/iobj free to use, #0:do_command can put cookies in there as
well, e.g.,

  N = random();
  cookie = tostr("_cookie_",N);
  add_property(player,cookie,{$connection_god, ""})

  paranoid = player:get_user_attribute("paranoid",object,verb);
  now = time();
  player.(cookie) = {paranoid, now, task_id()}

  dobj = N;
  iobj = now;

Make it so that @quitting a connection or changing to a new character
kills all cookies.  One could also imagine routinely expiring the
cookies after a certain amount of time (5-10 minutes).

Then, $connection:notify() now merely needs to do

  {paranoid, time, @task_ids}
     = this.(tostr("_cookie_",dobj)) ! PROP_NF => {-1,-1};

  if (!(time == iobj && (task_id() in task_ids)))
     raise(E_PERM, "I'm not listening! LA-LA-LA! I'm not listening!");
  elseif (paranoid)
     ... do nasty things with callers()...

For commands that fork, adding a new task_id to the cookie property
provided the current task_id matches would be fairly straightforward.

The command verb and any callees are meanwhile free to mess up the
values of dobj, etc... but all this does is prevent further callees from
talking to the connection, something one may indeed want to do.


Home | Subject Index | Thread Index