If you have any comments to make about this document, please feel free to contact me at morwen@evilmagic.org. Please note that it was largely written in the early 2000s
The Telnet protocol uses 0xff as an escape character, both ways. To send the data-byte 0xff, a client or server MUST send 0xff 0xff. (IAC IAC).
If a program justs want to ignore the telnet stuff, then it's easy to do.
IAC IAC -> data byte 255 IAC WILL/WONT/DO/DONT xxx -> ignore IAC SB xxx ... IAC SE -> ignore IAC other -> ignore
The telnet parser MUST be written as a state machine on a layer on its own - there's no reason why telnet sequences are not allowed to come in the middle of an escape sequence. Do not assume that telnet or ansi sequences are not split across packets.
The client MUST defer sending its own telnet negotiation requests until such time as it knows the server will be able to understand them. This means getting WILL or DO request for an option other than ECHO. (In the 00s there was a fairly nasty interaction between some Dikus and enthusiatic clients such that if the client sends IAC WILL LINEMODE, they fail to filter out the LINEMODE (it being 34, and thus the printable character "), and think it intended as part of the user name. If the server sends IAC sequences to a client that it doesn't know supports it, the worst that can happen is that the client displays crap on the screen.)
According to the base telnet spec, only US-ASCII is allowed on the wire. There is a binary option but this is unhelpful, as it changes the end-of-line codes from the standard values to platform-specific ones. Therefore, BINARY mode MUST NOT be requested by servers or clients, however clients MAY accept it if offered.)
Of course, this notional restriction to US-ASCII was never really honoured and instead people used whatever local codepages they liked. In the early 00s this presented a problem in that computing was changing to UTF-8 encoded Unicode, and our survey revealed very little support for RFC 2066's CHARSET option. This has changed and Cryosphere 2.7.3 (after the server migration) will support CHARSET.
There's a lot which a normal terminal will do, and most MUD clients implement hardly any of it. But as long as the server keeps itself to a limited set, all is safe. See ECMA 48 (downloadable for free as a pdf) for the full details, the minimal collection is -
code | meaning |
ESC[0m | reset all attributes and colours to default |
ESC[1m | bold |
ESC[30m ... ESC[37m | black fg .. white fg |
ESC[40m ... ESC[47m | black bg .. white fg |
There are some other useful ones that are less reliably implemented.
ESC[3m | italic (unreliable) |
ESC[4m | underline (unreliable) |
ESC[22m | bold off (unreliable) |
ESC[23m | italic off (unreliable) |
ESC[24m | underline off (unreliable) |
ESC[39m | default fg (unreliable) |
ESC[49m | default fg (unreliable) |
We think that the client should default to white-on-black or, if you must, some other light-on-dark colour scheme where neither the background nor the foreground is one of the 7 other standard colours. If it doesn't, then the server's colour scheme might result in invisible text. The client MAY allow the user to customise their colour scheme to be black-on-white or however they like.
ESC[m is defined to be an alias for ESC[0m, but quite a lot of mud clients don't know this. Many even have problems with parsing sequences like ESC[0;1;3;32;45m. These MUST be supported by clients, but SHOULD NOT be sent by servers - they provide no advantage bar a few network bytes.
xterm originated a very useful extension to a 256-colour thing, including a 6x6x6 colour cube. To set fg, do ESC[38;5;nm, and bg ESC[48;5;nm. This seems to have caught on in quite an exciting way.
There is no need in a mud server to worry about programming the palette, the mud client should do any reprogramming of the palette to the standard one if possible. 0..15 are the regular colours, once in normal form, one in bright form. 16-231 are a 6x6x6 colour cube. To get the offset, (red * 36) + (green * 6) + blue. Finally, 232-255 is a greyscale. 232 is very dark gray, 255 is very light gray. See xterm's 256colres.h for the exact RGB values.
There is even now a 24-bit colour extension of this where you can specify actual RGB values using e.g. ESC[38;2;r;g;gm (or 48 for background).
Any other sequences than ESC[...m are not implemented by most mud clients and SHOULD NOT be used unless known safe through other means. This includes cursor movement.
broken client | nice client | |||||||
user sees... | > |
A "turn local echo off always" MAY be provided, but the newline must still be printed even if this is turned on. Likewise, if the mud has turned echo off, you MUST NOT put anything into the buffer, as the mud supplies its own newline.
The mud sends "IAC WILL ECHO", meaning "I, the server, will do any echoing from now on." The client should acknowledge this with an IAC DO ECHO, and then stop putting echoed text in the input buffer. This worked great in base telnet, and, since a MUD only does this when entering a password, the client could do other password things, likesuch as blank or asterisk out the text box. We also strongly recommend that text entered in server-echoes mode should also not be placed any command history.
When the mud wants the client to start local echoing again, it sends "IAC WONT ECHO" - the client must respond to this with "IAC DONT ECHO".
See RFC 857.
The usual way of doing this is (a) to send IAC GA after a prompt, or (b) for the mud to send IAC WILL TELOPT_EOR, then the client to send IAC DO TELOPT_EOR, and then the mud will send IAC EOR after every prompt. (see RFC 885 for EOR). According to the tinyfugue documents, some muds use "*\b" as a prompt terminator. I have never seen this in the wild.
However, there is also the problem of asynchronous communication (ie players doing stuff that appears on your screen). In a traditional command line interface this is limited to (a) jobs you did in the background and aren't redirecting output from, or (b) bizarre archaic stuff, like write(1). So the fact that a typical terminal messes up its display with these is accepted, as just part of the underyling reality that multiple processes have access to the same tty and no way of coordinating their output.
This is generally not considerd OK for a MUD. A mud really would like to be able to withdraw the prompt that has been sent, in order to not spam your window with unused prompts while you are AFK. There is no standard way of doing this.
Some LP muds and possibly others assume that text up to the prompt terminator (however specified) be spirited away into some other prompt buffer - and any text they send from then on be displayed before the prompt.
sent from mud | expected to be displayed | displayed under 'telnet' |
100/100>[IAC GA] | 100/100> | 100/100> |
You are hungry.\r\n | You are hungry. 100/100> | 100/100>You are hungry. |
This is indeed what tinyfugue does, and a few others, and can be implemted carefully on the client-side so as to avoid any breakage.
The very old way to do this, done in Abermuds and perhaps a few others, is to send "100/100>\rYou are hungry.\r\n100/100>". This has the downside that not all the prompt gets wiped if the message is shorter than the prompt. ESC[2k could be used for this if the mud knows it has a client that supports it. (Cryosphere sends this when it detects that a client is capable of it.) This could lead to a nasty race condition, though, so perhaps the magic prompt marking approach does have merits.
The client should not send out a useless generic name like "ansi" or "vt102" (or worse, "linux"). Instead it should send out a name like "zmud", "mushclient", "lyntin". This allows the authors of the mud server to make decisions based on what the mud client is, and what the known capabilities are. However, a bare client name in itself is not itself very useful.
According to the protocol if the server doesn't understand the terminal type, it can request another one, and the client can go through a list. For example, zmud, vt102, ansi, unknown. The end of the list is signalled by repetition.
A proposal with some implementation, named MTTS adds structured meaning to the TTYPE field by appending an ASCII decimal coded bitmask as an affix to a sentinel terminal type "MTTS". This specification does not give an upper bound on the number of bit values that may be encoded, so we urge caution when interpreting this value (perhaps try and format it back and see if it matches).
We suggest that a version number could be placed as an additional item here. The next version of crystal will be sending "crystal:001_005_000" as a TTYPE, if the server makes enough requests. We suggest that the version number is placed after ":", and be formatted in such a way that strcmp() sorts it correctly, so that servers can test for whether the client is older or newer than particular releases without knowing how your user-facing version numbers are structured.
The server sends IAC DO TELOPT_NAWS, the client sends IAC WILL TELOPT_NAWS. It then immediately, and whenever the window size changes (even if its just a case of the window staying the same size and the font size changing), sends the server the window size. The server makes no requests. I'd seen some clients that agree to do NAWS and then await a IAC SB TELOPT_NAWS SEND IAC SE from the server before actually sending it out. This is incorrect.
The format of the NAWS subnegotiation is specified in RFC 1073. To quote -
The syntax for the subnegotiation is: IAC SB NAWS WIDTH[1] WIDTH[0] HEIGHT[1] HEIGHT[0] IAC SE As required by the Telnet protocol, any occurrence of 255 in the subnegotiation must be doubled to distinguish it from the IAC character (which has a value of 255).When we did our survey, not doubling the 255 was a common error, but for a case that doesn't show up very often. And it's a nasty one - it can lead to the telnet stack getting into an inconsistent state, and possibly ignoring all future input. Don't be this person. Also remember, the width and height are in "network byte order" (big-endian).
\33]0;
string\33\
This could be used by a mud to display status info that isn't appropriate for a prompt,
such as player name and mud name and location within the mud.
We're not sure if anyone apart from us ended up doing this. It's kind of cool though!
Note that xterm's help files say to use "\a" [BEL] as a terminator. If you do this and it somehow ends up in mudlet it will swallow all output until the end of time. So only do this on clients you know are safe.
There is a v1 of the spec which contains an error in how a telnet suboption should be implemented, which appears to be harmless to implement, and then MCCP2 which fixes this (which had to use an entirely different subprotocol number, burning one of our precious 256 possible telnet options.
Perhaps someone should invent a TELNET option to add a TELNET extension to increase the width of telnet option fields to a word. Oh wait, they have:
Even if one were to ignore the issue of protocol layering design, this proposal just simply means that a programme, currently conformant that is fed data in this new format, will choke. Let us suppose that a server is attempting to negotiate EXOP extended protocol 1023 (which would correspond to the unicode codepoint U+03FF). The UTF-8 for this character is <CF> <80>. This will therefore come out on the wire as
IAC WILL <CF> <BF>A conforming TELNET implemetation will implement ignore only the first octet after the WILL, and pass through the <BF> to its terminal stack. If this is then sent for display as Latin1, a spurious ¿ may appear. If it is sent for display UTF-8 it may be displayed as the Unicode replacement character, ?.
We therefore think it however unwise for the community to go forward with this, as this means a compliant RFC 854/855 implementation will not be able to succesfully ignore unknown TELNET commands.
We note that in fact RFC 861, Telnet Extended Options - List Option (EXOPL), written by Jon Postel and Joyce Reynolds, in 1983, adopted as an internet standard in the same month as TELNET itself, already provides for 256 additional TELNET options. While this does not use one of the 'reserved' bytes by EXOP, we think it it is much more technically sound specification.
In the unlikely event that it was ever deemed necessary to add more codes we could take a similar approach by allocating an additional new TELNET option to negotiate a new mode similar to RFC 861, but encoding larger integers as new options.
The author is currently trying to register MCCP2 as an option code, citing decades of usage. We will report back how that goes.
We no longer recommend its use and will be removing it from the Cryosphere in 2.7.3.
For hyperlinks we suggest OSC8. We think for URI schemes of "send:" and "prompt:" for mud specific use to be suitable.
Having said that, it seems safe enough to implement.