I recently decided to add MXP support to my project based on TinyMud, and I used Nick's guide found at - http://www.gammon.com.au/mushclient/addingservermxp.htm
Obviously that guide is geared towards Diku-based muds, so I had to do a lot of adapting to even figure out where to put some things. Nonetheless, it seems to be working without problems now, so I thought I'd share a few "how to" tips and also some code snippets in case anyone else wants to do this.
A few things to keep in mind first:
1) You won't have all of the data structures that I do - I've tried to strip as much of that as possible for this example but you will have to at least add a boolean to tPlayer called "mxp".
2) You might still need to make some interpretations of your own.
First, you need to include the following #defines somewhere:
Then, I added the following functions:
In ProcessNewConnection, after the "cout" line, add the following:
Now, ProcessRead is a function that I have actually modified a bit, so you will have to do some interpreting here:
In the part of ProcessRead just before "ProcessPlayerInput", add the following code (keeping in mind that sLine is a string I'm using to represent each line read from player input. Yours may differ)
EnableMXP is a function I have attached to tPlayer that sends the default MXP definitions and sets this->mxp to true. You'll want to write your own function for this, but make sure it includes the following:
The above assumes it's a function that's part of tPlayer.
Finally, process the tags in ProcessWrite. Somewhere before the while loop, add the following:
And as far as I remember, you're done! MXP support ftw.
Hope this helped.
Obviously that guide is geared towards Diku-based muds, so I had to do a lot of adapting to even figure out where to put some things. Nonetheless, it seems to be working without problems now, so I thought I'd share a few "how to" tips and also some code snippets in case anyone else wants to do this.
A few things to keep in mind first:
1) You won't have all of the data structures that I do - I've tried to strip as much of that as possible for this example but you will have to at least add a boolean to tPlayer called "mxp".
2) You might still need to make some interpretations of your own.
First, you need to include the following #defines somewhere:
#define TELOPT_MXP '\x5B'
#define SE '\xF0'
#define GA '\xF9'
#define SB '\xFA'
#define WILL '\xFB'
#define WONT '\xFC'
#define DO '\xFD'
#define DONT '\xFE'
#define IAC '\xFF'
#define ESC '\x1B'Then, I added the following functions:
string MXP_TAG (string input)
{
return ("\x03" + input + "\x04");
}
string MXP_AMP (void)
{
return "\x06";
}
string MXP_BEG (void)
{
return "\x03";
}
string MXP_END (void)
{
return "\x04";
}
string MXP_MODE (int mode)
{
return (MAKE_STRING(ESC)+"["+MAKE_STRING(mode)+"z");
}
string convert_mxp_tags (bool mxp, string source)
{
string destination;
bool in_tag = false, in_entity = false;
for (unsigned int index = 0; index < source.length(); index++)
{
if (in_tag)
{
if (source[index] == '\x04')
{
in_tag = false;
if (mxp)
destination.push_back('>');
}
else if (mxp)
{
destination.push_back(source[index]);
}
}
else if (in_entity)
{
if (mxp)
{
destination.push_back(source[index]);
}
if (source[index] == ';')
{
in_entity = false;
}
}
else switch (source[index])
{
case '\x03':
in_tag = true;
if (mxp)
destination.push_back('<');
break;
case '\x05':
in_entity = true;
if (mxp)
destination.push_back('&');
break;
case '<':
if (mxp)
destination.append("<");
else
destination.push_back('<');
break;
case '>':
if (mxp)
destination.append(">");
else
destination.push_back('>');
break;
case '&':
if (mxp)
destination.append("&");
else
destination.push_back('&');
break;
case '\"':
if (mxp)
destination.append(""");
else
destination.push_back('\"');
break;
default:
destination.push_back(source[index]);
break;
}
}
return destination;
}In ProcessNewConnection, after the "cout" line, add the following:
*p << IAC << WILL << TELOPT_MXP;
p->ProcessWrite();Now, ProcessRead is a function that I have actually modified a bit, so you will have to do some interpreting here:
In the part of ProcessRead just before "ProcessPlayerInput", add the following code (keeping in mind that sLine is a string I'm using to represent each line read from player input. Yours may differ)
// Check if the input is a telnet negotiation first
if (sLine[0] == IAC)
{
string WillTelnet;
WillTelnet.push_back(IAC);
WillTelnet.push_back(DO);
WillTelnet.push_back(TELOPT_MXP);
if (!sLine.compare(0, 3, WillTelnet))
{
this->EnableMXP();
sLine.erase(0, 3);
}
else
{
this->mxp = false;
sLine.erase(0, 3);
}
}EnableMXP is a function I have attached to tPlayer that sends the default MXP definitions and sets this->mxp to true. You'll want to write your own function for this, but make sure it includes the following:
(*this) << IAC << SB << TELOPT_MXP << IAC << SE;
(*this) << MXP_MODE(6);The above assumes it's a function that's part of tPlayer.
Finally, process the tags in ProcessWrite. Somewhere before the while loop, add the following:
// Convert MXP tags
if (this->mxp)
this->outbuf = convert_mxp_tags(true, this->outbuf);
else
this->outbuf = convert_mxp_tags(false, this->outbuf);And as far as I remember, you're done! MXP support ftw.
Hope this helped.