Still looking for a Ruby MUD client

J

Jacob Fugal

Okay. The thing making this difficult is handling things that span across
tags. Running a gsub that matches something entirely within a single
tag won't produce problems, nor will reversing it, nor will anything else
you do to it that I can think of. Matching a pattern across tags I'm
pretty sure can be done, but it'll probably be a pain to do, and I'm star= ting
to wonder if there's any point to it. Substitution across tags is probabl= y
doable if you can solve the pattern matching problem, but how do you
decide sensibly what ends up in what tag?

A solution, similar to that employed by ncurses and many other UI
systems, is to use the concept of an extended character. Each
character in the string is flagged with applicable attributes.
Translating marked up ASCII to a list of extended characters is easy
enough: maintain a bitmask of attributes and turn them on/off as you
encounter tags; apply the current bitmask to each character
encountered.

Translating back from an extended character string to ascii markup can
be accomplished with an algorithm like the following (I'm using an
array instead of bitmask for readability):

def encode( extended_chars, start_flags=3D[], clean=3D0 )
current_flags =3D start_flags
encoded_ascii =3D ''
extended_chars.each do |char|
(current_flags - char.flags).each do |flag|
encoded_ascii << flag.close_tag
end
(char.flags - current_flags).each do |flag|
encoded_ascii << flag.open_tag
end
current_flags =3D char.flags
encoded_ascii << char.ascii_char
end
if clean
current_flags.each do |flag|
encoded_ascii << flag.close_tag
end
current_flags =3D []
end
return (encoded_ascii, current_flags.clone)
end

Ideally, the list of encoded characters would be encapsulated in an
object that acts like a string (implementing gsub, reverse, etc.) The
operations would rearrange/remove individual extended characters from
the object without changing any of the flags associated with any one
character.

As an example application, your string would decode as follows:

something =3D decode("A <C red>red</C> and <C blue>blue</C> baseball bat.")
# =3D> A, ' ', r|red, e|red, d|red, ' ', a, n, d, ' ', b|blue, l|blue,
u|blue, e|blue, ' ', b, a, s, ...

The regex /red and blue/ would match this substring
# r|red, e|red, d|red, ' ', a, n, d, ' ', b|blue, l|blue, u|blue, e|blue

