ddnet/src/engine/shared/packer.cpp
heinrich5991 1d81d56850 Introduce new, vanilla-compatible server info protocol
This means that we have a reliable and fast way to query for extended info,
while also not wasting network bandwidth.

The protocol is designed to be extensible, there's four bytes space for
encoding more request types (currently zeroed), and there's one string in each
response packet and one string for each player available (currently the empty
string).

The protocol itself has no problems with more than 64 players, although the
current client implementation will drop the player info after the 64th player,
because it uses a static array for storage.

Also fixes #130, the player list is just sorted each time new player info
arrives.
2017-03-29 12:56:13 +02:00

165 lines
2.5 KiB
C++

/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
/* If you are missing that file, acquire a complete release at teeworlds.com. */
#include <base/system.h>
#include "packer.h"
#include "compression.h"
#include "config.h"
void CPacker::Reset()
{
m_Error = 0;
m_pCurrent = m_aBuffer;
m_pEnd = m_pCurrent + PACKER_BUFFER_SIZE;
}
void CPacker::AddInt(int i)
{
if(m_Error)
return;
// make sure that we have space enough
if(m_pEnd - m_pCurrent < 6)
{
dbg_break();
m_Error = 1;
}
else
m_pCurrent = CVariableInt::Pack(m_pCurrent, i);
}
void CPacker::AddString(const char *pStr, int Limit)
{
if(m_Error)
return;
//
if(Limit > 0)
{
while(*pStr && Limit != 0)
{
*m_pCurrent++ = *pStr++;
Limit--;
if(m_pCurrent >= m_pEnd)
{
m_Error = 1;
break;
}
}
*m_pCurrent++ = 0;
}
else
{
while(*pStr)
{
*m_pCurrent++ = *pStr++;
if(m_pCurrent >= m_pEnd)
{
m_Error = 1;
break;
}
}
*m_pCurrent++ = 0;
}
}
void CPacker::AddRaw(const void *pData, int Size)
{
if(m_Error)
return;
if(m_pCurrent+Size >= m_pEnd)
{
m_Error = 1;
return;
}
const unsigned char *pSrc = (const unsigned char *)pData;
while(Size)
{
*m_pCurrent++ = *pSrc++;
Size--;
}
}
void CUnpacker::Reset(const void *pData, int Size)
{
m_Error = 0;
m_pStart = (const unsigned char *)pData;
m_pEnd = m_pStart + Size;
m_pCurrent = m_pStart;
}
int CUnpacker::GetInt()
{
if(m_Error)
return 0;
if(m_pCurrent >= m_pEnd)
{
m_Error = 1;
return 0;
}
int i;
m_pCurrent = CVariableInt::Unpack(m_pCurrent, &i);
if(m_pCurrent > m_pEnd)
{
m_Error = 1;
return 0;
}
return i;
}
const char *CUnpacker::GetString(int SanitizeType)
{
if(m_Error)
return "";
if(m_pCurrent >= m_pEnd)
{
m_Error = 1;
return "";
}
char *pPtr = (char *)m_pCurrent;
while(*m_pCurrent) // skip the string
{
m_pCurrent++;
if(m_pCurrent == m_pEnd)
{
m_Error = 1;
return "";
}
}
m_pCurrent++;
// sanitize all strings
if(SanitizeType&SANITIZE)
str_sanitize(pPtr);
else if(SanitizeType&SANITIZE_CC)
str_sanitize_cc(pPtr);
return SanitizeType&SKIP_START_WHITESPACES ? str_utf8_skip_whitespaces(pPtr) : pPtr;
}
const unsigned char *CUnpacker::GetRaw(int Size)
{
const unsigned char *pPtr = m_pCurrent;
if(m_Error)
return 0;
// check for nasty sizes
if(Size < 0 || m_pCurrent+Size > m_pEnd)
{
m_Error = 1;
return 0;
}
// "unpack" the data
m_pCurrent += Size;
return pPtr;
}