From 885d3bc8d2789830fec9830f6965cf3aac33e00d Mon Sep 17 00:00:00 2001 From: rajkosto Date: Mon, 15 Feb 2010 18:11:08 +0000 Subject: [PATCH] Ported udp server to alhem sockets as well, more fixes/workarounds --- Reality/Source/GameClient.cpp | 21 ++-- Reality/Source/GameClient.h | 69 ++++++++--- Reality/Source/GameServer.cpp | 197 ++++--------------------------- Reality/Source/GameServer.h | 25 +--- Reality/Source/GameSocket.cpp | 126 ++++++++++++++++++++ Reality/Source/GameSocket.h | 34 ++++++ Reality/Source/Reality08.vcproj | 8 ++ Reality/Source/Reality10.vcxproj | 2 + 8 files changed, 257 insertions(+), 225 deletions(-) create mode 100644 Reality/Source/GameSocket.cpp create mode 100644 Reality/Source/GameSocket.h diff --git a/Reality/Source/GameClient.cpp b/Reality/Source/GameClient.cpp index c41ab50..250f050 100644 --- a/Reality/Source/GameClient.cpp +++ b/Reality/Source/GameClient.cpp @@ -33,10 +33,11 @@ #include "Database/Database.h" #include "Log.h" #include "GameServer.h" +#include "GameSocket.h" #pragma pack(1) -GameClient::GameClient(struct sockaddr_in address, SOCKET *sock) +GameClient::GameClient(shared_ptr address, GameSocket *sock) { m_sock = sock; m_address = address; @@ -70,7 +71,7 @@ GameClient::~GameClient() } } -void GameClient::HandlePacket(char *pData, uint16 nLength) +void GameClient::HandlePacket( const char *pData, uint16 nLength ) { if (nLength < 1 || m_validClient == false) return; @@ -160,8 +161,7 @@ void GameClient::HandlePacket(char *pData, uint16 nLength) } beatPacket << uint16(swap16(numberOfBeats)); - int clientlen=sizeof(m_address); - sendto(*m_sock, beatPacket.contents(), beatPacket.size(), 0, (struct sockaddr*)&m_address, clientlen); + m_sock->SendToBuf(*m_address, beatPacket.contents(), beatPacket.size(), 0); } //notify margin that udp session is established @@ -179,8 +179,7 @@ void GameClient::HandlePacket(char *pData, uint16 nLength) if (m_worldLoaded == true && pData[0] != 0x01) // Ping...just reply with the same thing { - int clientlen=sizeof(m_address); - sendto(*m_sock, pData, nLength, 0, (struct sockaddr*)&m_address,clientlen ); + m_sock->SendToBuf(*m_address, pData, nLength, 0); } else { @@ -347,7 +346,7 @@ void GameClient::HandleOrdered( ByteBuffer &orderedData ) } } -SequencedPacket GameClient::Decrypt(char *pData, uint16 nLength) +SequencedPacket GameClient::Decrypt(const char *pData, uint16 nLength) { EncryptedPacket decryptedData(ByteBuffer(pData,nLength),m_TFDecrypt.get()); return SequencedPacket(decryptedData); @@ -363,8 +362,7 @@ void GameClient::SendEncrypted(SequencedPacket withSequences) sendMe << uint8(1); sendMe.append(withEncryption.toCipherText(m_TFEncrypt.get())); - int clientlen=sizeof(m_address); - sendto(*m_sock, sendMe.contents(), (int)sendMe.size(), 0, (struct sockaddr*)&m_address,clientlen); + m_sock->SendToBuf(*m_address, sendMe.contents(), sendMe.size(), 0); } void GameClient::PSSChanged( uint8 oldPSS,uint8 newPSS ) @@ -575,10 +573,10 @@ void GameClient::CheckAndResend() bool deleted = false; uint32 currTime = getMSTime(); - if (currTime - it->msTimeSent > 500) //500 is timeout for resend + if (currTime - it->msTimeSent > 200) //200 is timeout for resend { //client obviously doesnt want to ack this packet - if (it->resentCounter > 2) + if (it->resentCounter > 10) { ByteBuffer resentPacketDumpedData; try @@ -661,6 +659,7 @@ void GameClient::CheckAndResend() //reverse iterator because push_front inserts into reverse anyway for (list::reverse_iterator hurr=newPacket->msgBlocks.rbegin();hurr!=newPacket->msgBlocks.rend();++hurr) { + uint16 oldSeqz = m_serverSequence; increaseServerSequence(); m_sendQueue.push_front( PacketInQueue(oldClientPSS,m_serverSequence,oldClientSeq,oldAck,shared_ptr(new OrderedPacket(*hurr))) ); } diff --git a/Reality/Source/GameClient.h b/Reality/Source/GameClient.h index a3ad701..b75b3e0 100644 --- a/Reality/Source/GameClient.h +++ b/Reality/Source/GameClient.h @@ -29,33 +29,27 @@ #include "PlayerObject.h" #include "MessageTypes.h" #include "Log.h" +#include class GameClient { public: - GameClient(sockaddr_in address, SOCKET *sock); + GameClient(shared_ptr address, class GameSocket *sock); ~GameClient(); inline uint32 LastActive() { return m_lastActivity; } inline bool IsValid() { return m_validClient; } - void Invalidate() - { - m_validClient=false; - } - string Address() - { - stringstream addressStr; - addressStr << inet_ntoa(m_address.sin_addr) << ":" << ntohs(m_address.sin_port); - return addressStr.str(); - } + void Invalidate() { m_validClient=false;} + string Address() { return m_address->Convert(true); } uint32 GetSessionId() { if (m_encryptionInitialized == true) return m_sessionId; - return 0; + else + return 0; } - void HandlePacket(char *pData, uint16 Length); + void HandlePacket(const char *pData, uint16 nLength); void HandleEncrypted(ByteBuffer &srcData); void HandleOther(ByteBuffer &otherData); void HandleOrdered(ByteBuffer &orderedData); @@ -80,12 +74,49 @@ public: void FlushQueue(); void CheckAndResend(); private: - SequencedPacket Decrypt(char *pData, uint16 nLength); + SequencedPacket Decrypt(const char *pData, uint16 nLength); void PSSChanged(uint8 oldPSS,uint8 newPSS); bool PacketReceived(uint16 clientSeq) { + bool wraparound=false; + if (isSequenceMoreRecent(clientSeq,m_lastClientSequence) == true) + { + if ( (m_lastClientSequence > 4096/2) && clientSeq < 4096/2 ) + wraparound=true; + m_lastClientSequence = clientSeq; + } + + if (wraparound == true) + { + size_t removedPacketsToAck = 0; + for (deque::iterator it=m_packetsToAck.begin();it!=m_packetsToAck.end();) + { + if (*it < 4096/2) + { + it = m_packetsToAck.erase(it); + removedPacketsToAck++; + } + else + { + ++it; + } + } + + size_t flaggedActualPackets = 0; + for (sendQueueList::iterator it=m_sendQueue.begin();it!=m_sendQueue.end();++it) + { + if (it->client_sequence < 4096/2) + { + it->client_sequence = m_lastClientSequence; + it->ack = false; + flaggedActualPackets++; + } + } + + INFO_LOG(format("(%1) Purged %2% potential and %3% real acks due to client wraparound") % Address() % removedPacketsToAck % flaggedActualPackets); + } if (find(m_packetsToAck.begin(),m_packetsToAck.end(),clientSeq) != m_packetsToAck.end()) return false; @@ -196,14 +227,14 @@ private: increaseServerSequence(); uint16 theServerSeq = m_serverSequence; ByteBuffer outputData = dataToSend->toBuf(); - if (outputData.size() > 0) +/* if (outputData.size() > 0) { DEBUG_LOG(format("(%s) Queue SSeq: %d CSeq: %d Ack: %d Data: |%s|") % Address() % theServerSeq % clientSeq % ackPacket % Bin2Hex(outputData)); } else { DEBUG_LOG(format("(%s) Queue SSeq: %d CSeq: %d Ack: %d No Data") % Address() % theServerSeq % clientSeq % ackPacket); - } + }*/ m_sendQueue.push_back(PacketInQueue(m_clientPSS,theServerSeq,clientSeq,ackPacket,dataToSend,immediateOnly)); } void AddPacketToQueue(msgBaseClassPtr dataToSend, bool immediateOnly=false) @@ -228,8 +259,8 @@ private: bool m_characterSpawned; // Master Sock handle, client's address structure, last received packet - SOCKET *m_sock; - struct sockaddr_in m_address; + class GameSocket *m_sock; + shared_ptr m_address; uint32 m_lastActivity; uint32 m_lastPacketReceivedMS; uint32 m_lastOrderedFlush; @@ -247,7 +278,9 @@ private: { m_serverSequence++; if (m_serverSequence == 4096) + { m_serverSequence=0; + } } uint16 m_lastClientSequence; inline bool isSequenceMoreRecent( uint16 biggerSequence, uint16 smallerSequence, uint32 max_sequence=4096 ) diff --git a/Reality/Source/GameServer.cpp b/Reality/Source/GameServer.cpp index 5663fc9..be8c1ab 100644 --- a/Reality/Source/GameServer.cpp +++ b/Reality/Source/GameServer.cpp @@ -26,217 +26,64 @@ #include "Log.h" #include "Timer.h" #include "Config.h" -#include "Sockets.h" +#include "GameSocket.h" +#include initialiseSingleton( GameServer ); bool GameServer::Start() { - int Port; - - Port = sConfig.GetIntDefault("GameServer.Port", 10000); - + int Port = sConfig.GetIntDefault("GameServer.Port", 10000); INFO_LOG(format("Starting Game server on port %1%") % Port); -#if PLATFORM == PLATFORM_WIN32 - // Winsock Startup - WSADATA wsa; - memset(&wsa, 0x0, sizeof(WSADATA)); - if( WSAStartup( MAKEWORD(2,0), &wsa ) != 0x0 ) + m_mainSocket.reset(new GameSocket(m_udpHandler)); + port_t thePortToBind = Port; + if (m_mainSocket->Bind(thePortToBind) != 0) { - CRITICAL_LOG("Unable to initialize WinSock2!"); + ERROR_LOG(format("Error binding Game Server to port %1%") % thePortToBind); return false; } -#endif - - m_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - - if (m_socket == INVALID_SOCKET) - { - CRITICAL_LOG("Unable to create socket!"); - return false; - } - - // 0 = Blocking sockets - 1 = Non-blocking - unsigned long mode = 1; - -#if PLATFORM == PLATFORM_WIN32 - int ret = ioctlsocket(m_socket, FIONBIO, &mode ); -#else - int ret = ioctl(m_socket, FIONBIO, &mode); -#endif - - if (ret < 0) - { - CRITICAL_LOG("Unable to set socket to non blocking!"); - m_socket = INVALID_SOCKET; - return false; - } - - memset(&listen_addr, 0, sizeof(listen_addr)); - listen_addr.sin_family = AF_INET; - listen_addr.sin_addr.s_addr = INADDR_ANY; - listen_addr.sin_port = htons(Port); - - if (::bind(m_socket, (struct sockaddr *) &listen_addr, sizeof(listen_addr)) < 0) - { - CRITICAL_LOG("Unable to bind socket!"); - m_socket = INVALID_SOCKET; - return false; - } - m_lastCleanupTime = getTime(); + m_udpHandler.Add(m_mainSocket.get()); return true; } void GameServer::Stop() { + m_mainSocket.reset(); INFO_LOG("Game Server shutdown"); - -#if PLATFORM == PLATFORM_WIN32 - closesocket(m_socket); - WSACleanup(); -#else - close(m_socket); -#endif - - m_socket = INVALID_SOCKET; } void GameServer::Loop(void) { - FD_ZERO(&m_readable); - FD_SET(m_socket, &m_readable); + if (m_mainSocket == NULL) + return; - m_timeout.tv_sec = 0; - m_timeout.tv_usec = 100; - - if (select(0, &m_readable, NULL, NULL, &m_timeout) == SOCKET_ERROR) - { - CRITICAL_LOG("Select() Failed, Shutting down World server"); - Stop(); - } - - if (FD_ISSET(m_socket, &m_readable)) - { - Handle_Incoming(); - } - - CheckAndResend(); - - m_currTime = getTime(); - if ((m_currTime - m_lastCleanupTime) >= 5) - { - // Do client cleanup - GClientList::iterator i = m_clients.begin(); - for (;;) - { - if (i == m_clients.end()) - break; - - GameClient *Client = (*i).second; - if (Client->IsValid() == false || (m_currTime - - Client->LastActive()) >= 20) - { - DEBUG_LOG( format("Routine dead client removal [%1%]") % Client->Address() ); - m_clients.erase(i++); - delete Client; - } - else - ++i; - } - m_lastCleanupTime = m_currTime; - } + m_mainSocket->PruneDeadClients(); + m_mainSocket->CheckAndResend(); + m_udpHandler.Select(0,4000); //4ms } -void GameServer::Handle_Incoming() -{ - char Buffer[RECV_BUFFER_SIZE]; - - socklen_t addr_len = sizeof(inc_addr); - - uint16 len = 0; - std::stringstream IP; - - len = recvfrom(m_socket, Buffer, RECV_BUFFER_SIZE, 0, - (struct sockaddr*) &inc_addr, &addr_len); - - if ((len > 0 && len <= RECV_BUFFER_SIZE && errno != EWOULDBLOCK) || - (len > 0 && len <= RECV_BUFFER_SIZE)) - { - IP << inet_ntoa(inc_addr.sin_addr) << ":" << inc_addr.sin_port; - GClientList::iterator i = m_clients.find(IP.str()); - if (i != m_clients.end()) - { - if (m_clients[IP.str()]->IsValid() == false) - { - DEBUG_LOG( format("Removing dead client [%1%]") % IP.str() ); - delete m_clients[IP.str()]; - m_clients.erase(m_clients.find(IP.str())); - } - else - { - m_clients[IP.str()]->HandlePacket(Buffer, len); - } - } - else - { - m_clients[IP.str()] = new GameClient(inc_addr, &m_socket); - DEBUG_LOG(format ("Client connected [%1%], now have [%2%] clients") - % IP.str() % Clients_Connected()); - m_clients[IP.str()]->HandlePacket(Buffer, len); - } - - } -} - - void GameServer::Broadcast( const ByteBuffer &message ) { - /*for (GClientList::iterator i = m_clients.begin();i != m_clients.end();++i) + if (m_mainSocket != NULL) { - i->second->QueueState(message); - }*/ -} - -GameClient *GameServer::GetClientWithSessionId(uint32 sessionId) -{ - for (GClientList::iterator it=m_clients.begin();it!=m_clients.end();++it) - { - if (it->second->GetSessionId() == sessionId) - { - return it->second; - } - } - return NULL; -} - -void GameServer::CheckAndResend() -{ - for (GClientList::iterator it=m_clients.begin();it!=m_clients.end();++it) - { - it->second->CheckAndResend(); + m_mainSocket->Broadcast(message); } } -void GameServer::AnnounceStateUpdate( class GameClient* clFrom,msgBaseClassPtr theMsg, bool immediateOnly ) +void GameServer::AnnounceStateUpdate( GameClient* clFrom,msgBaseClassPtr theMsg, bool immediateOnly ) { - for (GClientList::iterator it=m_clients.begin();it!=m_clients.end();++it) + if (m_mainSocket != NULL) { - if (it->second!=clFrom) - { - it->second->QueueState(theMsg,immediateOnly); - } + m_mainSocket->AnnounceStateUpdate(clFrom,theMsg,immediateOnly); } } -void GameServer::AnnounceCommand( class GameClient* clFrom,msgBaseClassPtr theCmd ) +void GameServer::AnnounceCommand( GameClient* clFrom,msgBaseClassPtr theCmd ) { - for (GClientList::iterator it=m_clients.begin();it!=m_clients.end();++it) + if (m_mainSocket != NULL) { - if (it->second!=clFrom) - { - it->second->QueueCommand(theCmd); - } + m_mainSocket->AnnounceCommand(clFrom,theCmd); } } \ No newline at end of file diff --git a/Reality/Source/GameServer.h b/Reality/Source/GameServer.h index 452eeee..8431fc6 100644 --- a/Reality/Source/GameServer.h +++ b/Reality/Source/GameServer.h @@ -25,12 +25,10 @@ #include "Common.h" #include "ByteBuffer.h" #include "Singleton.h" -#include "Sockets.h" #include "ObjectMgr.h" +#include #include "MessageTypes.h" -#define RECV_BUFFER_SIZE 2048 - class GameServer : public Singleton { public: @@ -39,30 +37,15 @@ public: bool Start(); void Stop(); void Loop(); - int Clients_Connected(void) { return (int)m_clients.size(); } - void Handle_Incoming(); + ObjectMgr &getObjMgr() { return m_objMgr; } class GameClient *GetClientWithSessionId(uint32 sessionId); - void CheckAndResend(); void Broadcast(const ByteBuffer &message); void AnnounceStateUpdate(class GameClient* clFrom,msgBaseClassPtr theMsg, bool immediateOnly=false); void AnnounceCommand(class GameClient* clFrom,msgBaseClassPtr theCmd); - ObjectMgr &getObjMgr() { return m_objMgr; } - private: - // Client List - typedef std::map GClientList; - GClientList m_clients; - struct sockaddr_in listen_addr, inc_addr; - - // Socket stuff - SOCKET m_socket; - fd_set m_readable; - - struct timeval m_timeout; - uint32 m_lastCleanupTime; - uint32 m_currTime; - ObjectMgr m_objMgr; + SocketHandler m_udpHandler; + shared_ptr m_mainSocket; }; diff --git a/Reality/Source/GameSocket.cpp b/Reality/Source/GameSocket.cpp new file mode 100644 index 0000000..a7f1ba6 --- /dev/null +++ b/Reality/Source/GameSocket.cpp @@ -0,0 +1,126 @@ +#include "Common.h" +#include "GameSocket.h" +#include "Log.h" +#include "GameClient.h" +#include "Timer.h" +#include + +GameSocket::GameSocket( ISocketHandler& theHandler ) : UdpSocket(theHandler) +{ + m_lastCleanupTime = getTime(); +} + +GameSocket::~GameSocket() +{ + +} + +void GameSocket::OnRawData( const char *pData,size_t len,struct sockaddr *sa_from,socklen_t sa_len ) +{ + stringstream IP; + struct sockaddr_in inc_addr; + memcpy(&inc_addr,sa_from,sa_len); + shared_ptr theAddr(new Ipv4Address(inc_addr)); + + if (theAddr->IsValid() == false) + return; + + string IPStr = theAddr->Convert(true); + GClientList::iterator it = m_clients.find(IPStr); + if (it != m_clients.end()) + { + GameClient *Client = it->second; + if (Client->IsValid() == false) + { + DEBUG_LOG( format("Removing dead client [%1%]") % IPStr ); + m_clients.erase(it); + delete Client; + } + else + { + Client->HandlePacket(pData, len); + } + } + else + { + m_clients[IPStr] = new GameClient(theAddr, this); + DEBUG_LOG(format ("Client connected [%1%], now have [%2%] clients") + % IPStr % Clients_Connected()); + + m_clients[IPStr]->HandlePacket(pData, len); + } +} + +void GameSocket::PruneDeadClients() +{ + m_currTime = getTime(); + if ((m_currTime - m_lastCleanupTime) >= 5) + { + // Do client cleanup + for (GClientList::iterator it=m_clients.begin();it!=m_clients.end();) + { + GameClient *Client = it->second; + if (Client->IsValid() == false || (m_currTime - Client->LastActive()) >= 20) + { + DEBUG_LOG( format("Routine dead client removal [%1%]") % Client->Address() ); + m_clients.erase(it++); + delete Client; + } + else + { + ++it; + } + } + m_lastCleanupTime = m_currTime; + } +} + +GameClient * GameSocket::GetClientWithSessionId( uint32 sessionId ) +{ + for (GClientList::iterator it=m_clients.begin();it!=m_clients.end();++it) + { + if (it->second->GetSessionId() == sessionId) + { + return it->second; + } + } + return NULL; +} + +void GameSocket::CheckAndResend() +{ + for (GClientList::iterator it=m_clients.begin();it!=m_clients.end();++it) + { + it->second->CheckAndResend(); + } +} + +void GameSocket::Broadcast( const ByteBuffer &message ) +{ + /*for (GClientList::iterator i = m_clients.begin();i != m_clients.end();++i) + { + i->second->QueueState(message); + }*/ +} + +void GameSocket::AnnounceStateUpdate( GameClient* clFrom,msgBaseClassPtr theMsg, bool immediateOnly/*=false*/ ) +{ + for (GClientList::iterator it=m_clients.begin();it!=m_clients.end();++it) + { + if (it->second!=clFrom) + { + it->second->QueueState(theMsg,immediateOnly); + } + } +} + +void GameSocket::AnnounceCommand( GameClient* clFrom,msgBaseClassPtr theCmd ) +{ + for (GClientList::iterator it=m_clients.begin();it!=m_clients.end();++it) + { + if (it->second!=clFrom) + { + it->second->QueueCommand(theCmd); + } + } +} \ No newline at end of file diff --git a/Reality/Source/GameSocket.h b/Reality/Source/GameSocket.h new file mode 100644 index 0000000..9f1d3e8 --- /dev/null +++ b/Reality/Source/GameSocket.h @@ -0,0 +1,34 @@ +#ifndef MXOSIM_GAMESOCKET_H +#define MXOSIM_GAMESOCKET_H + +#include "Common.h" +#include "ByteBuffer.h" +#include "MessageTypes.h" +#include +#include +#include + +class GameSocket : public UdpSocket +{ +public: + GameSocket(ISocketHandler& theHandler); + ~GameSocket(); + void OnRawData( const char *pData,size_t len,struct sockaddr *sa_from,socklen_t sa_len ); + void PruneDeadClients(); + void CheckAndResend(); + size_t Clients_Connected(void) { return m_clients.size(); } + class GameClient *GetClientWithSessionId(uint32 sessionId); + void Broadcast(const ByteBuffer &message); + void AnnounceStateUpdate(class GameClient* clFrom,msgBaseClassPtr theMsg, bool immediateOnly=false); + void AnnounceCommand(class GameClient* clFrom,msgBaseClassPtr theCmd); +private: + // Client List + typedef map GClientList; + GClientList m_clients; + + uint32 m_lastCleanupTime; + uint32 m_currTime; +}; + + +#endif \ No newline at end of file diff --git a/Reality/Source/Reality08.vcproj b/Reality/Source/Reality08.vcproj index a884f4a..142ebac 100644 --- a/Reality/Source/Reality08.vcproj +++ b/Reality/Source/Reality08.vcproj @@ -343,6 +343,14 @@ RelativePath=".\GameServer.h" > + + + + diff --git a/Reality/Source/Reality10.vcxproj b/Reality/Source/Reality10.vcxproj index ae47a62..4e644a2 100644 --- a/Reality/Source/Reality10.vcxproj +++ b/Reality/Source/Reality10.vcxproj @@ -100,6 +100,7 @@ + @@ -157,6 +158,7 @@ +