07 Jun, 2011, ProjectMoon wrote in the 1st comment:
Votes: 0
In my quest to understand the telnet protocol (which started this morning), I am trying to do disable and enable linemode on the server side. Right now, this is in a very controlled environment, so I'm not too worried about special cases or different telnet clients. Currently, to disable line mode, I am sending:

IAC DO LINEMODE
IAC SB 0 1 IAC SE
\r \n

The 1 above is for the EDIT mode. All of the above is sent in a single stream. This does indeed disable linemode, but I don't think it's correct because I get a bunch of weird crap back from the client. For enabling, I am sending the reverse:

IAC DO LINEMODE
IAC SB 1 1 IAC SE
\r \n

That does not re-enable linemode.

I thought I was understanding the RFC properly, but obviously I'm not. What is the correct way to switch between character mode and linemode?
07 Jun, 2011, Scandum wrote in the 2nd comment:
Votes: 0
The correct way to enable character mode is to disable localecho and enable supress go ahead.

More precisely:

SERVER: IAC WILL SGA
CLIENT: IAC DO SGA
SERVER: IAC WILL ECHO
CLIENT: IAC DO ECHO

I'm not exactly sure how telnet handles disabling character mode, but with TinTin++ you can't disable SGA (guess that ought to go on my todo list), so you need to send IAC WONT ECHO to put tt++ back into line mode. I'm quite sure that approach would work with telnet as well. I'm not aware of other mud clients supporting the feature.

Windows telnet automatically goes into character mode the moment it receives a telnet negotiation, which can't be undone as far as I know. Can't remember if I ever tried sending sending IAC WONT SGA.
07 Jun, 2011, ProjectMoon wrote in the 3rd comment:
Votes: 0
Ok, so then what exactly was I doing with the subnegotiation?
07 Jun, 2011, Scandum wrote in the 4th comment:
Votes: 0
You mean the LINEMODE RFC at http://tools.ietf.org/html/rfc1116 ?

I think that specification is a classic case of TL;DR.
07 Jun, 2011, ProjectMoon wrote in the 5th comment:
Votes: 0
That's the one I was reading earlier, yes. I was trying to make an editor-type thing where text entry was on the bottom. And I figured the first step was to turn off line mode.
08 Jun, 2011, Scandum wrote in the 6th comment:
Votes: 0
Pretty much all the telnet BBSes use SGA and ECHO to enable character mode, and Linux systems go that route as well.
08 Jun, 2011, ProjectMoon wrote in the 7th comment:
Votes: 0
Hmm. Does enabling SGA and Echo give you enough control over the screen to make sort of a curses-like UI?
08 Jun, 2011, Scandum wrote in the 8th comment:
Votes: 0
You can look for the escape sequences for arrow left, right, up, down, etc, and handle that. For the graphical part you'd have to use VT100 escape codes.

Something else you may want to look into is MSDP. Rather than creating the interface you provide all the data the client needs to create one. TinTin++ comes equiped with utilities for creating a VT100 interface, and MUSHClient comes with utilities for creating a graphical interface.

Here's a screenshot of KaVir's MSDP MUSHClient GUI: http://www.godwars2.org/images/plugin_v1...
09 Jun, 2011, quixadhal wrote in the 9th comment:
Votes: 0
The answer is, it depends. Enabling character mode gives you the ability to have full control over the terminal, but it depends on the specific terminal as to how to go about that.

Scandum is wearing the Everything-Is-VT100 blinders, so he'll tell you to use raw VT100 sequences and hope for the best. To be fair, that will probably work for a good number of terminal emulators these days. However, the more you rely on actually making things work exactly right, the less well the one-size-fits-all method works.

If you *REALLY* want to do it right, you need to properly use the TELNET TTYPE negotiation to retrieve the entire list of terminal types (it sends multiple values; one each time you do the negotiation, untill it repeats the same one, meaning you hit the end of the list). Using those, you need to check your local termcap/termlib library for terminal capabilities and query that library for the correct escape sequences to perform the function you want to perform.

