Hello, hello!
Over the last week and a half, 8sync participated in the
Spring 2016 Lisp Game Jam.
In short, I had ten days to build a game, so I decided to build a
MUD
on top of 8sync.
And so, mudsync
was born!
> go east
**Smoking Parlor**
This room looks quite posh. There are huge comfy seats you can sit in
if you like.
Strangely, you see a large sign saying "No Smoking". The owners must
have installed this place and then changed their mind later.
There's a door to the west leading back to the grand hallway, and
a nondescript steel door to the south, leading apparently outside.
You see here: a comfy leather chair, a plush leather sofa, Ford Prefect and a
bar stool.
> sit in the chair
You sink into the comfy leather chair.
> say Ahhhhhh, this is the life!
paroneayea says: Ahhhhhh, this is the life!
How'd it go?
I'm happy to say that mudsync was successful overall.
The end released project includes a fairly solid, live hackable
game engine for multiplayer interactive fiction.
This ended up being more of a piece of infrastructure to
build games rather than being a nice game itself, but
it does ship with a mini-world called "Hotel Bricabrac" which
has some interesting things, but is more of a tech demo.
Most importantly, I feel like mudsync is a really nice foundation
for future games, and the amount of world building I did do in it
was a joy to put together.
So! More of that in the future?
Mudsync also made great use of 8sync's new actor model subsystem,
which will publicly premiere in the upcoming 0.2.0 release.
Why participate in the game jam?
At first participating in the game jam may sound merely like an
exercise in fun, and of course it was this too, but greater ambitions
were also at play.
I have plans to use 8sync for some larger projects involving federated
networks, and I wanted to be sure that the technology worked well
enough to invest my time by building something more complicated.
Happily, all seems to have gone well;
I am feeling much more confident in 8sync now having completed the jam
and am looking forward to my larger projects with it.
And along other federation-related lines, I've long been interested in
the overlap between the type of federation-related work I am
persuing with the
ActivityPub standard
MUDs, and the actor model of programming.
MUDS, the actor model, and
ActivityStreams
(the syntactic basis upon which ActivityPub rests)
all share a subject-predicate-object syntax and use message passing as
a primary communication mechanism.
And lest it sound like I've gone completely off my rocker
(maybe I have, of course)
I might note that research in MUDs as a decentralized mechanism of
communication was very high before the web took over everything,
and even an active topic in a couple of research labs!
(I recommend checking out the
Design Requirements for Network Spaces
document to get a sense of this.)
So I figured implementing a MUD might help me better think through
some of this space, and it did!
Furthermore, 8sync itself owes part of its existence to a much older
interest of mine in creating networked games.
XUDD,
the actor model system I wrote in Python and which preceded my
thoughts on how to build 8sync, originally stood for
"eXtensible User Dungeon Design"
(but I gave it a sillier acronymn when its goals shifted).
I haven't lost interest in how to design such systems, and the
implications of them fueled a lot of my reading in this area.
Plus, it sounded like a whole lot of fun!
What does it look like?
From a player's perspective, mudsync is your classic MUD-over-telnet.
There are some things missing, but it works: you can walk around from
room to room, interactive with objects and other players, and so on.
From an administrator / hacker's perspective, mudsync is much more
interesting.
You write out a "game spec" like the following
(which is borrowed from
real game code):
(define lobby
(lol ('room:lobby
<room> #f
#:name "Hotel Lobby"
#:desc
" You're in some sort of hotel lobby. You see a large sign hanging
over the desk that says \"Hotel Bricabrac\". On the desk is a bell
that says \"ring for service\". Terrible music plays from a speaker
somewhere overhead.
The room is lined with various curio cabinets, filled with all sorts
of kitschy junk. It looks like whoever decorated this place had great
ambitions, but actually assembled it all in a hurry and used whatever
kind of objects they found lying around.
There's a door to the north leading to some kind of hallway."
#:exits
(list (make <exit>
#:name "north"
#:to 'room:grand-hallway)))
('thing:lobby:bell
<summoning-bell> 'room:lobby
#:name "a shiny brass bell"
#:goes-by '("shiny brass bell" "shiny bell" "brass bell" "bell")
#:desc " A shiny brass bell. Inscribed on its wooden base is the text
\"ring me for service\". You probably could \"ring the bell\" if you
wanted to."
#:summons 'npc:break-room:desk-clerk)))
This builds the kind of world you might expect from the above:
> look
**Hotel Lobby**
You're in some sort of hotel lobby. You see a large sign hanging
over the desk that says "Hotel Bricabrac". On the desk is a bell
that says "ring for service". Terrible music plays from a speaker
somewhere overhead.
The room is lined with various curio cabinets, filled with all sorts
of kitschy junk. It looks like whoever decorated this place had great
ambitions, but actually assembled it all in a hurry and used whatever
kind of objects they found lying around.
There's a door to the north leading to some kind of hallway.
You see here: a curio cabinet, a shiny brass bell, the Hotel Bricabrac sign, a
frumpy fellow and sign-in form.
> look at bell
A shiny brass bell. Inscribed on its wooden base is the text
"ring me for service". You probably could "ring the bell" if you
wanted to.
> ring the bell
*ring ring!* You ring the bell!
Suddenly, a uniformed woman rushes into the room! She's wearing a
badge that says "Desk Clerk".
"Hello, yes," she says between breaths, "welcome to Hotel Bricabrac!
We look forward to your stay. If you'd like help getting acclimated,
feel free to ask me. For example, 'ask clerk about changing name'.
You can ask me about the following:
'changing name', 'common commands', and 'about the hotel'."
(I inserted the ">" characters to make where the input was a bit
clearer, but otherwise that's verbatim output from the game.)
What's nice is that you can live hack everything that's going on.
And by that of course I mean the kind of usual nice live hacking
you have going on with 8sync and Guile... but it's not only that!
Consider the following challenge: you've just tweaked the description
of the room your friends are hanging out in, but you already
have a room set up in the game's universe, so how on earth
can you replace it?
Luckily Mudsync provides a nice protocol for replacing objects,
and even provides a nice utility to inject it into the currently
running game (the actors know how to "transfer important details",
in this case, the participants in the room):
(inject-gameobj! game-spec 'room:lobby)
Obviously, you don't want to do this for anything other than live
hacking.
But it is nice that you can live modify how things are
running in the game.
For example, at one point a player asked me how they could "emote"
in the game, and we didn't have an emote command, so I added it
and told them, and then we all happily did some emote'ing.
At another point a player said they felt bad for the desk clerk
and wanted a way to dismiss her so she wouldn't have to hang
around the desk, so I added a dismiss command where she'll
thank the player and "run off to do something important"
(as it turns out, doing something important means to go smoke in the
employee break room).
A story about stories
So, speaking of that, the "main game" that ships with Mudsync right
now is a little mo... hotel named "Hotel Bricabrac" full of various
strange and fun things (though, not as many as I would like).
Unfortunately, it lacks any significant story.
There's something that almost resembles a story, and which players
seemed to like: the desk clerk, upon ringing the bell, arrives,
but quickly becomes bored, starts fidgeting distractedly, and
eventually leaves.
When I first started coding this, I had envisioned the character as
perhaps a disaffected and incompetent teenager, but as I was playing
around I started to feel empathy for them... it really was
a boring job that they had, and newcomers ringing the bell all the
time would get annoying.
Thus, if you explore well enough, you might find the
"employee break room", a cage affixed to the exterior of the building.
Whenever the clerk leaves her post, she goes here to smoke and
check her phone and slack off.
But if you talk to her enough in this off-duty space, she'll tell you
that she's a student studying high energy particle physics, that she's
underpaid, but she's working the job because she doesn't have many other
options and her student loans are crushing her.
So she was never really incompetent after all, just overqualified,
bored, and, well, still disaffected.
Some players seemed to enjoy that and expressed empathy for the character
(though it didn't seem to stop them from ringing the bell to provoke
a response from her).
I was glad about that, and I wish I had more time to script in more
things like this.
Indeed, my original ambitions were to build a full and immersive
game as part of the game jam.
I sketched in a notebook a layout for a haunted mansion or college
campus.
My original vision for the jam was that players would be collaborating
in exploring an area on figuring out how to help various ghosts be put
to rest.
You'd find out what's binding them to this world and help them resolve
their problems, and you as a player would have an "achievements" list
showing off just how many interesting things you had done in the
world.
Unfortunately (though not surprisingly), building the engine itself
ended up being enough work that I didn't have a lot of time for
content.
Thus the experimental little world where I was testing various game
mechanics became the actual deliverable of a demo-world to be
played.
In the end, that's probably not the worst situation.
I feel I have a good design on which future games could be built.
And despite the chaos of it, players did seem to have a lot of fun
(I think at one point we had about 8 players or so on the game who
were hanging out and mostly socializing).
The chaotic gathering place on some mysterious property atmosphere
looks a lot like LambdaMOO, and as
LambdaMOO enthusiast Rob Myers
said to me at one point,
"Themeliness is next to timeliness."
It's not such a bad start!
Though I do hope to have more interesting worlds to show in the
future.
Challenges
No sprint like this is without its challenges.
Here were mine:
- It was really hard to decide what "flavor" of sentence
parsing to go with.
Basically you can either have more rich sentences which require
a lot of complicated parsers, but then you can say complicated
things like "Put the melon in the fruit basket."
(Zork/ZIL/Z-Machine type single-player interactive fiction
games tend to go like this.)
Or you can go with more predictable but limited parser
and parse more basic sentences like "put melon in fruit basket".
The former lends itself to more immersive gameplay.
The latter is faster, more predictable, and works better for
either combining with non-text interfaces (maybe a web UI)
and could concievably work with a federated game.
I spent some time
agonizing over this.
In the end I built kind of a hybrid: actors select from
a few prebuilt "rich" command processors which handle all the
basic cases you might want.
- 8sync relies heavily on delimited continuations.
However, sometimes if scheme calls C which then calls out to
scheme again in Guile, things break.
A few things caused this, including using the "@@" special
form to try to get around dependency loops lazily.
Most significantly, since 8sync's actor model system uses
GOOPS, I learned the hard way that GOOPS being very C based
in Guile 2.0 means that I had to forego many interesting
GOOPS features, like generic methods or accessors.
Once I realized this, things went a lot more smoothly,
and I didn't really need these features... but sometimes
I wished I had them.
- You might say, "but Guile 2.2's GOOPS was rewritten in scheme!"
And you'd be right.
Unfortunately, I
uncovered a pretty critical bug
with GOOPS and Guile 2.2, and didn't have time to look into it
seriously.
Again, not a huge deal, but it did take up some time to look into.
- I hit a really weird SIGABRT issue in 8sync's actor model.
Initially
I thought it was a Guile issue,
but it turned out I
just didn't understand some things about how prompts
and exceptions work.
- I really wanted to have a nice text formatter, but I ran out
of time to integrate one.
(If you talk to the hotel owner in the lobby of Hotel
Bricabrac, you'll find he complains about this kind of
indirectly, expressing my wishes to explore something along
the lines of
Skribilo or
fmt.
Alas! No time for it...)
- Again, I would have loved to have more time to build a more
coherent story.
But time is always a challenge in these things!
What's next?
So mudsync will be a background hacking task for me probably
for a while, something to hack on for my and friends' amusement
every now and then.
I may end up writing some small and interesting stories.
But it won't be my main project.
But it probably will be a basis for research (and fun)
for me.
It's a nice system to test out ideas in.
And I'll probably add some cool optional components, like
persistent-world-supporting databases.
(Maybe even player-scripted items?
Unlikely in that it would require a carefully crafted sandboxed
execution environment, but don't think I haven't been thinking
about it...!)
In the meanwhile, if you want to check it out, the
source is available.
Patches most welcome!
And if you hop in #8sync on irc.freenode.net, maybe you can
find the address of the current dev server!
Thanks to Rob Myers and Jason Self, who both know a lot more about
interactive fiction than I do and answered a lot of questions
and offered ideas.
Thanks also to everyone who gave the game a try.
I hope it was as fun for you as it was for me!
Happy hacking...