That substring is replaced with the substring (since it wasn't encoded):
# o, s, t, r, i, c, h

And the result is:
# A, ' ', o, s, t, r, i, c, h, ' ', b, a, s, ...

Obviously, no part is red or blue. Assume we'd actually marked up "ostrich"=
as
# "<C red>os</C>tri<C green>ch</C>"
# =3D> o|red, s|red, t, r, i, c|green, h|green

And matched against the shorted substring "d and bl" then the result would =
be:
# A, ' ', r|red, e|red, o|red, s|red, t, r, i, c|green, h|green,
u|blue, e|blue, ' ', b, a, s, ...
# =3D> "A <C red>reos</C>tri<C green>ch</C><C blue>ue</C> baseball bat."

Jacob Fugal

DISCLAIMER: None of the above is intended to be complete, bug-free or
efficient. An actual implementation would need all of those. This is
just meant to be an example algorithm that would make the discussed
operations possible.
 
M

Morgan

Jacob said:
A solution, similar to that employed by ncurses and many other UI
systems, is to use the concept of an extended character. Each
character in the string is flagged with applicable attributes.
Translating marked up ASCII to a list of extended characters is easy
enough: maintain a bitmask of attributes and turn them on/off as you
encounter tags; apply the current bitmask to each character
encountered.

Seems reasonable, but I do wonder how efficient the process would be.
Something like that on each character makes me nervous. `.`
As an example application, your string would decode as follows:

something = decode("A <C red>red</C> and <C blue>blue</C> baseball bat.")
# => A, ' ', r|red, e|red, d|red, ' ', a, n, d, ' ', b|blue, l|blue,
u|blue, e|blue, ' ', b, a, s, ...

The regex /red and blue/ would match this substring
# r|red, e|red, d|red, ' ', a, n, d, ' ', b|blue, l|blue, u|blue, e|blue

That substring is replaced with the substring (since it wasn't encoded):
# o, s, t, r, i, c, h

And then you'd get a bug report about how your replace is dropping color...

At least, the behavior I would expect is to preserve the existing coloration.
(Of course, then I specifically picked an example where there's no obvious
sensible way to do that. Still, I think keeping the color would generally be
expected.)

-Morgan
 
J

Jacob Fugal

=20
Seems reasonable, but I do wonder how efficient the process would be.
Something like that on each character makes me nervous. `.`

Hence my disclaimer. :) I'm just demonstrating that it's possible,
making it efficient will be a big project.
=20
And then you'd get a bug report about how your replace is dropping color.= ..
=20
At least, the behavior I would expect is to preserve the existing colorat= ion.
(Of course, then I specifically picked an example where there's no obviou= s
sensible way to do that. Still, I think keeping the color would generally= be
expected.)

Well, once possible workaround for this is to pass initial state when
decoding raw ascii. However, as you state, this example is ambiguous.
Assume we replaced the shorter substring "d and bl" with the raw
"ostrich", using this technique. We'd end up with something like:

"A <C red>reostrich</C><C blue>ue</C> baseball bat."

This is because the raw string "ostrich" was passed char.flags as its
initial flags (where char is the extended char object for the 'd' in
red, the first character in the replaced substring) rather than an
empty mask.

Jacob Fugal
 
S

Sy

=20
I'm not quite sure why you'd want to do that, let alone how. `.`;

I can have the scripting notice if I was afk or sleeping or whatever,
and automatically perform an action and then repeat the command. So I
can "look" while sleeping and it'll notice that I'm asleep and wake up
to perform the action.

For mmucl, it is:
parse [lindex [cline history] end]

This functionality has been remarkably valuable to me. I hate going
"whoops, was afk.. afk <cursor-up> <enter>".. it's a small thing,
but very handy imo.

=20
My experience with OLC suggests that the designers disagree with
you. `.`

Well if I ever meat them in a dark alley, I'll punch their lights out.
A lot of these old fashioned "standards" are just leftovers from more
shortsighted days. Like, oh, email wrapping settings. Geeze, offline
mail readers from the BBS days had it right, email clients these days
are just pathetic.

Okay. The thing making this difficult is handling things that span across
tags. Running a gsub that matches something entirely within a single
tag won't produce problems, nor will reversing it, nor will anything else
you do to it that I can think of. Matching a pattern across tags I'm
pretty sure can be done, but it'll probably be a pain to do, and I'm star= ting
to wonder if there's any point to it. Substitution across tags is probabl= y
doable if you can solve the pattern matching problem, but how do you
decide sensibly what ends up in what tag?

That was some awesome description for reversing..

and it went sailing over my head.. whoa. I see some of the problems
which people face with string manipulation like this.

But as a user, I could live without any kind of reversing
functionality.. I could live without matching by-colour in fancy ways.
Substitution can be based on matching the plain text.. that's just
fine.
 
G

Greg Millam

This is getting to be a long conversation that's diverging from Ruby and
onto more generic mud-related topics. But anyway:

Whoops, I seem to have caused some confusion in my last reply. There's
two things I had in mind here while writing that:

1) I verged off a bit and discussed both client and server bits.
(i.e: MXP, and having MXP deal with markup - and this includes color,
html-like clickable links, even urls to sound files, etc.)

2) I'm from a PennMUSH background. I used to be big on Smaug, Rom and
other Diku-Derivatives up until I discovered Penn. Now I won't go back.
:D. (Can you play scrabble, all forms of poker, boggle, chess, bridge,
and many, many more games on a Diku? How about writing 'softcode'
on-game to make almost anything imaginable?)

MUSHes (PennMUSH, TinyMUSH, and Muxes) are not only entirely created
online, but also entirely scripted online. They provide a very powerful
scripting engine to its users that fairly resembles what you'd get if
you had lisp with string interpolation and function(...) rather than
(function,...) The "programming" available on Diku derivatives is like
using old Apple ][e BASIC for making websites in comparison.)

On protocols:

This is important to both the server and the client. Many users or
clients would enjoy an ability to have certain lines of input flagged as
for a certain purpose. For you mudders, this may be "shout"s or "OOC" or
other channels. For MUSHers, we have explicit channels that begin with
<channelname>

