2010-11-20 10:37:14 +00:00
|
|
|
/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
|
|
|
|
/* If you are missing that file, acquire a complete release at teeworlds.com. */
|
2010-05-29 07:25:38 +00:00
|
|
|
|
2011-07-31 11:17:38 +00:00
|
|
|
#include <base/math.h>
|
2010-05-29 07:25:38 +00:00
|
|
|
#include <base/system.h>
|
|
|
|
|
2011-02-27 14:03:57 +00:00
|
|
|
#include <engine/config.h>
|
|
|
|
#include <engine/console.h>
|
|
|
|
#include <engine/engine.h>
|
|
|
|
#include <engine/map.h>
|
|
|
|
#include <engine/masterserver.h>
|
|
|
|
#include <engine/server.h>
|
|
|
|
#include <engine/storage.h>
|
2010-05-29 07:25:38 +00:00
|
|
|
|
|
|
|
#include <engine/shared/compression.h>
|
|
|
|
#include <engine/shared/config.h>
|
|
|
|
#include <engine/shared/datafile.h>
|
2010-09-12 10:16:51 +00:00
|
|
|
#include <engine/shared/demo.h>
|
2011-07-30 11:40:01 +00:00
|
|
|
#include <engine/shared/econ.h>
|
2011-07-30 16:29:40 +00:00
|
|
|
#include <engine/shared/filecollection.h>
|
2011-03-31 13:13:49 +00:00
|
|
|
#include <engine/shared/mapchecker.h>
|
2011-12-29 22:36:53 +00:00
|
|
|
#include <engine/shared/netban.h>
|
2011-02-27 14:03:57 +00:00
|
|
|
#include <engine/shared/network.h>
|
|
|
|
#include <engine/shared/packer.h>
|
|
|
|
#include <engine/shared/protocol.h>
|
|
|
|
#include <engine/shared/snapshot.h>
|
2010-05-29 07:25:38 +00:00
|
|
|
|
|
|
|
#include <mastersrv/mastersrv.h>
|
|
|
|
|
|
|
|
#include "register.h"
|
|
|
|
#include "server.h"
|
|
|
|
|
2011-04-13 18:37:12 +00:00
|
|
|
#if defined(CONF_FAMILY_WINDOWS)
|
2011-02-27 14:03:57 +00:00
|
|
|
#define _WIN32_WINNT 0x0501
|
|
|
|
#define WIN32_LEAN_AND_MEAN
|
2010-05-29 07:25:38 +00:00
|
|
|
#include <windows.h>
|
|
|
|
#endif
|
|
|
|
|
2011-12-29 12:34:13 +00:00
|
|
|
static const char *StrUTF8Ltrim(const char *pStr)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2011-12-29 12:34:13 +00:00
|
|
|
while(*pStr)
|
|
|
|
{
|
|
|
|
const char *pStrOld = pStr;
|
|
|
|
int Code = str_utf8_decode(&pStr);
|
|
|
|
|
|
|
|
// check if unicode is not empty
|
|
|
|
if(Code > 0x20 && Code != 0xA0 && Code != 0x034F && (Code < 0x2000 || Code > 0x200F) && (Code < 0x2028 || Code > 0x202F) &&
|
|
|
|
(Code < 0x205F || Code > 0x2064) && (Code < 0x206A || Code > 0x206F) && (Code < 0xFE00 || Code > 0xFE0F) &&
|
|
|
|
Code != 0xFEFF && (Code < 0xFFF9 || Code > 0xFFFC))
|
|
|
|
{
|
|
|
|
return pStrOld;
|
|
|
|
}
|
|
|
|
}
|
2010-05-29 07:25:38 +00:00
|
|
|
return pStr;
|
|
|
|
}
|
|
|
|
|
2011-12-29 12:34:13 +00:00
|
|
|
static void StrUTF8Rtrim(char *pStr)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2011-12-29 12:34:13 +00:00
|
|
|
const char *p = pStr;
|
|
|
|
const char *pEnd = 0;
|
|
|
|
while(*p)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2011-12-29 12:34:13 +00:00
|
|
|
const char *pStrOld = p;
|
|
|
|
int Code = str_utf8_decode(&p);
|
|
|
|
|
|
|
|
// check if unicode is not empty
|
|
|
|
if(Code > 0x20 && Code != 0xA0 && Code != 0x034F && (Code < 0x2000 || Code > 0x200F) && (Code < 0x2028 || Code > 0x202F) &&
|
|
|
|
(Code < 0x205F || Code > 0x2064) && (Code < 0x206A || Code > 0x206F) && (Code < 0xFE00 || Code > 0xFE0F) &&
|
|
|
|
Code != 0xFEFF && (Code < 0xFFF9 || Code > 0xFFFC))
|
|
|
|
{
|
|
|
|
pEnd = 0;
|
|
|
|
}
|
|
|
|
else if(pEnd == 0)
|
|
|
|
pEnd = pStrOld;
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
2011-12-29 12:34:13 +00:00
|
|
|
if(pEnd != 0)
|
|
|
|
*(const_cast<char *>(pEnd)) = 0;
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
CSnapIDPool::CSnapIDPool()
|
|
|
|
{
|
|
|
|
Reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
void CSnapIDPool::Reset()
|
|
|
|
{
|
|
|
|
for(int i = 0; i < MAX_IDS; i++)
|
|
|
|
{
|
|
|
|
m_aIDs[i].m_Next = i+1;
|
|
|
|
m_aIDs[i].m_State = 0;
|
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
m_aIDs[MAX_IDS-1].m_Next = -1;
|
|
|
|
m_FirstFree = 0;
|
|
|
|
m_FirstTimed = -1;
|
|
|
|
m_LastTimed = -1;
|
|
|
|
m_Usage = 0;
|
|
|
|
m_InUsage = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void CSnapIDPool::RemoveFirstTimeout()
|
|
|
|
{
|
|
|
|
int NextTimed = m_aIDs[m_FirstTimed].m_Next;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
// add it to the free list
|
|
|
|
m_aIDs[m_FirstTimed].m_Next = m_FirstFree;
|
|
|
|
m_aIDs[m_FirstTimed].m_State = 0;
|
|
|
|
m_FirstFree = m_FirstTimed;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
// remove it from the timed list
|
|
|
|
m_FirstTimed = NextTimed;
|
|
|
|
if(m_FirstTimed == -1)
|
|
|
|
m_LastTimed = -1;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
m_Usage--;
|
|
|
|
}
|
|
|
|
|
|
|
|
int CSnapIDPool::NewID()
|
|
|
|
{
|
|
|
|
int64 Now = time_get();
|
|
|
|
|
|
|
|
// process timed ids
|
|
|
|
while(m_FirstTimed != -1 && m_aIDs[m_FirstTimed].m_Timeout < Now)
|
|
|
|
RemoveFirstTimeout();
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2011-02-12 10:40:36 +00:00
|
|
|
int ID = m_FirstFree;
|
|
|
|
dbg_assert(ID != -1, "id error");
|
|
|
|
if(ID == -1)
|
|
|
|
return ID;
|
2010-05-29 07:25:38 +00:00
|
|
|
m_FirstFree = m_aIDs[m_FirstFree].m_Next;
|
2011-02-12 10:40:36 +00:00
|
|
|
m_aIDs[ID].m_State = 1;
|
2010-05-29 07:25:38 +00:00
|
|
|
m_Usage++;
|
|
|
|
m_InUsage++;
|
2011-02-12 10:40:36 +00:00
|
|
|
return ID;
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CSnapIDPool::TimeoutIDs()
|
|
|
|
{
|
|
|
|
// process timed ids
|
|
|
|
while(m_FirstTimed != -1)
|
|
|
|
RemoveFirstTimeout();
|
|
|
|
}
|
|
|
|
|
2011-02-12 10:40:36 +00:00
|
|
|
void CSnapIDPool::FreeID(int ID)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2011-02-12 10:40:36 +00:00
|
|
|
if(ID < 0)
|
2010-12-16 02:29:08 +00:00
|
|
|
return;
|
2011-02-12 10:40:36 +00:00
|
|
|
dbg_assert(m_aIDs[ID].m_State == 1, "id is not alloced");
|
2010-05-29 07:25:38 +00:00
|
|
|
|
|
|
|
m_InUsage--;
|
2011-02-12 10:40:36 +00:00
|
|
|
m_aIDs[ID].m_State = 2;
|
|
|
|
m_aIDs[ID].m_Timeout = time_get()+time_freq()*5;
|
|
|
|
m_aIDs[ID].m_Next = -1;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
if(m_LastTimed != -1)
|
|
|
|
{
|
2011-02-12 10:40:36 +00:00
|
|
|
m_aIDs[m_LastTimed].m_Next = ID;
|
|
|
|
m_LastTimed = ID;
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2011-02-12 10:40:36 +00:00
|
|
|
m_FirstTimed = ID;
|
|
|
|
m_LastTimed = ID;
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2011-12-29 22:36:53 +00:00
|
|
|
|
|
|
|
void CServerBan::Init(IConsole *pConsole, IStorage *pStorage, CServer* pServer)
|
|
|
|
{
|
|
|
|
CNetBan::Init(pConsole, pStorage);
|
|
|
|
|
|
|
|
m_pServer = pServer;
|
|
|
|
|
|
|
|
// overwrites base command, todo: improve this
|
|
|
|
Console()->Register("ban", "s?ir", CFGFLAG_SERVER|CFGFLAG_STORE, ConBanExt, this, "Ban player with ip/client id for x minutes for any reason");
|
|
|
|
}
|
|
|
|
|
|
|
|
template<class T>
|
|
|
|
int CServerBan::BanExt(T *pBanPool, const typename T::CDataType *pData, int Seconds, const char *pReason)
|
|
|
|
{
|
|
|
|
// validate address
|
|
|
|
if(Server()->m_RconClientID >= 0 && Server()->m_RconClientID < MAX_CLIENTS &&
|
|
|
|
Server()->m_aClients[Server()->m_RconClientID].m_State != CServer::CClient::STATE_EMPTY)
|
|
|
|
{
|
|
|
|
if(NetMatch(pData, Server()->m_NetServer.ClientAddr(Server()->m_RconClientID)))
|
|
|
|
{
|
|
|
|
Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "net_ban", "ban error (you can't ban yourself)");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
for(int i = 0; i < MAX_CLIENTS; ++i)
|
|
|
|
{
|
|
|
|
if(i == Server()->m_RconClientID || Server()->m_aClients[i].m_State == CServer::CClient::STATE_EMPTY)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if(Server()->m_aClients[i].m_Authed >= Server()->m_RconAuthLevel && NetMatch(pData, Server()->m_NetServer.ClientAddr(i)))
|
|
|
|
{
|
|
|
|
Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "net_ban", "ban error (command denied)");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2011-12-31 11:11:48 +00:00
|
|
|
else if(Server()->m_RconClientID == IServer::RCON_CID_VOTE)
|
|
|
|
{
|
|
|
|
for(int i = 0; i < MAX_CLIENTS; ++i)
|
|
|
|
{
|
|
|
|
if(Server()->m_aClients[i].m_State == CServer::CClient::STATE_EMPTY)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if(Server()->m_aClients[i].m_Authed != CServer::AUTHED_NO && NetMatch(pData, Server()->m_NetServer.ClientAddr(i)))
|
|
|
|
{
|
|
|
|
Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "net_ban", "ban error (command denied)");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2011-12-29 22:36:53 +00:00
|
|
|
|
|
|
|
int Result = Ban(pBanPool, pData, Seconds, pReason);
|
|
|
|
if(Result != 0)
|
|
|
|
return Result;
|
|
|
|
|
|
|
|
// drop banned clients
|
2011-12-30 18:35:57 +00:00
|
|
|
typename T::CDataType Data = *pData;
|
2011-12-29 22:36:53 +00:00
|
|
|
for(int i = 0; i < MAX_CLIENTS; ++i)
|
|
|
|
{
|
|
|
|
if(Server()->m_aClients[i].m_State == CServer::CClient::STATE_EMPTY)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if(NetMatch(&Data, Server()->m_NetServer.ClientAddr(i)))
|
|
|
|
{
|
|
|
|
CNetHash NetHash(&Data);
|
|
|
|
char aBuf[256];
|
|
|
|
MakeBanInfo(pBanPool->Find(&Data, &NetHash), aBuf, sizeof(aBuf), MSGTYPE_PLAYER);
|
|
|
|
Server()->m_NetServer.Drop(i, aBuf);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return Result;
|
|
|
|
}
|
|
|
|
|
|
|
|
int CServerBan::BanAddr(const NETADDR *pAddr, int Seconds, const char *pReason)
|
|
|
|
{
|
|
|
|
return BanExt(&m_BanAddrPool, pAddr, Seconds, pReason);
|
|
|
|
}
|
|
|
|
|
|
|
|
int CServerBan::BanRange(const CNetRange *pRange, int Seconds, const char *pReason)
|
|
|
|
{
|
|
|
|
if(pRange->IsValid())
|
|
|
|
return BanExt(&m_BanRangePool, pRange, Seconds, pReason);
|
|
|
|
|
|
|
|
Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "net_ban", "ban failed (invalid range)");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CServerBan::ConBanExt(IConsole::IResult *pResult, void *pUser)
|
|
|
|
{
|
|
|
|
CServerBan *pThis = static_cast<CServerBan *>(pUser);
|
|
|
|
|
|
|
|
const char *pStr = pResult->GetString(0);
|
|
|
|
int Minutes = pResult->NumArguments()>1 ? clamp(pResult->GetInteger(1), 0, 44640) : 30;
|
|
|
|
const char *pReason = pResult->NumArguments()>2 ? pResult->GetString(2) : "No reason given";
|
|
|
|
|
|
|
|
if(StrAllnum(pStr))
|
|
|
|
{
|
|
|
|
int ClientID = str_toint(pStr);
|
|
|
|
if(ClientID < 0 || ClientID >= MAX_CLIENTS || pThis->Server()->m_aClients[ClientID].m_State == CServer::CClient::STATE_EMPTY)
|
|
|
|
pThis->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "net_ban", "ban error (invalid client id)");
|
|
|
|
else
|
|
|
|
pThis->BanAddr(pThis->Server()->m_NetServer.ClientAddr(ClientID), Minutes*60, pReason);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
ConBan(pResult, pUser);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
void CServer::CClient::Reset()
|
|
|
|
{
|
|
|
|
// reset input
|
|
|
|
for(int i = 0; i < 200; i++)
|
|
|
|
m_aInputs[i].m_GameTick = -1;
|
|
|
|
m_CurrentInput = 0;
|
|
|
|
mem_zero(&m_LatestInput, sizeof(m_LatestInput));
|
|
|
|
|
|
|
|
m_Snapshots.PurgeAll();
|
|
|
|
m_LastAckedSnapshot = -1;
|
|
|
|
m_LastInputTick = -1;
|
|
|
|
m_SnapRate = CClient::SNAPRATE_INIT;
|
|
|
|
m_Score = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
CServer::CServer() : m_DemoRecorder(&m_SnapshotDelta)
|
|
|
|
{
|
|
|
|
m_TickSpeed = SERVER_TICK_SPEED;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
m_pGameServer = 0;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
m_CurrentGameTick = 0;
|
|
|
|
m_RunServer = 1;
|
|
|
|
|
|
|
|
m_pCurrentMapData = 0;
|
|
|
|
m_CurrentMapSize = 0;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-30 12:01:11 +00:00
|
|
|
m_MapReload = 0;
|
2010-05-29 07:25:38 +00:00
|
|
|
|
2011-12-31 11:11:48 +00:00
|
|
|
m_RconClientID = IServer::RCON_CID_SERV;
|
2011-07-05 19:54:10 +00:00
|
|
|
m_RconAuthLevel = AUTHED_ADMIN;
|
2010-09-12 11:52:25 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
Init();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int CServer::TrySetClientName(int ClientID, const char *pName)
|
|
|
|
{
|
|
|
|
char aTrimmedName[64];
|
|
|
|
|
|
|
|
// trim the name
|
2011-12-29 12:34:13 +00:00
|
|
|
str_copy(aTrimmedName, StrUTF8Ltrim(pName), sizeof(aTrimmedName));
|
|
|
|
StrUTF8Rtrim(aTrimmedName);
|
2011-03-15 10:23:49 +00:00
|
|
|
|
|
|
|
// check if new and old name are the same
|
2011-04-06 15:53:05 +00:00
|
|
|
if(m_aClients[ClientID].m_aName[0] && str_comp(m_aClients[ClientID].m_aName, aTrimmedName) == 0)
|
2011-03-15 10:23:49 +00:00
|
|
|
return 0;
|
|
|
|
|
2010-08-17 22:06:00 +00:00
|
|
|
char aBuf[256];
|
|
|
|
str_format(aBuf, sizeof(aBuf), "'%s' -> '%s'", pName, aTrimmedName);
|
|
|
|
Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "server", aBuf);
|
2010-05-29 07:25:38 +00:00
|
|
|
pName = aTrimmedName;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
// check for empty names
|
|
|
|
if(!pName[0])
|
|
|
|
return -1;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
// make sure that two clients doesn't have the same name
|
|
|
|
for(int i = 0; i < MAX_CLIENTS; i++)
|
|
|
|
if(i != ClientID && m_aClients[i].m_State >= CClient::STATE_READY)
|
|
|
|
{
|
|
|
|
if(str_comp(pName, m_aClients[i].m_aName) == 0)
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// set the client name
|
|
|
|
str_copy(m_aClients[ClientID].m_aName, pName, MAX_NAME_LENGTH);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void CServer::SetClientName(int ClientID, const char *pName)
|
|
|
|
{
|
|
|
|
if(ClientID < 0 || ClientID >= MAX_CLIENTS || m_aClients[ClientID].m_State < CClient::STATE_READY)
|
|
|
|
return;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
if(!pName)
|
|
|
|
return;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
char aNameTry[MAX_NAME_LENGTH];
|
|
|
|
str_copy(aNameTry, pName, MAX_NAME_LENGTH);
|
|
|
|
if(TrySetClientName(ClientID, aNameTry))
|
|
|
|
{
|
|
|
|
// auto rename
|
|
|
|
for(int i = 1;; i++)
|
|
|
|
{
|
|
|
|
str_format(aNameTry, MAX_NAME_LENGTH, "(%d)%s", i, pName);
|
|
|
|
if(TrySetClientName(ClientID, aNameTry) == 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-03-15 10:23:49 +00:00
|
|
|
void CServer::SetClientClan(int ClientID, const char *pClan)
|
|
|
|
{
|
|
|
|
if(ClientID < 0 || ClientID >= MAX_CLIENTS || m_aClients[ClientID].m_State < CClient::STATE_READY || !pClan)
|
|
|
|
return;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2011-03-15 10:23:49 +00:00
|
|
|
str_copy(m_aClients[ClientID].m_aClan, pClan, MAX_CLAN_LENGTH);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CServer::SetClientCountry(int ClientID, int Country)
|
|
|
|
{
|
|
|
|
if(ClientID < 0 || ClientID >= MAX_CLIENTS || m_aClients[ClientID].m_State < CClient::STATE_READY)
|
|
|
|
return;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2011-03-15 10:23:49 +00:00
|
|
|
m_aClients[ClientID].m_Country = Country;
|
|
|
|
}
|
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
void CServer::SetClientScore(int ClientID, int Score)
|
|
|
|
{
|
|
|
|
if(ClientID < 0 || ClientID >= MAX_CLIENTS || m_aClients[ClientID].m_State < CClient::STATE_READY)
|
|
|
|
return;
|
|
|
|
m_aClients[ClientID].m_Score = Score;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CServer::Kick(int ClientID, const char *pReason)
|
|
|
|
{
|
2010-09-12 10:07:10 +00:00
|
|
|
if(ClientID < 0 || ClientID >= MAX_CLIENTS || m_aClients[ClientID].m_State == CClient::STATE_EMPTY)
|
2010-09-12 11:52:25 +00:00
|
|
|
{
|
|
|
|
Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", "invalid client id to kick");
|
|
|
|
return;
|
|
|
|
}
|
2011-02-12 10:40:36 +00:00
|
|
|
else if(m_RconClientID == ClientID)
|
2010-09-12 11:52:25 +00:00
|
|
|
{
|
|
|
|
Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", "you can't kick yourself");
|
|
|
|
return;
|
2010-09-12 10:07:10 +00:00
|
|
|
}
|
2011-07-05 19:54:10 +00:00
|
|
|
else if(m_aClients[ClientID].m_Authed > m_RconAuthLevel)
|
|
|
|
{
|
|
|
|
Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", "kick command denied");
|
|
|
|
return;
|
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-09-12 10:07:10 +00:00
|
|
|
m_NetServer.Drop(ClientID, pReason);
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*int CServer::Tick()
|
|
|
|
{
|
|
|
|
return m_CurrentGameTick;
|
|
|
|
}*/
|
|
|
|
|
|
|
|
int64 CServer::TickStartTime(int Tick)
|
|
|
|
{
|
|
|
|
return m_GameStartTime + (time_freq()*Tick)/SERVER_TICK_SPEED;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*int CServer::TickSpeed()
|
|
|
|
{
|
|
|
|
return SERVER_TICK_SPEED;
|
|
|
|
}*/
|
|
|
|
|
|
|
|
int CServer::Init()
|
|
|
|
{
|
|
|
|
for(int i = 0; i < MAX_CLIENTS; i++)
|
|
|
|
{
|
|
|
|
m_aClients[i].m_State = CClient::STATE_EMPTY;
|
|
|
|
m_aClients[i].m_aName[0] = 0;
|
|
|
|
m_aClients[i].m_aClan[0] = 0;
|
2011-03-15 10:23:49 +00:00
|
|
|
m_aClients[i].m_Country = -1;
|
2010-05-29 07:25:38 +00:00
|
|
|
m_aClients[i].m_Snapshots.Init();
|
|
|
|
}
|
|
|
|
|
|
|
|
m_CurrentGameTick = 0;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-12-31 11:11:48 +00:00
|
|
|
void CServer::SetRconCID(int ClientID)
|
|
|
|
{
|
|
|
|
m_RconClientID = ClientID;
|
|
|
|
}
|
|
|
|
|
2010-06-02 02:42:17 +00:00
|
|
|
bool CServer::IsAuthed(int ClientID)
|
|
|
|
{
|
2010-06-02 19:33:19 +00:00
|
|
|
return m_aClients[ClientID].m_Authed;
|
2010-06-02 02:42:17 +00:00
|
|
|
}
|
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
int CServer::GetClientInfo(int ClientID, CClientInfo *pInfo)
|
|
|
|
{
|
|
|
|
dbg_assert(ClientID >= 0 && ClientID < MAX_CLIENTS, "client_id is not valid");
|
|
|
|
dbg_assert(pInfo != 0, "info can not be null");
|
|
|
|
|
|
|
|
if(m_aClients[ClientID].m_State == CClient::STATE_INGAME)
|
|
|
|
{
|
|
|
|
pInfo->m_pName = m_aClients[ClientID].m_aName;
|
|
|
|
pInfo->m_Latency = m_aClients[ClientID].m_Latency;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-03-28 18:11:28 +00:00
|
|
|
void CServer::GetClientAddr(int ClientID, char *pAddrStr, int Size)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
|
|
|
if(ClientID >= 0 && ClientID < MAX_CLIENTS && m_aClients[ClientID].m_State == CClient::STATE_INGAME)
|
2011-12-29 22:36:53 +00:00
|
|
|
net_addr_str(m_NetServer.ClientAddr(ClientID), pAddrStr, Size, false);
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
|
2011-02-12 10:40:36 +00:00
|
|
|
const char *CServer::ClientName(int ClientID)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2011-02-12 10:40:36 +00:00
|
|
|
if(ClientID < 0 || ClientID >= MAX_CLIENTS || m_aClients[ClientID].m_State == CServer::CClient::STATE_EMPTY)
|
2011-04-16 16:41:44 +00:00
|
|
|
return "(invalid)";
|
2011-03-15 10:23:49 +00:00
|
|
|
if(m_aClients[ClientID].m_State == CServer::CClient::STATE_INGAME)
|
|
|
|
return m_aClients[ClientID].m_aName;
|
|
|
|
else
|
2011-04-16 16:41:44 +00:00
|
|
|
return "(connecting)";
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2011-03-15 10:23:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const char *CServer::ClientClan(int ClientID)
|
|
|
|
{
|
|
|
|
if(ClientID < 0 || ClientID >= MAX_CLIENTS || m_aClients[ClientID].m_State == CServer::CClient::STATE_EMPTY)
|
|
|
|
return "";
|
|
|
|
if(m_aClients[ClientID].m_State == CServer::CClient::STATE_INGAME)
|
|
|
|
return m_aClients[ClientID].m_aClan;
|
|
|
|
else
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
int CServer::ClientCountry(int ClientID)
|
|
|
|
{
|
|
|
|
if(ClientID < 0 || ClientID >= MAX_CLIENTS || m_aClients[ClientID].m_State == CServer::CClient::STATE_EMPTY)
|
|
|
|
return -1;
|
|
|
|
if(m_aClients[ClientID].m_State == CServer::CClient::STATE_INGAME)
|
|
|
|
return m_aClients[ClientID].m_Country;
|
|
|
|
else
|
|
|
|
return -1;
|
|
|
|
}
|
2010-05-29 07:25:38 +00:00
|
|
|
|
|
|
|
bool CServer::ClientIngame(int ClientID)
|
|
|
|
{
|
|
|
|
return ClientID >= 0 && ClientID < MAX_CLIENTS && m_aClients[ClientID].m_State == CServer::CClient::STATE_INGAME;
|
|
|
|
}
|
|
|
|
|
2011-12-04 15:51:33 +00:00
|
|
|
int CServer::MaxClients() const
|
|
|
|
{
|
|
|
|
return m_NetServer.MaxClients();
|
|
|
|
}
|
|
|
|
|
2011-02-12 10:40:36 +00:00
|
|
|
int CServer::SendMsg(CMsgPacker *pMsg, int Flags, int ClientID)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2011-02-12 10:40:36 +00:00
|
|
|
return SendMsgEx(pMsg, Flags, ClientID, false);
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int CServer::SendMsgEx(CMsgPacker *pMsg, int Flags, int ClientID, bool System)
|
|
|
|
{
|
|
|
|
CNetChunk Packet;
|
|
|
|
if(!pMsg)
|
|
|
|
return -1;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
mem_zero(&Packet, sizeof(CNetChunk));
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
Packet.m_ClientID = ClientID;
|
|
|
|
Packet.m_pData = pMsg->Data();
|
|
|
|
Packet.m_DataSize = pMsg->Size();
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
// HACK: modify the message id in the packet and store the system flag
|
|
|
|
*((unsigned char*)Packet.m_pData) <<= 1;
|
|
|
|
if(System)
|
|
|
|
*((unsigned char*)Packet.m_pData) |= 1;
|
|
|
|
|
|
|
|
if(Flags&MSGFLAG_VITAL)
|
|
|
|
Packet.m_Flags |= NETSENDFLAG_VITAL;
|
|
|
|
if(Flags&MSGFLAG_FLUSH)
|
|
|
|
Packet.m_Flags |= NETSENDFLAG_FLUSH;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
// write message to demo recorder
|
|
|
|
if(!(Flags&MSGFLAG_NORECORD))
|
|
|
|
m_DemoRecorder.RecordMessage(pMsg->Data(), pMsg->Size());
|
|
|
|
|
|
|
|
if(!(Flags&MSGFLAG_NOSEND))
|
|
|
|
{
|
|
|
|
if(ClientID == -1)
|
|
|
|
{
|
|
|
|
// broadcast
|
|
|
|
int i;
|
|
|
|
for(i = 0; i < MAX_CLIENTS; i++)
|
|
|
|
if(m_aClients[i].m_State == CClient::STATE_INGAME)
|
|
|
|
{
|
|
|
|
Packet.m_ClientID = i;
|
|
|
|
m_NetServer.Send(&Packet);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
m_NetServer.Send(&Packet);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CServer::DoSnapshot()
|
|
|
|
{
|
|
|
|
GameServer()->OnPreSnap();
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
// create snapshot for demo recording
|
|
|
|
if(m_DemoRecorder.IsRecording())
|
|
|
|
{
|
|
|
|
char aData[CSnapshot::MAX_SIZE];
|
|
|
|
int SnapshotSize;
|
|
|
|
|
|
|
|
// build snap and possibly add some messages
|
|
|
|
m_SnapshotBuilder.Init();
|
|
|
|
GameServer()->OnSnap(-1);
|
|
|
|
SnapshotSize = m_SnapshotBuilder.Finish(aData);
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
// write snapshot
|
|
|
|
m_DemoRecorder.RecordSnapshot(Tick(), aData, SnapshotSize);
|
|
|
|
}
|
|
|
|
|
|
|
|
// create snapshots for all clients
|
|
|
|
for(int i = 0; i < MAX_CLIENTS; i++)
|
|
|
|
{
|
|
|
|
// client must be ingame to recive snapshots
|
|
|
|
if(m_aClients[i].m_State != CClient::STATE_INGAME)
|
|
|
|
continue;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
// this client is trying to recover, don't spam snapshots
|
|
|
|
if(m_aClients[i].m_SnapRate == CClient::SNAPRATE_RECOVER && (Tick()%50) != 0)
|
|
|
|
continue;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
// this client is trying to recover, don't spam snapshots
|
|
|
|
if(m_aClients[i].m_SnapRate == CClient::SNAPRATE_INIT && (Tick()%10) != 0)
|
|
|
|
continue;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
|
|
|
char aData[CSnapshot::MAX_SIZE];
|
2010-06-09 16:24:38 +00:00
|
|
|
CSnapshot *pData = (CSnapshot*)aData; // Fix compiler warning for strict-aliasing
|
2010-05-29 07:25:38 +00:00
|
|
|
char aDeltaData[CSnapshot::MAX_SIZE];
|
|
|
|
char aCompData[CSnapshot::MAX_SIZE];
|
|
|
|
int SnapshotSize;
|
|
|
|
int Crc;
|
|
|
|
static CSnapshot EmptySnap;
|
|
|
|
CSnapshot *pDeltashot = &EmptySnap;
|
|
|
|
int DeltashotSize;
|
|
|
|
int DeltaTick = -1;
|
|
|
|
int DeltaSize;
|
|
|
|
|
|
|
|
m_SnapshotBuilder.Init();
|
|
|
|
|
|
|
|
GameServer()->OnSnap(i);
|
|
|
|
|
|
|
|
// finish snapshot
|
2010-06-09 16:24:38 +00:00
|
|
|
SnapshotSize = m_SnapshotBuilder.Finish(pData);
|
|
|
|
Crc = pData->Crc();
|
2010-05-29 07:25:38 +00:00
|
|
|
|
|
|
|
// remove old snapshos
|
|
|
|
// keep 3 seconds worth of snapshots
|
|
|
|
m_aClients[i].m_Snapshots.PurgeUntil(m_CurrentGameTick-SERVER_TICK_SPEED*3);
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
// save it the snapshot
|
2010-06-09 16:24:38 +00:00
|
|
|
m_aClients[i].m_Snapshots.Add(m_CurrentGameTick, time_get(), SnapshotSize, pData, 0);
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
// find snapshot that we can preform delta against
|
|
|
|
EmptySnap.Clear();
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
|
|
|
DeltashotSize = m_aClients[i].m_Snapshots.Get(m_aClients[i].m_LastAckedSnapshot, 0, &pDeltashot, 0);
|
|
|
|
if(DeltashotSize >= 0)
|
|
|
|
DeltaTick = m_aClients[i].m_LastAckedSnapshot;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// no acked package found, force client to recover rate
|
|
|
|
if(m_aClients[i].m_SnapRate == CClient::SNAPRATE_FULL)
|
|
|
|
m_aClients[i].m_SnapRate = CClient::SNAPRATE_RECOVER;
|
|
|
|
}
|
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
// create delta
|
2010-06-09 16:24:38 +00:00
|
|
|
DeltaSize = m_SnapshotDelta.CreateDelta(pDeltashot, pData, aDeltaData);
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
if(DeltaSize)
|
|
|
|
{
|
|
|
|
// compress it
|
|
|
|
int SnapshotSize;
|
|
|
|
const int MaxSize = MAX_SNAPSHOT_PACKSIZE;
|
|
|
|
int NumPackets;
|
|
|
|
|
|
|
|
SnapshotSize = CVariableInt::Compress(aDeltaData, DeltaSize, aCompData);
|
|
|
|
NumPackets = (SnapshotSize+MaxSize-1)/MaxSize;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
for(int n = 0, Left = SnapshotSize; Left; n++)
|
|
|
|
{
|
|
|
|
int Chunk = Left < MaxSize ? Left : MaxSize;
|
|
|
|
Left -= Chunk;
|
|
|
|
|
|
|
|
if(NumPackets == 1)
|
|
|
|
{
|
|
|
|
CMsgPacker Msg(NETMSG_SNAPSINGLE);
|
|
|
|
Msg.AddInt(m_CurrentGameTick);
|
|
|
|
Msg.AddInt(m_CurrentGameTick-DeltaTick);
|
|
|
|
Msg.AddInt(Crc);
|
|
|
|
Msg.AddInt(Chunk);
|
|
|
|
Msg.AddRaw(&aCompData[n*MaxSize], Chunk);
|
|
|
|
SendMsgEx(&Msg, MSGFLAG_FLUSH, i, true);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
CMsgPacker Msg(NETMSG_SNAP);
|
|
|
|
Msg.AddInt(m_CurrentGameTick);
|
|
|
|
Msg.AddInt(m_CurrentGameTick-DeltaTick);
|
|
|
|
Msg.AddInt(NumPackets);
|
2011-04-13 18:37:12 +00:00
|
|
|
Msg.AddInt(n);
|
2010-05-29 07:25:38 +00:00
|
|
|
Msg.AddInt(Crc);
|
|
|
|
Msg.AddInt(Chunk);
|
|
|
|
Msg.AddRaw(&aCompData[n*MaxSize], Chunk);
|
|
|
|
SendMsgEx(&Msg, MSGFLAG_FLUSH, i, true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
CMsgPacker Msg(NETMSG_SNAPEMPTY);
|
|
|
|
Msg.AddInt(m_CurrentGameTick);
|
|
|
|
Msg.AddInt(m_CurrentGameTick-DeltaTick);
|
|
|
|
SendMsgEx(&Msg, MSGFLAG_FLUSH, i, true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
GameServer()->OnPostSnap();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-02-12 10:40:36 +00:00
|
|
|
int CServer::NewClientCallback(int ClientID, void *pUser)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
|
|
|
CServer *pThis = (CServer *)pUser;
|
2011-02-12 10:40:36 +00:00
|
|
|
pThis->m_aClients[ClientID].m_State = CClient::STATE_AUTH;
|
|
|
|
pThis->m_aClients[ClientID].m_aName[0] = 0;
|
|
|
|
pThis->m_aClients[ClientID].m_aClan[0] = 0;
|
2011-03-15 10:23:49 +00:00
|
|
|
pThis->m_aClients[ClientID].m_Country = -1;
|
2011-07-05 19:54:10 +00:00
|
|
|
pThis->m_aClients[ClientID].m_Authed = AUTHED_NO;
|
2011-02-12 10:40:36 +00:00
|
|
|
pThis->m_aClients[ClientID].m_AuthTries = 0;
|
2011-07-14 20:07:21 +00:00
|
|
|
pThis->m_aClients[ClientID].m_pRconCmdToSend = 0;
|
2011-02-12 10:40:36 +00:00
|
|
|
pThis->m_aClients[ClientID].Reset();
|
2010-05-29 07:25:38 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-02-12 10:40:36 +00:00
|
|
|
int CServer::DelClientCallback(int ClientID, const char *pReason, void *pUser)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
|
|
|
CServer *pThis = (CServer *)pUser;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2011-03-28 18:11:28 +00:00
|
|
|
char aAddrStr[NETADDR_MAXSTRSIZE];
|
2011-12-29 22:36:53 +00:00
|
|
|
net_addr_str(pThis->m_NetServer.ClientAddr(ClientID), aAddrStr, sizeof(aAddrStr), true);
|
2010-08-17 22:06:00 +00:00
|
|
|
char aBuf[256];
|
2011-03-28 18:11:28 +00:00
|
|
|
str_format(aBuf, sizeof(aBuf), "client dropped. cid=%d addr=%s reason='%s'", ClientID, aAddrStr, pReason);
|
2010-08-17 22:06:00 +00:00
|
|
|
pThis->Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "server", aBuf);
|
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
// notify the mod about the drop
|
2011-02-12 10:40:36 +00:00
|
|
|
if(pThis->m_aClients[ClientID].m_State >= CClient::STATE_READY)
|
2011-02-14 18:41:32 +00:00
|
|
|
pThis->GameServer()->OnClientDrop(ClientID, pReason);
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2011-02-12 10:40:36 +00:00
|
|
|
pThis->m_aClients[ClientID].m_State = CClient::STATE_EMPTY;
|
|
|
|
pThis->m_aClients[ClientID].m_aName[0] = 0;
|
|
|
|
pThis->m_aClients[ClientID].m_aClan[0] = 0;
|
2011-03-15 10:23:49 +00:00
|
|
|
pThis->m_aClients[ClientID].m_Country = -1;
|
2011-07-05 19:54:10 +00:00
|
|
|
pThis->m_aClients[ClientID].m_Authed = AUTHED_NO;
|
2011-02-12 10:40:36 +00:00
|
|
|
pThis->m_aClients[ClientID].m_AuthTries = 0;
|
2011-07-14 20:07:21 +00:00
|
|
|
pThis->m_aClients[ClientID].m_pRconCmdToSend = 0;
|
2011-02-12 10:40:36 +00:00
|
|
|
pThis->m_aClients[ClientID].m_Snapshots.PurgeAll();
|
2010-05-29 07:25:38 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-02-12 10:40:36 +00:00
|
|
|
void CServer::SendMap(int ClientID)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
|
|
|
CMsgPacker Msg(NETMSG_MAP_CHANGE);
|
2010-09-03 19:28:31 +00:00
|
|
|
Msg.AddString(GetMapName(), 0);
|
2010-05-29 07:25:38 +00:00
|
|
|
Msg.AddInt(m_CurrentMapCrc);
|
2011-02-16 11:07:54 +00:00
|
|
|
Msg.AddInt(m_CurrentMapSize);
|
2011-02-12 10:40:36 +00:00
|
|
|
SendMsgEx(&Msg, MSGFLAG_VITAL|MSGFLAG_FLUSH, ClientID, true);
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
|
2011-03-15 08:58:57 +00:00
|
|
|
void CServer::SendConnectionReady(int ClientID)
|
|
|
|
{
|
|
|
|
CMsgPacker Msg(NETMSG_CON_READY);
|
|
|
|
SendMsgEx(&Msg, MSGFLAG_VITAL|MSGFLAG_FLUSH, ClientID, true);
|
|
|
|
}
|
|
|
|
|
2011-02-12 10:40:36 +00:00
|
|
|
void CServer::SendRconLine(int ClientID, const char *pLine)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
|
|
|
CMsgPacker Msg(NETMSG_RCON_LINE);
|
|
|
|
Msg.AddString(pLine, 512);
|
2011-02-12 10:40:36 +00:00
|
|
|
SendMsgEx(&Msg, MSGFLAG_VITAL, ClientID, true);
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
|
2011-07-30 11:40:01 +00:00
|
|
|
void CServer::SendRconLineAuthed(const char *pLine, void *pUser)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
|
|
|
CServer *pThis = (CServer *)pUser;
|
|
|
|
static volatile int ReentryGuard = 0;
|
|
|
|
int i;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
if(ReentryGuard) return;
|
|
|
|
ReentryGuard++;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
for(i = 0; i < MAX_CLIENTS; i++)
|
|
|
|
{
|
2011-07-05 19:54:10 +00:00
|
|
|
if(pThis->m_aClients[i].m_State != CClient::STATE_EMPTY && pThis->m_aClients[i].m_Authed >= pThis->m_RconAuthLevel)
|
2010-05-29 07:25:38 +00:00
|
|
|
pThis->SendRconLine(i, pLine);
|
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
ReentryGuard--;
|
|
|
|
}
|
|
|
|
|
2011-07-14 20:07:21 +00:00
|
|
|
void CServer::SendRconCmdAdd(const IConsole::CCommandInfo *pCommandInfo, int ClientID)
|
|
|
|
{
|
|
|
|
CMsgPacker Msg(NETMSG_RCON_CMD_ADD);
|
2011-07-30 16:19:15 +00:00
|
|
|
Msg.AddString(pCommandInfo->m_pName, IConsole::TEMPCMD_NAME_LENGTH);
|
|
|
|
Msg.AddString(pCommandInfo->m_pHelp, IConsole::TEMPCMD_HELP_LENGTH);
|
|
|
|
Msg.AddString(pCommandInfo->m_pParams, IConsole::TEMPCMD_PARAMS_LENGTH);
|
2011-07-14 20:07:21 +00:00
|
|
|
SendMsgEx(&Msg, MSGFLAG_VITAL, ClientID, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CServer::SendRconCmdRem(const IConsole::CCommandInfo *pCommandInfo, int ClientID)
|
|
|
|
{
|
|
|
|
CMsgPacker Msg(NETMSG_RCON_CMD_REM);
|
|
|
|
Msg.AddString(pCommandInfo->m_pName, 256);
|
|
|
|
SendMsgEx(&Msg, MSGFLAG_VITAL, ClientID, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CServer::UpdateClientRconCommands()
|
|
|
|
{
|
|
|
|
int ClientID = Tick() % MAX_CLIENTS;
|
2011-08-11 08:59:14 +00:00
|
|
|
|
2011-07-14 20:07:21 +00:00
|
|
|
if(m_aClients[ClientID].m_State != CClient::STATE_EMPTY && m_aClients[ClientID].m_Authed)
|
|
|
|
{
|
|
|
|
int ConsoleAccessLevel = m_aClients[ClientID].m_Authed == AUTHED_ADMIN ? IConsole::ACCESS_LEVEL_ADMIN : IConsole::ACCESS_LEVEL_MOD;
|
|
|
|
for(int i = 0; i < MAX_RCONCMD_SEND && m_aClients[ClientID].m_pRconCmdToSend; ++i)
|
|
|
|
{
|
|
|
|
SendRconCmdAdd(m_aClients[ClientID].m_pRconCmdToSend, ClientID);
|
|
|
|
m_aClients[ClientID].m_pRconCmdToSend = m_aClients[ClientID].m_pRconCmdToSend->NextCommandInfo(ConsoleAccessLevel, CFGFLAG_SERVER);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
void CServer::ProcessClientPacket(CNetChunk *pPacket)
|
|
|
|
{
|
2011-02-12 10:40:36 +00:00
|
|
|
int ClientID = pPacket->m_ClientID;
|
2010-05-29 07:25:38 +00:00
|
|
|
CUnpacker Unpacker;
|
|
|
|
Unpacker.Reset(pPacket->m_pData, pPacket->m_DataSize);
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
// unpack msgid and system flag
|
|
|
|
int Msg = Unpacker.GetInt();
|
|
|
|
int Sys = Msg&1;
|
|
|
|
Msg >>= 1;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
if(Unpacker.Error())
|
|
|
|
return;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2011-03-15 08:58:57 +00:00
|
|
|
if(Sys)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2011-03-15 08:58:57 +00:00
|
|
|
// system message
|
|
|
|
if(Msg == NETMSG_INFO)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2011-03-15 08:58:57 +00:00
|
|
|
if(m_aClients[ClientID].m_State == CClient::STATE_AUTH)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2011-03-15 08:58:57 +00:00
|
|
|
const char *pVersion = Unpacker.GetString(CUnpacker::SANITIZE_CC);
|
|
|
|
if(str_comp(pVersion, GameServer()->NetVersion()) != 0)
|
|
|
|
{
|
|
|
|
// wrong version
|
|
|
|
char aReason[256];
|
|
|
|
str_format(aReason, sizeof(aReason), "Wrong version. Server is running '%s' and client '%s'", GameServer()->NetVersion(), pVersion);
|
|
|
|
m_NetServer.Drop(ClientID, aReason);
|
|
|
|
return;
|
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2011-03-15 08:58:57 +00:00
|
|
|
const char *pPassword = Unpacker.GetString(CUnpacker::SANITIZE_CC);
|
|
|
|
if(g_Config.m_Password[0] != 0 && str_comp(g_Config.m_Password, pPassword) != 0)
|
|
|
|
{
|
|
|
|
// wrong password
|
|
|
|
m_NetServer.Drop(ClientID, "Wrong password");
|
|
|
|
return;
|
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2011-03-15 08:58:57 +00:00
|
|
|
m_aClients[ClientID].m_State = CClient::STATE_CONNECTING;
|
|
|
|
SendMap(ClientID);
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
}
|
2011-03-15 08:58:57 +00:00
|
|
|
else if(Msg == NETMSG_REQUEST_MAP_DATA)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2011-03-15 08:58:57 +00:00
|
|
|
int Chunk = Unpacker.GetInt();
|
|
|
|
int ChunkSize = 1024-128;
|
|
|
|
int Offset = Chunk * ChunkSize;
|
|
|
|
int Last = 0;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2011-03-15 08:58:57 +00:00
|
|
|
// drop faulty map data requests
|
|
|
|
if(Chunk < 0 || Offset > m_CurrentMapSize)
|
|
|
|
return;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2011-03-15 08:58:57 +00:00
|
|
|
if(Offset+ChunkSize >= m_CurrentMapSize)
|
|
|
|
{
|
|
|
|
ChunkSize = m_CurrentMapSize-Offset;
|
|
|
|
if(ChunkSize < 0)
|
|
|
|
ChunkSize = 0;
|
|
|
|
Last = 1;
|
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2011-03-15 08:58:57 +00:00
|
|
|
CMsgPacker Msg(NETMSG_MAP_DATA);
|
|
|
|
Msg.AddInt(Last);
|
|
|
|
Msg.AddInt(m_CurrentMapCrc);
|
|
|
|
Msg.AddInt(Chunk);
|
|
|
|
Msg.AddInt(ChunkSize);
|
|
|
|
Msg.AddRaw(&m_pCurrentMapData[Offset], ChunkSize);
|
|
|
|
SendMsgEx(&Msg, MSGFLAG_VITAL|MSGFLAG_FLUSH, ClientID, true);
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2011-03-15 08:58:57 +00:00
|
|
|
if(g_Config.m_Debug)
|
|
|
|
{
|
|
|
|
char aBuf[256];
|
|
|
|
str_format(aBuf, sizeof(aBuf), "sending chunk %d with size %d", Chunk, ChunkSize);
|
|
|
|
Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "server", aBuf);
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
2011-03-15 08:58:57 +00:00
|
|
|
}
|
|
|
|
else if(Msg == NETMSG_READY)
|
|
|
|
{
|
|
|
|
if(m_aClients[ClientID].m_State == CClient::STATE_CONNECTING)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2011-03-28 18:11:28 +00:00
|
|
|
char aAddrStr[NETADDR_MAXSTRSIZE];
|
2011-12-29 22:36:53 +00:00
|
|
|
net_addr_str(m_NetServer.ClientAddr(ClientID), aAddrStr, sizeof(aAddrStr), true);
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2011-03-15 08:58:57 +00:00
|
|
|
char aBuf[256];
|
2011-03-28 18:11:28 +00:00
|
|
|
str_format(aBuf, sizeof(aBuf), "player is ready. ClientID=%x addr=%s", ClientID, aAddrStr);
|
2011-03-15 08:58:57 +00:00
|
|
|
Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "server", aBuf);
|
|
|
|
m_aClients[ClientID].m_State = CClient::STATE_READY;
|
|
|
|
GameServer()->OnClientConnected(ClientID);
|
|
|
|
SendConnectionReady(ClientID);
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
2011-03-15 08:58:57 +00:00
|
|
|
}
|
|
|
|
else if(Msg == NETMSG_ENTERGAME)
|
|
|
|
{
|
|
|
|
if(m_aClients[ClientID].m_State == CClient::STATE_READY && GameServer()->IsClientReady(ClientID))
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2011-03-28 18:11:28 +00:00
|
|
|
char aAddrStr[NETADDR_MAXSTRSIZE];
|
2011-12-29 22:36:53 +00:00
|
|
|
net_addr_str(m_NetServer.ClientAddr(ClientID), aAddrStr, sizeof(aAddrStr), true);
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2011-03-15 08:58:57 +00:00
|
|
|
char aBuf[256];
|
2011-03-28 18:11:28 +00:00
|
|
|
str_format(aBuf, sizeof(aBuf), "player has entered the game. ClientID=%x addr=%s", ClientID, aAddrStr);
|
2011-03-15 08:58:57 +00:00
|
|
|
Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf);
|
|
|
|
m_aClients[ClientID].m_State = CClient::STATE_INGAME;
|
|
|
|
GameServer()->OnClientEnter(ClientID);
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
2011-03-15 08:58:57 +00:00
|
|
|
}
|
|
|
|
else if(Msg == NETMSG_INPUT)
|
|
|
|
{
|
|
|
|
CClient::CInput *pInput;
|
|
|
|
int64 TagTime;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2011-03-15 08:58:57 +00:00
|
|
|
m_aClients[ClientID].m_LastAckedSnapshot = Unpacker.GetInt();
|
|
|
|
int IntendedTick = Unpacker.GetInt();
|
|
|
|
int Size = Unpacker.GetInt();
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2011-03-15 08:58:57 +00:00
|
|
|
// check for errors
|
|
|
|
if(Unpacker.Error() || Size/4 > MAX_INPUT_SIZE)
|
|
|
|
return;
|
2010-05-29 07:25:38 +00:00
|
|
|
|
2011-03-15 08:58:57 +00:00
|
|
|
if(m_aClients[ClientID].m_LastAckedSnapshot > 0)
|
|
|
|
m_aClients[ClientID].m_SnapRate = CClient::SNAPRATE_FULL;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2011-03-15 08:58:57 +00:00
|
|
|
if(m_aClients[ClientID].m_Snapshots.Get(m_aClients[ClientID].m_LastAckedSnapshot, &TagTime, 0, 0) >= 0)
|
|
|
|
m_aClients[ClientID].m_Latency = (int)(((time_get()-TagTime)*1000)/time_freq());
|
2010-05-29 07:25:38 +00:00
|
|
|
|
2011-03-15 08:58:57 +00:00
|
|
|
// add message to report the input timing
|
|
|
|
// skip packets that are old
|
|
|
|
if(IntendedTick > m_aClients[ClientID].m_LastInputTick)
|
|
|
|
{
|
|
|
|
int TimeLeft = ((TickStartTime(IntendedTick)-time_get())*1000) / time_freq();
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2011-03-15 08:58:57 +00:00
|
|
|
CMsgPacker Msg(NETMSG_INPUTTIMING);
|
|
|
|
Msg.AddInt(IntendedTick);
|
|
|
|
Msg.AddInt(TimeLeft);
|
|
|
|
SendMsgEx(&Msg, 0, ClientID, true);
|
|
|
|
}
|
2010-05-29 07:25:38 +00:00
|
|
|
|
2011-03-15 08:58:57 +00:00
|
|
|
m_aClients[ClientID].m_LastInputTick = IntendedTick;
|
2010-05-29 07:25:38 +00:00
|
|
|
|
2011-03-15 08:58:57 +00:00
|
|
|
pInput = &m_aClients[ClientID].m_aInputs[m_aClients[ClientID].m_CurrentInput];
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2011-03-15 08:58:57 +00:00
|
|
|
if(IntendedTick <= Tick())
|
|
|
|
IntendedTick = Tick()+1;
|
2010-05-29 07:25:38 +00:00
|
|
|
|
2011-03-15 08:58:57 +00:00
|
|
|
pInput->m_GameTick = IntendedTick;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2011-03-15 08:58:57 +00:00
|
|
|
for(int i = 0; i < Size/4; i++)
|
|
|
|
pInput->m_aData[i] = Unpacker.GetInt();
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2011-03-15 08:58:57 +00:00
|
|
|
mem_copy(m_aClients[ClientID].m_LatestInput.m_aData, pInput->m_aData, MAX_INPUT_SIZE*sizeof(int));
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2011-03-15 08:58:57 +00:00
|
|
|
m_aClients[ClientID].m_CurrentInput++;
|
|
|
|
m_aClients[ClientID].m_CurrentInput %= 200;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2011-03-15 08:58:57 +00:00
|
|
|
// call the mod with the fresh input data
|
|
|
|
if(m_aClients[ClientID].m_State == CClient::STATE_INGAME)
|
|
|
|
GameServer()->OnClientDirectInput(ClientID, m_aClients[ClientID].m_LatestInput.m_aData);
|
|
|
|
}
|
|
|
|
else if(Msg == NETMSG_RCON_CMD)
|
|
|
|
{
|
|
|
|
const char *pCmd = Unpacker.GetString();
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2011-03-15 08:58:57 +00:00
|
|
|
if(Unpacker.Error() == 0 && m_aClients[ClientID].m_Authed)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2011-03-15 08:58:57 +00:00
|
|
|
char aBuf[256];
|
|
|
|
str_format(aBuf, sizeof(aBuf), "ClientID=%d rcon='%s'", ClientID, pCmd);
|
|
|
|
Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "server", aBuf);
|
|
|
|
m_RconClientID = ClientID;
|
2011-07-05 19:54:10 +00:00
|
|
|
m_RconAuthLevel = m_aClients[ClientID].m_Authed;
|
|
|
|
Console()->SetAccessLevel(m_aClients[ClientID].m_Authed == AUTHED_ADMIN ? IConsole::ACCESS_LEVEL_ADMIN : IConsole::ACCESS_LEVEL_MOD);
|
2011-12-30 18:12:31 +00:00
|
|
|
Console()->ExecuteLineFlag(pCmd, CFGFLAG_SERVER);
|
2011-07-05 19:54:10 +00:00
|
|
|
Console()->SetAccessLevel(IConsole::ACCESS_LEVEL_ADMIN);
|
2011-12-31 11:11:48 +00:00
|
|
|
m_RconClientID = IServer::RCON_CID_SERV;
|
2011-07-05 19:54:10 +00:00
|
|
|
m_RconAuthLevel = AUTHED_ADMIN;
|
2011-03-15 08:58:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if(Msg == NETMSG_RCON_AUTH)
|
|
|
|
{
|
|
|
|
const char *pPw;
|
|
|
|
Unpacker.GetString(); // login name, not used
|
|
|
|
pPw = Unpacker.GetString(CUnpacker::SANITIZE_CC);
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2011-03-15 08:58:57 +00:00
|
|
|
if(Unpacker.Error() == 0)
|
|
|
|
{
|
2011-07-20 08:32:37 +00:00
|
|
|
if(g_Config.m_SvRconPassword[0] == 0 && g_Config.m_SvRconModPassword[0] == 0)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2011-07-05 19:54:10 +00:00
|
|
|
SendRconLine(ClientID, "No rcon password set on server. Set sv_rcon_password and/or sv_rcon_mod_password to enable the remote console.");
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
2011-07-07 22:00:38 +00:00
|
|
|
else if(g_Config.m_SvRconPassword[0] && str_comp(pPw, g_Config.m_SvRconPassword) == 0)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2011-03-15 08:58:57 +00:00
|
|
|
CMsgPacker Msg(NETMSG_RCON_AUTH_STATUS);
|
2011-07-14 20:07:21 +00:00
|
|
|
Msg.AddInt(1); //authed
|
|
|
|
Msg.AddInt(1); //cmdlist
|
2011-03-15 08:58:57 +00:00
|
|
|
SendMsgEx(&Msg, MSGFLAG_VITAL, ClientID, true);
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2011-07-05 19:54:10 +00:00
|
|
|
m_aClients[ClientID].m_Authed = AUTHED_ADMIN;
|
2011-07-14 20:07:21 +00:00
|
|
|
int SendRconCmds = Unpacker.GetInt();
|
|
|
|
if(Unpacker.Error() == 0 && SendRconCmds)
|
|
|
|
m_aClients[ClientID].m_pRconCmdToSend = Console()->FirstCommandInfo(IConsole::ACCESS_LEVEL_ADMIN, CFGFLAG_SERVER);
|
2011-07-05 19:54:10 +00:00
|
|
|
SendRconLine(ClientID, "Admin authentication successful. Full remote console access granted.");
|
|
|
|
char aBuf[256];
|
|
|
|
str_format(aBuf, sizeof(aBuf), "ClientID=%d authed (admin)", ClientID);
|
|
|
|
Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf);
|
|
|
|
}
|
2011-07-07 22:00:38 +00:00
|
|
|
else if(g_Config.m_SvRconModPassword[0] && str_comp(pPw, g_Config.m_SvRconModPassword) == 0)
|
2011-07-05 19:54:10 +00:00
|
|
|
{
|
|
|
|
CMsgPacker Msg(NETMSG_RCON_AUTH_STATUS);
|
2011-07-14 20:07:21 +00:00
|
|
|
Msg.AddInt(1); //authed
|
|
|
|
Msg.AddInt(1); //cmdlist
|
2011-07-05 19:54:10 +00:00
|
|
|
SendMsgEx(&Msg, MSGFLAG_VITAL, ClientID, true);
|
|
|
|
|
|
|
|
m_aClients[ClientID].m_Authed = AUTHED_MOD;
|
2011-07-14 20:07:21 +00:00
|
|
|
int SendRconCmds = Unpacker.GetInt();
|
|
|
|
if(Unpacker.Error() == 0 && SendRconCmds)
|
|
|
|
m_aClients[ClientID].m_pRconCmdToSend = Console()->FirstCommandInfo(IConsole::ACCESS_LEVEL_MOD, CFGFLAG_SERVER);
|
2011-07-05 19:54:10 +00:00
|
|
|
SendRconLine(ClientID, "Moderator authentication successful. Limited remote console access granted.");
|
2011-03-15 08:58:57 +00:00
|
|
|
char aBuf[256];
|
2011-07-05 19:54:10 +00:00
|
|
|
str_format(aBuf, sizeof(aBuf), "ClientID=%d authed (moderator)", ClientID);
|
2011-03-15 08:58:57 +00:00
|
|
|
Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf);
|
|
|
|
}
|
|
|
|
else if(g_Config.m_SvRconMaxTries)
|
|
|
|
{
|
|
|
|
m_aClients[ClientID].m_AuthTries++;
|
|
|
|
char aBuf[128];
|
|
|
|
str_format(aBuf, sizeof(aBuf), "Wrong password %d/%d.", m_aClients[ClientID].m_AuthTries, g_Config.m_SvRconMaxTries);
|
|
|
|
SendRconLine(ClientID, aBuf);
|
|
|
|
if(m_aClients[ClientID].m_AuthTries >= g_Config.m_SvRconMaxTries)
|
2010-09-16 11:06:11 +00:00
|
|
|
{
|
2011-03-15 08:58:57 +00:00
|
|
|
if(!g_Config.m_SvRconBantime)
|
|
|
|
m_NetServer.Drop(ClientID, "Too many remote console authentication tries");
|
|
|
|
else
|
2011-12-29 22:36:53 +00:00
|
|
|
m_ServerBan.BanAddr(m_NetServer.ClientAddr(ClientID), g_Config.m_SvRconBantime*60, "Too many remote console authentication tries");
|
2010-09-16 11:06:11 +00:00
|
|
|
}
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
2011-03-15 08:58:57 +00:00
|
|
|
else
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2011-03-15 08:58:57 +00:00
|
|
|
SendRconLine(ClientID, "Wrong password.");
|
2011-01-19 14:39:04 +00:00
|
|
|
}
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
}
|
2011-03-15 08:58:57 +00:00
|
|
|
else if(Msg == NETMSG_PING)
|
|
|
|
{
|
|
|
|
CMsgPacker Msg(NETMSG_PING_REPLY);
|
|
|
|
SendMsgEx(&Msg, 0, ClientID, true);
|
|
|
|
}
|
2010-05-29 07:25:38 +00:00
|
|
|
else
|
|
|
|
{
|
2011-03-15 08:58:57 +00:00
|
|
|
if(g_Config.m_Debug)
|
|
|
|
{
|
|
|
|
char aHex[] = "0123456789ABCDEF";
|
|
|
|
char aBuf[512];
|
|
|
|
|
|
|
|
for(int b = 0; b < pPacket->m_DataSize && b < 32; b++)
|
|
|
|
{
|
|
|
|
aBuf[b*3] = aHex[((const unsigned char *)pPacket->m_pData)[b]>>4];
|
|
|
|
aBuf[b*3+1] = aHex[((const unsigned char *)pPacket->m_pData)[b]&0xf];
|
|
|
|
aBuf[b*3+2] = ' ';
|
|
|
|
aBuf[b*3+3] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
char aBufMsg[256];
|
|
|
|
str_format(aBufMsg, sizeof(aBufMsg), "strange message ClientID=%d msg=%d data_size=%d", ClientID, Msg, pPacket->m_DataSize);
|
|
|
|
Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "server", aBufMsg);
|
|
|
|
Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "server", aBuf);
|
|
|
|
}
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
}
|
2011-03-15 08:58:57 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
// game message
|
|
|
|
if(m_aClients[ClientID].m_State >= CClient::STATE_READY)
|
|
|
|
GameServer()->OnMessage(Msg, &Unpacker, ClientID);
|
|
|
|
}
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2011-12-29 22:36:53 +00:00
|
|
|
void CServer::SendServerInfo(const NETADDR *pAddr, int Token)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
|
|
|
CNetChunk Packet;
|
|
|
|
CPacker p;
|
|
|
|
char aBuf[128];
|
|
|
|
|
|
|
|
// count the players
|
2011-03-20 14:33:49 +00:00
|
|
|
int PlayerCount = 0, ClientCount = 0;
|
2010-05-29 07:25:38 +00:00
|
|
|
for(int i = 0; i < MAX_CLIENTS; i++)
|
|
|
|
{
|
|
|
|
if(m_aClients[i].m_State != CClient::STATE_EMPTY)
|
2011-03-20 14:33:49 +00:00
|
|
|
{
|
|
|
|
if(GameServer()->IsClientPlayer(i))
|
|
|
|
PlayerCount++;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2011-03-20 14:33:49 +00:00
|
|
|
ClientCount++;
|
|
|
|
}
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
p.Reset();
|
|
|
|
|
2011-03-04 17:14:08 +00:00
|
|
|
p.AddRaw(SERVERBROWSE_INFO, sizeof(SERVERBROWSE_INFO));
|
|
|
|
str_format(aBuf, sizeof(aBuf), "%d", Token);
|
|
|
|
p.AddString(aBuf, 6);
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
p.AddString(GameServer()->Version(), 32);
|
|
|
|
p.AddString(g_Config.m_SvName, 64);
|
2010-09-03 19:28:31 +00:00
|
|
|
p.AddString(GetMapName(), 32);
|
2010-05-29 07:25:38 +00:00
|
|
|
|
|
|
|
// gametype
|
2011-02-16 11:31:47 +00:00
|
|
|
p.AddString(GameServer()->GameType(), 16);
|
2010-05-29 07:25:38 +00:00
|
|
|
|
|
|
|
// flags
|
|
|
|
int i = 0;
|
2011-04-13 18:37:12 +00:00
|
|
|
if(g_Config.m_Password[0]) // password set
|
2010-05-29 07:25:38 +00:00
|
|
|
i |= SERVER_FLAG_PASSWORD;
|
|
|
|
str_format(aBuf, sizeof(aBuf), "%d", i);
|
|
|
|
p.AddString(aBuf, 2);
|
|
|
|
|
2011-04-13 18:37:12 +00:00
|
|
|
str_format(aBuf, sizeof(aBuf), "%d", PlayerCount); p.AddString(aBuf, 3); // num players
|
2011-03-20 14:33:49 +00:00
|
|
|
str_format(aBuf, sizeof(aBuf), "%d", m_NetServer.MaxClients()-g_Config.m_SvSpectatorSlots); p.AddString(aBuf, 3); // max players
|
2011-04-13 18:37:12 +00:00
|
|
|
str_format(aBuf, sizeof(aBuf), "%d", ClientCount); p.AddString(aBuf, 3); // num clients
|
2011-03-20 14:33:49 +00:00
|
|
|
str_format(aBuf, sizeof(aBuf), "%d", m_NetServer.MaxClients()); p.AddString(aBuf, 3); // max clients
|
2010-05-29 07:25:38 +00:00
|
|
|
|
|
|
|
for(i = 0; i < MAX_CLIENTS; i++)
|
|
|
|
{
|
|
|
|
if(m_aClients[i].m_State != CClient::STATE_EMPTY)
|
|
|
|
{
|
2011-04-13 18:37:12 +00:00
|
|
|
p.AddString(ClientName(i), MAX_NAME_LENGTH); // client name
|
|
|
|
p.AddString(ClientClan(i), MAX_CLAN_LENGTH); // client clan
|
|
|
|
str_format(aBuf, sizeof(aBuf), "%d", m_aClients[i].m_Country); p.AddString(aBuf, 6); // client country
|
|
|
|
str_format(aBuf, sizeof(aBuf), "%d", m_aClients[i].m_Score); p.AddString(aBuf, 6); // client score
|
|
|
|
str_format(aBuf, sizeof(aBuf), "%d", GameServer()->IsClientPlayer(i)?1:0); p.AddString(aBuf, 2); // is player?
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
Packet.m_ClientID = -1;
|
|
|
|
Packet.m_Address = *pAddr;
|
|
|
|
Packet.m_Flags = NETSENDFLAG_CONNLESS;
|
|
|
|
Packet.m_DataSize = p.Size();
|
|
|
|
Packet.m_pData = p.Data();
|
|
|
|
m_NetServer.Send(&Packet);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CServer::UpdateServerInfo()
|
|
|
|
{
|
|
|
|
for(int i = 0; i < MAX_CLIENTS; ++i)
|
|
|
|
{
|
|
|
|
if(m_aClients[i].m_State != CClient::STATE_EMPTY)
|
2011-12-29 22:36:53 +00:00
|
|
|
SendServerInfo(m_NetServer.ClientAddr(i), -1);
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void CServer::PumpNetwork()
|
|
|
|
{
|
|
|
|
CNetChunk Packet;
|
|
|
|
|
|
|
|
m_NetServer.Update();
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
// process packets
|
|
|
|
while(m_NetServer.Recv(&Packet))
|
|
|
|
{
|
|
|
|
if(Packet.m_ClientID == -1)
|
|
|
|
{
|
|
|
|
// stateless
|
|
|
|
if(!m_Register.RegisterProcessPacket(&Packet))
|
|
|
|
{
|
|
|
|
if(Packet.m_DataSize == sizeof(SERVERBROWSE_GETINFO)+1 &&
|
|
|
|
mem_comp(Packet.m_pData, SERVERBROWSE_GETINFO, sizeof(SERVERBROWSE_GETINFO)) == 0)
|
|
|
|
{
|
|
|
|
SendServerInfo(&Packet.m_Address, ((unsigned char *)Packet.m_pData)[sizeof(SERVERBROWSE_GETINFO)]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
ProcessClientPacket(&Packet);
|
|
|
|
}
|
2011-07-02 06:36:14 +00:00
|
|
|
|
2011-12-29 22:36:53 +00:00
|
|
|
m_ServerBan.Update();
|
2011-07-30 11:40:01 +00:00
|
|
|
m_Econ.Update();
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
|
2010-09-03 19:28:31 +00:00
|
|
|
char *CServer::GetMapName()
|
|
|
|
{
|
|
|
|
// get the name of the map without his path
|
|
|
|
char *pMapShortName = &g_Config.m_SvMap[0];
|
|
|
|
for(int i = 0; i < str_length(g_Config.m_SvMap)-1; i++)
|
|
|
|
{
|
|
|
|
if(g_Config.m_SvMap[i] == '/' || g_Config.m_SvMap[i] == '\\')
|
|
|
|
pMapShortName = &g_Config.m_SvMap[i+1];
|
|
|
|
}
|
|
|
|
return pMapShortName;
|
|
|
|
}
|
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
int CServer::LoadMap(const char *pMapName)
|
|
|
|
{
|
|
|
|
//DATAFILE *df;
|
|
|
|
char aBuf[512];
|
|
|
|
str_format(aBuf, sizeof(aBuf), "maps/%s.map", pMapName);
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
/*df = datafile_load(buf);
|
|
|
|
if(!df)
|
|
|
|
return 0;*/
|
2011-03-31 13:13:49 +00:00
|
|
|
|
|
|
|
// check for valid standard map
|
|
|
|
if(!m_MapChecker.ReadAndValidateMap(Storage(), aBuf, IStorage::TYPE_ALL))
|
|
|
|
{
|
|
|
|
Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "mapchecker", "invalid standard map");
|
|
|
|
return 0;
|
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
if(!m_pMap->Load(aBuf))
|
|
|
|
return 0;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
// stop recording when we change map
|
|
|
|
m_DemoRecorder.Stop();
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
// reinit snapshot ids
|
|
|
|
m_IDPool.TimeoutIDs();
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
// get the crc of the map
|
|
|
|
m_CurrentMapCrc = m_pMap->Crc();
|
2010-08-17 22:06:00 +00:00
|
|
|
char aBufMsg[256];
|
|
|
|
str_format(aBufMsg, sizeof(aBufMsg), "%s crc is %08x", aBuf, m_CurrentMapCrc);
|
|
|
|
Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "server", aBufMsg);
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
str_copy(m_aCurrentMap, pMapName, sizeof(m_aCurrentMap));
|
|
|
|
//map_set(df);
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2011-08-02 10:14:11 +00:00
|
|
|
// load complete map into memory for download
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2010-10-06 21:07:35 +00:00
|
|
|
IOHANDLE File = Storage()->OpenFile(aBuf, IOFLAG_READ, IStorage::TYPE_ALL);
|
2010-05-29 07:25:38 +00:00
|
|
|
m_CurrentMapSize = (int)io_length(File);
|
|
|
|
if(m_pCurrentMapData)
|
|
|
|
mem_free(m_pCurrentMapData);
|
|
|
|
m_pCurrentMapData = (unsigned char *)mem_alloc(m_CurrentMapSize, 1);
|
|
|
|
io_read(File, m_pCurrentMapData, m_CurrentMapSize);
|
|
|
|
io_close(File);
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2010-08-17 22:06:00 +00:00
|
|
|
void CServer::InitRegister(CNetServer *pNetServer, IEngineMasterServer *pMasterServer, IConsole *pConsole)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2010-08-17 22:06:00 +00:00
|
|
|
m_Register.Init(pNetServer, pMasterServer, pConsole);
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int CServer::Run()
|
|
|
|
{
|
|
|
|
m_pGameServer = Kernel()->RequestInterface<IGameServer>();
|
|
|
|
m_pMap = Kernel()->RequestInterface<IEngineMap>();
|
|
|
|
m_pStorage = Kernel()->RequestInterface<IStorage>();
|
|
|
|
|
|
|
|
//
|
2011-07-30 11:40:01 +00:00
|
|
|
m_PrintCBIndex = Console()->RegisterPrintCallback(g_Config.m_ConsoleOutputLevel, SendRconLineAuthed, this);
|
2010-05-29 07:25:38 +00:00
|
|
|
|
|
|
|
// load map
|
|
|
|
if(!LoadMap(g_Config.m_SvMap))
|
|
|
|
{
|
|
|
|
dbg_msg("server", "failed to load map. mapname='%s'", g_Config.m_SvMap);
|
|
|
|
return -1;
|
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
// start server
|
|
|
|
NETADDR BindAddr;
|
2011-03-28 18:11:28 +00:00
|
|
|
if(g_Config.m_SvBindaddr[0] && net_host_lookup(g_Config.m_SvBindaddr, &BindAddr, NETTYPE_ALL) == 0)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
|
|
|
// sweet!
|
|
|
|
BindAddr.port = g_Config.m_SvPort;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
mem_zero(&BindAddr, sizeof(BindAddr));
|
2011-03-28 18:11:28 +00:00
|
|
|
BindAddr.type = NETTYPE_ALL;
|
2010-05-29 07:25:38 +00:00
|
|
|
BindAddr.port = g_Config.m_SvPort;
|
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2011-12-29 22:36:53 +00:00
|
|
|
if(!m_NetServer.Open(BindAddr, &m_ServerBan, g_Config.m_SvMaxClients, g_Config.m_SvMaxClientsPerIP, 0))
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
|
|
|
dbg_msg("server", "couldn't open socket. port might already be in use");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_NetServer.SetCallbacks(NewClientCallback, DelClientCallback, this);
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2011-12-29 22:36:53 +00:00
|
|
|
m_ServerBan.Init(Console(), Storage(), this);
|
|
|
|
m_Econ.Init(Console(), &m_ServerBan);
|
2011-07-02 06:36:14 +00:00
|
|
|
|
2010-08-17 22:06:00 +00:00
|
|
|
char aBuf[256];
|
|
|
|
str_format(aBuf, sizeof(aBuf), "server name is '%s'", g_Config.m_SvName);
|
|
|
|
Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf);
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
GameServer()->OnInit();
|
2010-08-17 22:06:00 +00:00
|
|
|
str_format(aBuf, sizeof(aBuf), "version %s", GameServer()->NetVersion());
|
|
|
|
Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf);
|
2010-05-29 07:25:38 +00:00
|
|
|
|
2010-08-07 18:22:25 +00:00
|
|
|
// process pending commands
|
|
|
|
m_pConsole->StoreCommands(false);
|
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
// start game
|
|
|
|
{
|
|
|
|
int64 ReportTime = time_get();
|
|
|
|
int ReportInterval = 3;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
m_Lastheartbeat = 0;
|
|
|
|
m_GameStartTime = time_get();
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
if(g_Config.m_Debug)
|
2010-08-17 22:06:00 +00:00
|
|
|
{
|
|
|
|
str_format(aBuf, sizeof(aBuf), "baseline memory usage %dk", mem_stats()->allocated/1024);
|
|
|
|
Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "server", aBuf);
|
|
|
|
}
|
2010-05-29 07:25:38 +00:00
|
|
|
|
|
|
|
while(m_RunServer)
|
|
|
|
{
|
|
|
|
int64 t = time_get();
|
|
|
|
int NewTicks = 0;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
// load new map TODO: don't poll this
|
2010-05-30 12:01:11 +00:00
|
|
|
if(str_comp(g_Config.m_SvMap, m_aCurrentMap) != 0 || m_MapReload)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2010-05-30 12:01:11 +00:00
|
|
|
m_MapReload = 0;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
// load map
|
|
|
|
if(LoadMap(g_Config.m_SvMap))
|
|
|
|
{
|
|
|
|
// new map loaded
|
|
|
|
GameServer()->OnShutdown();
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
for(int c = 0; c < MAX_CLIENTS; c++)
|
|
|
|
{
|
2010-06-26 15:59:59 +00:00
|
|
|
if(m_aClients[c].m_State <= CClient::STATE_AUTH)
|
2010-05-29 07:25:38 +00:00
|
|
|
continue;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
SendMap(c);
|
|
|
|
m_aClients[c].Reset();
|
|
|
|
m_aClients[c].m_State = CClient::STATE_CONNECTING;
|
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
m_GameStartTime = time_get();
|
|
|
|
m_CurrentGameTick = 0;
|
|
|
|
Kernel()->ReregisterInterface(GameServer());
|
|
|
|
GameServer()->OnInit();
|
2010-06-07 11:34:47 +00:00
|
|
|
UpdateServerInfo();
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2010-08-17 22:06:00 +00:00
|
|
|
str_format(aBuf, sizeof(aBuf), "failed to load map. mapname='%s'", g_Config.m_SvMap);
|
|
|
|
Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf);
|
2010-05-29 07:25:38 +00:00
|
|
|
str_copy(g_Config.m_SvMap, m_aCurrentMap, sizeof(g_Config.m_SvMap));
|
|
|
|
}
|
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
while(t > TickStartTime(m_CurrentGameTick+1))
|
|
|
|
{
|
|
|
|
m_CurrentGameTick++;
|
|
|
|
NewTicks++;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
// apply new input
|
|
|
|
for(int c = 0; c < MAX_CLIENTS; c++)
|
|
|
|
{
|
|
|
|
if(m_aClients[c].m_State == CClient::STATE_EMPTY)
|
|
|
|
continue;
|
|
|
|
for(int i = 0; i < 200; i++)
|
|
|
|
{
|
|
|
|
if(m_aClients[c].m_aInputs[i].m_GameTick == Tick())
|
|
|
|
{
|
|
|
|
if(m_aClients[c].m_State == CClient::STATE_INGAME)
|
|
|
|
GameServer()->OnClientPredictedInput(c, m_aClients[c].m_aInputs[i].m_aData);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
GameServer()->OnTick();
|
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
// snap game
|
|
|
|
if(NewTicks)
|
|
|
|
{
|
|
|
|
if(g_Config.m_SvHighBandwidth || (m_CurrentGameTick%2) == 0)
|
|
|
|
DoSnapshot();
|
2011-07-14 20:07:21 +00:00
|
|
|
|
|
|
|
UpdateClientRconCommands();
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
// master server stuff
|
2011-07-06 23:48:00 +00:00
|
|
|
m_Register.RegisterUpdate(m_NetServer.NetType());
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
PumpNetwork();
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
if(ReportTime < time_get())
|
|
|
|
{
|
|
|
|
if(g_Config.m_Debug)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
static NETSTATS prev_stats;
|
|
|
|
NETSTATS stats;
|
|
|
|
netserver_stats(net, &stats);
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
perf_next();
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
if(config.dbg_pref)
|
|
|
|
perf_dump(&rootscope);
|
|
|
|
|
|
|
|
dbg_msg("server", "send=%8d recv=%8d",
|
|
|
|
(stats.send_bytes - prev_stats.send_bytes)/reportinterval,
|
|
|
|
(stats.recv_bytes - prev_stats.recv_bytes)/reportinterval);
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
prev_stats = stats;
|
|
|
|
*/
|
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
ReportTime += time_freq()*ReportInterval;
|
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
// wait for incomming data
|
|
|
|
net_socket_read_wait(m_NetServer.Socket(), 5);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// disconnect all clients on shutdown
|
|
|
|
for(int i = 0; i < MAX_CLIENTS; ++i)
|
|
|
|
{
|
|
|
|
if(m_aClients[i].m_State != CClient::STATE_EMPTY)
|
2010-10-09 18:19:58 +00:00
|
|
|
m_NetServer.Drop(i, "Server shutdown");
|
2011-07-31 00:20:46 +00:00
|
|
|
|
|
|
|
m_Econ.Shutdown();
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
GameServer()->OnShutdown();
|
|
|
|
m_pMap->Unload();
|
|
|
|
|
|
|
|
if(m_pCurrentMapData)
|
|
|
|
mem_free(m_pCurrentMapData);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CServer::ConKick(IConsole::IResult *pResult, void *pUser)
|
|
|
|
{
|
2010-10-09 18:19:58 +00:00
|
|
|
if(pResult->NumArguments() > 1)
|
|
|
|
{
|
|
|
|
char aBuf[128];
|
2010-11-25 15:16:52 +00:00
|
|
|
str_format(aBuf, sizeof(aBuf), "Kicked (%s)", pResult->GetString(1));
|
2010-10-09 18:19:58 +00:00
|
|
|
((CServer *)pUser)->Kick(pResult->GetInteger(0), aBuf);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
((CServer *)pUser)->Kick(pResult->GetInteger(0), "Kicked by console");
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CServer::ConStatus(IConsole::IResult *pResult, void *pUser)
|
|
|
|
{
|
|
|
|
char aBuf[1024];
|
2011-03-28 18:11:28 +00:00
|
|
|
char aAddrStr[NETADDR_MAXSTRSIZE];
|
2011-12-29 22:36:53 +00:00
|
|
|
CServer* pThis = static_cast<CServer *>(pUser);
|
2010-05-29 07:25:38 +00:00
|
|
|
|
2011-12-29 22:36:53 +00:00
|
|
|
for(int i = 0; i < MAX_CLIENTS; i++)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2011-12-29 22:36:53 +00:00
|
|
|
if(pThis->m_aClients[i].m_State != CClient::STATE_EMPTY)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2011-12-29 22:36:53 +00:00
|
|
|
net_addr_str(pThis->m_NetServer.ClientAddr(i), aAddrStr, sizeof(aAddrStr), true);
|
|
|
|
if(pThis->m_aClients[i].m_State == CClient::STATE_INGAME)
|
2011-03-28 18:11:28 +00:00
|
|
|
str_format(aBuf, sizeof(aBuf), "id=%d addr=%s name='%s' score=%d", i, aAddrStr,
|
2011-12-29 22:36:53 +00:00
|
|
|
pThis->m_aClients[i].m_aName, pThis->m_aClients[i].m_Score);
|
2010-06-03 12:48:32 +00:00
|
|
|
else
|
2011-03-28 18:11:28 +00:00
|
|
|
str_format(aBuf, sizeof(aBuf), "id=%d addr=%s connecting", i, aAddrStr);
|
2011-12-29 22:36:53 +00:00
|
|
|
pThis->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "Server", aBuf);
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CServer::ConShutdown(IConsole::IResult *pResult, void *pUser)
|
|
|
|
{
|
|
|
|
((CServer *)pUser)->m_RunServer = 0;
|
|
|
|
}
|
|
|
|
|
2011-07-22 21:17:16 +00:00
|
|
|
void CServer::DemoRecorder_HandleAutoStart()
|
|
|
|
{
|
|
|
|
if(g_Config.m_SvAutoDemoRecord)
|
|
|
|
{
|
|
|
|
m_DemoRecorder.Stop();
|
|
|
|
char aFilename[128];
|
|
|
|
char aDate[20];
|
|
|
|
str_timestamp(aDate, sizeof(aDate));
|
|
|
|
str_format(aFilename, sizeof(aFilename), "demos/%s_%s.demo", "auto/autorecord", aDate);
|
|
|
|
m_DemoRecorder.Start(Storage(), m_pConsole, aFilename, GameServer()->NetVersion(), m_aCurrentMap, m_CurrentMapCrc, "server");
|
|
|
|
if(g_Config.m_SvAutoDemoMax)
|
|
|
|
{
|
|
|
|
// clean up auto recorded demos
|
|
|
|
CFileCollection AutoDemos;
|
|
|
|
AutoDemos.Init(Storage(), "demos/server", "autorecord", ".demo", g_Config.m_SvAutoDemoMax);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
void CServer::ConRecord(IConsole::IResult *pResult, void *pUser)
|
|
|
|
{
|
2010-12-07 23:42:32 +00:00
|
|
|
CServer* pServer = (CServer *)pUser;
|
|
|
|
char aFilename[128];
|
|
|
|
|
|
|
|
if(pResult->NumArguments())
|
|
|
|
str_format(aFilename, sizeof(aFilename), "demos/%s.demo", pResult->GetString(0));
|
|
|
|
else
|
|
|
|
{
|
|
|
|
char aDate[20];
|
|
|
|
str_timestamp(aDate, sizeof(aDate));
|
|
|
|
str_format(aFilename, sizeof(aFilename), "demos/demo_%s.demo", aDate);
|
|
|
|
}
|
|
|
|
pServer->m_DemoRecorder.Start(pServer->Storage(), pServer->Console(), aFilename, pServer->GameServer()->NetVersion(), pServer->m_aCurrentMap, pServer->m_CurrentMapCrc, "server");
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CServer::ConStopRecord(IConsole::IResult *pResult, void *pUser)
|
|
|
|
{
|
|
|
|
((CServer *)pUser)->m_DemoRecorder.Stop();
|
|
|
|
}
|
|
|
|
|
2010-05-30 12:01:11 +00:00
|
|
|
void CServer::ConMapReload(IConsole::IResult *pResult, void *pUser)
|
|
|
|
{
|
|
|
|
((CServer *)pUser)->m_MapReload = 1;
|
|
|
|
}
|
|
|
|
|
2011-12-26 21:07:57 +00:00
|
|
|
void CServer::ConLogout(IConsole::IResult *pResult, void *pUser)
|
|
|
|
{
|
|
|
|
CServer *pServer = (CServer *)pUser;
|
|
|
|
|
2011-12-29 23:07:17 +00:00
|
|
|
if(pServer->m_RconClientID >= 0 && pServer->m_RconClientID < MAX_CLIENTS &&
|
|
|
|
pServer->m_aClients[pServer->m_RconClientID].m_State != CServer::CClient::STATE_EMPTY)
|
|
|
|
{
|
|
|
|
CMsgPacker Msg(NETMSG_RCON_AUTH_STATUS);
|
|
|
|
Msg.AddInt(0); //authed
|
|
|
|
Msg.AddInt(0); //cmdlist
|
|
|
|
pServer->SendMsgEx(&Msg, MSGFLAG_VITAL, pServer->m_RconClientID, true);
|
|
|
|
|
|
|
|
pServer->m_aClients[pServer->m_RconClientID].m_Authed = AUTHED_NO;
|
|
|
|
pServer->m_aClients[pServer->m_RconClientID].m_pRconCmdToSend = 0;
|
|
|
|
pServer->SendRconLine(pServer->m_RconClientID, "Logout successful.");
|
|
|
|
char aBuf[32];
|
|
|
|
str_format(aBuf, sizeof(aBuf), "ClientID=%d logged out", pServer->m_RconClientID);
|
|
|
|
pServer->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf);
|
|
|
|
}
|
2011-12-26 21:07:57 +00:00
|
|
|
}
|
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
void CServer::ConchainSpecialInfoupdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData)
|
|
|
|
{
|
|
|
|
pfnCallback(pResult, pCallbackUserData);
|
|
|
|
if(pResult->NumArguments())
|
|
|
|
((CServer *)pUserData)->UpdateServerInfo();
|
|
|
|
}
|
|
|
|
|
2010-06-03 12:48:32 +00:00
|
|
|
void CServer::ConchainMaxclientsperipUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData)
|
|
|
|
{
|
|
|
|
pfnCallback(pResult, pCallbackUserData);
|
|
|
|
if(pResult->NumArguments())
|
|
|
|
((CServer *)pUserData)->m_NetServer.SetMaxClientsPerIP(pResult->GetInteger(0));
|
|
|
|
}
|
|
|
|
|
2011-07-14 20:07:21 +00:00
|
|
|
void CServer::ConchainModCommandUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData)
|
|
|
|
{
|
|
|
|
if(pResult->NumArguments() == 2)
|
|
|
|
{
|
|
|
|
CServer *pThis = static_cast<CServer *>(pUserData);
|
|
|
|
const IConsole::CCommandInfo *pInfo = pThis->Console()->GetCommandInfo(pResult->GetString(0), CFGFLAG_SERVER, false);
|
2011-08-01 10:11:10 +00:00
|
|
|
int OldAccessLevel = 0;
|
2011-07-14 20:07:21 +00:00
|
|
|
if(pInfo)
|
|
|
|
OldAccessLevel = pInfo->GetAccessLevel();
|
|
|
|
pfnCallback(pResult, pCallbackUserData);
|
|
|
|
if(pInfo && OldAccessLevel != pInfo->GetAccessLevel())
|
|
|
|
{
|
|
|
|
for(int i = 0; i < MAX_CLIENTS; ++i)
|
|
|
|
{
|
|
|
|
if(pThis->m_aClients[i].m_State == CServer::CClient::STATE_EMPTY || pThis->m_aClients[i].m_Authed != CServer::AUTHED_MOD ||
|
|
|
|
(pThis->m_aClients[i].m_pRconCmdToSend && str_comp(pResult->GetString(0), pThis->m_aClients[i].m_pRconCmdToSend->m_pName) >= 0))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if(OldAccessLevel == IConsole::ACCESS_LEVEL_ADMIN)
|
|
|
|
pThis->SendRconCmdAdd(pInfo, i);
|
|
|
|
else
|
|
|
|
pThis->SendRconCmdRem(pInfo, i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
pfnCallback(pResult, pCallbackUserData);
|
|
|
|
}
|
|
|
|
|
2011-07-30 11:40:01 +00:00
|
|
|
void CServer::ConchainConsoleOutputLevelUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData)
|
|
|
|
{
|
|
|
|
pfnCallback(pResult, pCallbackUserData);
|
|
|
|
if(pResult->NumArguments() == 1)
|
|
|
|
{
|
|
|
|
CServer *pThis = static_cast<CServer *>(pUserData);
|
|
|
|
pThis->Console()->SetPrintOutputLevel(pThis->m_PrintCBIndex, pResult->GetInteger(0));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
void CServer::RegisterCommands()
|
|
|
|
{
|
|
|
|
m_pConsole = Kernel()->RequestInterface<IConsole>();
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2011-07-14 20:07:21 +00:00
|
|
|
Console()->Register("kick", "i?r", CFGFLAG_SERVER, ConKick, this, "Kick player with specified id for any reason");
|
|
|
|
Console()->Register("status", "", CFGFLAG_SERVER, ConStatus, this, "List players");
|
|
|
|
Console()->Register("shutdown", "", CFGFLAG_SERVER, ConShutdown, this, "Shut down");
|
2011-12-29 23:07:17 +00:00
|
|
|
Console()->Register("logout", "", CFGFLAG_SERVER, ConLogout, this, "Logout of rcon");
|
2010-05-29 07:25:38 +00:00
|
|
|
|
2011-07-14 20:07:21 +00:00
|
|
|
Console()->Register("record", "?s", CFGFLAG_SERVER|CFGFLAG_STORE, ConRecord, this, "Record to a file");
|
|
|
|
Console()->Register("stoprecord", "", CFGFLAG_SERVER, ConStopRecord, this, "Stop recording");
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2011-07-14 20:07:21 +00:00
|
|
|
Console()->Register("reload", "", CFGFLAG_SERVER, ConMapReload, this, "Reload the map");
|
2010-05-29 07:25:38 +00:00
|
|
|
|
|
|
|
Console()->Chain("sv_name", ConchainSpecialInfoupdate, this);
|
|
|
|
Console()->Chain("password", ConchainSpecialInfoupdate, this);
|
2010-06-03 12:48:32 +00:00
|
|
|
|
|
|
|
Console()->Chain("sv_max_clients_per_ip", ConchainMaxclientsperipUpdate, this);
|
2011-07-14 20:07:21 +00:00
|
|
|
Console()->Chain("mod_command", ConchainModCommandUpdate, this);
|
2011-07-30 11:40:01 +00:00
|
|
|
Console()->Chain("console_output_level", ConchainConsoleOutputLevelUpdate, this);
|
2011-04-13 18:37:12 +00:00
|
|
|
}
|
2010-05-29 07:25:38 +00:00
|
|
|
|
|
|
|
|
|
|
|
int CServer::SnapNewID()
|
|
|
|
{
|
|
|
|
return m_IDPool.NewID();
|
|
|
|
}
|
|
|
|
|
|
|
|
void CServer::SnapFreeID(int ID)
|
|
|
|
{
|
|
|
|
m_IDPool.FreeID(ID);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-02-12 10:40:36 +00:00
|
|
|
void *CServer::SnapNewItem(int Type, int ID, int Size)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
|
|
|
dbg_assert(Type >= 0 && Type <=0xffff, "incorrect type");
|
2011-02-12 10:40:36 +00:00
|
|
|
dbg_assert(ID >= 0 && ID <=0xffff, "incorrect id");
|
2011-04-13 18:37:12 +00:00
|
|
|
return ID < 0 ? 0 : m_SnapshotBuilder.NewItem(Type, ID, Size);
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CServer::SnapSetStaticsize(int ItemType, int Size)
|
|
|
|
{
|
|
|
|
m_SnapshotDelta.SetStaticsize(ItemType, Size);
|
|
|
|
}
|
|
|
|
|
|
|
|
static CServer *CreateServer() { return new CServer(); }
|
|
|
|
|
|
|
|
int main(int argc, const char **argv) // ignore_convention
|
|
|
|
{
|
|
|
|
#if defined(CONF_FAMILY_WINDOWS)
|
|
|
|
for(int i = 1; i < argc; i++) // ignore_convention
|
|
|
|
{
|
|
|
|
if(str_comp("-s", argv[i]) == 0 || str_comp("--silent", argv[i]) == 0) // ignore_convention
|
|
|
|
{
|
|
|
|
ShowWindow(GetConsoleWindow(), SW_HIDE);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
CServer *pServer = CreateServer();
|
|
|
|
IKernel *pKernel = IKernel::Create();
|
|
|
|
|
|
|
|
// create the components
|
2011-02-27 14:03:57 +00:00
|
|
|
IEngine *pEngine = CreateEngine("Teeworlds");
|
2010-05-29 07:25:38 +00:00
|
|
|
IEngineMap *pEngineMap = CreateEngineMap();
|
|
|
|
IGameServer *pGameServer = CreateGameServer();
|
2011-12-30 18:12:31 +00:00
|
|
|
IConsole *pConsole = CreateConsole(CFGFLAG_SERVER|CFGFLAG_ECON);
|
2010-05-29 07:25:38 +00:00
|
|
|
IEngineMasterServer *pEngineMasterServer = CreateEngineMasterServer();
|
2010-08-05 18:26:03 +00:00
|
|
|
IStorage *pStorage = CreateStorage("Teeworlds", argc, argv); // ignore_convention
|
2010-05-29 07:25:38 +00:00
|
|
|
IConfig *pConfig = CreateConfig();
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-08-17 22:06:00 +00:00
|
|
|
pServer->InitRegister(&pServer->m_NetServer, pEngineMasterServer, pConsole);
|
2010-05-29 07:25:38 +00:00
|
|
|
|
|
|
|
{
|
|
|
|
bool RegisterFail = false;
|
|
|
|
|
|
|
|
RegisterFail = RegisterFail || !pKernel->RegisterInterface(pServer); // register as both
|
2011-02-27 14:03:57 +00:00
|
|
|
RegisterFail = RegisterFail || !pKernel->RegisterInterface(pEngine);
|
2010-05-29 07:25:38 +00:00
|
|
|
RegisterFail = RegisterFail || !pKernel->RegisterInterface(static_cast<IEngineMap*>(pEngineMap)); // register as both
|
|
|
|
RegisterFail = RegisterFail || !pKernel->RegisterInterface(static_cast<IMap*>(pEngineMap));
|
|
|
|
RegisterFail = RegisterFail || !pKernel->RegisterInterface(pGameServer);
|
|
|
|
RegisterFail = RegisterFail || !pKernel->RegisterInterface(pConsole);
|
|
|
|
RegisterFail = RegisterFail || !pKernel->RegisterInterface(pStorage);
|
|
|
|
RegisterFail = RegisterFail || !pKernel->RegisterInterface(pConfig);
|
|
|
|
RegisterFail = RegisterFail || !pKernel->RegisterInterface(static_cast<IEngineMasterServer*>(pEngineMasterServer)); // register as both
|
|
|
|
RegisterFail = RegisterFail || !pKernel->RegisterInterface(static_cast<IMasterServer*>(pEngineMasterServer));
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
if(RegisterFail)
|
|
|
|
return -1;
|
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2011-03-05 10:46:24 +00:00
|
|
|
pEngine->Init();
|
2010-05-29 07:25:38 +00:00
|
|
|
pConfig->Init();
|
2011-02-27 14:03:57 +00:00
|
|
|
pEngineMasterServer->Init();
|
2010-05-29 07:25:38 +00:00
|
|
|
pEngineMasterServer->Load();
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
// register all console commands
|
|
|
|
pServer->RegisterCommands();
|
|
|
|
pGameServer->OnConsoleInit();
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
// execute autoexec file
|
|
|
|
pConsole->ExecuteFile("autoexec.cfg");
|
|
|
|
|
|
|
|
// parse the command line arguments
|
|
|
|
if(argc > 1) // ignore_convention
|
|
|
|
pConsole->ParseArguments(argc-1, &argv[1]); // ignore_convention
|
2010-08-10 22:31:42 +00:00
|
|
|
|
|
|
|
// restore empty config strings to their defaults
|
|
|
|
pConfig->RestoreStrings();
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2011-02-27 14:03:57 +00:00
|
|
|
pEngine->InitLogfile();
|
2010-08-06 18:38:13 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
// run the server
|
2011-02-27 14:03:57 +00:00
|
|
|
dbg_msg("server", "starting...");
|
2010-05-29 07:25:38 +00:00
|
|
|
pServer->Run();
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
// free
|
|
|
|
delete pServer;
|
|
|
|
delete pKernel;
|
|
|
|
delete pEngineMap;
|
|
|
|
delete pGameServer;
|
|
|
|
delete pConsole;
|
|
|
|
delete pEngineMasterServer;
|
|
|
|
delete pStorage;
|
|
|
|
delete pConfig;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|