Advertisement

a small mud

Started by May 01, 2005 12:51 PM
16 comments, last by ErUs 19 years, 9 months ago
hmm gonna need todo some sort of append thing and check for the enter key :(

EDIT: oh im so good im evil! :p
-www.freewebs.com/tm1rbrt -> check out my gameboy emulator ( worklog updated regularly )
hmm is there any way to query wether a client is still there in telnet. my code currently doesnt detect users closing there telnet window :(

i used packet sniffer and telnet doesnt send anythin before exit so there must be a winsock command or i will have to use timeout's :(
-www.freewebs.com/tm1rbrt -> check out my gameboy emulator ( worklog updated regularly )
Advertisement
Quote:
Original post by alchemar
windows telnet does send everytime you type a letter but I think unix sends only when you hit return.
If you type quickly, you can get 2 or 3 letters into one packet (checked with Ethereal). PuTTY also sends one entire line, unless it gets telnet negotiation telling it to send one character at a time.

Another thing to be wary of with telnet is negotiation. Some clients (e.g. PuTTY, Windows default telnet doesn't) send telnet negotiation when they connect. It's easy to ignore, but if you don't ignore it properly, you'll have problems. The first byte is 0xff (255). If you see that byte when a client connects, you should erase that byte and the following two. Repeat that process until the first byte isn't 0xff.
Also, remember to parse backspace ('\b) when you're using telnet. If a client sends one letter at a time, it could also send a backspace character. If you get that, you should erase the backspace and the preceding letter.

If you want to handle unicode, PuTTY can handle UTF-8, which is pretty easy to process. Windows default telnet just displays gibberish however.

Here's a my CConnectedUser::Tick() function, if you want to see how I do things in my MUD:
bool CConnectedUser::Tick(){time_t timeCurr = time(NULL);	// Is socket valid? //	if(!m_pSocket || !m_pSocket->IsValid())		return false;	// Any data? //	size_t dwLen = m_pSocket->GetBufferLen();	if(dwLen >= m_theProfile.GetMaxBufferLen())	{		Log(ToString(m_theProfile.GetUsername() << " disconnected due to single line flood\r\n"));		m_pSocket->Send("Connection closed due to flooding\r\n");		m_pSocket->Disconnect();		return false;	}	// Any data? //	else if((dwLen != 0) || (!m_vBuffer.empty()))	{		// Get buffer //		size_t dwOldLen = m_vBuffer.size();		if(dwLen != 0)		{			m_vBuffer.resize(dwOldLen+dwLen);			m_pSocket->ExtractBuffer(&m_vBuffer[dwOldLen],dwLen);		}		// Format it //		for(size_t i=0; i<m_vBuffer.size(); ++i)		{			if((m_vBuffer == '\b') || (m_vBuffer == '\x7f'))			{				if(i == 0) {m_vBuffer.erase(m_vBuffer.begin()); --i;}				else {m_vBuffer.erase(m_vBuffer.begin()+i-1,m_vBuffer.begin()+i+1); i -= 2;}			}			else if(m_vBuffer == '\n')			{				// Only copy non-null buffers //				if(i != 0)				{					m_timeLastInput = timeCurr;					m_dwFloodCount++;					m_strBuffer.resize(i,'*');					memcpy(&m_strBuffer[0],&m_vBuffer[0],i);				}				m_vBuffer.erase(m_vBuffer.begin(),m_vBuffer.begin()+i+1);				break;			}			// Process telnet commands //			else if((m_theProfile.GetProtocol() < CProfile::Proto_TA2) && (m_vBuffer == 0xff) && (i != m_vBuffer.size()-1))			{				switch(m_vBuffer[i+1])				{				case 246:	// Are you there?					m_strBuffer = ".AYT";					m_vBuffer.erase(m_vBuffer.begin()+i,m_vBuffer.begin()+i+2);					--i;					break;				case 247:	// Erase character (backspace)					if(i == 0) {m_vBuffer.erase(m_vBuffer.begin(),m_vBuffer.begin()+2); --i;}					else {m_vBuffer.erase(m_vBuffer.begin()+i-1,m_vBuffer.begin()+i+2); i -= 2;}					break;				case 248:	// Erase line					m_vBuffer.erase(m_vBuffer.begin(),m_vBuffer.begin()+i+2);					i = (size_t)-1;					break;				default:	// Unknown, remove IAC and command					m_vBuffer.erase(m_vBuffer.begin()+i,m_vBuffer.begin()+i+2);					--i;					break;				}			}			// UTF-8? //			else if(i < m_vBuffer.size()-1)			{				// 2-byte code? //				if(((m_vBuffer&0xe0) == 0xc0) && ((m_vBuffer[i+1]&0xc0) == 0x80))				{					char szBuff[32];					const DWORD dwCode = ((m_vBuffer & 0x1F) << 6) | (m_vBuffer[i+1] & 0x3F);					size_t nLen = (size_t)sprintf(szBuff,"x%d>",dwCode);					m_vBuffer = '<';					m_vBuffer[i+1] = '-';					m_vBuffer.insert(m_vBuffer.begin()+i+2,nLen,'*');					memcpy(&m_vBuffer[i+2],szBuff,nLen);					i += nLen+1;				}				// 3-byte code? //				else if((i < m_vBuffer.size()-2) && ((m_vBuffer&0xf0) == 0xe0) && ((m_vBuffer[i+1]&0xc0) == 0x80) && ((m_vBuffer[i+2]&0xc0) == 0x80))				{					char szBuff[32];					const DWORD dwCode = ((m_vBuffer & 0x0F) << 12) | ((m_vBuffer[i+1] & 0x3F) << 6) | (m_vBuffer[i+2] & 0x3F);					size_t nLen = (size_t)sprintf(szBuff,"x%d>",dwCode);					m_vBuffer = '<';					m_vBuffer[i+1] = '-';					m_vBuffer.insert(m_vBuffer.begin()+i+2,nLen,'*');					memcpy(&m_vBuffer[i+2],szBuff,nLen);					i += nLen+1;				}				// 4-byte code? //				else if((i < m_vBuffer.size()-3) && ((m_vBuffer&0xf8) == 0xf0) && ((m_vBuffer[i+1]&0xc0) == 0x80) && ((m_vBuffer[i+2]&0xc0) == 0x80) && ((m_vBuffer[i+3]&0xc0) == 0x80))				{					char szBuff[32];					const DWORD dwCode = ((m_vBuffer & 0x07) << 18) | ((m_vBuffer[i+1] & 0x3F) << 12) | ((m_vBuffer[i+2] & 0x3F) << 6) | (m_vBuffer[i+3] & 0x3F);					size_t nLen = (size_t)sprintf(szBuff,"x%d>",dwCode);					m_vBuffer = '<';					m_vBuffer[i+1] = '-';					m_vBuffer.insert(m_vBuffer.begin()+i+2,nLen,'*');					memcpy(&m_vBuffer[i+2],szBuff,nLen);					i += nLen+1;				}				// Strip non-ASCII //				else if((m_vBuffer < ' ') || (m_vBuffer > 127))				{					m_vBuffer.erase(m_vBuffer.begin()+i);					--i;				}			}			// Strip non-ASCII //			else if((m_vBuffer < ' ') || (m_vBuffer > 127))			{				m_vBuffer.erase(m_vBuffer.begin()+i);				--i;			}		}	}	// Time to lower flood counter? //	if((m_dwFloodCount > 0) && (timeCurr >= m_timeNextFloodReduce))	{		--m_dwFloodCount;		m_timeNextFloodReduce = timeCurr+m_theProfile.GetFloodReduce();	}	// Idle timeout? //	if(m_timeLastInput+m_theProfile.GetIdleTimeout() < (DWORD)timeCurr)	{		Log(ToString(m_theProfile.GetUsername() << " disconnected due to inactivity\r\n"));		m_pSocket->Send("Connection closed due to inactivity\r\n");		m_pSocket->Disconnect();		return false;	}	// Flooding? -1 because the first line gets de-flooded immediately //	if(m_dwFloodCount > m_theProfile.GetFloodMax()-1)	{		Log(ToString(m_theProfile.GetUsername() << " disconnected due to flooding\r\n"));		m_pSocket->Send("Connection closed due to flooding\r\n");		m_pSocket->Disconnect();		return false;	}	return (m_strBuffer.empty());}

It's a bit large, I know, but it should give you an idea of what you need to process. The telnet codes I process (AYT, erase line, etc) can usually be ignored, I just process them for completeness.
Let me know if you have any questions about the code.
done that bit evil steve :p
-www.freewebs.com/tm1rbrt -> check out my gameboy emulator ( worklog updated regularly )
Quote:
Original post by ErUs
hmm is there any way to query wether a client is still there in telnet. my code currently doesnt detect users closing there telnet window :(

i used packet sniffer and telnet doesnt send anythin before exit so there must be a winsock command or i will have to use timeout's :(
When you close the telnet window, the socket will be closed. So recv() will return 0 (or it'll return -1 on error).
i think theres something wrong.
in allways returning an error on my main listening socket but it stll works :/
-www.freewebs.com/tm1rbrt -> check out my gameboy emulator ( worklog updated regularly )
Advertisement
What is the error it returns, and what function fails? Check WSAGetLastError() for an error code after a function fails.
error #0 :S

is that a propper error?

msdn want at all helpful

its actully the Select thats returning -1 sorry
-www.freewebs.com/tm1rbrt -> check out my gameboy emulator ( worklog updated regularly )

This topic is closed to new replies.

Advertisement