978 lines
23 KiB
C++
978 lines
23 KiB
C++
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Crytek Network source code
|
|
//
|
|
// File: Server.cpp
|
|
// Description:
|
|
//
|
|
// History:
|
|
// -July 25,2001:Created by Alberto Demichelis
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
#include "stdafx.h"
|
|
#include "Network.h"
|
|
#include "CNP.h"
|
|
#include "Server.h"
|
|
#include "ServerSlot.h"
|
|
#include "ILog.h"
|
|
#include "IConsole.h"
|
|
#include "NewUbisoftClient.h" // NewUbisoftClient
|
|
#include <IScriptSystem.h>
|
|
|
|
#if defined(_DEBUG) && !defined(LINUX)
|
|
static char THIS_FILE[] = __FILE__;
|
|
#define DEBUG_CLIENTBLOCK new( _NORMAL_BLOCK, THIS_FILE, __LINE__)
|
|
#define new DEBUG_CLIENTBLOCK
|
|
#endif
|
|
|
|
|
|
#if !defined(WIN64) && !defined(LINUX64) && !defined(NOT_USE_ASE_SDK)
|
|
|
|
#pragma comment(lib, "ASEQuerySDK.lib")
|
|
|
|
static CServer *g_pServer = 0;
|
|
|
|
extern "C"{
|
|
#include "ASEQuerySDK.h"
|
|
|
|
//-------------------------------------------------------------------------------------------------
|
|
void ASEQuery_wantstatus()
|
|
{
|
|
if (!g_pServer || !g_pServer->GetServerSlotFactory())
|
|
{
|
|
ASEQuery_status("", "", "", "", 1, 0, 0);
|
|
|
|
return;
|
|
}
|
|
|
|
string szName, szGameType, szMap, szVersion;
|
|
bool bPassword = false;
|
|
int nPlayers = 0;
|
|
int nMaxPlayers = 0;
|
|
g_pServer->GetServerSlotFactory()->GetServerInfoStatus(szName, szGameType, szMap, szVersion, &bPassword, &nPlayers, &nMaxPlayers);
|
|
|
|
ASEQuery_status(szName.c_str(), szGameType.c_str(), szMap.c_str(), szVersion.c_str(), bPassword, nPlayers, nMaxPlayers);
|
|
}
|
|
|
|
//-------------------------------------------------------------------------------------------------
|
|
void ASEQuery_wantrules()
|
|
{
|
|
IScriptSystem *pSS = GetISystem()->GetIScriptSystem();
|
|
|
|
_SmartScriptObject QueryHandler(pSS, 1);
|
|
|
|
if (!pSS->GetGlobalValue("QueryHandler", (IScriptObject *)QueryHandler))
|
|
{
|
|
return;
|
|
}
|
|
|
|
_SmartScriptObject ServerRules(pSS, 1);
|
|
pSS->BeginCall("QueryHandler", "GetServerRules");
|
|
pSS->PushFuncParam((IScriptObject *)QueryHandler);
|
|
pSS->EndCall((IScriptObject *)ServerRules);
|
|
|
|
for (int i = 1; i <= ServerRules->Count(); i++)
|
|
{
|
|
_SmartScriptObject Rule(pSS, 1);
|
|
|
|
if (ServerRules->GetAt(i, (IScriptObject *)Rule))
|
|
{
|
|
char *szRuleName = 0;
|
|
char *szRuleValue = 0;
|
|
|
|
Rule->GetAt(1, szRuleName);
|
|
Rule->GetAt(2, szRuleValue);
|
|
|
|
if (szRuleValue && szRuleName)
|
|
{
|
|
ASEQuery_addrule(szRuleName, szRuleValue);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------------------------------------------
|
|
void ASEQuery_wantplayers()
|
|
{
|
|
IScriptSystem *pSS = GetISystem()->GetIScriptSystem();
|
|
|
|
_SmartScriptObject QueryHandler(pSS, 1);
|
|
|
|
if (!pSS->GetGlobalValue("QueryHandler", (IScriptObject *)QueryHandler))
|
|
{
|
|
return;
|
|
}
|
|
|
|
_SmartScriptObject PlayerStats(pSS, 1);
|
|
pSS->BeginCall("QueryHandler", "GetPlayerStats");
|
|
pSS->PushFuncParam((IScriptObject *)QueryHandler);
|
|
pSS->EndCall((IScriptObject *)PlayerStats);
|
|
|
|
for (int i = 1; i <= PlayerStats->Count(); i++)
|
|
{
|
|
_SmartScriptObject Player(pSS, 1);
|
|
|
|
if (PlayerStats->GetAt(i, (IScriptObject *)Player))
|
|
{
|
|
char *szName = 0;
|
|
char *szTeam = 0;
|
|
char *szSkin = 0;
|
|
char *szScore = 0;
|
|
char *szPing = 0;
|
|
char *szTime = 0;
|
|
|
|
Player->GetValue("Name", (const char* &)szName);
|
|
Player->GetValue("Team", (const char* &)szTeam);
|
|
Player->GetValue("Skin", (const char* &)szSkin);
|
|
Player->GetValue("Score", (const char* &)szScore);
|
|
Player->GetValue("Ping", (const char* &)szPing);
|
|
Player->GetValue("Time", (const char* &)szTime);
|
|
|
|
ASEQuery_addplayer(szName, szTeam, szSkin, szScore, szPing, szTime);
|
|
}
|
|
}
|
|
}
|
|
} // extern "C"
|
|
#endif
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Construction/Destruction
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
CServer::CServer(CNetwork *pNetwork)
|
|
{
|
|
m_cLastClientID = 0;
|
|
m_pFactory = NULL;
|
|
m_ServerVariables.nDataStreamTimeout = 30000;// 30 seconds
|
|
m_pNetwork=pNetwork;
|
|
m_wPort=0;
|
|
m_bMulticastSocket=true;
|
|
m_pSecuritySink=0;
|
|
m_MPServerType=eMPST_LAN;
|
|
}
|
|
|
|
CServer::~CServer()
|
|
{
|
|
//-------------------------------------------------------------------------------------------------
|
|
// ASE Deinitialization
|
|
//-------------------------------------------------------------------------------------------------
|
|
//-------------------------------------------------------------------------------------------------
|
|
#if !defined(WIN64) && !defined(LINUX64) && !defined(NOT_USE_ASE_SDK)
|
|
ASEQuery_shutdown();
|
|
#endif
|
|
//-------------------------------------------------------------------------------------------------
|
|
|
|
#ifndef NOT_USE_UBICOM_SDK
|
|
// If it is a UBI type server we should unregister
|
|
m_pNetwork->m_pUbiSoftClient->Server_DestroyServer();
|
|
#endif // NOT_USE_UBICOM_SDK
|
|
|
|
m_pNetwork->UnregisterServer(m_wPort);
|
|
}
|
|
|
|
EMPServerType CServer::GetServerType() const
|
|
{
|
|
return m_MPServerType;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// IServer
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
bool CServer::Init(IServerSlotFactory *pFactory, WORD wPort, bool listen)
|
|
{
|
|
CIPAddress ipMulticast(SERVER_MULTICAST_PORT, SERVER_MULTICAST_ADDRESS);
|
|
|
|
m_pFactory = pFactory;
|
|
|
|
if(m_bListen = listen)
|
|
{
|
|
CIPAddress ipLocal;
|
|
|
|
ipLocal.m_Address.ADDR = m_pNetwork->GetLocalIP();
|
|
|
|
// only create the multicast socket if it's not internet server
|
|
ICVar *sv_ServerType = GetISystem()->GetIConsole()->GetCVar("sv_ServerType"); assert(sv_ServerType);
|
|
|
|
m_MPServerType=eMPST_LAN;
|
|
|
|
if(stricmp(sv_ServerType->GetString(),"UBI")==0)
|
|
m_MPServerType=eMPST_UBI;
|
|
else if(stricmp(sv_ServerType->GetString(),"NET")==0)
|
|
m_MPServerType=eMPST_NET;
|
|
|
|
// if this is a lan server
|
|
//if (m_MPServerType==eMPST_LAN)
|
|
{
|
|
if (NET_SUCCEDED(m_socketMulticast.Create()))
|
|
{
|
|
if (NET_SUCCEDED(m_socketMulticast.Listen(SERVER_MULTICAST_PORT, &ipMulticast, &ipLocal)))
|
|
{
|
|
m_bMulticastSocket=true;
|
|
}
|
|
}
|
|
}
|
|
/* else
|
|
{
|
|
m_bMulticastSocket=false;
|
|
}
|
|
*/
|
|
|
|
if (NET_FAILED(m_socketMain.Create()))
|
|
return false;
|
|
|
|
if (NET_FAILED(m_socketMain.Listen(wPort, 0, &ipLocal)))
|
|
return false;
|
|
|
|
//-------------------------------------------------------------------------------------------------
|
|
// ASE Initialization
|
|
//-------------------------------------------------------------------------------------------------
|
|
//-------------------------------------------------------------------------------------------------
|
|
DWORD dwLocalIP = GetISystem()->GetINetwork()->GetLocalIP();
|
|
char *szIP = 0;
|
|
CIPAddress ip;
|
|
|
|
if (dwLocalIP)
|
|
{
|
|
ip.m_Address.ADDR = dwLocalIP;
|
|
szIP = ip.GetAsString();
|
|
}
|
|
|
|
#if !defined(WIN64) && !defined(LINUX64) && !defined(NOT_USE_ASE_SDK)
|
|
ASEQuery_initialize((int)wPort, m_MPServerType!=eMPST_LAN ? 1 : 0, szIP);
|
|
#endif
|
|
//-------------------------------------------------------------------------------------------------
|
|
};
|
|
|
|
m_wPort=wPort;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
#ifdef _INTERNET_SIMULATOR
|
|
#include <stdlib.h>
|
|
#if !defined(LINUX)
|
|
#include <assert.h>
|
|
#endif
|
|
|
|
#include "ITimer.h"
|
|
#endif
|
|
|
|
|
|
void CServer::Update(unsigned int nTime)
|
|
{
|
|
//-------------------------------------------------------------------------------------------------
|
|
// ASE Update
|
|
//-------------------------------------------------------------------------------------------------
|
|
//-------------------------------------------------------------------------------------------------
|
|
#if !defined(WIN64) && !defined(LINUX64) && !defined(NOT_USE_ASE_SDK)
|
|
g_pServer = this;
|
|
ASEQuery_check();
|
|
#endif
|
|
//-------------------------------------------------------------------------------------------------
|
|
|
|
#ifdef _INTERNET_SIMULATOR
|
|
static ICVar *pVarPacketloss=GetISystem()->GetIConsole()->GetCVar("g_internet_simulator_packetloss");
|
|
|
|
TDelayPacketList2::iterator i;
|
|
|
|
for (i = m_delayedPacketList.begin(); i != m_delayedPacketList.end();)
|
|
{
|
|
DelayedPacket2 *dp = (*i);
|
|
if (dp->m_fTimeToSend <= GetISystem()->GetITimer()->GetCurrTime())
|
|
{
|
|
i = m_delayedPacketList.erase(i);
|
|
// Send it
|
|
if ((rand() %100) > pVarPacketloss->GetFVal())
|
|
m_socketMain.Send(dp->data,dp->len,dp->address);
|
|
// Delete it
|
|
delete dp->address;
|
|
delete dp;
|
|
}
|
|
else
|
|
++i;
|
|
}
|
|
#endif
|
|
|
|
m_nCurrentTime = nTime;
|
|
int nRecvBytes;
|
|
// do{
|
|
/////////////////////////////////////////////////////////
|
|
/////////////////////////////////////////////////////////
|
|
static CIPAddress ipFrom;
|
|
static CStream buf;
|
|
|
|
/////////////////////////////////////////////////////////
|
|
if(m_bListen)
|
|
do
|
|
{
|
|
buf.Reset();
|
|
nRecvBytes = 0;
|
|
m_socketMain.Receive(buf.GetPtr(),
|
|
(int)BITS2BYTES(buf.GetAllocatedSize()),
|
|
nRecvBytes,
|
|
ipFrom);
|
|
|
|
///////////////////////////////////////////////////////
|
|
if (nRecvBytes>0)
|
|
{
|
|
buf.SetSize(BYTES2BITS(nRecvBytes));
|
|
ProcessPacket(buf, ipFrom);
|
|
}
|
|
}while (nRecvBytes>0);
|
|
|
|
/////////////////////////////////////////////////////////
|
|
// handle multicast packets
|
|
/////////////////////////////////////////////////////////
|
|
if(m_bMulticastSocket && m_bListen && m_MPServerType==eMPST_LAN)
|
|
{
|
|
do
|
|
{
|
|
buf.Reset();
|
|
nRecvBytes = 0;
|
|
m_socketMulticast.Receive(buf.GetPtr(),
|
|
(int)BITS2BYTES(buf.GetAllocatedSize()),
|
|
nRecvBytes,
|
|
ipFrom);
|
|
|
|
///////////////////////////////////////////////////////
|
|
if (nRecvBytes>0)
|
|
{
|
|
buf.SetSize(BYTES2BITS(nRecvBytes));
|
|
ProcessMulticastPacket(buf, ipFrom);
|
|
}
|
|
}while (nRecvBytes>0);
|
|
}
|
|
/////////////////////////////////////////////////////////
|
|
// Update slots State machine
|
|
SLOTS_MAPItr itr = m_mapSlots.begin();
|
|
|
|
while (itr != m_mapSlots.end())
|
|
{
|
|
CServerSlot *pSlot = itr->second;
|
|
if (pSlot->IsActive())
|
|
{
|
|
pSlot->Update(m_nCurrentTime, NULL, NULL);
|
|
}
|
|
++itr;
|
|
}
|
|
|
|
m_pNetwork->OnServerUpdate();
|
|
}
|
|
|
|
void CServer::GetBandwidth( float &fIncomingKbPerSec, float &fOutgoinKbPerSec, DWORD &nIncomingPackets, DWORD &nOutgoingPackets )
|
|
{
|
|
fIncomingKbPerSec = m_socketMain.m_fIncomingKbPerSec;
|
|
fOutgoinKbPerSec = m_socketMain.m_fOutgoingKbPerSec;
|
|
nIncomingPackets=m_socketMain.m_nIncomingPacketsPerSec;
|
|
nOutgoingPackets=m_socketMain.m_nOutgoingPacketsPerSec;
|
|
}
|
|
|
|
void CServer::SetVariable(enum CryNetworkVarible eVarName, unsigned int nValue)
|
|
{
|
|
switch (eVarName)
|
|
{
|
|
case cnvDataStreamTimeout:
|
|
m_ServerVariables.nDataStreamTimeout = nValue;
|
|
break;
|
|
default:
|
|
NET_ASSERT(0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void CServer::GetProtocolVariables(CNPServerVariables &sv)
|
|
{
|
|
sv = m_ServerVariables;
|
|
}
|
|
|
|
void CServer::Release()
|
|
{
|
|
delete this;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// _IServerServices
|
|
//////////////////////////////////////////////////////////////////////
|
|
bool CServer::Send(CStream &stm, CIPAddress &ip)
|
|
{
|
|
#ifndef _INTERNET_SIMULATOR
|
|
DWORD nSize = BITS2BYTES(stm.GetSize());
|
|
|
|
if(NET_SUCCEDED(m_socketMain.Send(stm.GetPtr(), nSize, &ip)))
|
|
return true;
|
|
|
|
return false;
|
|
#else
|
|
|
|
static ICVar *pVarPacketloss=GetISystem()->GetIConsole()->GetCVar("g_internet_simulator_packetloss");
|
|
static ICVar *pVarMinPing=GetISystem()->GetIConsole()->GetCVar("g_internet_simulator_minping");
|
|
static ICVar *pVarMaxPing=GetISystem()->GetIConsole()->GetCVar("g_internet_simulator_maxping");
|
|
|
|
int iMaxPing=pVarMaxPing->GetIVal();
|
|
int iMinPing=pVarMinPing->GetIVal();
|
|
|
|
if(iMinPing>iMaxPing)
|
|
iMaxPing=iMinPing;
|
|
|
|
if (pVarPacketloss->GetFVal()>0 || iMaxPing>0)
|
|
{
|
|
DelayedPacket2 *delayed = new DelayedPacket2;
|
|
|
|
int iRand=0;
|
|
|
|
if(iMaxPing>iMinPing)
|
|
iRand=rand() % (iMaxPing-iMinPing);
|
|
|
|
if(iMaxPing>0)
|
|
delayed->m_fTimeToSend = GetISystem()->GetITimer()->GetCurrTime() + (iMinPing + iRand) / 1000.0f;
|
|
else
|
|
delayed->m_fTimeToSend = GetISystem()->GetITimer()->GetCurrTime();
|
|
delayed->len = BITS2BYTES(stm.GetSize());
|
|
delayed->address = new CIPAddress(ip);
|
|
assert(delayed->len < sizeof(delayed->data)/sizeof(delayed->data[0]));
|
|
memcpy(delayed->data, stm.GetPtr(), delayed->len);
|
|
m_delayedPacketList.push_back(delayed);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
DWORD nSize = BITS2BYTES(stm.GetSize());
|
|
|
|
if(NET_SUCCEDED(m_socketMain.Send(stm.GetPtr(), nSize, &ip)))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
void CServer::OnDestructSlot( const CServerSlot *inpServerSlot )
|
|
{
|
|
SLOTS_MAPItr itr = m_mapSlots.begin();
|
|
|
|
while (itr != m_mapSlots.end())
|
|
{
|
|
CServerSlot *pSlot = itr->second;
|
|
if(pSlot==inpServerSlot)
|
|
{
|
|
m_mapSlots.erase(itr);
|
|
return;
|
|
}
|
|
++itr;
|
|
}
|
|
|
|
assert(0); // can't be
|
|
}
|
|
|
|
void CServer::UnregisterSlot(CIPAddress &ip)
|
|
{
|
|
SLOTS_MAPItr itor=m_mapSlots.find(ip);
|
|
if(itor!=m_mapSlots.end())
|
|
{
|
|
IServerSlot *pServerSlot=itor->second;
|
|
|
|
pServerSlot->Advise(NULL); // remove connection to IServerSlotSink (CXServerSlot)
|
|
}
|
|
}
|
|
|
|
void CServer::RegisterLocalServerSlot(CServerSlot *pSlot,CIPAddress &ip)
|
|
{
|
|
m_mapSlots.insert(SLOTS_MAPItr::value_type(ip,pSlot));
|
|
if(m_pFactory)
|
|
m_pFactory->CreateServerSlot(pSlot);
|
|
}
|
|
|
|
CServerSlot *CServer::GetPacketOwner(CIPAddress &ip)
|
|
{
|
|
SLOTS_MAPItr itor;
|
|
itor=m_mapSlots.find(ip);
|
|
if(itor==m_mapSlots.end())
|
|
return 0;
|
|
return itor->second;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// core
|
|
//////////////////////////////////////////////////////////////////////
|
|
void TraceUnrecognizedPacket( const char *inszTxt, CStream &stmPacket, CIPAddress &ip)
|
|
{
|
|
#ifdef _DEBUG
|
|
OutputDebugString("\n");
|
|
OutputDebugString(inszTxt);
|
|
OutputDebugString("\n");
|
|
|
|
static char sTemp[1024];
|
|
static BYTE cBuf[1024];
|
|
DWORD nCount;
|
|
::OutputDebugString("-------------------------------\n");
|
|
sprintf(sTemp,"INVALID PACKET FROM [%s]\n",ip.GetAsString(true));
|
|
::OutputDebugString(sTemp);
|
|
stmPacket.GetBuffer(cBuf,1024);
|
|
nCount=BYTES2BITS(stmPacket.GetSize());
|
|
for(DWORD n=0;n<nCount;n++)
|
|
{
|
|
sprintf(sTemp,"%02X ",cBuf[n]);
|
|
::OutputDebugString(sTemp);
|
|
if(n && (n%16)==0)
|
|
::OutputDebugString("\n");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void CServer::ProcessPacket(CStream &stmPacket, CIPAddress &ip)
|
|
{
|
|
if (!m_pNetwork->CheckPBPacket( stmPacket,ip ))
|
|
return;
|
|
|
|
CNP cnp;
|
|
cnp.LoadAndSeekToZero(stmPacket);
|
|
switch (cnp.m_cFrameType)
|
|
{
|
|
// these packets will not be checked for ban
|
|
// since they are sent just too often,
|
|
// and would slow down the server
|
|
// they will be reject anyway, because no banned ip can have a server slot associated with it
|
|
case FT_CCP_DISCONNECT:
|
|
case FT_CCP_ACK:
|
|
case FT_CTP_DATA:
|
|
case FT_CTP_ACK:
|
|
case FT_CTP_NAK:
|
|
case FT_CTP_PONG:
|
|
DispatchToServerSlots(cnp, stmPacket,ip);
|
|
break;
|
|
|
|
default:
|
|
{
|
|
if (m_pSecuritySink->IsIPBanned(ip.GetAsUINT()))
|
|
{
|
|
return;
|
|
}
|
|
|
|
// these packets' ip will be checked for ban
|
|
// since they are not sent very often
|
|
switch(cnp.m_cFrameType)
|
|
{
|
|
case FT_CCP_CONNECT:
|
|
case FT_CCP_CONNECT_RESP:
|
|
case FT_CCP_CONTEXT_READY:
|
|
case FT_CCP_SECURITY_QUERY:
|
|
case FT_CCP_SECURITY_RESP:
|
|
case FT_CCP_PUNK_BUSTER_MSG:
|
|
DispatchToServerSlots(cnp, stmPacket,ip);
|
|
break;
|
|
case FT_CCP_SETUP:
|
|
ProcessSetup(cnp, stmPacket, ip);
|
|
break;
|
|
case FT_CQP_INFO_REQUEST:
|
|
ProcessInfoRequest(stmPacket, ip);
|
|
break;
|
|
case FT_CQP_XML_REQUEST:
|
|
ProcessInfoXMLRequest(stmPacket,ip);
|
|
break;
|
|
default:
|
|
{
|
|
TPacketSinks::iterator it = m_PacketSinks.find(cnp.m_cFrameType);
|
|
|
|
if(it!=m_PacketSinks.end())
|
|
{
|
|
INetworkPacketSink *pSink = (*it).second;
|
|
|
|
pSink->OnReceivingPacket(cnp.m_cFrameType,stmPacket,ip);
|
|
break;
|
|
}
|
|
}
|
|
TraceUnrecognizedPacket("ProcessPacket",stmPacket,ip);
|
|
break;
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
void CServer::RegisterPacketSink( const unsigned char inID, INetworkPacketSink *inpSink )
|
|
{
|
|
assert(m_PacketSinks.count(inID)==0);
|
|
|
|
m_PacketSinks[inID] = inpSink;
|
|
}
|
|
|
|
void CServer::SetSecuritySink(IServerSecuritySink *pSecuritySink)
|
|
{
|
|
m_pSecuritySink = pSecuritySink;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
IServerSecuritySink* CServer::GetSecuritySink()
|
|
{
|
|
return m_pSecuritySink;
|
|
}
|
|
|
|
bool CServer::IsIPBanned(const unsigned int dwIP)
|
|
{
|
|
if (m_pSecuritySink)
|
|
{
|
|
return m_pSecuritySink->IsIPBanned(dwIP);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void CServer::BanIP(const unsigned int dwIP)
|
|
{
|
|
if (m_pSecuritySink)
|
|
{
|
|
m_pSecuritySink->BanIP(dwIP);
|
|
}
|
|
}
|
|
|
|
void CServer::UnbanIP(const unsigned int dwIP)
|
|
{
|
|
if (m_pSecuritySink)
|
|
{
|
|
m_pSecuritySink->UnbanIP(dwIP);
|
|
}
|
|
}
|
|
|
|
void CServer::ProcessMulticastPacket(CStream &stmPacket, CIPAddress &ip)
|
|
{
|
|
CNP cnp;
|
|
cnp.LoadAndSeekToZero(stmPacket);
|
|
switch (cnp.m_cFrameType)
|
|
{
|
|
case FT_CQP_INFO_REQUEST:
|
|
ProcessInfoRequest(stmPacket, ip);
|
|
break;
|
|
case FT_CQP_XML_REQUEST:
|
|
ProcessInfoXMLRequest(stmPacket,ip);
|
|
break;
|
|
default:
|
|
TraceUnrecognizedPacket("ProcessMulticastPacket",stmPacket,ip);
|
|
break;
|
|
};
|
|
}
|
|
|
|
void CServer::ProcessSetup(CNP &cnp, CStream &stmStream, CIPAddress &ip)
|
|
{
|
|
CServerSlot *pSSlot;
|
|
CServerSlot *pTemp=GetPacketOwner(ip);
|
|
|
|
if(pTemp!=NULL)
|
|
{
|
|
NET_TRACE("Setup discarded for IP [%s]\n",pTemp->GetIP().GetAsString(true));
|
|
return;
|
|
}
|
|
|
|
if (m_pSecuritySink)
|
|
{
|
|
if (m_pSecuritySink->IsIPBanned(ip.GetAsUINT()))
|
|
{
|
|
NET_TRACE("Setup discarded for IP [%s] (BANNED)\n",ip.GetAsString(true));
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
NET_TRACE("Setup accepted for IP [%s]\n",ip.GetAsString(true));
|
|
|
|
pSSlot = new CServerSlotImpl(m_pNetwork,this);
|
|
|
|
/////////////////////////////////////////////////////////!!!!
|
|
int nID = GenerateNewClientID();
|
|
/////////////////////////////////////////////////////////!!!!
|
|
pSSlot->Start((BYTE)nID, ip);
|
|
|
|
// build event
|
|
if (m_pFactory)
|
|
{
|
|
if (m_pFactory->CreateServerSlot(pSSlot) == true)
|
|
{
|
|
m_mapSlots.insert(SLOTS_MAPItr::value_type(ip, pSSlot));
|
|
//m_mapIPs.insert(IPS_MAPItr::value_type(ip,nID));
|
|
|
|
// if this is a lan server
|
|
if(m_MPServerType==eMPST_LAN)
|
|
{
|
|
if (!IsLANIP(ip))
|
|
{
|
|
pSSlot->Disconnect("@LanIPOnly");
|
|
}
|
|
}
|
|
|
|
// get server password cvar
|
|
ICVar *sv_password = GetISystem()->GetIConsole()->GetCVar("sv_password");
|
|
assert(sv_password);
|
|
|
|
// check if server is password protected
|
|
if (sv_password->GetString() && (strlen(sv_password->GetString()) > 0))
|
|
{
|
|
CCPSetup pccp;
|
|
|
|
pccp.Load(stmStream);
|
|
|
|
if (!pccp.m_sPlayerPassword.size() || (strcmp(sv_password->GetString(), pccp.m_sPlayerPassword.c_str()) != 0))
|
|
{
|
|
pSSlot->Disconnect("@InvalidServerPassword");
|
|
|
|
return;
|
|
}
|
|
}
|
|
pSSlot->Update(m_nCurrentTime, &cnp, &stmStream);
|
|
}
|
|
else
|
|
{
|
|
//<<FIXME>> ?!?!?!?
|
|
//pSSlot->Update(m_nCurrentTime, &cnp, &stmStream);
|
|
pSSlot->Disconnect("@ConnectionRejected");
|
|
//pSSlot->Update(m_nCurrentTime, NULL, NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
bool CServer::IsLANIP(const CIPAddress &ip)
|
|
{
|
|
unsigned char ipb[4];
|
|
#if defined(LINUX)
|
|
ipb[0] = ip.m_Address.sin_addr_win.S_un.S_un_b.s_b1;
|
|
ipb[1] = ip.m_Address.sin_addr_win.S_un.S_un_b.s_b2;
|
|
ipb[2] = ip.m_Address.sin_addr_win.S_un.S_un_b.s_b3;
|
|
ipb[3] = ip.m_Address.sin_addr_win.S_un.S_un_b.s_b4;
|
|
#else
|
|
ipb[0] = ip.m_Address.sin_addr.S_un.S_un_b.s_b1;
|
|
ipb[1] = ip.m_Address.sin_addr.S_un.S_un_b.s_b2;
|
|
ipb[2] = ip.m_Address.sin_addr.S_un.S_un_b.s_b3;
|
|
ipb[3] = ip.m_Address.sin_addr.S_un.S_un_b.s_b4;
|
|
#endif
|
|
|
|
if (ipb[0] == 127)
|
|
return true;
|
|
if (ipb[0] == 10)
|
|
return true;
|
|
if ((ipb[0] == 192) && (ipb[1] == 168))
|
|
return true;
|
|
if (ipb[0] == 172)
|
|
if ((ipb[1] >= 16) && (ipb[1] <= 31))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
void CServer::DispatchToServerSlots(CNP &cnp, CStream &stm, CIPAddress &ip)
|
|
{
|
|
SLOTS_MAPItr itr;
|
|
itr = m_mapSlots.find(ip);
|
|
if (itr != m_mapSlots.end())
|
|
{
|
|
(itr->second)->Update(m_nCurrentTime, &cnp, &stm); // update the server slot
|
|
}
|
|
else
|
|
{
|
|
NET_TRACE("CServer::DispatchToServerSlots() Unknown client ID\n");
|
|
}
|
|
}
|
|
|
|
void CServer::ProcessInfoRequest(CStream &stmIn, CIPAddress &ip)
|
|
{
|
|
if (stmIn.GetSize() < 32)
|
|
{
|
|
return; // must be at least 16bytes(64bits) int
|
|
}
|
|
|
|
CQPInfoRequest Query;
|
|
CQPInfoResponse Response;
|
|
CStream stmPacket;
|
|
|
|
Query.Load(stmIn); // load and seek to zero
|
|
stmIn.Seek(0);
|
|
|
|
if (!Query.IsOk())
|
|
{
|
|
return;
|
|
}
|
|
|
|
assert((stmIn.GetSize() % 8) == 0);
|
|
assert(m_pFactory);
|
|
|
|
if (Query.szRequest == "ping")
|
|
{
|
|
Response.szResponse = "X";// identify the packet
|
|
Response.Save(stmPacket);
|
|
|
|
assert((stmPacket.GetSize() % 8) == 0);
|
|
|
|
if (stmPacket.GetSize())
|
|
{
|
|
m_socketMain.Send(stmPacket.GetPtr(), BITS2BYTES(stmPacket.GetSize()), &ip);
|
|
}
|
|
}
|
|
// status
|
|
else if (Query.szRequest == "status")
|
|
{
|
|
#if defined(WIN64) && !defined(NDEBUG)
|
|
//workarround for bug caused by string reallocation across dll's and not shared crt libs in debug mode
|
|
Response.szResponse.resize(0);
|
|
Response.szResponse.reserve(100);
|
|
Response.szResponse += "S"; // identify the packet
|
|
#else
|
|
Response.szResponse = "S"; // identify the packet
|
|
#endif
|
|
if (!m_pFactory->GetServerInfoStatus(Response.szResponse))
|
|
{
|
|
return;
|
|
}
|
|
|
|
Response.Save(stmPacket);
|
|
|
|
assert((stmPacket.GetSize() % 8) == 0);
|
|
|
|
if (stmPacket.GetSize())
|
|
{
|
|
m_socketMain.Send(stmPacket.GetPtr(), BITS2BYTES(stmPacket.GetSize()), &ip);
|
|
}
|
|
}
|
|
// rules
|
|
else if (Query.szRequest == "rules")
|
|
{
|
|
Response.szResponse = "R"; // identify the packet
|
|
|
|
if (!m_pFactory->GetServerInfoRules(Response.szResponse))
|
|
{
|
|
return;
|
|
}
|
|
|
|
Response.Save(stmPacket);
|
|
|
|
assert((stmPacket.GetSize() % 8) == 0);
|
|
m_socketMain.Send(stmPacket.GetPtr(), BITS2BYTES(stmPacket.GetSize()), &ip);
|
|
}
|
|
// players
|
|
else if (Query.szRequest == "players")
|
|
{
|
|
string szString[SERVER_QUERY_MAX_PACKETS];
|
|
string *vszString[SERVER_QUERY_MAX_PACKETS];
|
|
int nStrings = 0;
|
|
|
|
for (int i = 0; i < SERVER_QUERY_MAX_PACKETS; i++)
|
|
{
|
|
vszString[i] = &szString[i];
|
|
szString[i] = "P";
|
|
}
|
|
|
|
if (!m_pFactory->GetServerInfoPlayers(vszString, nStrings))
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (int j = 0; j < nStrings; j++)
|
|
{
|
|
Response.szResponse = *vszString[j];
|
|
Response.Save(stmPacket);
|
|
|
|
assert((stmPacket.GetSize() % 8) == 0);
|
|
m_socketMain.Send(stmPacket.GetPtr(), BITS2BYTES(stmPacket.GetSize()), &ip);
|
|
|
|
stmPacket.Reset();
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CServer::ProcessInfoXMLRequest(CStream &stmIn,CIPAddress &ip)
|
|
{
|
|
CQPXMLRequest cqpRequest;
|
|
CQPXMLResponse cqpResponse;
|
|
|
|
cqpRequest.Load(stmIn);
|
|
|
|
char sResponse[MAX_REQUEST_XML_LENGTH+1];
|
|
strcpy(sResponse,"");
|
|
|
|
// ask the application to process this XML request.
|
|
if (m_pFactory)
|
|
{
|
|
if(!m_pFactory->ProcessXMLInfoRequest( cqpRequest.m_sXML.c_str(),sResponse,sizeof(sResponse)))
|
|
return;
|
|
}
|
|
|
|
CStream stmOut;
|
|
cqpResponse.m_sXML = sResponse;
|
|
cqpResponse.Save(stmOut);
|
|
|
|
m_socketMain.Send(stmOut.GetPtr(), BITS2BYTES(stmOut.GetSize()), &ip);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
IServerSlot *CServer::GetServerSlotbyID( const unsigned char ucId ) const
|
|
{
|
|
for(SLOTS_MAP::const_iterator it=m_mapSlots.begin();it!=m_mapSlots.end();++it)
|
|
{
|
|
if(it->second->GetID()==ucId)
|
|
return it->second;
|
|
}
|
|
|
|
return 0; // not found
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
unsigned char CServer::GenerateNewClientID()
|
|
{
|
|
for(unsigned char id=1;id<0xFF;id++) // find a free client id
|
|
{
|
|
bool bFree=true;
|
|
|
|
for(SLOTS_MAPItr it=m_mapSlots.begin();it!=m_mapSlots.end();++it)
|
|
{
|
|
if(it->second->GetID()==id)
|
|
{
|
|
bFree=false; // this one is occupied
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(bFree)
|
|
return id; // found one
|
|
}
|
|
|
|
assert(0); // 256 players are already connected ?
|
|
return 0;
|
|
}
|
|
|
|
|
|
uint8 CServer::GetMaxClientID() const
|
|
{
|
|
uint8 ucMax=0;
|
|
|
|
// save solution (slow, but speed is not a concern here)
|
|
for(SLOTS_MAP::const_iterator it=m_mapSlots.begin();it!=m_mapSlots.end();++it)
|
|
{
|
|
uint8 ucId=it->second->GetID();
|
|
|
|
if(ucId>ucMax)
|
|
ucMax=ucId;
|
|
}
|
|
|
|
return ucMax;
|
|
}
|
|
|
|
|
|
const char *CServer::GetHostName()
|
|
{
|
|
return m_socketMain.GetHostName();
|
|
}
|
|
|
|
void CServer::GetMemoryStatistics(ICrySizer *pSizer)
|
|
{
|
|
pSizer->AddObject(this,sizeof(CServer));
|
|
SLOTS_MAPItr itor=m_mapSlots.begin();
|
|
while(itor!=m_mapSlots.end())
|
|
{
|
|
itor->second->GetMemoryStatistics(pSizer);
|
|
}
|
|
} |