diff options
Diffstat (limited to 'src/engine/shared/network_conn.cpp')
| -rw-r--r-- | src/engine/shared/network_conn.cpp | 354 |
1 files changed, 354 insertions, 0 deletions
diff --git a/src/engine/shared/network_conn.cpp b/src/engine/shared/network_conn.cpp new file mode 100644 index 00000000..4ed157eb --- /dev/null +++ b/src/engine/shared/network_conn.cpp @@ -0,0 +1,354 @@ +#include <base/system.h> +#include "config.h" +#include "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; +} + +int 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)"); + return -1; + } + } + + return 0; +} + +int CNetConnection::QueueChunk(int Flags, int DataSize, const void *pData) +{ + if(Flags&NET_CHUNKFLAG_VITAL) + m_Sequence = (m_Sequence+1)%NET_MAX_SEQUENCE; + return 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; 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, str_length(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) + { + if(net_addr_comp(&m_PeerAddr, pAddr) == 0) + { + 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(g_Config.m_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(g_Config.m_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(g_Config.m_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(g_Config.m_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 && g_Config.m_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; +} |