08 Feb, 2012, Deimos wrote in the 1st comment:
Votes: 0
I'm trying to figure out where IAC (0xff) characters should be escaped in my game. I'd like to have it done in one spot, but I can't figure out how to ensure that the byte isn't part of a valid command in the buffer (like an IAC EOR after a prompt or IAC AYT ping or something). How did you separate the two without repeating replacement logic everywhere?
08 Feb, 2012, Tyche wrote in the 2nd comment:
Votes: 0
I do it all in the Telnet state machine.

FilterInput routine..
switch (state) {
case NORMAL:
if (*buf == IAC) { // handles IAC
state = COMMAND;
break;
}
… other stuff
break;
case COMMAND:
if (*buf == IAC) { // handles IAC IAC
.. copy single IAC to input
state = NORMAL;

}
if (*buf == AYT) { // IAC

}
… other commands
… other states
}
}


I don't do it in my output routine, because I don't ever send an IAC that needs IAC IAC escaping.
However if I needed it, I would insert it in one place, my FilterOutput routine.
08 Feb, 2012, Kaz wrote in the 3rd comment:
Votes: 0
The approach I take is that of a series of chained streams, each representing a protocol layer. Sitting immediately above the socket stream is a telnet stream. This has several functions available to deal with Telnet functionality: sending and receiving negotiations, subnegotiations, telnet command bytes (AYT, etc.), etc. The write and read methods to this stream automatically duplicate and unduplicate IAC bytes respectively, which hides that little implementation detail from the rest of the program.
08 Feb, 2012, Deimos wrote in the 4th comment:
Votes: 0
Sorry, I should have specified that I was only referring to outgoing data from my server (for incoming data I handle it like you do, Tyche).

My data flow goes something like this:
game -> client -> telnet filter -> socket -> user

If I have TelnetFilter escape IAC bytes going out, then nothing coming before it in the chain (game, client, etc.) can send any IACs safely - like for a prompt.
09 Feb, 2012, Tyche wrote in the 5th comment:
Votes: 0
My TelnetFilter.filter_out doesn't really do anything much
  • except pass what the game send out to the client, so there isn't a problem for the game to send
  • any commands like IAC AYT.

    The input/output cycle is like this:
    user -> socket -> TelnetFilter.filter_ in -> TerminalFilter.filter.in -> client -> game
    user <- socket <- TelnetFilter.filter_out <- TerminalFilter.filter.out <- client <-game

    If I wanted to send IAC GA and single IAC's from the game, but wanted only the single IAC's to be escaped as IAC IAC, I'd simply reverse the logic in
    in my filter_in routine when building a filter_out routine.

    ostr filter_out(istr) {  // Cplus-ish psuedo-code
    loop through istr {
    switch (outstate) {
    case NORMAL:
    if (*istr == IAC) { // handles IAC
    ostr << *istr++;
    state = COMMAND;
    break;
    } else {
    ostr << *istr++;
    }
    break;
    case COMMAND:
    if (*istr == GA )||
    (*isrt == AYT)||
    …. more commands
    ostr << *istr++;
    state = NORMAL;
    } else {
    ostr << IAC
    state = NORMAL;
    }
    break;
    }
    }




    No?

    I personally wouldn't send raw Telnet codes like IAC GA from the game,
    because it breaks encapsulation. I would either send an abstract command string
    in the game output like '[GOAHEAD]' or call a routine on the client session that
    inserts a such a code, and then have an out filter decode that.

  • "anything much" - I'm lying… it translates LF to CRLF. ;-)
  • 09 Feb, 2012, Deimos wrote in the 6th comment:
    Votes: 0
    The [GOAHEAD] string idea is a good one that I hadn't thought of (even though I technically already do this by embedding stuff like |R and having AnsiFilter make the appropriate substitutions). I think I'll go that route, thanks.
    11 Feb, 2012, Twisol wrote in the 7th comment:
    Votes: 0
    I prefer to say that all IACs from above the Telnet layer are to be escaped; only IACs generated from the Telnet layer itself (and by association, IACs resulting from calls to the Telnet layer's API) are Telnet commands.

    In other words, don't send a prompt as '> \xFF\xF9'; send '> ' on its own and call telnet_command(0xF9).

    (I know the thread's a few days old, but I just got around to catching up. :sad:)
    12 Feb, 2012, Deimos wrote in the 8th comment:
    Votes: 0
    I don't disagree with you conceptually, but practically, this would kind of ruin the flow of data. Since output buffering is done in the client (before the Telnet layer), that means the flow would go from Game -> Client -> Telnet -> Player, to Game -> Telnet -> Client -> Telnet -> Player, and even worse if multiple prompts were needing to be buffered.

    Maybe I need a buffering layer after Telnet…
    12 Feb, 2012, David Haley wrote in the 9th comment:
    Votes: 0
    Well, yes… why are you buffering at the client layer? It seems that whether or not you'd want to buffer should be a factor of the actual output layer, not the thing that talks to an output layer.

    Of course, this gets interesting when the decision to page long output lives with the player's preferences, but you can work around that too.
    12 Feb, 2012, Deimos wrote in the 10th comment:
    Votes: 0
    Most codebases buffer at the client layer, from what I've seen, so it's not exactly like I'm going against the grain by doing so. :-p

    Lots of player-specific settings affect output buffering. Their chosen page size, block size, color on/off, EOR, GA, and so forth. It seems the natural place for the output buffer to live. In my game, the Client class compositions a Socket class, which itself is just a wrapper for the low level socket operations, so the "output layer" is the Client right now. Client::send() attaches a newline and appends the data to the output buffer. Later, the game will call Client::writeToSocket() in order to flush the buffer to the players.
    13 Feb, 2012, David Haley wrote in the 11th comment:
    Votes: 0
    Well sure – but then again most codebases do a lot of strange things, too. :wink: Anyhow, ya, it's one of those places where there are lots of codependencies, so it's not obvious where to cut things up.
    0.0/11