30 Sep, 2008, Vassi wrote in the 1st comment:
Votes: 0
I want to first briefly draw attention to this thread: http://www.gammon.com.au/forum/bbshowpos...

To paraphrase it, Nick says that the server must do IAC WILL MXP in order for the client to turn on the option. The guy on the server side claimed this was wrong, that MushClient should accept IAC DO MXP and went on to add that since zMUD correctly interpreted this then he should too.

I have a big problem with this, but it may not be something I can do anything about, I guess. For one, I agree with Nick that IAC DO MXP does not seem like a valid interpretation of the RFC. While the client is "doing" some parsing when MXP is on it is in fact the server that is "DOing" the MXP code, the client is just responding to that fact.

Anyway, so I guess my question is, with all these lovely standards set by clients like zMUD, that cater to broken or bad interpretations, are there any other major quirks\mishandlings that somebody writing a client needs to be aware of? My own Session management keeps track of local options and remote options separately, so - for instance - in the case of the above thread, I would have internally checked to see if MXP was enabled remotely before attempting to parse any MXP tags out of the input. If that server had activated via DO, my interpretation would have turned MXP on locally. Obviously it isn't a big deal to check both, but i can see it leading to problems with options where directionality does matter (like echo). I don't want to have to make little patches for individual MUDs just because I, or they, do not understand negotiation.

I'm also a little unclear on how MUDs use go-ahead, as some seem to use it to terminate lines and others don't.


V
30 Sep, 2008, Guest wrote in the 2nd comment:
Votes: 0
Unfortunately it's been my experience that Zmud does not handle MXP correctly. Not only during the negotiation steps, but also in general. I used tags that worked in Mushclient, the Mudmagic client, and Kmuddy, but failed entirely or spit out the raw MXP code when using Zmud. It seems that for whatever reason Zugg doesn't comply with his own standard as it was published the last time I messed with it. This sort of thing is why I gave up and took MXP out of AFKMud all together in the last version.
30 Sep, 2008, David Haley wrote in the 3rd comment:
Votes: 0
Given that
(a) "some clients" do not implement the protocol correctly
(b) some MUDs – by ignorance or incompetence – don't implement it correctly
© the said clients have a big enough market share that other clients must emulate their brokenness

… I think that the answer to your question "do we have to" is basically "yes". Unless, of course, you can (a) convince the servers in question to clean up their act (not likely), or (b) convince said clients to clean up theirs (also fairly unlikely, but less so), or © you are willing to sacrifice those servers.
30 Sep, 2008, Vassi wrote in the 4th comment:
Votes: 0
Quote
© you are willing to sacrifice those servers.


Sacrifices make a society stronger. :evil:

But in all seriousness, that's more or less what I expected. I suppose I'll have to see how I feel about it later on. I suppose one could use TerminalType to deal with inconsistencies (on the server end), assuming TerminalType is used by the client - which I believe zMUD does and I don't see why Mush wouldn't, he seems very thorough.

I don't really like MXP, nor do I plan to implement it right off the bat anyway. Mostly I just want to have a re-usable session handler for client or server code without needing to have it 'tweaked' every time I use it to make up for this or that deficiency.


V
30 Sep, 2008, Vassi wrote in the 5th comment:
Votes: 0
Ok, since I still get massive headaches when I try to think about this (albeit simple) Telnet symmetry thing I'm hoping for some criticism and\or correction on the following implementation.

I'm trying to get the code down to as efficient a chunk as possible to make the state machine more manageable. There are a couple of quirks here. I was told a while back that I should not reply when I don't recognize an option or don't support it, to prevent the other side from becoming confused if it was a bad implementation. Hence why Will\Do do not reply negatively. I'm also assuming that when I receive DO or DONT it's always regarding a local option, and that when I receive WILL or WONT it's always regarding a remote option. Lastly, the Queried boolean on my options is there to indicate whether the implementation initiated the request (as in via WILL or DO).

Anyhow, I think I managed to trim the ugliest part of the statemachine as follows. The comments are mostly there to try to help me keep it in order. (Edit: This is C#, but it's pretty readable as is anyway I think.)

//B = Byte. This is inside of a for loop processing the incoming buffer.
case TelnetState.Do:
doPositiveReply(b, true);
return;
case TelnetState.Will:
doPositiveReply(b, false);
return;
case TelnetState.Wont:
doNegativeReply(b, false);
return;
case TelnetState.Dont:
doNegativeReply(b, true);
return;

…….

public void doPositiveReply(byte b, bool local)
{
TelnetOption option = local ? session.LocalOption(b) : session.RemoteOption(b);
byte feedback = local ? TelnetSession.WILL : TelnetSession.DO;

//Ignore if we don't support it or it's already enabled.
if (!option.Supported || option.Enabled)
{
TState = TelnetState.Normal;
return;
}

//Since it's not enabled, and we're supporting, switch on!
if (!option.Queried)
Feedback(TelnetSession.IAC, feedback, b);

option.Enabled = true;
TState = TelnetState.Normal;
return;
}

public void doNegativeReply(byte b, bool local)
{
TelnetOption option = local ? session.LocalOption(b) : session.RemoteOption(b);
byte feedback = local ? TelnetSession.WONT : TelnetSession.DONT;

//Ignore if it's not enabled and we didn't ask.
if (!option.Enabled && !option.Queried)
{
TState = TelnetState.Normal;
return;
}

//Disable the option and such, just to make sure we're compliant.
if (!option.Queried)
Feedback(TelnetSession.IAC, feedback, b);
else
option.Queried = false;

option.Enabled = false;
TState = TelnetState.Normal;
return;
}


The session is just a class that holds two dictionaries (hash tables) indexed by the byte of the option. LocalOption\RemoteOption return the indexed key OR return a default option object (with supported\enabled set to false) if the one we're asking for hasn't been registered. The option object is a simple container for the three bools (Supported, Queried, or Enabled) along with some simple logic in the setters:

class TelnetOption
{
private bool queried, enabled;

public bool Enabled {
get { return enabled; }
set
{
enabled = value;

//Reset queried.
if (value)
queried = false;
}
}
public bool Supported { get; set; }

public bool Queried
{
get { return queried; }
set
{
queried = value;

//If we're asking for it we probably want to make sure we support it.
if (value)
Supported = true;
}
}

public TelnetOption()
{
}

public TelnetOption(bool support)
{
Supported = support;
}
}


I think that should explain enough to judge the code.


V
0.0/5