diff options
| author | Magnus Auvinen <magnus.auvinen@gmail.com> | 2009-10-27 14:38:53 +0000 |
|---|---|---|
| committer | Magnus Auvinen <magnus.auvinen@gmail.com> | 2009-10-27 14:38:53 +0000 |
| commit | 878ede3080ab2cfb627aca505c397d9765052996 (patch) | |
| tree | 98bff371070e1dca0295f0ca58d64ac4ee8042ce /src/engine/e_network_conn.cpp | |
| parent | 9b99ec0e60b60134e46f2f71d707230948f7db03 (diff) | |
| download | zcatch-878ede3080ab2cfb627aca505c397d9765052996.tar.gz zcatch-878ede3080ab2cfb627aca505c397d9765052996.zip | |
major update with stuff
Diffstat (limited to 'src/engine/e_network_conn.cpp')
| -rw-r--r-- | src/engine/e_network_conn.cpp | 349 |
1 files changed, 349 insertions, 0 deletions
diff --git a/src/engine/e_network_conn.cpp b/src/engine/e_network_conn.cpp new file mode 100644 index 00000000..a54c9ce3 --- /dev/null +++ b/src/engine/e_network_conn.cpp @@ -0,0 +1,349 @@ +#include <base/system.h> +#include <string.h> +#include "e_config.h" +#include "e_network.h" + +void CNetConnection::ResetStats() +{ + mem_zero(&m_Stats, sizeof(m_Stats)); +} + +void CNetConnection::Reset() +{ + m_Sequence = 0; + m_Ack = 0; + m_RemoteClosed = 0; + + m_State = NET_CONNSTATE_OFFLINE; + m_LastSendTime = 0; + m_LastRecvTime = 0; + m_LastUpdateTime = 0; + m_Token = -1; + mem_zero(&m_PeerAddr, sizeof(m_PeerAddr)); + + m_Buffer.Init(); + + mem_zero(&m_Construct, sizeof(m_Construct)); +} + +const char *CNetConnection::ErrorString() +{ + return m_ErrorString; +} + +void CNetConnection::SetError(const char *pString) +{ + str_copy(m_ErrorString, pString, sizeof(m_ErrorString)); +} + +void CNetConnection::Init(NETSOCKET Socket) +{ + Reset(); + ResetStats(); + + m_Socket = Socket; + mem_zero(m_ErrorString, sizeof(m_ErrorString)); +} + +void CNetConnection::AckChunks(int Ack) +{ + while(1) + { + CNetChunkResend *pResend = m_Buffer.First(); + if(!pResend) + break; + + if(CNetBase::IsSeqInBackroom(pResend->m_Sequence, Ack)) + m_Buffer.PopFirst(); + else + break; + } +} + +void CNetConnection::SignalResend() +{ + m_Construct.m_Flags |= NET_PACKETFLAG_RESEND; +} + +int CNetConnection::Flush() +{ + int NumChunks = m_Construct.m_NumChunks; + if(!NumChunks && !m_Construct.m_Flags) + return 0; + + /* send of the packets */ + m_Construct.m_Ack = m_Ack; + CNetBase::SendPacket(m_Socket, &m_PeerAddr, &m_Construct); + + /* update send times */ + m_LastSendTime = time_get(); + + /* clear construct so we can start building a new package */ + mem_zero(&m_Construct, sizeof(m_Construct)); + return NumChunks; +} + +void CNetConnection::QueueChunkEx(int Flags, int DataSize, const void *pData, int Sequence) +{ + unsigned char *pChunkData; + + /* check if we have space for it, if not, flush the connection */ + if(m_Construct.m_DataSize + DataSize + NET_MAX_CHUNKHEADERSIZE > (int)sizeof(m_Construct.m_aChunkData)) + Flush(); + + /* pack all the data */ + CNetChunkHeader Header; + Header.m_Flags = Flags; + Header.m_Size = DataSize; + Header.m_Sequence = Sequence; + pChunkData = &m_Construct.m_aChunkData[m_Construct.m_DataSize]; + pChunkData = Header.Pack(pChunkData); + mem_copy(pChunkData, pData, DataSize); + pChunkData += DataSize; + + /* */ + m_Construct.m_NumChunks++; + m_Construct.m_DataSize = (int)(pChunkData-m_Construct.m_aChunkData); + + /* set packet flags aswell */ + + if(Flags&NET_CHUNKFLAG_VITAL && !(Flags&NET_CHUNKFLAG_RESEND)) + { + /* save packet if we need to resend */ + CNetChunkResend *pResend = m_Buffer.Allocate(sizeof(CNetChunkResend)+DataSize); + if(pResend) + { + pResend->m_Sequence = Sequence; + pResend->m_Flags = Flags; + pResend->m_DataSize = DataSize; + pResend->m_pData = (unsigned char *)(pResend+1); + pResend->m_FirstSendTime = time_get(); + pResend->m_LastSendTime = pResend->m_FirstSendTime; + mem_copy(pResend->m_pData, pData, DataSize); + } + else + { + /* out of buffer */ + Disconnect("too weak connection (out of buffer)"); + } + } +} + +void CNetConnection::QueueChunk(int Flags, int DataSize, const void *pData) +{ + if(Flags&NET_CHUNKFLAG_VITAL) + m_Sequence = (m_Sequence+1)%NET_MAX_SEQUENCE; + QueueChunkEx(Flags, DataSize, pData, m_Sequence); +} + +void CNetConnection::SendControl(int ControlMsg, const void *pExtra, int ExtraSize) +{ + /* send the control message */ + m_LastSendTime = time_get(); + CNetBase::SendControlMsg(m_Socket, &m_PeerAddr, m_Ack, ControlMsg, pExtra, ExtraSize); +} + +void CNetConnection::ResendChunk(CNetChunkResend *pResend) +{ + QueueChunkEx(pResend->m_Flags|NET_CHUNKFLAG_RESEND, pResend->m_DataSize, pResend->m_pData, pResend->m_Sequence); + pResend->m_LastSendTime = time_get(); +} + +void CNetConnection::Resend() +{ + for(CNetChunkResend *pResend = m_Buffer.First(); pResend; m_Buffer.Next(pResend)) + ResendChunk(pResend); +} + +int CNetConnection::Connect(NETADDR *pAddr) +{ + if(State() != NET_CONNSTATE_OFFLINE) + return -1; + + /* init connection */ + Reset(); + m_PeerAddr = *pAddr; + mem_zero(m_ErrorString, sizeof(m_ErrorString)); + m_State = NET_CONNSTATE_CONNECT; + SendControl(NET_CTRLMSG_CONNECT, 0, 0); + return 0; +} + +void CNetConnection::Disconnect(const char *pReason) +{ + if(State() == NET_CONNSTATE_OFFLINE) + return; + + if(m_RemoteClosed == 0) + { + if(pReason) + SendControl(NET_CTRLMSG_CLOSE, pReason, strlen(pReason)+1); + else + SendControl(NET_CTRLMSG_CLOSE, 0, 0); + + m_ErrorString[0] = 0; + if(pReason) + str_copy(m_ErrorString, pReason, sizeof(m_ErrorString)); + } + + Reset(); +} + +int CNetConnection::Feed(CNetPacketConstruct *pPacket, NETADDR *pAddr) +{ + int64 now = time_get(); + m_LastRecvTime = now; + + /* check if resend is requested */ + if(pPacket->m_Flags&NET_PACKETFLAG_RESEND) + Resend(); + + /* */ + if(pPacket->m_Flags&NET_PACKETFLAG_CONTROL) + { + int CtrlMsg = pPacket->m_aChunkData[0]; + + if(CtrlMsg == NET_CTRLMSG_CLOSE) + { + m_State = NET_CONNSTATE_ERROR; + m_RemoteClosed = 1; + + if(pPacket->m_DataSize) + { + /* make sure to sanitize the error string form the other party*/ + char Str[128]; + if(pPacket->m_DataSize < 128) + str_copy(Str, (char *)pPacket->m_aChunkData, pPacket->m_DataSize); + else + str_copy(Str, (char *)pPacket->m_aChunkData, sizeof(Str)); + str_sanitize_strong(Str); + + /* set the error string */ + SetError(Str); + } + else + SetError("no reason given"); + + if(config.debug) + dbg_msg("conn", "closed reason='%s'", ErrorString()); + return 0; + } + else + { + if(State() == NET_CONNSTATE_OFFLINE) + { + if(CtrlMsg == NET_CTRLMSG_CONNECT) + { + /* send response and init connection */ + Reset(); + m_State = NET_CONNSTATE_PENDING; + m_PeerAddr = *pAddr; + m_LastSendTime = now; + m_LastRecvTime = now; + m_LastUpdateTime = now; + SendControl(NET_CTRLMSG_CONNECTACCEPT, 0, 0); + if(config.debug) + dbg_msg("connection", "got connection, sending connect+accept"); + } + } + else if(State() == NET_CONNSTATE_CONNECT) + { + /* connection made */ + if(CtrlMsg == NET_CTRLMSG_CONNECTACCEPT) + { + SendControl(NET_CTRLMSG_ACCEPT, 0, 0); + m_State = NET_CONNSTATE_ONLINE; + if(config.debug) + dbg_msg("connection", "got connect+accept, sending accept. connection online"); + } + } + else if(State() == NET_CONNSTATE_ONLINE) + { + /* connection made */ + /* + if(ctrlmsg == NET_CTRLMSG_CONNECTACCEPT) + { + + }*/ + } + } + } + else + { + if(State() == NET_CONNSTATE_PENDING) + { + m_State = NET_CONNSTATE_ONLINE; + if(config.debug) + dbg_msg("connection", "connecting online"); + } + } + + if(State() == NET_CONNSTATE_ONLINE) + { + AckChunks(pPacket->m_Ack); + } + + return 1; +} + +int CNetConnection::Update() +{ + int64 now = time_get(); + + if(State() == NET_CONNSTATE_OFFLINE || State() == NET_CONNSTATE_ERROR) + return 0; + + /* check for timeout */ + if(State() != NET_CONNSTATE_OFFLINE && + State() != NET_CONNSTATE_CONNECT && + (now-m_LastRecvTime) > time_freq()*10) + { + m_State = NET_CONNSTATE_ERROR; + SetError("timeout"); + } + + /* fix resends */ + if(m_Buffer.First()) + { + CNetChunkResend *pResend = m_Buffer.First(); + + /* check if we have some really old stuff laying around and abort if not acked */ + if(now-pResend->m_FirstSendTime > time_freq()*10) + { + m_State = NET_CONNSTATE_ERROR; + SetError("too weak connection (not acked for 10 seconds)"); + } + else + { + /* resend packet if we havn't got it acked in 1 second */ + if(now-pResend->m_LastSendTime > time_freq()) + ResendChunk(pResend); + } + } + + /* send keep alives if nothing has happend for 250ms */ + if(State() == NET_CONNSTATE_ONLINE) + { + if(time_get()-m_LastSendTime > time_freq()/2) /* flush connection after 500ms if needed */ + { + int NumFlushedChunks = Flush(); + if(NumFlushedChunks && config.debug) + dbg_msg("connection", "flushed connection due to timeout. %d chunks.", NumFlushedChunks); + } + + if(time_get()-m_LastSendTime > time_freq()) + SendControl(NET_CTRLMSG_KEEPALIVE, 0, 0); + } + else if(State() == NET_CONNSTATE_CONNECT) + { + if(time_get()-m_LastSendTime > time_freq()/2) /* send a new connect every 500ms */ + SendControl(NET_CTRLMSG_CONNECT, 0, 0); + } + else if(State() == NET_CONNSTATE_PENDING) + { + if(time_get()-m_LastSendTime > time_freq()/2) /* send a new connect/accept every 500ms */ + SendControl(NET_CTRLMSG_CONNECTACCEPT, 0, 0); + } + + return 0; +} |