How To: Add MXP to TinyMud v2.0

Posted by Japheth on Fri 17 Jul 2009 08:01 AM — 4 posts, 21,450 views.

#0
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:

#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("&lt;");
				else
					destination.push_back('<');
				break;
			case '>':
				if (mxp)
					destination.append("&gt;");
				else
					destination.push_back('>');
				break;
			case '&':
				if (mxp)
					destination.append("&amp;");
				else
					destination.push_back('&');
				break;
			case '\"':
				if (mxp)
					destination.append("&quot;");
				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.
Amended on Fri 17 Jul 2009 09:03 PM by Nick Gammon
Australia Forum Administrator #1
Thanks for that.

I edited your post to "quote" the forum tags (eg. put a \ before square brackets). I think an earlier post of yours was partly in italics because of that being missing.

Quote:

if (mxp)
destination.append("&lt;");
else
destination.push_back('<');


Just out of curiosity, what is the difference between append and push_back? Don't they both put something at the back of the string?

USA #2
append is for whole strings, whereas push_back is for single characters only (as a string is basically a vector of characters).

It took me a moment to realize that as well, because working in Lua/Python etc. for so long, I stop thinking that "" and '' are different things :-)
Australia Forum Administrator #3
Yes, I spotted the single and double quotes. ;)

He could have done:


if (mxp)
  destination.append("&lt;");
else
  destination.append("<");


I suppose that would be slightly less efficient.