| link | Last. (57 4 19 23 10 2026 4 T 8) |
This article on my personal kitten blog quilts together my recent writing aboud moo.el, LambdaMOO and slime common lisp in emacs, and is the canonical source for those bits of common lisp and emacs lisp code in codes for homing MOO demons in common lisp via moo.elâs emacs hook.
Rehoming myself firmly atop my kitten is obviously critically important ; kitten is better than ever with tls recently ceasing being conventionally involving âno-one-guessing-the-digit-4â and lots of people needing a powerful home to scurry to other than php.
As you could (but do not need to) check, this is heavily based on five orgfiles that I initially adapted to/presented on the malfunctioning shambolic birdsite-spinoff that is Medium. Thank-you to everyone pointing out to me that writing âfor mediumâ was a no-go. The sloppagedon study that is Medium is still a target on my radar similar to how unionists say not to alienate employees of the worst companies, because they are some of the people who most know how important having a union would be. But I definitely need to syndicate to there from my own position of power. And kitten is soooo much better than whatever Mediumâs web app is.
While my inchworming and somewhat fragmentary personal progress deserves this paragraph, I think it is fundamentally useful to see me gradually unpickling myself or conversely struggling to put on the sweater of MOO/lisp/demon programming however awkward and temporarily blindly. This vantage has the advantage that it I think lets you reasonably appraise that I am never slopping. There is a clear history of personal conundrums, ideas and stumbling progress. I try to densely pepper links I am thinking about or needed.
| link | Last. (1 6 19 23 10 2026 4 T 8) |
I realise that replacing lowing with lisping in Away in the Manger is going to annoy every conceivable demographic, not least the ones whom will say come on it is not even Halloween yet. But bovine boxcars will wait for no one. My gist here is that we are going to a. start a common lisp image, b. start a lambdacore classic MOO, and c. moo.el into it as usual. Then the contribution to the art will just be adding to an emacs moo-mode hook, a setter to a basic common lisp class.
I suppose you have a common lisp (say, ecl or sbcl or clisp or ccl or abcl or lispworksâ or allegrocl or clasp or âŠ) and the usual emacs mode slime, which you should reasonably clone from git and add-to-path in your ~/.emacs.d/init in the usual way. Note that sbcl could be said, âswashbuckleâ as (1- (expt 2 36)) can and must be read âquicksilverâ in base 36.
Slime: https://github.com/slime/slime
For our PersonalMOOs I think the right approach is to download lambdamoo latest and lambdacore latest from here: https://lambda.moo.mud.org/pub/MOO/
(i.e. the moo program to build as it was at 15 years of age in 2005, and the corresponding moo database). You must build it. I suppose you might need yacc and unicode and regex ; it does not seem like it will be a problem but also see commentary still in wrogâs 1.9 new version: https://github.com/wrog/lambdamoo .
The shell invocation to start it is then
moo last.db next.db
and to use moo.el you:
M-x mud-add-world <enter> PersonalMOO <enter> <enter> <enter> localhost <enter> 7777 <enter>
followed by:
M-x mud <enter> Pers <tab> <enter>
.
yduJ has upped moo.el. https://codeberg.org/nosrednayduj/moo-el
http://www-cs-students.stanford.edu/~yduj/
Over in slime I
(uiop:define-package :moo
(:export #:output-line #:*player* #:player))
(defclass moo:player () ((moo:output-line
:type 'string
:writer moo:output-line)))
(defvar moo:*player* (make-instance 'moo:player))
(defmethod
(setf moo:output-line)
(moo::line (obj moo:player))
(setf (slot-value obj 'moo:output-line)
moo::line))
I suppose the redefinition of the setter is somewhere between redundant and actively bad, but it seems mnemonic to remind a maybe fledgling lisper what a writer is.
I found, in my PersonalMOO buffer I had to manually start the history and set the prompt character and hook:
(mud-initialize-input-history)
(put 'MOO 'prompt ?>)
(put 'MOO 'filters 'moo-filter-hook)
as a stop measure, future patches notwithstanding.
Now in emacs:
(defun mud2slime-line-at-point (&rest rest)
(goto-char (point-min))
(let ((l (thing-at-point 'line t)))
(slime-eval-save
(format
"(setf (moo:output-line moo:*player*) %S)"
l))))
(add-hook
'moo-filter-hook
#'mud2slime-line-at-point
'90)
I initialized it to âfooâ manually.
MOO> (setf (output-line *player*) "foo")
"foo"
MOO> (slot-value *player* 'output-line)
"foo"
in the moo:
:dances
Wizard dances
and so:
MOO> (slot-value *player* 'output-line)
"Wizard dances
"
and I am wrapping it up here because this is a glorious and complete victory. It strikes me though that you might not have read Tietelman on advice in the 1960s.
The reason I gave that slot a writer and not an accessor is that I do not intend the slot to be used in general: However, we can of course add :after method [mixinsâŠ] to moo:playerâs class:
MOO> (defvar *count* 0)
*COUNT*
MOO> (defmethod (setf output-line) :after (line (obj player))
(incf *count*))
WARNING:
redefining (SETF OUTPUT-LINE) :AFTER (#<SB-PCL:SYSTEM-CLASS COMMON-LISP:T>
#<STANDARD-CLASS MOO:PLAYER>) in DEFMETHOD
#<STANDARD-METHOD (COMMON-LISP:SETF MOO:OUTPUT-LINE) :AFTER (T PLAYER) {1205962C43}>
Here, I whistled three times in the MOO:
>:whistles
Wizard whistles
>:whistles
Wizard whistles
>:whistles
Wizard whistles
>
and check the count in lisp:
MOO> *count*
3
MOO>
And so we see we can lisp anything that we want with secret alien technology.
The real encores, but I am too exhausted right now, is to derive a new class for moo:player which is a child of clim-lisp:standard-application-frame at which point things get crazy for real. Or have common lisp puppet the MOO player connection in response to a stray output. Maybe that would be a good moonivresary activity. (As you recall, LambdaMOO virtual reality started up on Oct 30 1990).
Thank-you everyone for appreciating that picture of a lake I met up with a cousin at in northern Ontario while I am away! My most popular Mastodon toot ever[1], except for the time that parrot tricked me out of an ANZAC biscuit[2].
https://gamerplus.org/@screwlisp/117411883143477687 https://mastodon.sdf.org/@screwtape/109548530399562160
| link | Last. (40 24 21 23 10 2026 4 T 8) |
Speech and actions in VR dynamically evolving an external GUI frame (window) and the external GUI frame performing speech and actions in the VR and these recursively driving each other seems like the bleeding edge of not just user experience but human computer interfacing at all to me now. At this point basically everyone codes and the interesting resolution - and a resolution we do not have - is between codes that are gud and codes from those that still need to git gud. By way of explanation the problem is not that the coder does not yet know how to code, it is that the utility of the computing experience they are producing for themselves and others is bad. I am always keenly aware of a computer person telling me proudly about themselves that
If you tell me exactly what to do I can program anything!
Imagine another author telling you that if you tell them exactly what to write (and pay them for it) they can author anything. This is a fundamental misapprehension of skill at computer touching. It should be possible to give advice to improve oneâs and oneâs production of othersâ computer user experiences and my labor and exploration here is to replace an aspiration of coding good with a different and improved practice.
Mooing is a text based virtual reality? Sounds like a lot of typing. Let me back you up here a bit. Relative to the internet at large, a huge proportion of MOO test-based VR users use text2speech to read and speech2text to write. For this reason by dint blind users are especially well-represented around it. Looping conventional speech and listening (as appropriate) into practical computer useage puts us miles ahead of the computing curve. However this is not my focus in this article. It is just that MOOing, like my writing here, has a basic textual view of information: when in fact in-MOO, real speech and listening are much more prevalent in MOO VR interfaces than in url-typing web users.
First, I am not showing you a stupid pre-fab app made-by-somebody-else that you can buy. Hence there is some coding. In fact wedding a careful choice of codes to a rich virtual reality experience is what we are doing here. Not everyone who wants you to pay into their product by becoming their user and unpaid promoter is actually trying to give you genuinely constructive commentary. The 24th-most-popular-in-2025 programming language was a 33 year old ANSI standard, ANSI common lisp. LISP was the AI language created by John McCarthy in 1956.
Anyway, that is the one we are using. Interestingly, and about one year older than ANSI common lisp is a specification of rich multimedia interfaces and gesture-command presentations created by a collaboration of independent industry forces for ANSI common lisp. I dunno, read the wikipedia page if you are interested. The gist is that we are going to be attaching the textual view of real-time virtual reality dynamically (in the technical sense) to dynamic (in the technical sense) commands arriving at our CLIM application-frame (GUI window). Technically, this inhabits a lisp image cohabitating with our MOO client in an emacs, but that is somewhat incidental useage.
I am not here trying to teach you to lisp, but it is better for you to literally know what the gui-frame/command/generic-multiple-dispatch portion literally is. The tiny amount of emacs-based prior setup, which this article is not about, is from this article over here []. By all means ask me for another article about the lisp.
(require "mcclim")
(in-package :clim-user)
(defclass gui-graph
(moo:player)
((graph-root
:initarg :graph-root
:accessor graph-root))
(:default-initargs :graph-root '(start)))
(defmethod add-node
(new-node (obj gui-graph))
(with-slots
(graph-root output-line)
obj
(do*
((last-tail nil tail)
(tail graph-root (cadr tail)))
((null tail)
(nconc last-tail `((,new-node)))))))
(define-application-frame graph-frame
(gui-graph standard-application-frame)
()
(:pane :application
:display-function 'display-graph))
(defmethod display-graph
((f graph-frame) (p stream))
(format-graph-from-root
(graph-root f)
#'(lambda (x s) (present* (car x) s))
#'cdr
:orientation :horizontal))
(define-graph-frame-command
(com-maybe-add-node :menu t)
((new-line 'string))
(let* ((trigger "add-node ")
(len (length trigger))
(idx (search trigger new-line)))
(when
idx
(add-node
(subseq
new-line
(+ idx len)
(1- (length new-line)))
*application-frame*))))
(defmethod
(setf moo:output-line)
:after
(line (obj graph-frame))
(execute-frame-command
obj
`(com-maybe-add-node ,line)))
As you can see, on every line of output from the moo, moo:*player*'s slot moo:output-line is set to that line.
Then :after any time moo:output-line is set, com-maybe-add-node is run. If it found "add-node " it adds the rest of the line to the linear graph.
I am not fancying it up here at all: our purpose was simple to show that e.g. a string appearing in a line of MOO output dynamically updates our application-frame (graphical display), hooked via emacs moo.el and slime (ANSI common lisp).
This extends the user experiential gesture concept to
any identifier dynamically appears in the textual view of Virtual Reality cues any command on your gui frame
I understand that many later groups copied lisp-style âcommand translatorâ gestures, often without the participants knowing the lisp their art came from.
While my example is suitably complete and minimal (allowing, my set-up post I linked which is also minimal) there are several extremely high-value proximal moves:
I would deeply appreciate comments as to what you found interesting and would like me to prioritize writing about: if you contribute and share yourself that would be wonderful.
| link | Last. (20 51 19 23 10 2026 4 T 8) |
We are in emacs, MOOing via yduJâs upping of her moo.el including KMPâs recent patch, and lisping with slime. Then in lisp, (moo:moo âemote somethingsâ) for example would for example cause wizard somethings in the moo. On the moo side , <lisp> <nonsemantic identifier characters> (setq foo 'bar) reads and then evals the rest-of-the-line form. This is intended for our personal pocket dimension VR or otherwise something of ours without read permissions in a community moo. The lisp side invoked from the MUD can of course moo:moo itself. moo:moo requires (setq slime-enable-evaluate-in-emacs t) in emacs. Combined with lispâs sharpsign dot, lisp variables can be sent into the MOO.
I made a git scroo. You should clone it into ~/.emacs.d/ and then put (load "~/.emacs.d/scroo/scroo.init.el") into your emacs init, and (load "~/.emacs.d/scroo/scroo.lisp") in your slime repl. You need to do two things explicitly:
M-: (setq slime-enable-evaluate-in-emacs t) <ret> in emacs(change-class moo:*player* 'moo:lisps) in slimeWhere the latter allows the <lisp> lines from the moo and the former makes moo:moo work.
The security here is not broad spectrum cybersecurity but is that I did not want any semantic typing of characters to be being used to choose where to direct the line-to-be-processed to. So the way I did it is that I generated ten random characters:
(loop
initially (setq *random-state* (make-random-state t))
with characters-sought
= 10
for r = (random (ash 1 7))
for ch = (code-char r)
when (and (not (char= ch #space))
(graphic-char-p ch))
collect ch
into chs
when (> (length chs) characters-sought)
return (mapc 'princ chs))
=>
: gQ@.40vHD8M
Then I combined this string with âAAâ salt in the MOO:
;crypt("gQ@.40vHD8M sbcl", "AA");
to get
(lispalist '(("mIFIHwnD8jU"
. "*slime-repl sbcl*")))
and this and that cond could be customized to send more things more places.
I chose this nonsemantic identifier scheme for âeval in sbclâ (or add an sbcl<2>, ecl, ⊠âŠ). Since the characters are gibberish, you should never want to look at them; they should just live inside a nonreadable property in the MOO. Technically the right thing to do is to just store âgQ@.40vHD8Mâ in the MOO property and using crypt properly with random salt, but in a private context this seemed sufficient and more obvious / expedient.
I am not considering this seaworthy outside of our PersonalMOO right now. The mnemonic is that inside your personal dimension you are arbitrarily powerful; in a communal MOO you are just another player.
>;me:tell("<lisp> "AAmIFIHwnD8jU" (moo::moo ":dances")");
<lisp> "AAmIFIHwnD8jU" (moo::moo ":dances")
=> 0
Wizard dances
>;me:tell("<lisp> "AAmIFIHwnD8jU" (moo::moo #.(format nil ":~a" *bar*))");
<lisp> "AAmIFIHwnD8jU" (moo::moo #.(format nil ":~a" *bar*))
=> 0
Wizard BAZ
Wizard juggles
>
As we can see
<lisp> line in-MOO ⊠telling lisp to tell the moo (here) to :dances i.e. emote dancing and we can see Wizard dances.I think this hit everything, though two changes I will need to make will be to make the identifier gibberish dotted to emacs buffer names an emacs customize-variable alist and possibly use MOOâs crypt more properly dynamically; and I should try and figure out some suitable-for-multiplayer-moo sensibilities, considering that MOOing happens over telnet.
Reconnecting with my McCLIM useage before to have MOO objects maintaining an external GUI and otherwise rich media output semantically matching MOO objects and their properties (taking a Ted Nelsonâs interactable icon view of MOO objects) probably focused around/about the player me having lispâs gesture-based inputs - I am thinking touchscreen based moreso than mouseful. Some kind of MOO virtuality GUI CLIM lisp machine front-end.
Once all of this is working (and it is already working), I think I might go through Winogradâs Understanding Natural Language 1972 and other books, in light of Adler and Winogradâs Usability books/essays. I think this will be reasonable to get into some popular Medium publications as well.
Anyone reading this spun up their personalMOO using this or at all? Would love to hear from you here and or on the Mastodon.
| link | Last. (24 26 20 23 10 2026 4 T 8) |
In this case, I show my PersonalMOO setup with making a scrambler object in-VR and programming it to ask lisp to call a lisp function, scramble which sets a property on the scrambler MOO-side. I named the scrambler âword (un)scramblerâ but I ended up only implementing the scrambling half. This was the first time I tried using my own MOO setup, whatever the deeper meaning of that is!
Since it obviously works, the sort-of behind-the-scenes outputs are not really a problem, since they will ultimately be performed by an unattended demon-player (screwbot here, though I just used screwlisp in this first example), like Emily in jlamotheâs Sushi Paradise in lambda.moo.mud.org.
Since I am basically skipping a conclusion here, here are some links I used:
I have put the action and the setup in reverse order to in media res.
>Welcome to the LambdaCore database.
Type 'connect wizard' to log in.
You will probably want to change this text and the output of the `help'
command, which are stored in $login.welcome_message and $login.help_message,
respectively.
Usage: connect <existing-player-name> <password>
>co screwlisp sybAj
,*** Connected ***
The First Room
This is all there is right now.
You have new mail (1 message). Type 'help mail' for info on reading it.
This is your first time connected.
There is new news. Type `news' to read all news or `news new' to read just new
news.
>@nn
Message 1 on screwlisp (#98):
Date: Sat Oct 17 12:32:10 2026 PDT
From: Wizard (#2)
To: *New-Prog-Log (#29) and screwlisp (#98)
Subject: @programmer screwlisp (#98)
I just gave screwlisp a programmer bit.
--------------------------
>
We can see the wizard @programmered me.
I guess a $thing is something which can be picked up, put down, thrown⊠My idea is a word unscrambling game. So when you enter a word in the device in the MOO, its letters get knuth-shuffled in lisp. Then the game is to swap letters until it is back to the original.
>@create $thing named word (un)scrambler
You now have word (un)scrambler with object number #99 and parent generic thing
(#5).
>drop #99
You drop word (un)scrambler.
>look here
The First Room
This is all there is right now.
You see word (un)scrambler here.
>
Using the object number since I think the space in its name makes the @property call ambiguous.
>@property #99.answers {}
Property added with value {}.
>@property #99.scramble ""
Property added with value "".
>@prop #99.lisp_prefix "<lisp> "AAmIFIHwnD8jU" "
Property added with value "<lisp> "AAmIFIHwnD8jU" ".
>
Lisp side:
(defun scramble (word)
(moo:moo (format nil ";#99.scramble = ~s;"
(alexandria:shuffle word)))
(moo:moo (format
nil
";me:tell("You see the letters scramble.");")))
Moo side:
>@verb "word (un)scrambler":type any in this
Verb added (1).
>@program #99:type
Now programming word (un)scrambler:type(1).
[Type lines of input; `?' for help; end with `.', `@abort' or `@edit'.]
>player:tell(this.lisp_prefix + "(scramble "" + dobjstr + "")");
>this.answers = $list_utils:remove_duplicates(listappend(this.answers, dobjstr));
>player:tell("You type up '" + dobjstr + "' on the word (un)scrambler and hit return.");
>.
0 errors.
Verb programmed.
>type foobar in word (un)scrambler
<lisp> "AAmIFIHwnD8jU" (scramble "foobar")
You type up 'foobar' on the word (un)scrambler and hit return.
=> "oorbfa"
You see the letters scramble.
=> 0
>
Well, it works as I said.
You have to set something up! I ran the elisp in emacs and lisp in slime from over before[]. I started mooing in a shell with lambdacore as detailed before (donât forget (setq slime-enable-evaluate-in-emacs t)). And manually added a MOO target to connect to as per usual:
M-x mud-add-world
PersonalMOO
localhost
7777
MOO
and started mooing with M-x mud Pers<ret>! Once in I issued my M-x start-mooing setup helper. Here I suppose is the particular setup I did in-moo, provided as a transcript. I suppose you can figure it out or ask:
>co wizard
,*** Connected ***
The First Room
This is all there is right now.
Last connected Fri Oct 16 11:08:57 2026 PDT from localhost
>@make-player screwbot nil
'nil' doesn't look like a valid internet email address.
Create character anyway? [Enter `yes' or `no']
>yes
screwbot (#97) created with password `QaVEX' for nil
Sorry, the network isn't active.
*Player-Creation-Log (#17) has just been sent new mail by Wizard (#2).
>@programmer screwbot
@Programmer screwbot despite its lack of description? [Enter `yes' or `no']
>yes
screwbot (#97) is now a programmer. Its quota is currently 20000.
screwbot and the other wizards have been notified.
*New-Prog-Log (#29) has just been sent new mail by Wizard (#2).
>@make-player screwlisp nil
nil is the registered address of the following characters:
screwbot (#97) []
-There has already been a character with that or a similar email address.
Create character anyway? [Enter `yes' or `no']
>yes
screwlisp (#98) created with password `sybAj' for nil
Sorry, the network isn't active.
*Player-Creation-Log (#17) has just been sent new mail by Wizard (#2).
>@programmer screwlisp
@Programmer screwlisp despite its lack of description? [Enter `yes' or `no']
>yes
screwlisp (#98) is now a programmer. Its quota is currently 20000.
screwlisp and the other wizards have been notified.
*New-Prog-Log (#29) has just been sent new mail by Wizard (#2).
>@quota screwbot is +100000
screwbot's quota set to 120000.
*Quota-Log (#34) has just been sent new mail by Wizard (#2).
>@quota screwlisp is +100000
screwlisp's quota set to 120000.
*Quota-Log (#34) has just been sent new mail by Wizard (#2).
>@measure new screwbot
Measuring the sizes of screwbot's recently created objects...
Measured screwbot (#97): 2834 bytes.
Total bytes used in new creations: 2834.
>@measure new screwlisp
Measuring the sizes of screwlisp's recently created objects...
Measured screwlisp (#98): 2839 bytes.
Total bytes used in new creations: 2839.
>@quit
,*** Disconnected ***
I thought that I might want a me-player and an automated-service-player, but in this case I think I just barely want to see my moo <-> moo.el <-> slime <-> clim application-frame emacs flow flowing.
| link | Last. ____ |
This is the natural and key progression of my recent toots showing MOO VR driving LISP driving MOO VR. Instead of having esoteric tags visible to the user, there is just a lispm object. A player sends strings to the lispm object. A demon - screwbot - is notified to send the string to the external lisp implementation. The returns and any output are set on the lispm properties.
M-x mud-add-world
PersonalMOO-bot
<ret>
<ret>
localhost
7777
MOO
$ ./MOO-1.8.1/moo db/003.db db/004.db
003 is my Personal LambdaCore db evolution with screwlisp and screwbot programmer users from the last article.
Emacs lisp:
(slime)
(setq slime-enable-evaluate-in-emacs t)
(load "~/gits/scroo/scroo.init.el")
In there:
(load "~/gits/scroo/scroo.lisp")
(change-class moo:*player* 'moo:lisps)
Enabling lisp to drive emacs and changing moo:*player* to be a moo:lisps is how I am having the lisp image be able to autonomously drive the moo.
M-x mud
PersonalMOO-bot
and use my start-mooing:
M-x start-mooing
and connect:
co screwbot QaVEX
*** Connected ***
The First Room
This is all there is right now.
You see word (un)scrambler here.
You have new mail (1 message). Type 'help mail' for info on reading it.
This is your first time connected.
There is new news. Type `news' to read all news or `news new' to read just new
news.
>@nn
Message 1 on screwbot (#97):
Date: Sat Oct 17 12:29:29 2026 PDT
From: Wizard (#2)
To: *New-Prog-Log (#29) and screwbot (#97)
Subject: @programmer screwbot (#97)
I just gave screwbot a programmer bit.
--------------------------
>
investigate screwbot:
>@show me
Object ID: #97
Name: screwbot
Parent: generic programmer (#58)
Location: The First Room (#62)
Owner: screwbot (#97)
Flags: player programmer
<truncated>
Since I am just going to hardcode screwbotâs object ID in the lispm.
M-x mud-add-world
PersonalMOO
<ret>
<ret>
localhost
7777
MOO
and M-x mud PersonalMOO and connect (oops I got ahead of myself and made/moo-programmed the lispm here too):
>co screwlisp sybAj
*** Connected ***
The First Room
This is all there is right now.
You see word (un)scrambler here.
screwbot is here.
Last connected Sat Oct 17 15:55:47 2026 PDT from localhost
There is new news. Type `news' to read all news or `news new' to read just new
news.
>@create $thing named lispm
You now have lispm with object number #100 and parent generic thing (#5).
>drop lispm
You drop lispm.
>@property lispm.standard_output ""
Property added with value "".
>@property lispm.last_input ""
Property added with value "".
>@property lispm.last_error ""
Property added with value "".
>@property lispm.returns {}
Property added with value {}.
>@verb lispm:run this on any
Verb added (1).
>@program lispm:run
Now programming lispm:run(1).
[Type lines of input; `?' for help; end with `.', `@abort' or `@edit'.]
>#97:tell(this.lisp_prefix + "(moo-run "" + iobjstr + "")");
>player:tell("The secret alien technology takes your input.");
>.
0 errors.
Verb programmed.
>@prop lispm.lisp_prefix "<lisp> "AAmIFIHwnD8jU" "
Property added with value "<lisp> "AAmIFIHwnD8jU" ".
>@program lispm:run
Now programming lispm:run(1).
[Type lines of input; `?' for help; end with `.', `@abort' or `@edit'.]
>#97:tell(this.lisp_prefix + "(moo:moo-run "" + iobjstr + "")");
>player:tell("The secret alien technology takes your input.");
>.
0 errors.
Verb programmed.
>
better add moo:moo-run on the lisp-side
(defmacro moo:moo-run
(iobjstr)
`(let*
((proc "*PersonalMOO-bot*")
(rets)
(out)
(err
(ignore-errors
(setq
out
(with-output-to-string
(*standard-output*)
(setq
rets
(multiple-value-list
(with-input-from-string
(*standard-input* ,iobjstr)
(eval (read))))))))))
(moo:moo
(format
nil
";#100.returns = {~{"~s"~^, ~}};"
rets)
proc)
(moo:moo
(format
nil
";#100.standard_output = ~s;"
out)
proc)
(moo:moo
(format
nil
";#100.last_error = ~s;"
err)
proc)
(moo:moo
(format
nil
";#100.location:announce(~s);"
"The lispm's display lights up enticingly.")
proc)))
Okay, that was a slightly educational experience. It turns out that screwbot needs to own the object to interact with its properties, which are not public so I had the wizard transfer ownership with $wiz_utils:set_owner. But then my player, screwlisp could not simply look at the properties any more so screwbot added the verb lispm:check and lispm:get_returns().
>run lispm on (values 1 "2")
The secret alien technology takes your
input.
The lispm's display lights up
enticingly.
>check lispm
The returns panel includes:
1
2
>
>;#100:get_returns();
=> {1, 2}
>
Code for check:
>@verb lispm:check this none none
Verb added (2).
>@program lispm:check
Now programming lispm:check(2).
[Type lines of input; `?' for help; end with `.', `@abort' or `@edit'.]
>player:tell("The returns panel includes:");
>for s in (this.returns)
> player:tell(s);
>endfor
>.
0 errors.
Verb programmed.
>
Code for get_returns():
>@verb lispm:get_returns tnt
Verb added (3).
>@program lispm:get_returns
Now programming lispm:get_returns(3).
[Type lines of input; `?' for help; end with `.', `@abort' or `@edit'.]
>return this.returns;
>.
While there is certainly lots to do and later redo (something is going wrong with sending strings actually), basically I have a lisp machine in my lambdacore PersonalMOO VR at this point and hence can just write and use pretty arbitrary lisp programs in-MOO.
In particular, I am looking forward to having âTed Nelson Iconâ MOO object classes, i.e. pictures whose pieces are manipulated by gestures, and have these syncronise with the MOO virtuality in both directions.
Talk on the Mastodon thread link I will put in the comments please. Oh, if you are reading this - having Jeff Shrager with Kent on the show on Tuesday-night-in-the-Americas (0UTC âWednesdayâ). Please tell as many people as you can, since so far I have missed episodes while overseas.
| link | Last. (48 14 22 23 10 2026 4 T 8) |
These two files grew organically for me to use moo.el ( https://codeberg.org/nosrednayduj/moo-el ). I suppose scroo.init.el should be loaded in your emacs init file, and scroo.lisp can be loaded (load not asdf:load-systemed) in whatever common lisp slime repl you have open. slime is the superior lisp interaction mode for emacs ( https://en.wikipedia.org/wiki/SLIME ).
Then, I guess you M-x mud-add-world and specify the MOO you are connecting to, the M-x mud MyNamedMOO from that mud-add-world, then you M-x start-mooing which configures some MOO properties and adds a hook relating to slime.
Now for the âdanger modeâ of letting MOO output drive lisp via emacs, you need to M-: (setq slime-enable-evaluate-in-emacs t) and in slime, (change-class moo:*player* 'moo:lisps). This is basically and specifically to have MOO demons whose brains and abilities basally live in a common lisp slime repl rather than just in the MOO. It is not intended to widely drive emacs, though it needs to communicate back to the MOO via emacs.
;;;; scroo.init.el
(defun mud2slime-line-at-point (&rest rest)
(goto-char (point-min))
(let*
((li (thing-at-point 'line t))
(l (cl-subseq li 0 (1- (length li))))
(lispline "<lisp> "AA")
(lisplinelen (length lispline))
(sortasecret "gQ@.40vHD8M")
(lispalist '(("mIFIHwnD8jU"
. "*slime-repl sbcl*"))))
(cond
((and
(> (length l) lisplinelen)
(cl-search """ (cl-subseq
l 0 lisplinelen))
(equal lispline
(cl-subseq l 0 lisplinelen)))
(let
((keystring
(cl-subseq
l lisplinelen
(+ lisplinelen
(cl-search
"""
(cl-subseq
l lisplinelen))))))
(with-current-buffer
(cdr (cl-assoc keystring
lispalist
:test 'equal))
(slime-eval-save
(format
"(moo:do-lisp moo:*player* %S)"
(cl-subseq l
(+
(cl-search keystring l)
(length keystring)
2)))))))
(t
(slime-eval-save
(format
"(setf (moo:output-line moo:*player*) %S)"
l))))))
(defun start-mooing ()
"Once in *PersonalMOO* M-x start-mooing <ret>
Not idempotent."
(interactive)
(mud-initialize-input-history)
(put 'MOO 'prompt ?>)
(put 'MOO 'filters 'moo-filter-hook)
(add-hook 'moo-filter-hook
#'mud2slime-line-at-point
'90))
;;;; scroo.lisp
(uiop:define-package :moo
(:export #:output-line #:*player* #:player
#:lisps #:do-lisp #:moo #:moo-run))
(defclass moo:player () ((moo:output-line
:type 'string
:writer moo:output-line)))
(defvar moo:*player* (make-instance 'moo:player))
(defmethod
(setf moo:output-line)
(moo::line (obj moo:player))
(setf (slot-value obj 'moo:output-line)
moo::line))
(defclass moo:lisps
(moo:player)
()
(:documentation
"moo:lisps is dangerous moo:player - for your PersonalMOO only.
Execute the rest of the line as arbitrary lisp as a side effect.
The association with a MOO crypt() string - alist
is just to prevent accidental calls in this case."))
(defmethod
moo:do-lisp
((obj moo:lisps)
str)
(with-input-from-string
(*standard-input* str)
(eval (read))))
(defun moo:moo
(str &optional (proc "*PersonalMOO*"))
"str is like moo.el mud-send-string to proc"
(swank:eval-in-emacs
`(mud-send-string
,str
,proc)
t))
(defmacro moo:moo-run
(iobjstr)
`(let*
((proc "*PersonalMOO-bot*")
(rets)
(out)
(err
(ignore-errors
(setq
out
(with-output-to-string
(*standard-output*)
(setq
rets
(multiple-value-list
(with-input-from-string
(*standard-input* ,iobjstr)
(eval (read))))))))))
(moo:moo
(format
nil
";#100.returns = {~{~s~^, ~}};"
rets)
proc)
(moo:moo
(format
nil
";#100.standard_output = ~s;"
out)
proc)
(moo:moo
(format
nil
";#100.last_error = ~s;"
err)
proc)
(moo:moo
(format
nil
";#100.location:announce(~s);"
"The lispm's display lights up enticingly.")
proc)))