08 Apr, 2014, lilmike wrote in the 1st comment:
Votes: 0
Hi all,
I was looking to put mccp into my mud, along side KaVir's snippet I just got working. However, all of the snippets I can find are either 1. bundled with a whole bunch of other stuff like the telopt handler similar to KaVir's snippets, or use mccp v1, which I hear is bad, or are simply a patch for some other mud like godwars, with no code files, just the .patch (and I definitely don't want to have to read that :P).
Any help in pointing me to a snippet preferably codebase neutral would be great.
Thanks,
-Michael.
08 Apr, 2014, plamzi wrote in the 2nd comment:
Votes: 0
When I was looking, I couldn't find anything like a codebase-neutral MCCP2 snippet. I ended up ripping out some code from a C codebase that had MCCP2 (it may have been deltamud) and adapting it to my needs.

I think it's hard to make this particular feature codebase-neutral. KaVir's snippet supports the negotiation part, but the compression itself requires modifying the output of the server at pretty much the lowest level. So there's a good chance that you'll have to understand the principle of the thing before you can add it.
08 Apr, 2014, lilmike wrote in the 3rd comment:
Votes: 0
Hey,
I see what you mean. I guess the negotiation would have been the hard part for me anyway. I just added it in in about an hour or so that included looking up zlib info, etc. I think I've got it working although don't have an mccp capable client to test with atm, at least it didn't break uncompressed output :P.
-Michael.
08 Apr, 2014, plamzi wrote in the 4th comment:
Votes: 0
lilmike said:
although don't have an mccp capable client to test with atm


If you have a modern browser, just use the MUD portal app (see my sig.) If you append "&debug=1" to the URL of your connection, it will show you negotiation and decompression details in the console log.

Few clients have a debug mode for things like these. I think TINTIN++ is among them.
08 Apr, 2014, lilmike wrote in the 5th comment:
Votes: 0
Thanks for that. I will definitely keep that in mind.
-Michael.
08 Apr, 2014, lilmike wrote in the 6th comment:
Votes: 0
Hi,
I guess this is more about KaVir's handler, but when I look through tt++ with debug telnet turned on, I don't see any IAC SB compress2 IAC SE before I assume it starts compressing, as it comes up with jiberish (at least I"m guessing it's jiberish… it looks like a bunch of x?x?x? etc). this makes me think for some reason the snippet isn't sending the start sequence of the compression stream. I could easily be wrong… but it isn't getting the IAC SB compress2 IAC SE sequence before it starts compressing. I thought at first it was because I didn't flush the socket before starting compression, but now I flush before enabling compression and no go.
Anyone have any idea?
Thanks,
-Michael.
09 Apr, 2014, KaVir wrote in the 7th comment:
Votes: 0
Assuming you've correctly installed an MCCP snippet, take a look at the section in my README.TXT entitled "My mud previously supported MCCP, but it's stopped working!"
09 Apr, 2014, lilmike wrote in the 8th comment:
Votes: 0
Hi,
I got it… well working but not by changing the call to SendNegotiation(apDescriptor, TELOPT_MCCP, Will, true) to PerformSubnegotiation(apDescriptor, TELOPT_MCCP, NULL, 0) and adding a block in PerformSubnegotiation which sends IAC SB TELOPT_MCCP IAC SE to the player. only problem now is that tt++ is saying compression error and then giving a bunch of weird IAC xxx and IAC bad telopt things below it.
-Michael.
09 Apr, 2014, KaVir wrote in the 9th comment:
Votes: 0
I'd suggest temporarily disabling my protocol handler, and focus on getting the MCCP snippet working first. Once that's working, you can just follow the instructions in the README.TXT.
09 Apr, 2014, lilmike wrote in the 10th comment:
Votes: 0
Hi,
I think I've got it mostly working, I think the problem now is with the compression code of mine. mush client gives invalid stored block lengths, which I can't seem to find what this error means anywhere heh. I tried using z_sync_flush, as I saw that mentioned in a thread on this forum from a while ago. Is this what I want?
Basically what I am doing is as follows:

1. Send IAC SB compress2 IAC SE.
2. immediately call SetCompression(true), which flushes the buffer to the socket immediately as to avoid any data being written to it after the SE, and then sets compression.
3. When the next bit of data comes into my socket object, I initialize a new z_stream object, and then call deflate(…) with z_sync_flush as the second parameter, and then send that data out to the socket.
Anything obviously wrong here?
Thanks,
-Michael.
09 Apr, 2014, quixadhal wrote in the 11th comment:
Votes: 0
Assuming you already did the negotiation prior to that to ensure the other end will actually use mccp2, seems reasonable.

Be aware that if your MUD does hotbooting/copyover (preserves socket connections on a reboot), you have to tell the other end to stop compression first and re-negotiate it when you come back up…. or cross your fingers and hope the interruption doesn't lose state somewhere in the compression library and give you garbage. :)
09 Apr, 2014, Nathan wrote in the 12th comment:
Votes: 0
I don't it has such a nifty debug feature as MUD Portal, but mushclient supports MCCP 1 & 2. Also, codebase neutral would almost imply language neutral which you simply aren't going to find and even if you did it might be too vague to help you.
10 Apr, 2014, lilmike wrote in the 13th comment:
Votes: 0
Hi,
Yeah I don't really need a snippet anymore, as I understand the spec enough to build it into my mud. I just have a few bugs which I'm having trouble finding out.

The current problems:

1. When I connect with mush client and it negotiates mccp, it obviously does it correctly, as it tries to uncompress the data, which is being compressed by my mud server, just not quite correctly.
2. When I connect using tintin++ I can see that it is getting the negotiation right, as I turn on {debug telnet} and it says 'received IAC will MCCP2, then it says sent IAC do MCCP2, then later on it says 'Received IAC SB MCCP2 [without IAC SE oddly enough, although I know it's sending that] and then says enabling mccp.
However, once it starts up it immediately gives a compression error (like mush client gives invalid stored block length, but it doesn't say what the error is).
This is followed by a bunch of garbage then regular text after tintin++ sends IAC don't MCCP2, and the mud reverts to normal output.
I'm not quite sure what's going on, so hope someone knows of something that I could be doing wrong.
-Michael.
edit:
P.S. Also on tintin++ it gives a bunch of random received IAC xxx xxx for example IAC 200 197 or whatever, and then #bad IAC after starting and then ending compression..
10 Apr, 2014, Idealiad wrote in the 14th comment:
Votes: 0
Maybe a silly question, but your server isn't just compressing game data is it? I mean, it is compressing everything sent to the socket?
10 Apr, 2014, lilmike wrote in the 15th comment:
Votes: 0
Hi,
Yep, it is. Basically my socket class has two function, well actually three, but the third was only put in because I couldn't get something working another way… which may not even be the problem anyway.
1. Write(string data): That adds the data to the queue of output.
2: Flush(): That sends all the data out to the socket.
It's in flush that I compress the data if required before sending.

If anyone think they could look at the related code to see what might be going on that would be great. It's not alot, just one function.
-Michael.
10 Apr, 2014, lilmike wrote in the 16th comment:
Votes: 0
Hi,
Ok, I figure since I'm about to go to bed I'll post the code. Note it's my first time working with zlib, so I could easily be doing something wrong.

bool Socket::Flush()
{
int b=0, w=0;
World* world = World::GetPtr();
if (!_outBuffer.length())
{
//if buffer is empty but we need to end compression.
if(_compression == 2) {
_strm.avail_in = 1;
_strm.next_in = (unsigned char*)"\0";
char ch2[1025];//hopefully enough for the zlib data and a terminating null.
std::string buff2;
do {
_strm.avail_out=1024;
_strm.next_out = (unsigned char*)ch2;
deflate(&_strm, Z_FINISH);
ch2[_strm.avail_out] = '\0';
//_outBuffer = _outBuffer+(char*)ch2;
buff2 = buff2+ch2;
}
while(_strm.avail_out == 0);
if(_control!=-1) {
//std::cout << buff2.c_str() << std::endl;
if(send(_control, buff2.c_str(), buff2.length(), 0)) {
//_outBuffer = "";
return false;
}
else {
//_outBuffer = "";
_compression = 0;
deflateEnd(&_strm);
}
}
else {
//_outBuffer = "";
return false;
}
}
return true;
}
//prepend buffer to prompt
if ((_mobile!=NULL)&&(_con==con_game))
{
if(_mobile && !_mobile->pProtocol->WriteOOB) {
std::string telnet = "";
if(_mobile && (_mobile->GetOption("telnetga") != NULL) && (_mobile->GetOption("telnetga")->_data == Variant(1))) {
telnet = ""+TELNET_IAC+TELNET_GA;
}
else {
telnet = "";
}
_outBuffer+="\r\n"+world->BuildPrompt(_mobile->GetPrompt(), _mobile)+telnet;
}
}
if(_mobile) {
char *temp2 = new char[_outBuffer.length()];
strcpy(temp2, _outBuffer.c_str());
int len = _outBuffer.length();
const char* temp = ProtocolOutput(_mobile, temp2, &len);
delete[] temp2;
_outBuffer = temp;
if(_mobile && _mobile->pProtocol->WriteOOB > 0) {
–_mobile->pProtocol->WriteOOB;
}
} // end protocol output stuff.
std::string buff;
if(_compression == 3) { //need to begin compression and immediately start compressing.
_strm.zalloc = Z_NULL;
_strm.zfree = Z_NULL;
_strm.opaque = Z_NULL;
deflateInit(&_strm, Z_DEFAULT_COMPRESSION);
_compression = 1; //now compress.
}
if(_compression == 0) { //no compression needed.
buff =_outBuffer;
}
else if(_compression == 1 || _compression == 2) { //1 means compress, 2 means compress remaining data then finish.
char ch[4097]; //4096 + 1 terminating NULL
_strm.avail_in = _outBuffer.length();
_strm.next_in = (unsigned char*)(_outBuffer.c_str());
//_strm.avail_out = 4096;
//_strm.next_out = ch;
int flush = (_compression == 2)?Z_FINISH:Z_SYNC_FLUSH;
do {
_strm.avail_out = 4096;
_strm.next_out = (unsigned char*)(ch);
deflate(&_strm, flush);
ch[_strm.avail_out] = '\0';
buff = buff+ch;
}
while(_strm.avail_out == 0);
if(_compression == 2) {
_compression = 0;
deflateEnd(&_strm);
}
}
_outBuffer = "";

while (buff.length() > 0) //send all the data.
{
b = (buff.length() < 4096) ? buff.length() : 4096;
//std::cout << buff.c_str() << std::endl;

// any write failures ?
if (_control!=-1)
{
if ((w = send(_control, buff.c_str(), b, 0)) == -1)
{
return false;
}
}
// move the buffer down
buff.erase(0, w);
}
return true;
}


Also, sorry about any weird tab stuff, I'm blind so I tend not to indent my code alot but the guy who worked on this codebase before did indent his code. (bad, I know.)

The only thing I can possibly think of are:
1. Do I need to put a terminating null in the zlib data? I think I do to add it to an std::string, but… Also, if I do, will it add the null to the string? I don't think so, but…
-Michael.
10 Apr, 2014, Tyche wrote in the 17th comment:
Votes: 0
Zlib compressed data may contain NUL characters.
10 Apr, 2014, lilmike wrote in the 18th comment:
Votes: 0
Hi,
Thanks. I have changed buff = buff+ch, and equivalent anywhere else to buff.append(ch, _strm.avail_out) so as to copy the characters into the string character by character, and not depend on finding a null character, as you said zlib could include those into the array. I also didn't null terminate it because it no longer needs to.
I'm now getting invalid code lengths set, which apparently means my data is corrupt.
-Michael.
Edit:
I changed all referenced to .length() to .size() and all reference to .c_str() to .data() for the compression and sending through the socket based on a snippet found here: http://panthema.net/2007/0328-ZLibString... which said that c_str() should be avoided because zlib data is binary.
10 Apr, 2014, lilmike wrote in the 19th comment:
Votes: 0
Hi all,
It all works now, thanks for the pointers given by those in the thread. For the record, i wasn't adding all the data onto the string of output, I was only adding up to avail_out when it should have been size-avail_out.
-Michael.
0.0/19