Ported udp server to alhem sockets as well, more fixes/workarounds

This commit is contained in:
rajkosto 2010-02-15 18:11:08 +00:00
parent 2c29aedc51
commit 885d3bc8d2
8 changed files with 257 additions and 225 deletions

View file

@ -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<SocketAddress> 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<MsgBlock>::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<OrderedPacket>(new OrderedPacket(*hurr))) );
}

View file

@ -29,33 +29,27 @@
#include "PlayerObject.h"
#include "MessageTypes.h"
#include "Log.h"
#include <Sockets/SocketAddress.h>
class GameClient
{
public:
GameClient(sockaddr_in address, SOCKET *sock);
GameClient(shared_ptr<SocketAddress> 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<uint16>::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<SocketAddress> 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 )

View file

@ -26,217 +26,64 @@
#include "Log.h"
#include "Timer.h"
#include "Config.h"
#include "Sockets.h"
#include "GameSocket.h"
#include <Sockets/Ipv4Address.h>
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);
}
}

View file

@ -25,12 +25,10 @@
#include "Common.h"
#include "ByteBuffer.h"
#include "Singleton.h"
#include "Sockets.h"
#include "ObjectMgr.h"
#include <Sockets/SocketHandler.h>
#include "MessageTypes.h"
#define RECV_BUFFER_SIZE 2048
class GameServer : public Singleton <GameServer>
{
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<std::string, class GameClient*> 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<class GameSocket> m_mainSocket;
};

View file

@ -0,0 +1,126 @@
#include "Common.h"
#include "GameSocket.h"
#include "Log.h"
#include "GameClient.h"
#include "Timer.h"
#include <Sockets/Ipv4Address.h>
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<SocketAddress> 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);
}
}
}

View file

@ -0,0 +1,34 @@
#ifndef MXOSIM_GAMESOCKET_H
#define MXOSIM_GAMESOCKET_H
#include "Common.h"
#include "ByteBuffer.h"
#include "MessageTypes.h"
#include <Sockets/UdpSocket.h>
#include <Sockets/ISocketHandler.h>
#include <Sockets/SocketAddress.h>
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<string, class GameClient*> GClientList;
GClientList m_clients;
uint32 m_lastCleanupTime;
uint32 m_currTime;
};
#endif

View file

@ -343,6 +343,14 @@
RelativePath=".\GameServer.h"
>
</File>
<File
RelativePath=".\GameSocket.cpp"
>
</File>
<File
RelativePath=".\GameSocket.h"
>
</File>
<File
RelativePath=".\LocationVector.h"
>

View file

@ -100,6 +100,7 @@
<ItemGroup>
<ClInclude Include="CrashHandler.h" />
<ClInclude Include="CryptoTest.h" />
<ClInclude Include="GameSocket.h" />
<ClInclude Include="Master.h" />
<ClInclude Include="seqchecktest.h" />
<ClInclude Include="StackWalker.h" />
@ -157,6 +158,7 @@
</ItemGroup>
<ItemGroup>
<ClCompile Include="CrashHandler.cpp" />
<ClCompile Include="GameSocket.cpp" />
<ClCompile Include="Main.cpp" />
<ClCompile Include="Master.cpp" />
<ClCompile Include="DotConfPP\dotconfpp.cpp" />