Now suppose your client wanted to filter shouts into a separate window
so you can follow a global conversation while you're hacking a hundred
monsters to bits. It would miss multi-line shouts:

Joe the Slayer shouts, "This is a
shout with more
than one line!"

Your shout window would catch: 'Joe the slayer shouts, "This is a'

Not too fun. Having some form of protocol *available* to (but not forced
upon) the user is a Good Idea (tm). i.e:

<Chat: Shout>Joe the slayer shouts, "This is a
shout with more
than one line!"</Chat>

On text manipulation:

MUSHes provide ways for world-builders to customize the description of
all the rooms at the same time. You can write "who" listings, combat
systems, and more - all through string manipulation. String manip in
'mushcode' is considerably easier to deal with than Ruby.

For example, a common 'room parent' would be:

@descformat room parent=[repeat(-,10)]r[wrap(%0,10)]%r[repeat(-,10)]

@desc room 1=This is a fairly long description that is wrapped at 10 characters.

look
----------
This is a
fairly
long
descriptio
n that is
wrapped at
10
characters
 
M

Morgan

This is getting to be a long conversation that's diverging from Ruby and
onto more generic mud-related topics. But anyway:

*shrug* I'm not sure. I know I'm still looking at all of this with the intent
of learning things to help me write the client I'm working on. And since
it's in Ruby... `.`
Whoops, I seem to have caused some confusion in my last reply. There's
two things I had in mind here while writing that:

1) I verged off a bit and discussed both client and server bits.
(i.e: MXP, and having MXP deal with markup - and this includes color,
html-like clickable links, even urls to sound files, etc.)

Yeah. Personally, I'm a little too old-fashioned for that stuff (despite
not being that old). Colored text is about fancy enough for mud output
for me.

(Now, since we're already getting sidetracked... ^_- )
(For reference puproses, pretty much the only MU* I've found that I'm
willing to spend time on is Aardwolf, so most of my experience will
relate to it.)
2) I'm from a PennMUSH background. I used to be big on Smaug, Rom and
other Diku-Derivatives up until I discovered Penn. Now I won't go back.
:D. (Can you play scrabble, all forms of poker, boggle, chess, bridge,
and many, many more games on a Diku? How about writing 'softcode'
on-game to make almost anything imaginable?)

Would I *want* to play those things on the MU* is what I'd ask. `.`
Most times I'd rather use other specialized programs for those things.
(Also why I don't have much interest in social MUSHes - I consider
IRC more suitable for socializing.)
MUSHes (PennMUSH, TinyMUSH, and Muxes) are not only entirely created
online, but also entirely scripted online. They provide a very powerful
scripting engine to its users that fairly resembles what you'd get if
you had lisp with string interpolation and function(...) rather than
(function,...) The "programming" available on Diku derivatives is like
using old Apple ][e BASIC for making websites in comparison.)

I've actually tried doing some mush coding. It's pretty powerful, but
it didn't seem possible to do the things I wanted to without horrifying
monstrous long constructions with a few dozen brackets here and
there. I could have made what I wanted easier in mIRC script...