Of course, this entails a lot of reading, and some folks would rather take the easy route and tell people who try to use substandard clients to just pick a different one. Windows TELNET is an example of this, it has a known bug with character mode and character echoing where the two states become linked incorrectly.
09 Jun, 2011, Scandum wrote in the 10th comment:
Votes: 0
95% of the newer terminals identify themselves as xterm, the others pick rxvt or something generic like vt100. This is because it's near impossible to get a termcap listing, and as you got 50 different terminal emulators calling themselves xterm the whole termcap thing doesn't work. On the bright side I got thousands of people using tt++ on a large variety of terminal emulators, and non complain about the raw vt100 sequences not working.
09 Jun, 2011, ProjectMoon wrote in the 11th comment:
Votes: 0
All I really envisioned was a static text input line, maybe separated from game text by a bar. When I was working on RingMUD, it wasn't like that–although it did use character mode to "preserve" echoed input as each new command entry prompt showed up. So if some text showed up while you were typing, it wouldn't be as jarring.

I've decided to start work on a new, much simpler MUD engine using NodeJS. I also decided to learn the telnet protocol at the same time, mostly because there's no telnet library for Node yet. So that's where all of this is coming from. Work on the telnet library is actually progressing somewhat. I'm using Anachronism as a reference for the Q Method and it's more or less working. Now I just have to figure out how to buffer the input asynchronously and separate commands out from the data. Yeehaw.
09 Jun, 2011, Vigud wrote in the 12th comment:
Votes: 0
Quote
All I really envisioned was a static text input line, maybe separated from game text by a bar.
I've seen something like that in Lolamud, but it wasn't working perfectly :(
09 Jun, 2011, KaVir wrote in the 13th comment:
Votes: 0
I also offer VT100 energy bars, but last time I checked only a couple of players were using them. Even my TinTin++ users prefer to use MSDP to control the display themselves at the cl.... Personally I'd rather focus on a more graphical interface.
09 Jun, 2011, Twisol wrote in the 14th comment:
Votes: 0
ProjectMoon said:
I also decided to learn the telnet protocol at the same time, mostly because there's no telnet library for Node yet.

Just published this yesterday. It's nowhere near done - it only emits data events, and only sends text, and doesn't support channels yet at all. But it's enough to run Aspect as it stands. :)

ProjectMoon said:
Now I just have to figure out how to buffer the input asynchronously and separate commands out from the data. Yeehaw.

I used Ragel to generate a conformant parser, but you probably don't have that luxury in Javascript… I'd recommend writing a loop that goes over each byte of the Buffer, and keeping track of your current state. I wrote an ANSI parser for Node that you might want to refer to.
09 Jun, 2011, Scandum wrote in the 15th comment:
Votes: 0
Vigud said:
Quote
All I really envisioned was a static text input line, maybe separated from game text by a bar.
I've seen something like that in Lolamud, but it wasn't working perfectly :(

Odd, maybe you were using a client with poor vt100 support?
09 Jun, 2011, Vigud wrote in the 16th comment:
Votes: 0
I think PuTTY is good enough at that.
10 Jun, 2011, ProjectMoon wrote in the 17th comment:
Votes: 0
Twisol said:
ProjectMoon said:
I also decided to learn the telnet protocol at the same time, mostly because there's no telnet library for Node yet.

Just published this yesterday. It's nowhere near done - it only emits data events, and only sends text, and doesn't support channels yet at all. But it's enough to run Aspect as it stands. :)

ProjectMoon said:
Now I just have to figure out how to buffer the input asynchronously and separate commands out from the data. Yeehaw.

I used Ragel to generate a conformant parser, but you probably don't have that luxury in Javascript… I'd recommend writing a loop that goes over each byte of the Buffer, and keeping track of your current state. I wrote an ANSI parser for Node that you might want to refer to.


Well it will definitely have to be a loop of some kind. The biggest problem is when there are 2 commands in one stream, or only part of a command in one data event, or even better: one and a half commands in a data event! I'm thinking something like this:
on data:
for each byte in data:
if byte == IAC:
buffer cmd byte
expect(COMMAND || SUBNEGOTIATION) //2 more bytes: WILL/WONT/DO/DONT option, OR subnegotiation
else if byte == SB:
buffer SB byte
expect(SB_OPTION)


…And so on. Then at the end of the loop, check the buffer(s) and see if we can emit a full data event. The idea is to have my library emit events based on data, option negotiation, or subnegotiation. Just have to rip all of that out of the single wrapped data event of the socket.
10 Jun, 2011, quixadhal wrote in the 18th comment:
Votes: 0
Scandum said:
95% of the newer terminals identify themselves as xterm, the others pick rxvt or something generic like vt100. This is because it's near impossible to get a termcap listing, and as you got 50 different terminal emulators calling themselves xterm the whole termcap thing doesn't work. On the bright side I got thousands of people using tt++ on a large variety of terminal emulators, and non complain about the raw vt100 sequences not working.


If it's so "near impossible" to get termcap to work properly, how is that that it's used to handle shell logins on pretty much EVERY unix or unix-like system out there? If sshd can figure out a workable terminal type solution, there's no reason any given MUD server can't do the same.
10 Jun, 2011, ProjectMoon wrote in the 19th comment:
Votes: 0
SSH is using a tty library directly though. Telnet doesn't really have that kind of direct control over the screen.
10 Jun, 2011, Twisol wrote in the 20th comment:
Votes: 0
ProjectMoon said:
The biggest problem is when there are 2 commands in one stream, or only part of a command in one data event, or even better: one and a half commands in a data event!

Huh. Why is multiple commands in a stream a problem?


ProjectMoon said:
I'm thinking something like this:
on data:
for each byte in data:
if byte == IAC:
buffer cmd byte
expect(COMMAND || SUBNEGOTIATION) //2 more bytes: WILL/WONT/DO/DONT option, OR subnegotiation
else if byte == SB:
buffer SB byte
expect(SB_OPTION)


…And so on. Then at the end of the loop, check the buffer(s) and see if we can emit a full data event. The idea is to have my library emit events based on data, option negotiation, or subnegotiation. Just have to rip all of that out of the single wrapped data event of the socket.


Well, instead of "expecting" things explicitly, just switch to a state that matches those things. For example, in a "text" state (the default), you'd look for IAC or CR bytes. If you find either, you'd switch to the "iac" or "cr" state. Likewise, the "cr" state would look for \n and \0, and switch back to the "text" state.

function TelnetParser() {
this.state = "text";
}

function TelnetParser.prototype.parse(data) {
// Convert from binary Buffer to UTF-8-encoded string
// It's just easier for the sake of example than using a Buffer directly.
var a = [];
for (var i = 0, len = data.length; i < len; ++i) {
a[i] = String.fromCharCode(data[i]);
}
data = a.join("");

var nextNonText, ch;
for (var i = 0, len = data.length; i < len; ++i) {
ch = data[i];

switch (this.state) {
case "text":
nextNonText = Math.min(data.indexOf("\r", i), data.indexOf("\xFF", i));
if (nextNonText === -1) {
// emit everything up to the end as a text event
i = len; // break out early
} else {
// emit everything up to this point as a text event

i = nextNonText;
this.state == (data[nextNonText] == "\r") ? "cr" : "iac";
}
break;

case "cr":
if (ch == "\n") {
// emit "\r\n"
} else if (ch == "\0") {
// emit "\r"
} else {
// emit "\r", and don't consume this char.
–i;
}
this.state = "text";
break;

case "iac":
// more goes here
break;
}
}
}

(I haven't tested this code, I just modeled it after my ANSI parser. Hopefully it helps!)
0.0/75