ddnet/src/engine/shared/network.cpp

364 lines
9.1 KiB
C++
Raw Normal View History

2010-11-20 10:37:14 +00:00
/* (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. */
2009-10-27 14:38:53 +00:00
#include <base/system.h>
2010-05-29 07:25:38 +00:00
#include "config.h"
#include "network.h"
#include "huffman.h"
2009-10-27 14:38:53 +00:00
void CNetRecvUnpacker::Clear()
{
m_Valid = false;
}
void CNetRecvUnpacker::Start(const NETADDR *pAddr, CNetConnection *pConnection, int ClientID)
{
m_Addr = *pAddr;
m_pConnection = pConnection;
m_ClientID = ClientID;
m_CurrentChunk = 0;
m_Valid = true;
}
2010-05-29 07:25:38 +00:00
// TODO: rename this function
2009-10-27 14:38:53 +00:00
int CNetRecvUnpacker::FetchChunk(CNetChunk *pChunk)
{
CNetChunkHeader Header;
unsigned char *pEnd = m_Data.m_aChunkData + m_Data.m_DataSize;
2009-10-27 14:38:53 +00:00
while(1)
{
unsigned char *pData = m_Data.m_aChunkData;
2010-05-29 07:25:38 +00:00
// check for old data to unpack
2009-10-27 14:38:53 +00:00
if(!m_Valid || m_CurrentChunk >= m_Data.m_NumChunks)
{
Clear();
return 0;
}
2010-05-29 07:25:38 +00:00
// TODO: add checking here so we don't read too far
2009-10-27 14:38:53 +00:00
for(int i = 0; i < m_CurrentChunk; i++)
{
pData = Header.Unpack(pData);
pData += Header.m_Size;
}
2010-05-29 07:25:38 +00:00
// unpack the header
2009-10-27 14:38:53 +00:00
pData = Header.Unpack(pData);
m_CurrentChunk++;
2009-10-27 14:38:53 +00:00
if(pData+Header.m_Size > pEnd)
{
Clear();
return 0;
}
2010-05-29 07:25:38 +00:00
// handle sequence stuff
2009-10-27 14:38:53 +00:00
if(m_pConnection && (Header.m_Flags&NET_CHUNKFLAG_VITAL))
{
if(Header.m_Sequence == (m_pConnection->m_Ack+1)%NET_MAX_SEQUENCE)
{
2010-05-29 07:25:38 +00:00
// in sequence
2009-10-27 14:38:53 +00:00
m_pConnection->m_Ack = (m_pConnection->m_Ack+1)%NET_MAX_SEQUENCE;
}
else
{
2010-05-29 07:25:38 +00:00
// old packet that we already got
2009-10-27 14:38:53 +00:00
if(CNetBase::IsSeqInBackroom(Header.m_Sequence, m_pConnection->m_Ack))
continue;
2010-05-29 07:25:38 +00:00
// out of sequence, request resend
if(g_Config.m_Debug)
2009-10-27 14:38:53 +00:00
dbg_msg("conn", "asking for resend %d %d", Header.m_Sequence, (m_pConnection->m_Ack+1)%NET_MAX_SEQUENCE);
m_pConnection->SignalResend();
2010-05-29 07:25:38 +00:00
continue; // take the next chunk in the packet
2009-10-27 14:38:53 +00:00
}
}
2010-05-29 07:25:38 +00:00
// fill in the info
2009-10-27 14:38:53 +00:00
pChunk->m_ClientID = m_ClientID;
pChunk->m_Address = m_Addr;
pChunk->m_Flags = 0;
pChunk->m_DataSize = Header.m_Size;
pChunk->m_pData = pData;
return 1;
}
}
2010-05-29 07:25:38 +00:00
// packs the data tight and sends it
2009-10-27 14:38:53 +00:00
void CNetBase::SendPacketConnless(NETSOCKET Socket, NETADDR *pAddr, const void *pData, int DataSize)
{
unsigned char aBuffer[NET_MAX_PACKETSIZE];
aBuffer[0] = 0xff;
aBuffer[1] = 0xff;
aBuffer[2] = 0xff;
aBuffer[3] = 0xff;
aBuffer[4] = 0xff;
aBuffer[5] = 0xff;
mem_copy(&aBuffer[6], pData, DataSize);
net_udp_send(Socket, pAddr, aBuffer, 6+DataSize);
}
void CNetBase::SendPacket(NETSOCKET Socket, NETADDR *pAddr, CNetPacketConstruct *pPacket)
{
unsigned char aBuffer[NET_MAX_PACKETSIZE];
int CompressedSize = -1;
int FinalSize = -1;
2010-05-29 07:25:38 +00:00
// log the data
2009-10-27 14:38:53 +00:00
if(ms_DataLogSent)
{
2010-05-29 07:25:38 +00:00
int Type = 1;
io_write(ms_DataLogSent, &Type, sizeof(Type));
2009-10-27 14:38:53 +00:00
io_write(ms_DataLogSent, &pPacket->m_DataSize, sizeof(pPacket->m_DataSize));
io_write(ms_DataLogSent, &pPacket->m_aChunkData, pPacket->m_DataSize);
io_flush(ms_DataLogSent);
}
2010-05-29 07:25:38 +00:00
// compress
CompressedSize = ms_Huffman.Compress(pPacket->m_aChunkData, pPacket->m_DataSize, &aBuffer[3], NET_MAX_PACKETSIZE-4);
2009-10-27 14:38:53 +00:00
2010-05-29 07:25:38 +00:00
// check if the compression was enabled, successful and good enough
2014-12-06 15:17:04 +00:00
#ifndef FUZZING
2009-10-27 14:38:53 +00:00
if(CompressedSize > 0 && CompressedSize < pPacket->m_DataSize)
{
FinalSize = CompressedSize;
pPacket->m_Flags |= NET_PACKETFLAG_COMPRESSION;
}
else
2014-12-06 15:17:04 +00:00
#endif
2009-10-27 14:38:53 +00:00
{
2010-05-29 07:25:38 +00:00
// use uncompressed data
2009-10-27 14:38:53 +00:00
FinalSize = pPacket->m_DataSize;
mem_copy(&aBuffer[3], pPacket->m_aChunkData, pPacket->m_DataSize);
pPacket->m_Flags &= ~NET_PACKETFLAG_COMPRESSION;
}
2010-05-29 07:25:38 +00:00
// set header and send the packet if all things are good
2009-10-27 14:38:53 +00:00
if(FinalSize >= 0)
{
FinalSize += NET_PACKETHEADERSIZE;
aBuffer[0] = ((pPacket->m_Flags<<4)&0xf0)|((pPacket->m_Ack>>8)&0xf);
aBuffer[1] = pPacket->m_Ack&0xff;
aBuffer[2] = pPacket->m_NumChunks;
net_udp_send(Socket, pAddr, aBuffer, FinalSize);
2010-05-29 07:25:38 +00:00
// log raw socket data
2009-10-27 14:38:53 +00:00
if(ms_DataLogSent)
{
2010-05-29 07:25:38 +00:00
int Type = 0;
io_write(ms_DataLogSent, &Type, sizeof(Type));
2009-10-27 14:38:53 +00:00
io_write(ms_DataLogSent, &FinalSize, sizeof(FinalSize));
io_write(ms_DataLogSent, aBuffer, FinalSize);
io_flush(ms_DataLogSent);
}
}
}
2010-05-29 07:25:38 +00:00
// TODO: rename this function
2009-10-27 14:38:53 +00:00
int CNetBase::UnpackPacket(unsigned char *pBuffer, int Size, CNetPacketConstruct *pPacket)
{
2010-05-29 07:25:38 +00:00
// check the size
2009-10-27 14:38:53 +00:00
if(Size < NET_PACKETHEADERSIZE || Size > NET_MAX_PACKETSIZE)
{
2014-11-26 20:33:57 +00:00
//dbg_msg("", "packet too small, %d", Size);
2009-10-27 14:38:53 +00:00
return -1;
}
2010-05-29 07:25:38 +00:00
// log the data
2009-10-27 14:38:53 +00:00
if(ms_DataLogRecv)
{
2010-05-29 07:25:38 +00:00
int Type = 0;
io_write(ms_DataLogRecv, &Type, sizeof(Type));
2009-10-27 14:38:53 +00:00
io_write(ms_DataLogRecv, &Size, sizeof(Size));
io_write(ms_DataLogRecv, pBuffer, Size);
io_flush(ms_DataLogRecv);
}
2010-05-29 07:25:38 +00:00
// read the packet
2009-10-27 14:38:53 +00:00
pPacket->m_Flags = pBuffer[0]>>4;
pPacket->m_Ack = ((pBuffer[0]&0xf)<<8) | pBuffer[1];
pPacket->m_NumChunks = pBuffer[2];
pPacket->m_DataSize = Size - NET_PACKETHEADERSIZE;
if(pPacket->m_Flags&NET_PACKETFLAG_CONNLESS)
{
if(Size < 6)
{
2014-11-26 20:10:22 +00:00
//dbg_msg("", "connection less packet too small, %d", Size);
2009-10-27 14:38:53 +00:00
return -1;
}
2009-10-27 14:38:53 +00:00
pPacket->m_Flags = NET_PACKETFLAG_CONNLESS;
pPacket->m_Ack = 0;
pPacket->m_NumChunks = 0;
pPacket->m_DataSize = Size - 6;
mem_copy(pPacket->m_aChunkData, &pBuffer[6], pPacket->m_DataSize);
}
else
{
if(pPacket->m_Flags&NET_PACKETFLAG_COMPRESSION)
2010-05-29 07:25:38 +00:00
pPacket->m_DataSize = ms_Huffman.Decompress(&pBuffer[3], pPacket->m_DataSize, pPacket->m_aChunkData, sizeof(pPacket->m_aChunkData));
2009-10-27 14:38:53 +00:00
else
mem_copy(pPacket->m_aChunkData, &pBuffer[3], pPacket->m_DataSize);
}
2010-05-29 07:25:38 +00:00
// check for errors
2009-10-27 14:38:53 +00:00
if(pPacket->m_DataSize < 0)
{
2010-05-29 07:25:38 +00:00
if(g_Config.m_Debug)
2009-10-27 14:38:53 +00:00
dbg_msg("network", "error during packet decoding");
return -1;
}
2010-05-29 07:25:38 +00:00
// log the data
2009-10-27 14:38:53 +00:00
if(ms_DataLogRecv)
{
2010-05-29 07:25:38 +00:00
int Type = 1;
io_write(ms_DataLogRecv, &Type, sizeof(Type));
2009-10-27 14:38:53 +00:00
io_write(ms_DataLogRecv, &pPacket->m_DataSize, sizeof(pPacket->m_DataSize));
io_write(ms_DataLogRecv, pPacket->m_aChunkData, pPacket->m_DataSize);
io_flush(ms_DataLogRecv);
}
2010-05-29 07:25:38 +00:00
// return success
2009-10-27 14:38:53 +00:00
return 0;
}
void CNetBase::SendControlMsg(NETSOCKET Socket, NETADDR *pAddr, int Ack, int ControlMsg, const void *pExtra, int ExtraSize)
{
CNetPacketConstruct Construct;
Construct.m_Flags = NET_PACKETFLAG_CONTROL;
Construct.m_Ack = Ack;
Construct.m_NumChunks = 0;
Construct.m_DataSize = 1+ExtraSize;
Construct.m_aChunkData[0] = ControlMsg;
mem_copy(&Construct.m_aChunkData[1], pExtra, ExtraSize);
2010-05-29 07:25:38 +00:00
// send the control message
2009-10-27 14:38:53 +00:00
CNetBase::SendPacket(Socket, pAddr, &Construct);
}
unsigned char *CNetChunkHeader::Pack(unsigned char *pData)
{
pData[0] = ((m_Flags&3)<<6)|((m_Size>>4)&0x3f);
pData[1] = (m_Size&0xf);
if(m_Flags&NET_CHUNKFLAG_VITAL)
{
pData[1] |= (m_Sequence>>2)&0xf0;
pData[2] = m_Sequence&0xff;
return pData + 3;
}
return pData + 2;
}
unsigned char *CNetChunkHeader::Unpack(unsigned char *pData)
{
m_Flags = (pData[0]>>6)&3;
m_Size = ((pData[0]&0x3f)<<4) | (pData[1]&0xf);
m_Sequence = -1;
if(m_Flags&NET_CHUNKFLAG_VITAL)
{
m_Sequence = ((pData[1]&0xf0)<<2) | pData[2];
return pData + 3;
}
return pData + 2;
}
int CNetBase::IsSeqInBackroom(int Seq, int Ack)
{
int Bottom = (Ack-NET_MAX_SEQUENCE/2);
if(Bottom < 0)
{
if(Seq <= Ack)
return 1;
if(Seq >= (Bottom + NET_MAX_SEQUENCE))
return 1;
}
else
{
if(Seq <= Ack && Seq >= Bottom)
return 1;
}
2009-10-27 14:38:53 +00:00
return 0;
}
IOHANDLE CNetBase::ms_DataLogSent = 0;
IOHANDLE CNetBase::ms_DataLogRecv = 0;
2010-05-29 07:25:38 +00:00
CHuffman CNetBase::ms_Huffman;
2009-10-27 14:38:53 +00:00
void CNetBase::OpenLog(IOHANDLE DataLogSent, IOHANDLE DataLogRecv)
2009-10-27 14:38:53 +00:00
{
if(DataLogSent)
2009-10-27 14:38:53 +00:00
{
ms_DataLogSent = DataLogSent;
dbg_msg("network", "logging sent packages");
2009-10-27 14:38:53 +00:00
}
else
dbg_msg("network", "failed to start logging sent packages");
if(DataLogRecv)
2009-10-27 14:38:53 +00:00
{
ms_DataLogRecv = DataLogRecv;
dbg_msg("network", "logging recv packages");
}
else
dbg_msg("network", "failed to start logging recv packages");
}
void CNetBase::CloseLog()
{
if(ms_DataLogSent)
{
dbg_msg("network", "stopped logging sent packages");
io_close(ms_DataLogSent);
ms_DataLogSent = 0;
}
if(ms_DataLogRecv)
{
dbg_msg("network", "stopped logging recv packages");
io_close(ms_DataLogRecv);
ms_DataLogRecv = 0;
}
2009-10-27 14:38:53 +00:00
}
int CNetBase::Compress(const void *pData, int DataSize, void *pOutput, int OutputSize)
{
2010-05-29 07:25:38 +00:00
return ms_Huffman.Compress(pData, DataSize, pOutput, OutputSize);
2009-10-27 14:38:53 +00:00
}
int CNetBase::Decompress(const void *pData, int DataSize, void *pOutput, int OutputSize)
{
2010-05-29 07:25:38 +00:00
return ms_Huffman.Decompress(pData, DataSize, pOutput, OutputSize);
2009-10-27 14:38:53 +00:00
}
static const unsigned gs_aFreqTable[256+1] = {
1<<30,4545,2657,431,1950,919,444,482,2244,617,838,542,715,1814,304,240,754,212,647,186,
283,131,146,166,543,164,167,136,179,859,363,113,157,154,204,108,137,180,202,176,
872,404,168,134,151,111,113,109,120,126,129,100,41,20,16,22,18,18,17,19,
16,37,13,21,362,166,99,78,95,88,81,70,83,284,91,187,77,68,52,68,
59,66,61,638,71,157,50,46,69,43,11,24,13,19,10,12,12,20,14,9,
20,20,10,10,15,15,12,12,7,19,15,14,13,18,35,19,17,14,8,5,
15,17,9,15,14,18,8,10,2173,134,157,68,188,60,170,60,194,62,175,71,
148,67,167,78,211,67,156,69,1674,90,174,53,147,89,181,51,174,63,163,80,
167,94,128,122,223,153,218,77,200,110,190,73,174,69,145,66,277,143,141,60,
136,53,180,57,142,57,158,61,166,112,152,92,26,22,21,28,20,26,30,21,
32,27,20,17,23,21,30,22,22,21,27,25,17,27,23,18,39,26,15,21,
12,18,18,27,20,18,15,19,11,17,33,12,18,15,19,18,16,26,17,18,
9,10,25,22,22,17,20,16,6,16,15,20,14,18,24,335,1517};
void CNetBase::Init()
{
2010-05-29 07:25:38 +00:00
ms_Huffman.Init(gs_aFreqTable);
2009-10-27 14:38:53 +00:00
}