Not being familiar with Diku olc, I don't really know what it's like. (I'm
under the impression that Aardwolf's is fairly different. Not surprising
given that it's three generations descended from Diku...)
On protocols:

This is important to both the server and the client. Many users or
clients would enjoy an ability to have certain lines of input flagged as
for a certain purpose. For you mudders, this may be "shout"s or "OOC" or
other channels. For MUSHers, we have explicit channels that begin with
<channelname>

Aardwolf's mostly look like
<playername> <channelname>: <message>

A few are more complicated, but mostly that's it.
Now suppose your client wanted to filter shouts into a separate window
so you can follow a global conversation while you're hacking a hundred
monsters to bits. It would miss multi-line shouts:

Joe the Slayer shouts, "This is a
shout with more
than one line!"

Do many MUs send them as actual multiple lines? On Aard, it appears
that most such things are sent as single long lines, to be wrapped at the
clients discretion. (Which is distinctly different from the way room
descriptions are done; I believe hardwrapping those is pretty much obligatory.)

(I think the only thing I've seen on a channel that uses hard-wrapping is
that imm-only curse-only social with the ascii art of the raised middle
fingers...)
Your shout window would catch: 'Joe the slayer shouts, "This is a'

Not too fun. Having some form of protocol *available* to (but not forced
upon) the user is a Good Idea (tm). i.e:

<Chat: Shout>Joe the slayer shouts, "This is a
shout with more
than one line!"</Chat>

Maybe a nice idea, but probably something that needs to be taken up
on MU* design MLs before here. No point in writing a client to implement
it before there's someone who'll write a server to use it.
On text manipulation:

MUSHes provide ways for world-builders to customize the description of
all the rooms at the same time. You can write "who" listings, combat
systems, and more - all through string manipulation. String manip in
'mushcode' is considerably easier to deal with than Ruby.

For example, a common 'room parent' would be:

@descformat room parent=[repeat(-,10)]r[wrap(%0,10)]%r[repeat(-,10)]

Morgan.sendToWorld("flee\nflee\nflee\n")

I'm not sure I could figure out what that did... ever, if I didn't already
know.
[snip]
Terminal settings, or: "Why we should send width and height"

I was really more asking "Does anyone want us to", but this answers
that too...
As for the terminal sending its width/height: users can access each
other's terminal sizes by using width(<user>) and height(<user>). Plug
that in to the above @descformat and use:

repeat(-,sub(width(%#),2))%r[wrap(%0,sub(width(%#),2))]%r[repeat(-,sub(width(%#),2))]

And all your rooms will have their descriptions cleverly hard-wrapped to the
user's terminal size.

Hmmm. And that's done with standard telnet control codes, I believe?
Difficult to do with the current telnet implimentation. I don't believe there's
any way to tell it to respond to additional telnet commands other than the
ones that are built in.

What, I wonder, would something coded the way you describe do if the
client is unwilling or unable to provide that information?

And what happens if the size of their terminal changes?

-Morgan, fails saving throw to resist making a comment about how she
isn't sure she wants people to be able to see her sizes so easily...
 
M

Morgan

Jacob said:
Hence my disclaimer. :) I'm just demonstrating that it's possible,
making it efficient will be a big project.

It'd be something interesting to see at any rate, even if it weren't
as efficient as possible immediately. It's another of those things I'm
not sure I'm up to programming myself though. ^_^;

As I understand it, each color would have it's own bit, right?

So for ansi color that'd be 8 bits for basic colors, and a bit each for
bold, italic, underline, and strikethrough. What else other protocols
might need I'm not sure about, which is probably one of the things
complicating this...

A user might also find it desirable to be able to define their own
desired coding to be put into the text, to simplify later use. (As in,
why shouldn't I be able to tell it "when it changes to bold blue text,
send me this" and so forth, instead of sending me some other set
of tags that I then have to figure out how to parse?)

For that matter, I'm wondering just how configurable it could be made
without impacting speed. After all, such a thing could have uses other
than this MUD stuff...
Well, once possible workaround for this is to pass initial state when
decoding raw ascii. However, as you state, this example is ambiguous.
Assume we replaced the shorter substring "d and bl" with the raw
"ostrich", using this technique. We'd end up with something like:

Doing it that way I think would make more sense. In most sensible
applications that's probably the behavior you'd want, and as for
insensible applications... well, sometimes you can protect the user
only so much.

-Morgan
 
M

Morgan

Sy said:
I can have the scripting notice if I was afk or sleeping or whatever,
and automatically perform an action and then repeat the command. So I
can "look" while sleeping and it'll notice that I'm asleep and wake up
to perform the action.

For mmucl, it is:
parse [lindex [cline history] end]

This functionality has been remarkably valuable to me. I hate going
"whoops, was afk.. afk <cursor-up> <enter>".. it's a small thing,
but very handy imo.

Oh, I see. Not something I would want to do, but I can see how it would
be done... once I implement a command history. `.` (Which you've just
saved from being done purely gui-side.)
Well if I ever meat them in a dark alley, I'll punch their lights out.
A lot of these old fashioned "standards" are just leftovers from more
shortsighted days. Like, oh, email wrapping settings. Geeze, offline
mail readers from the BBS days had it right, email clients these days
are just pathetic.

Still, when one is writing a mud client, one must deal with what the mud
that exists is going to output. And I don't think it's unreasonable for the
server to expect at least a certain width. Be impossible to make a good
score display otherwise...
But as a user, I could live without any kind of reversing
functionality.. I could live without matching by-colour in fancy ways.
Substitution can be based on matching the plain text.. that's just
fine.

Even then it could still be a real pain to do.

I'm not sure I even want to try coding for substitution until I can find
a clean way of handling this.

-Morgan
 
J

Jacob Fugal

As I understand it, each color would have it's own bit, right?
=20
So for ansi color that'd be 8 bits for basic colors, and a bit each for
bold, italic, underline, and strikethrough. What else other protocols
might need I'm not sure about, which is probably one of the things
complicating this...

Well, you'd have one bit region for color. Colors are mutually
exclusive: you can't have text that's red and green at the same time.
So, for 8 ANSI colors as you said, the color bit region would be 3
bits.

On top of that, though, I'm of the camp that any markup should be
semantic, as opposed to immediate. Rather than marking a chunk of text
as "red", I'd prefer to mark it as, for instance "noun", and then the
client is at liberty how to display nouns (eg. by coloring them red).

One way to make this extensible is for the server to send a header
(once, on login) which defines the possible semantic flags and their
corresponding bit regions. That way one mud server can have one set of
flags, another have other sets of flags, or the same flags serialized
differently, and my client can interpret each appropriately.

This type of markup wouldn't handle the idea of embedded links very
well (it is possible though, ask if you want to know how) since it's
pretty much the equivalent of an XML tag with no attributes. But for
simple semantic markup it could work.

Jacob Fugal
 
J

Joshua Haberman

--Apple-Mail-1--709150927
Content-Transfer-Encoding: 7bit
Content-Type: text/plain;
charset=US-ASCII;
delsp=yes;
format=flowed

Does this mean that a "mud-telnet library" ought to be made, to help
various other clients with these intricacies?

I'm not a MUDder, but I'm working on a project that probably already
does a lot of what you want. It's called 'silkscreen' -- here's its
minimalist web page:

http://silkscreen.sourceforge.net/

In a word, it's designed to be an uber-scriptable text mode
environment, where you can run different sorts of applications in
nonoverlapping windows. The first such application is a terminal
emulator like GNU screen, so that you can run a shell.

And once you're running a shell, you could telnet to a MUD server.

I haven't actually written any scripts in silkscreen yet, but it
wouldn't be much work to support an interface like:

TerminalWindow.add_line_callback { |window, line|
if line =~ /you are hungry/
window.insert_keypress("eat\n")
end
}

If you're interested, download silkscreen and give it a try. Let me
know what you think.

I'm working on silkscreen all the time -- right now I'm working on
scrollback support.

Josh

--Apple-Mail-1--709150927--
 
S

Sy Ali

http://silkscreen.sourceforge.net/
=20
In a word, it's designed to be an uber-scriptable text mode
environment, where you can run different sorts of applications in
nonoverlapping windows. The first such application is a terminal
emulator like GNU screen, so that you can run a shell.
=20
And once you're running a shell, you could telnet to a MUD server.

Wait a second.. does this mean that I could also have a shell and have
MUD-style scripting functionality with *any* pure-text console
application from bash to .. anything?

that's.. huge..

whoa
 
J

Joshua Haberman

Wait a second.. does this mean that I could also have a shell and have
MUD-style scripting functionality with *any* pure-text console
application from bash to .. anything?

In essence, yes. And your scripts could do things like open other
terminals and send keystrokes to them. Or add color to data as it
goes by (like compiler output).

There are a couple caveats. It could be hard to set up the triggers
you want, because your terminal doesn't necessarily know what a
"line" of input is. We can look for a "\n" in the input, but that
might not always work. Consider an IRC client that has an area when
you edit your text. When you hit "ENTER," the client will send your
text to the channel and clear the text area, but might never send a
"\n" to the terminal. But you might be able to build some kind of
heuristic that looks for outgoing "ENTER" keypresses.

Josh
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,772
Messages
2,569,593
Members
45,104
Latest member
LesliVqm09
Top