about summary refs log tree commit diff
path: root/src/engine/e_network.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/engine/e_network.cpp')
-rw-r--r--src/engine/e_network.cpp347
1 files changed, 347 insertions, 0 deletions
diff --git a/src/engine/e_network.cpp b/src/engine/e_network.cpp
new file mode 100644
index 00000000..ac753e50
--- /dev/null
+++ b/src/engine/e_network.cpp
@@ -0,0 +1,347 @@
+/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
+#include <base/system.h>
+
+#include <string.h> /* strlen */
+
+#include "e_config.h"
+#include "e_engine.h"
+#include "e_network.h"
+#include "e_huffman.h"
+
+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;
+}
+
+/* TODO: rename this function */
+int CNetRecvUnpacker::FetchChunk(CNetChunk *pChunk)
+{
+	CNetChunkHeader Header;
+	unsigned char *pEnd = m_Data.m_aChunkData + m_Data.m_DataSize;
+	
+	while(1)
+	{
+		unsigned char *pData = m_Data.m_aChunkData;
+		
+		/* check for old data to unpack */
+		if(!m_Valid || m_CurrentChunk >= m_Data.m_NumChunks)
+		{
+			Clear();
+			return 0;
+		}
+		
+		/* TODO: add checking here so we don't read too far */
+		for(int i = 0; i < m_CurrentChunk; i++)
+		{
+			pData = Header.Unpack(pData);
+			pData += Header.m_Size;
+		}
+		
+		/* unpack the header */
+		pData = Header.Unpack(pData);
+		m_CurrentChunk++;
+		
+		if(pData+Header.m_Size > pEnd)
+		{
+			Clear();
+			return 0;
+		}
+		
+		/* handle sequence stuff */
+		if(m_pConnection && (Header.m_Flags&NET_CHUNKFLAG_VITAL))
+		{
+			if(Header.m_Sequence == (m_pConnection->m_Ack+1)%NET_MAX_SEQUENCE)
+			{
+				/* in sequence */
+				m_pConnection->m_Ack = (m_pConnection->m_Ack+1)%NET_MAX_SEQUENCE;
+			}
+			else
+			{
+				/* old packet that we already got */
+				if(CNetBase::IsSeqInBackroom(Header.m_Sequence, m_pConnection->m_Ack))
+					continue;
+
+				/* out of sequence, request resend */
+				if(config.debug)
+					dbg_msg("conn", "asking for resend %d %d", Header.m_Sequence, (m_pConnection->m_Ack+1)%NET_MAX_SEQUENCE);
+				m_pConnection->SignalResend();
+				continue; /* take the next chunk in the packet */
+			}
+		}
+		
+		/* fill in the info */
+		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;
+	}
+}
+
+/* packs the data tight and sends it */
+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;
+
+	/* log the data */
+	if(ms_DataLogSent)
+	{
+		int type = 1;
+		io_write(ms_DataLogSent, &type, sizeof(type));
+		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);
+	}
+	
+	/* compress */
+	CompressedSize = huffman_compress(&ms_HuffmanState, pPacket->m_aChunkData, pPacket->m_DataSize, &aBuffer[3], NET_MAX_PACKETSIZE-4);
+
+	/* check if the compression was enabled, successful and good enough	*/
+	if(CompressedSize > 0 && CompressedSize < pPacket->m_DataSize)
+	{
+		FinalSize = CompressedSize;
+		pPacket->m_Flags |= NET_PACKETFLAG_COMPRESSION;
+	}
+	else
+	{
+		/* use uncompressed data */
+		FinalSize = pPacket->m_DataSize;
+		mem_copy(&aBuffer[3], pPacket->m_aChunkData, pPacket->m_DataSize);
+		pPacket->m_Flags &= ~NET_PACKETFLAG_COMPRESSION;
+	}
+
+	/* set header and send the packet if all things are good */
+	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);
+
+		/* log raw socket data */
+		if(ms_DataLogSent)
+		{
+			int type = 0;
+			io_write(ms_DataLogSent, &type, sizeof(type));
+			io_write(ms_DataLogSent, &FinalSize, sizeof(FinalSize));
+			io_write(ms_DataLogSent, aBuffer, FinalSize);
+			io_flush(ms_DataLogSent);
+		}
+	}
+}
+
+/* TODO: rename this function */
+int CNetBase::UnpackPacket(unsigned char *pBuffer, int Size, CNetPacketConstruct *pPacket)
+{
+	/* check the size */
+	if(Size < NET_PACKETHEADERSIZE || Size > NET_MAX_PACKETSIZE)
+	{
+		dbg_msg("", "packet too small, %d", Size);
+		return -1;
+	}
+
+	/* log the data */
+	if(ms_DataLogRecv)
+	{
+		int type = 0;
+		io_write(ms_DataLogRecv, &type, sizeof(type));
+		io_write(ms_DataLogRecv, &Size, sizeof(Size));
+		io_write(ms_DataLogRecv, pBuffer, Size);
+		io_flush(ms_DataLogRecv);
+	}
+	
+	/* read the packet */
+	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)
+		{
+			dbg_msg("", "connection less packet too small, %d", Size);
+			return -1;
+		}
+			
+		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)
+			pPacket->m_DataSize = huffman_decompress(&ms_HuffmanState, &pBuffer[3], pPacket->m_DataSize, pPacket->m_aChunkData, sizeof(pPacket->m_aChunkData));
+		else
+			mem_copy(pPacket->m_aChunkData, &pBuffer[3], pPacket->m_DataSize);
+	}
+
+	/* check for errors */	
+	if(pPacket->m_DataSize < 0)
+	{
+		if(config.debug)
+			dbg_msg("network", "error during packet decoding");
+		return -1;
+	}
+
+	/* log the data */
+	if(ms_DataLogRecv)
+	{
+		int type = 1;
+		io_write(ms_DataLogRecv, &type, sizeof(type));
+		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);
+	}
+		
+	/* return success */
+	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);
+	
+	/* send the control message */
+	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;
+	}
+	
+	return 0;
+}
+
+IOHANDLE CNetBase::ms_DataLogSent = 0;
+IOHANDLE CNetBase::ms_DataLogRecv = 0;
+HUFFMAN_STATE CNetBase::ms_HuffmanState;
+
+
+void CNetBase::OpenLog(const char *pSentLog, const char *pRecvLog)
+{
+	if(pSentLog)
+	{
+		ms_DataLogSent = engine_openfile(pSentLog, IOFLAG_WRITE);
+		if(ms_DataLogSent)
+			dbg_msg("network", "logging sent packages to '%s'", pSentLog);
+		else
+			dbg_msg("network", "failed to open for logging '%s'", pSentLog);
+	}
+	
+	if(pRecvLog)
+	{
+		ms_DataLogRecv = engine_openfile(pRecvLog, IOFLAG_WRITE);
+		if(ms_DataLogRecv)
+			dbg_msg("network", "logging recv packages to '%s'", pRecvLog);
+		else
+			dbg_msg("network", "failed to open for logging '%s'", pRecvLog);
+	}
+}
+
+int CNetBase::Compress(const void *pData, int DataSize, void *pOutput, int OutputSize)
+{
+	return huffman_compress(&ms_HuffmanState, pData, DataSize, pOutput, OutputSize);
+}
+
+int CNetBase::Decompress(const void *pData, int DataSize, void *pOutput, int OutputSize)
+{
+	return huffman_decompress(&ms_HuffmanState, pData, DataSize, pOutput, OutputSize);
+}
+
+
+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()
+{
+	huffman_init(&ms_HuffmanState, gs_aFreqTable);
+}