ddnet/src/engine/server/server.cpp

2013 lines
58 KiB
C++
Raw Normal View History

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
#include <base/system.h>
#include <engine/shared/config.h>
#include <engine/shared/engine.h>
#include <engine/shared/protocol.h>
#include <engine/shared/snapshot.h>
#include <engine/shared/compression.h>
#include <engine/shared/network.h>
#include <engine/shared/config.h>
#include <engine/shared/packer.h>
#include <engine/shared/datafile.h>
2010-09-12 10:16:51 +00:00
#include <engine/shared/demo.h>
2010-05-29 07:25:38 +00:00
#include <engine/server.h>
#include <engine/map.h>
#include <engine/console.h>
#include <engine/storage.h>
#include <engine/masterserver.h>
#include <engine/config.h>
#include <mastersrv/mastersrv.h>
2010-10-10 14:19:10 +00:00
#include <string.h>
2011-02-07 06:25:38 +00:00
#include <banmaster/banmaster.h>
2010-05-29 07:25:38 +00:00
#include "register.h"
#include "server.h"
2010-11-22 10:59:25 +00:00
#include "../shared/linereader.h"
#include <vector>
2010-05-29 07:25:38 +00:00
#if defined(CONF_FAMILY_WINDOWS)
2010-05-29 07:25:38 +00:00
#define _WIN32_WINNT 0x0500
#define NOGDI
#include <windows.h>
#endif
2011-03-13 17:47:21 +00:00
#include <base/utf8convert.h>
2010-05-29 07:25:38 +00:00
2011-02-07 06:25:38 +00:00
static const char SERVER_BANMASTERFILE[] = "banmasters.cfg";
2010-05-29 07:25:38 +00:00
static const char *StrLtrim(const char *pStr)
{
2010-08-12 14:12:25 +00:00
while(*pStr && *pStr >= 0 && *pStr <= 32)
2010-05-29 07:25:38 +00:00
pStr++;
return pStr;
}
static void StrRtrim(char *pStr)
{
int i = str_length(pStr);
while(i >= 0)
{
2010-08-12 14:12:25 +00:00
if(pStr[i] < 0 || pStr[i] > 32)
2010-05-29 07:25:38 +00:00
break;
pStr[i] = 0;
i--;
}
}
static int StrAllnum(const char *pStr)
{
while(*pStr)
{
if(!(*pStr >= '0' && *pStr <= '9'))
return 0;
pStr++;
}
return 1;
}
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;
}
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;
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;
2010-05-29 07:25:38 +00:00
// remove it from the timed list
m_FirstTimed = NextTimed;
if(m_FirstTimed == -1)
m_LastTimed = -1;
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();
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;
m_aIDs[ID].m_State = 1;
2010-05-29 07:25:38 +00:00
m_Usage++;
m_InUsage++;
return ID;
2010-05-29 07:25:38 +00:00
}
void CSnapIDPool::TimeoutIDs()
{
// process timed ids
while(m_FirstTimed != -1)
RemoveFirstTimeout();
}
void CSnapIDPool::FreeID(int ID)
2010-05-29 07:25:38 +00:00
{
if(ID < 0)
return;
dbg_assert(m_aIDs[ID].m_State == 1, "id is not alloced");
2010-05-29 07:25:38 +00:00
m_InUsage--;
m_aIDs[ID].m_State = 2;
m_aIDs[ID].m_Timeout = time_get()+time_freq()*5;
m_aIDs[ID].m_Next = -1;
2010-05-29 07:25:38 +00:00
if(m_LastTimed != -1)
{
m_aIDs[m_LastTimed].m_Next = ID;
m_LastTimed = ID;
2010-05-29 07:25:38 +00:00
}
else
{
m_FirstTimed = ID;
m_LastTimed = ID;
2010-05-29 07:25:38 +00:00
}
}
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;
2010-05-29 07:25:38 +00:00
m_pGameServer = 0;
2010-05-29 07:25:38 +00:00
m_CurrentGameTick = 0;
m_RunServer = 1;
mem_zero(m_aBrowseinfoGametype, sizeof(m_aBrowseinfoGametype));
m_BrowseinfoProgression = -1;
m_pCurrentMapData = 0;
m_CurrentMapSize = 0;
m_MapReload = 0;
2010-05-29 07:25:38 +00:00
memset(m_aPrevStates, CClient::STATE_EMPTY, MAX_CLIENTS * sizeof(int));
m_RconClientID = -1;
2010-05-29 07:25:38 +00:00
Init();
}
int CServer::TrySetClientName(int ClientID, const char *pName)
{
char aTrimmedName[64];
// trim the name
str_copy(aTrimmedName, StrLtrim(pName), sizeof(aTrimmedName));
StrRtrim(aTrimmedName);
2011-01-06 03:46:10 +00:00
char aBuf[256];
str_format(aBuf, sizeof(aBuf), "'%s' -> '%s'", pName, aTrimmedName);
2011-03-13 17:47:21 +00:00
Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "server", aBuf);
2010-05-29 07:25:38 +00:00
// check for empty names
if(!pName[0])
return -1;
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;
2010-05-29 07:25:38 +00:00
if(!pName)
return;
2011-03-13 17:47:21 +00:00
// dirty hack that does only work if client uses special chars
// otherwise its always utf8
char aNameUTF8[MAX_NAME_LENGTH];
if (str_utf8_check(pName))
{
str_copy(aNameUTF8,pName,MAX_NAME_LENGTH);
m_aClients[ClientID].m_IsUsingUTF8Client = true;
dbg_msg("Server","Client uses UTF8");
}
else
{
Latin1toUTF8(aNameUTF8,pName,MAX_NAME_LENGTH);
m_aClients[ClientID].m_IsUsingUTF8Client = false;
dbg_msg("Server","Client uses Latin");
}
2010-05-29 07:25:38 +00:00
char aNameTry[MAX_NAME_LENGTH];
2011-03-13 17:47:21 +00:00
str_copy(aNameTry, aNameUTF8, MAX_NAME_LENGTH);
2010-05-29 07:25:38 +00:00
if(TrySetClientName(ClientID, aNameTry))
{
// auto rename
2011-03-13 17:47:21 +00:00
for(int i = 1,j = 0;; i++)
2010-05-29 07:25:38 +00:00
{
2011-03-13 17:47:21 +00:00
if (i>9)
j = 1;
if (aNameUTF8[16-j]&0xF0)
aNameUTF8[16-j]='\0';
else if (aNameUTF8[17-j]&0xE0)
aNameUTF8[17-j]='\0';
else if (aNameUTF8[18-j]&0xC0)
aNameUTF8[18-j]='\0';
else if (aNameUTF8[19-j]&0x80)
aNameUTF8[19-j]='\0';
// cut of x-th char if it is utf8 and length would exceed the following chopping length (chopped by str_format)
// if we wouldn't do that our utf8 char consisting of maybe 2 bytes would suddenly be a 1 byte fragment
str_format(aNameTry, MAX_NAME_LENGTH, "(%d)%s", i, aNameUTF8);
2010-05-29 07:25:38 +00:00
if(TrySetClientName(ClientID, aNameTry) == 0)
break;
}
}
}
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::SetBrowseInfo(const char *pGameType, int Progression)
{
str_copy(m_aBrowseinfoGametype, pGameType, sizeof(m_aBrowseinfoGametype));
m_BrowseinfoProgression = Progression;
if(m_BrowseinfoProgression > 100)
m_BrowseinfoProgression = 100;
if(m_BrowseinfoProgression < -1)
m_BrowseinfoProgression = -1;
}
void CServer::Kick(int ClientID, const char *pReason)
{
if(ClientID < 0 || ClientID >= MAX_CLIENTS || m_aClients[ClientID].m_State == CClient::STATE_EMPTY)
{
Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", "invalid client id to kick");
2010-05-29 07:25:38 +00:00
return;
}
else if(m_RconClientID == ClientID)
{
Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", "you can't kick yourself");
return;
}
2010-05-29 07:25:38 +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;
m_aClients[i].m_Snapshots.Init();
}
m_CurrentGameTick = 0;
2010-12-06 02:27:35 +00:00
m_AnnouncementLastLine = 0;
2010-05-29 07:25:38 +00:00
return 0;
}
int CServer::IsAuthed(int ClientID)
{
return m_aClients[ClientID].m_Authed;
}
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;
}
void CServer::GetClientIP(int ClientID, char *pIPString, int Size)
{
if(ClientID >= 0 && ClientID < MAX_CLIENTS && m_aClients[ClientID].m_State == CClient::STATE_INGAME)
{
NETADDR Addr = m_NetServer.ClientAddr(ClientID);
str_format(pIPString, Size, "%d.%d.%d.%d", Addr.ip[0], Addr.ip[1], Addr.ip[2], Addr.ip[3]);
}
}
2011-02-23 21:39:53 +00:00
void CServer::GetClientAddr(int ClientID, NETADDR *pAddr)
{
if(ClientID >= 0 && ClientID < MAX_CLIENTS && m_aClients[ClientID].m_State == CClient::STATE_INGAME)
*pAddr = m_NetServer.ClientAddr(ClientID);
}
int *CServer::LatestInput(int ClientID, int *size)
2010-05-29 07:25:38 +00:00
{
if(ClientID < 0 || ClientID >= MAX_CLIENTS || m_aClients[ClientID].m_State < CServer::CClient::STATE_READY)
2010-05-29 07:25:38 +00:00
return 0;
return m_aClients[ClientID].m_LatestInput.m_aData;
2010-05-29 07:25:38 +00:00
}
const char *CServer::ClientName(int ClientID)
2010-05-29 07:25:38 +00:00
{
if(ClientID < 0 || ClientID >= MAX_CLIENTS || m_aClients[ClientID].m_State == CServer::CClient::STATE_EMPTY)
2010-05-29 07:25:38 +00:00
return "(invalid client)";
else if(m_aClients[ClientID].m_State < CServer::CClient::STATE_READY)
return "(connecting client)";
return m_aClients[ClientID].m_aName;
}
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;
}
int CServer::SendMsg(CMsgPacker *pMsg, int Flags, int ClientID)
2010-05-29 07:25:38 +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;
2010-05-29 07:25:38 +00:00
mem_zero(&Packet, sizeof(CNetChunk));
2010-05-29 07:25:38 +00:00
Packet.m_ClientID = ClientID;
Packet.m_pData = pMsg->Data();
Packet.m_DataSize = pMsg->Size();
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;
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();
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);
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;
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;
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;
2010-05-29 07:25:38 +00:00
{
char aData[CSnapshot::MAX_SIZE];
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
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);
2010-05-29 07:25:38 +00:00
// save it the snapshot
m_aClients[i].m_Snapshots.Add(m_CurrentGameTick, time_get(), SnapshotSize, pData, 0);
2010-05-29 07:25:38 +00:00
// find snapshot that we can preform delta against
EmptySnap.Clear();
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;
}
}
2010-05-29 07:25:38 +00:00
// create delta
DeltaSize = m_SnapshotDelta.CreateDelta(pDeltashot, pData, aDeltaData);
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;
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);
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();
}
int CServer::NewClientCallback(int ClientID, void *pUser)
2010-05-29 07:25:38 +00:00
{
CServer *pThis = (CServer *)pUser;
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;
pThis->m_aClients[ClientID].m_Authed = 0;
pThis->m_aClients[ClientID].m_AuthTries = 0;
memset(&pThis->m_aClients[ClientID].m_Addr, 0, sizeof(NETADDR));
pThis->m_aClients[ClientID].Reset();
2010-05-29 07:25:38 +00:00
return 0;
}
int CServer::DelClientCallback(int ClientID, const char *pReason, void *pUser)
2010-05-29 07:25:38 +00:00
{
CServer *pThis = (CServer *)pUser;
NETADDR Addr = pThis->m_NetServer.ClientAddr(ClientID);
char aBuf[256];
str_format(aBuf, sizeof(aBuf), "client dropped. cid=%d ip=%d.%d.%d.%d reason=\"%s\"",
ClientID,
Addr.ip[0], Addr.ip[1], Addr.ip[2], Addr.ip[3],
pReason
);
pThis->Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "server", aBuf);
2010-05-29 07:25:38 +00:00
// notify the mod about the drop
if(pThis->m_aClients[ClientID].m_State >= CClient::STATE_READY)
pThis->GameServer()->OnClientDrop(ClientID);
2010-05-29 07:25:38 +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;
pThis->m_aClients[ClientID].m_Authed = 0;
pThis->m_aClients[ClientID].m_AuthTries = 0;
pThis->m_aPrevStates[ClientID] = CClient::STATE_EMPTY;
memset(&pThis->m_aClients[ClientID].m_Addr, 0, sizeof(NETADDR));
pThis->m_aClients[ClientID].m_Snapshots.PurgeAll();
2010-05-29 07:25:38 +00:00
return 0;
}
void CServer::SendMap(int ClientID)
2010-05-29 07:25:38 +00:00
{
CMsgPacker Msg(NETMSG_MAP_CHANGE);
Msg.AddString(GetMapName(), 0);
2010-05-29 07:25:38 +00:00
Msg.AddInt(m_CurrentMapCrc);
SendMsgEx(&Msg, MSGFLAG_VITAL|MSGFLAG_FLUSH, ClientID, true);
2010-05-29 07:25:38 +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);
SendMsgEx(&Msg, MSGFLAG_VITAL, ClientID, true);
2010-05-29 07:25:38 +00:00
}
void CServer::SendRconLineAuthed(const char *pLine, void *pUser)
{
CServer *pThis = (CServer *)pUser;
static volatile int ReentryGuard = 0;
int i;
2010-05-29 07:25:38 +00:00
if(ReentryGuard) return;
ReentryGuard++;
2010-05-29 07:25:38 +00:00
for(i = 0; i < MAX_CLIENTS; i++)
{
if(pThis->m_aClients[i].m_State != CClient::STATE_EMPTY && pThis->m_aClients[i].m_Authed)
pThis->SendRconLine(i, pLine);
}
2010-05-29 07:25:38 +00:00
ReentryGuard--;
}
void CServer::ProcessClientPacket(CNetChunk *pPacket)
{
int ClientID = pPacket->m_ClientID;
2010-05-29 07:25:38 +00:00
NETADDR Addr;
CUnpacker Unpacker;
Unpacker.Reset(pPacket->m_pData, pPacket->m_DataSize);
2010-05-29 07:25:38 +00:00
// unpack msgid and system flag
int Msg = Unpacker.GetInt();
int Sys = Msg&1;
Msg >>= 1;
2010-05-29 07:25:38 +00:00
if(Unpacker.Error())
return;
if(m_aClients[ClientID].m_State == CClient::STATE_AUTH)
2010-05-29 07:25:38 +00:00
{
if(Sys && Msg == NETMSG_INFO)
{
char aVersion[64];
const char *pPassword;
str_copy(aVersion, Unpacker.GetString(CUnpacker::SANITIZE_CC), 64);
2010-05-29 07:25:38 +00:00
if(str_comp(aVersion, GameServer()->NetVersion()) != 0)
{
// OH FUCK! wrong version, drop him
char aReason[256];
2010-10-10 23:06:44 +00:00
str_format(aReason, sizeof(aReason), "Wrong version. Server is running '%s' and client '%s'", GameServer()->NetVersion(), aVersion);
m_NetServer.Drop(ClientID, aReason);
2010-05-29 07:25:38 +00:00
return;
}
str_copy(m_aClients[ClientID].m_aName, Unpacker.GetString(CUnpacker::SANITIZE_CC|CUnpacker::SKIP_START_WHITESPACES), MAX_NAME_LENGTH);
str_copy(m_aClients[ClientID].m_aClan, Unpacker.GetString(CUnpacker::SANITIZE_CC|CUnpacker::SKIP_START_WHITESPACES), MAX_CLANNAME_LENGTH);
pPassword = Unpacker.GetString(CUnpacker::SANITIZE_CC);
2010-05-29 07:25:38 +00:00
if(g_Config.m_Password[0] != 0 && str_comp(g_Config.m_Password, pPassword) != 0)
{
// wrong password
m_NetServer.Drop(ClientID, "Wrong password");
2010-05-29 07:25:38 +00:00
return;
}
// reserved slot
if(ClientID >= (g_Config.m_SvMaxClients-g_Config.m_SvReservedSlots) && g_Config.m_SvReservedSlotsPass[0] != 0 && strcmp(g_Config.m_SvReservedSlotsPass, pPassword) != 0)
2010-10-11 01:50:43 +00:00
{
m_NetServer.Drop(ClientID, "This server is full");
2010-10-11 01:50:43 +00:00
return;
}
m_aClients[ClientID].m_State = CClient::STATE_CONNECTING;
SendMap(ClientID);
2010-05-29 07:25:38 +00:00
}
}
else
{
if(Sys)
2010-05-29 07:25:38 +00:00
{
// system message
if(Msg == NETMSG_REQUEST_MAP_DATA)
2010-07-29 19:55:33 +00:00
{
int Chunk = Unpacker.GetInt();
int ChunkSize = 1024-128;
int Offset = Chunk * ChunkSize;
int Last = 0;
// drop faulty map data requests
if(Chunk < 0 || Offset > m_CurrentMapSize)
return;
if(Offset+ChunkSize >= m_CurrentMapSize)
{
ChunkSize = m_CurrentMapSize-Offset;
if(ChunkSize < 0)
ChunkSize = 0;
Last = 1;
}
CMsgPacker Msg(NETMSG_MAP_DATA);
Msg.AddInt(Last);
Msg.AddInt(m_CurrentMapSize);
Msg.AddInt(ChunkSize);
Msg.AddRaw(&m_pCurrentMapData[Offset], ChunkSize);
SendMsgEx(&Msg, MSGFLAG_VITAL|MSGFLAG_FLUSH, ClientID, true);
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-07-29 19:55:33 +00:00
}
}
else if(Msg == NETMSG_READY)
2010-05-29 07:25:38 +00:00
{
if(m_aClients[ClientID].m_State == CClient::STATE_CONNECTING)
{
Addr = m_NetServer.ClientAddr(ClientID);
char aBuf[256];
str_format(aBuf, sizeof(aBuf), "player is ready. ClientID=%x ip=%d.%d.%d.%d",
ClientID, Addr.ip[0], Addr.ip[1], Addr.ip[2], Addr.ip[3]);
Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "server", aBuf);
m_aClients[ClientID].m_State = CClient::STATE_READY;
GameServer()->OnClientConnected(ClientID);
GameServer()->OnSetAuthed(ClientID, m_aClients[ClientID].m_Authed);
}
2010-05-29 07:25:38 +00:00
}
else if(Msg == NETMSG_ENTERGAME)
2010-05-29 07:25:38 +00:00
{
if(m_aClients[ClientID].m_State == CClient::STATE_READY)
{
Addr = m_NetServer.ClientAddr(ClientID);
m_aClients[ClientID].m_Addr = Addr;
char aBuf[256];
str_format(aBuf, sizeof(aBuf), "player has entered the game. ClientID=%x ip=%d.%d.%d.%d",
ClientID, Addr.ip[0], Addr.ip[1], Addr.ip[2], Addr.ip[3]);
Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf);
m_aClients[ClientID].m_State = CClient::STATE_INGAME;
GameServer()->OnClientEnter(ClientID);
}
}
else if(Msg == NETMSG_INPUT)
{
CClient::CInput *pInput;
int64 TagTime;
m_aClients[ClientID].m_LastAckedSnapshot = Unpacker.GetInt();
int IntendedTick = Unpacker.GetInt();
int Size = Unpacker.GetInt();
// check for errors
if(Unpacker.Error() || Size/4 > MAX_INPUT_SIZE)
return;
if(m_aClients[ClientID].m_LastAckedSnapshot > 0)
m_aClients[ClientID].m_SnapRate = CClient::SNAPRATE_FULL;
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());
// 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();
CMsgPacker Msg(NETMSG_INPUTTIMING);
Msg.AddInt(IntendedTick);
Msg.AddInt(TimeLeft);
SendMsgEx(&Msg, 0, ClientID, true);
2010-05-29 07:25:38 +00:00
}
m_aClients[ClientID].m_LastInputTick = IntendedTick;
pInput = &m_aClients[ClientID].m_aInputs[m_aClients[ClientID].m_CurrentInput];
if(IntendedTick <= Tick())
IntendedTick = Tick()+1;
pInput->m_GameTick = IntendedTick;
for(int i = 0; i < Size/4; i++)
pInput->m_aData[i] = Unpacker.GetInt();
mem_copy(m_aClients[ClientID].m_LatestInput.m_aData, pInput->m_aData, MAX_INPUT_SIZE*sizeof(int));
m_aClients[ClientID].m_CurrentInput++;
m_aClients[ClientID].m_CurrentInput %= 200;
// 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);
2010-05-29 07:25:38 +00:00
}
else if(Msg == NETMSG_RCON_CMD)
{
const char *pCmd = Unpacker.GetString();
if(Unpacker.Error() == 0/* && m_aClients[ClientID].m_Authed*/)
2010-05-29 07:25:38 +00:00
{
char aBuf[256];
if(m_aClients[ClientID].m_Authed >= 0)
2010-05-29 07:25:38 +00:00
{
Console()->RegisterAlternativePrintCallback(0, 0);
str_format(aBuf, sizeof(aBuf), "'%s' ClientID=%d Level=%d Rcon='%s'", ClientName(ClientID), ClientID, m_aClients[ClientID].m_Authed, pCmd);
Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "server", aBuf);
Console()->ReleaseAlternativePrintCallback();
m_RconClientID = ClientID;
RconResponseInfo Info;
Info.m_Server = this;
Info.m_ClientID = ClientID;
Console()->ExecuteLine(pCmd, m_aClients[ClientID].m_Authed, ClientID, SendRconLineAuthed, this, SendRconResponse, &Info);
m_RconClientID = -1;
}
}
}
else if(Msg == NETMSG_RCON_AUTH)
{
const char *pPw;
Unpacker.GetString(); // login name, not used
pPw = Unpacker.GetString(CUnpacker::SANITIZE_CC);
if(Unpacker.Error() == 0)
CheckPass(ClientID,pPw);
/*if(Unpacker.Error() == 0)
{
2010-10-11 01:50:43 +00:00
if(g_Config.m_SvRconPassword[0] == 0)
{
SendRconLine(ClientID, "No rcon password set on server. Set sv_rcon_password to enable the remote console.");
2010-10-11 01:50:43 +00:00
}
else if(str_comp(pPw, g_Config.m_SvRconPassword) == 0)
{
CMsgPacker Msg(NETMSG_RCON_AUTH_STATUS);
Msg.AddInt(1);
SendMsgEx(&Msg, MSGFLAG_VITAL, ClientID, true);
2010-10-11 01:50:43 +00:00
m_aClients[ClientID].m_Authed = 1;
SendRconLine(ClientID, "Authentication successful. Remote console access granted.");
2010-10-11 01:50:43 +00:00
char aBuf[256];
str_format(aBuf, sizeof(aBuf), "ClientID=%d authed", ClientID);
2010-10-11 01:50:43 +00:00
Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf);
}
else if(g_Config.m_SvRconMaxTries)
{
m_aClients[ClientID].m_AuthTries++;
2010-10-11 01:50:43 +00:00
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-10-11 01:50:43 +00:00
{
if(!g_Config.m_SvRconBantime)
m_NetServer.Drop(ClientID, "Too many remote console authentication tries");
2010-10-11 01:50:43 +00:00
else
{
NETADDR Addr = m_NetServer.ClientAddr(ClientID);
BanAdd(Addr, g_Config.m_SvRconBantime*60, "Too many remote console authentication tries");
2010-10-11 01:50:43 +00:00
}
}
}
else
{
SendRconLine(ClientID, "Wrong password.");
2010-10-11 01:50:43 +00:00
}
}*/
}
else if(Msg == NETMSG_PING)
{
CMsgPacker Msg(NETMSG_PING_REPLY);
SendMsgEx(&Msg, 0, ClientID, true);
}
else
{
2011-01-19 14:39:04 +00:00
if(g_Config.m_Debug)
2010-05-29 07:25:38 +00:00
{
2011-01-19 14:39:04 +00:00
char aHex[] = "0123456789ABCDEF";
char aBuf[512];
2011-01-19 14:39:04 +00:00
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);
2011-01-19 14:39:04 +00:00
Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "server", aBufMsg);
Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "server", aBuf);
}
2010-05-29 07:25:38 +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
}
}
2010-05-29 07:25:38 +00:00
}
2010-05-29 07:25:38 +00:00
void CServer::SendServerInfo(NETADDR *pAddr, int Token)
{
CNetChunk Packet;
CPacker p;
char aBuf[128];
// count the players
int PlayerCount = 0;
for(int i = 0; i < MAX_CLIENTS; i++)
{
if(m_aClients[i].m_State != CClient::STATE_EMPTY)
PlayerCount++;
}
2010-05-29 07:25:38 +00:00
p.Reset();
if(Token >= 0)
{
// new token based format
p.AddRaw(SERVERBROWSE_INFO, sizeof(SERVERBROWSE_INFO));
str_format(aBuf, sizeof(aBuf), "%d", Token);
p.AddString(aBuf, 6);
}
else
{
// old format
p.AddRaw(SERVERBROWSE_OLD_INFO, sizeof(SERVERBROWSE_OLD_INFO));
}
2010-05-29 07:25:38 +00:00
p.AddString(GameServer()->Version(), 32);
p.AddString(g_Config.m_SvName, 64);
p.AddString(GetMapName(), 32);
2010-05-29 07:25:38 +00:00
// gametype
p.AddString(m_aBrowseinfoGametype, 16);
// flags
int i = 0;
i |= SERVER_FLAG_VERSION;
2010-05-29 07:25:38 +00:00
if(g_Config.m_Password[0]) // password set
i |= SERVER_FLAG_PASSWORD;
2011-02-14 21:34:46 +00:00
if(g_Config.m_SvTeam == 1)
2011-02-05 01:14:17 +00:00
i |= SERVER_FLAG_TEAMS1;
2011-02-14 21:34:46 +00:00
else if(g_Config.m_SvTeam == 2)
2011-02-05 01:14:17 +00:00
i |= SERVER_FLAG_TEAMS2;
/*if(g_Config.m_SvTeamStrict)
i |= SERVER_FLAG_STRICTTEAMS;
if(g_Config.m_SvCheats)
i |= SERVER_FLAG_CHEATS;
if(g_Config.m_SvEndlessSuperHook)
i |= SERVER_FLAG_ENDLESSSUPERHOOKING;
if(g_Config.m_SvTimer)
i |= SERVER_FLAG_TIMERCOMMANDS;
if(g_Config.m_SvCheatTime && g_Config.m_SvCheats)
i |= SERVER_FLAG_CHEATTIME;
if(g_Config.m_SvPauseTime && g_Config.m_SvPauseable)
i |= SERVER_FLAG_PAUSETIME;*/
if(g_Config.m_SvPauseable)
i |= SERVER_FLAG_PAUSE;
if(GameServer()->PlayerCollision())
i |= SERVER_FLAG_PLAYERCOLLISION;
if(GameServer()->PlayerHooking())
i |= SERVER_FLAG_PLAYERHOOKING;
if(g_Config.m_SvEndlessDrag)
i |= SERVER_FLAG_ENDLESSHOOKING;
if(g_Config.m_SvHit)
i |= SERVER_FLAG_HIT;
if(g_Config.m_SvMapTest)
i |= SERVER_FLAG_MAPTEST;
if(g_Config.m_SvServerTest)
i |= SERVER_FLAG_SERVERTEST;
2011-02-05 01:14:17 +00:00
2010-05-29 07:25:38 +00:00
str_format(aBuf, sizeof(aBuf), "%d", i);
2011-02-05 02:37:31 +00:00
p.AddString(aBuf, 11);
2010-05-29 07:25:38 +00:00
// progression
str_format(aBuf, sizeof(aBuf), "%d", m_BrowseinfoProgression);
p.AddString(aBuf, 4);
2010-05-29 07:25:38 +00:00
str_format(aBuf, sizeof(aBuf), "%d", PlayerCount); p.AddString(aBuf, 3); // num players
str_format(aBuf, sizeof(aBuf), "%d", max(m_NetServer.MaxClients() - g_Config.m_SvReservedSlots, PlayerCount)); p.AddString(aBuf, 3); // max players
2010-05-29 07:25:38 +00:00
for(i = 0; i < MAX_CLIENTS; i++)
{
if(m_aClients[i].m_State != CClient::STATE_EMPTY)
{
p.AddString(ClientName(i), 48); // player name
2010-05-29 07:25:38 +00:00
str_format(aBuf, sizeof(aBuf), "%d", m_aClients[i].m_Score); p.AddString(aBuf, 6); // player score
}
}
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)
{
NETADDR Addr = m_NetServer.ClientAddr(i);
SendServerInfo(&Addr, -1); // SERVERBROWSE_OLD_INFO
}
}
}
int CServer::BanAdd(NETADDR Addr, int Seconds, const char *pReason)
2010-05-29 07:25:38 +00:00
{
Addr.port = 0;
char aAddrStr[128];
net_addr_str(&Addr, aAddrStr, sizeof(aAddrStr));
char aBuf[256];
if(Seconds)
str_format(aBuf, sizeof(aBuf), "banned %s for %d minutes", aAddrStr, Seconds/60);
else
str_format(aBuf, sizeof(aBuf), "banned %s for life", aAddrStr);
Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf);
return m_NetServer.BanAdd(Addr, Seconds, pReason);
2010-05-29 07:25:38 +00:00
}
int CServer::BanRemove(NETADDR Addr)
{
return m_NetServer.BanRemove(Addr);
}
2010-05-29 07:25:38 +00:00
void CServer::PumpNetwork()
{
CNetChunk Packet;
m_NetServer.Update();
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)]);
}
2010-05-29 07:25:38 +00:00
if(Packet.m_DataSize == sizeof(SERVERBROWSE_OLD_GETINFO) &&
mem_comp(Packet.m_pData, SERVERBROWSE_OLD_GETINFO, sizeof(SERVERBROWSE_OLD_GETINFO)) == 0)
{
SendServerInfo(&Packet.m_Address, -1);
}
2011-02-07 06:25:38 +00:00
2011-02-08 16:58:36 +00:00
if(Packet.m_DataSize >= (int)sizeof(BANMASTER_IPOK) &&
2011-02-07 06:25:38 +00:00
mem_comp(Packet.m_pData, BANMASTER_IPOK, sizeof(BANMASTER_IPOK)) == 0 &&
m_NetServer.BanmasterCheck(&Packet.m_Address) != -1)
{
}
2011-02-08 16:58:36 +00:00
if(Packet.m_DataSize >= (int)sizeof(BANMASTER_IPBAN) &&
2011-02-07 06:25:38 +00:00
mem_comp(Packet.m_pData, BANMASTER_IPBAN, sizeof(BANMASTER_IPBAN)) == 0 &&
g_Config.m_SvGlobalBantime &&
m_NetServer.BanmasterCheck(&Packet.m_Address) != -1)
{
CUnpacker Up;
char aIp[32];
char aReason[256];
NETADDR Addr;
Up.Reset((unsigned char*)Packet.m_pData + sizeof(BANMASTER_IPBAN), Packet.m_DataSize - sizeof(BANMASTER_IPBAN));
str_copy(aIp, Up.GetString(CUnpacker::SANITIZE_CC|CUnpacker::SKIP_START_WHITESPACES), sizeof(aIp));
str_copy(aReason, Up.GetString(CUnpacker::SANITIZE_CC|CUnpacker::SKIP_START_WHITESPACES), sizeof(aReason));
if (net_addr_from_str(&Addr, aIp))
{
dbg_msg("globalbans", "dropped weird message from banmaster");
return;
}
m_NetServer.BanAdd(Addr, g_Config.m_SvGlobalBantime * 60, aReason);
dbg_msg("globalbans", "added ban, ip=%d.%d.%d.%d, reason=\"%s\"", Addr.ip[0], Addr.ip[1], Addr.ip[2], Addr.ip[3], aReason);
}
2010-05-29 07:25:38 +00:00
}
}
else
ProcessClientPacket(&Packet);
}
}
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);
2010-05-29 07:25:38 +00:00
/*df = datafile_load(buf);
if(!df)
return 0;*/
2010-05-29 07:25:38 +00:00
if(!m_pMap->Load(aBuf))
return 0;
2010-05-29 07:25:38 +00:00
// stop recording when we change map
m_DemoRecorder.Stop();
2010-05-29 07:25:38 +00:00
// reinit snapshot ids
m_IDPool.TimeoutIDs();
2010-05-29 07:25:38 +00:00
// get the crc of the map
m_CurrentMapCrc = m_pMap->Crc();
char aBufMsg[256];
str_format(aBufMsg, sizeof(aBufMsg), "%s crc is %08x", aBuf, m_CurrentMapCrc);
Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "server", aBufMsg);
2010-05-29 07:25:38 +00:00
str_copy(m_aCurrentMap, pMapName, sizeof(m_aCurrentMap));
//map_set(df);
2010-05-29 07:25:38 +00:00
// load compelate map into memory for download
{
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);
}
for(int i=0; i<MAX_CLIENTS; i++) {
m_aPrevStates[i] = m_aClients[i].m_State;
}
2010-05-29 07:25:38 +00:00
return 1;
}
void CServer::InitEngine(const char *pAppname)
{
m_Engine.Init(pAppname);
}
void CServer::InitRegister(CNetServer *pNetServer, IEngineMasterServer *pMasterServer, IConsole *pConsole)
2010-05-29 07:25:38 +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>();
//snap_init_id();
net_init();
2010-05-29 07:25:38 +00:00
//
Console()->RegisterPrintCallback(SendRconLineAuthed, this);
Console()->RegisterPrintResponseCallback(SendRconLineAuthed, this);
Console()->RegisterClientOnlineCallback(ClientOnline, this);
Console()->RegisterCompareClientsCallback(CompareClients, 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;
}
2010-05-29 07:25:38 +00:00
// start server
// TODO: IPv6 support
NETADDR BindAddr;
if(g_Config.m_SvBindaddr[0] && net_host_lookup(g_Config.m_SvBindaddr, &BindAddr, NETTYPE_IPV4) == 0)
{
// sweet!
BindAddr.port = g_Config.m_SvPort;
}
else
{
mem_zero(&BindAddr, sizeof(BindAddr));
BindAddr.port = g_Config.m_SvPort;
}
if(!m_NetServer.Open(BindAddr, 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;
}
2010-10-11 01:50:43 +00:00
m_NetServer.SetCallbacks(NewClientCallback, DelClientCallback, this);
2010-05-29 07:25:38 +00:00
2011-02-07 06:53:41 +00:00
Console()->ExecuteFile(SERVER_BANMASTERFILE, 0, 0, 0, 0, 4);
char aBuf[256];
str_format(aBuf, sizeof(aBuf), "server name is '%s'", g_Config.m_SvName);
Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf);
2010-05-29 07:25:38 +00:00
GameServer()->OnInit();
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
// process pending commands
m_pConsole->StoreCommands(false,-1);
2010-05-29 07:25:38 +00:00
// start game
{
int64 ReportTime = time_get();
int ReportInterval = 3;
2010-05-29 07:25:38 +00:00
m_Lastheartbeat = 0;
m_GameStartTime = time_get();
2010-05-29 07:25:38 +00:00
if(g_Config.m_Debug)
{
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;
2010-05-29 07:25:38 +00:00
// load new map TODO: don't poll this
if(str_comp(g_Config.m_SvMap, m_aCurrentMap) != 0 || m_MapReload)
2010-05-29 07:25:38 +00:00
{
m_MapReload = 0;
2010-05-29 07:25:38 +00:00
// load map
if(LoadMap(g_Config.m_SvMap))
{
Console()->ExecuteLine("tune_reset", IConsole::CONSOLELEVEL_CONFIG, -1);
Console()->ExecuteLine("tune gun_speed 1400", IConsole::CONSOLELEVEL_CONFIG, -1);
Console()->ExecuteLine("tune shotgun_curvature 0", IConsole::CONSOLELEVEL_CONFIG, -1);
Console()->ExecuteLine("tune shotgun_speed 500", IConsole::CONSOLELEVEL_CONFIG, -1);
Console()->ExecuteLine("tune shotgun_speeddiff 0", IConsole::CONSOLELEVEL_CONFIG, -1);
Console()->ExecuteLine("tune gun_curvature 0", IConsole::CONSOLELEVEL_CONFIG, -1);
Console()->ExecuteLine("sv_hit 1", IConsole::CONSOLELEVEL_CONFIG, -1);
Console()->ExecuteLine("sv_npc 0", IConsole::CONSOLELEVEL_CONFIG, -1);
Console()->ExecuteLine("sv_phook 1", IConsole::CONSOLELEVEL_CONFIG, -1);
Console()->ExecuteLine("sv_endless_drag 0", IConsole::CONSOLELEVEL_CONFIG, -1);
Console()->ExecuteLine("sv_old_laser 0", IConsole::CONSOLELEVEL_CONFIG, -1);
2010-05-29 07:25:38 +00:00
// new map loaded
GameServer()->OnShutdown();
2010-05-29 07:25:38 +00:00
for(int c = 0; c < MAX_CLIENTS; c++)
{
if(m_aClients[c].m_State <= CClient::STATE_AUTH)
2010-05-29 07:25:38 +00:00
continue;
2010-05-29 07:25:38 +00:00
SendMap(c);
m_aClients[c].Reset();
m_aClients[c].m_State = CClient::STATE_CONNECTING;
}
2010-05-29 07:25:38 +00:00
m_GameStartTime = time_get();
m_CurrentGameTick = 0;
Kernel()->ReregisterInterface(GameServer());
GameServer()->OnInit();
UpdateServerInfo();
2010-05-29 07:25:38 +00:00
}
else
{
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));
}
}
2010-05-29 07:25:38 +00:00
while(t > TickStartTime(m_CurrentGameTick+1))
{
m_CurrentGameTick++;
NewTicks++;
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();
}
2010-05-29 07:25:38 +00:00
// snap game
if(NewTicks)
{
if(g_Config.m_SvHighBandwidth || (m_CurrentGameTick%2) == 0)
DoSnapshot();
}
2010-05-29 07:25:38 +00:00
// master server stuff
m_Register.RegisterUpdate();
2010-05-29 07:25:38 +00:00
PumpNetwork();
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);
2010-05-29 07:25:38 +00:00
perf_next();
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);
2010-05-29 07:25:38 +00:00
prev_stats = stats;
*/
}
2010-05-29 07:25:38 +00:00
ReportTime += time_freq()*ReportInterval;
}
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)
m_NetServer.Drop(i, "Server 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, int ClientID)
2010-05-29 07:25:38 +00:00
{
int Victim = pResult->GetVictim();
if(pResult->NumArguments() >= 1)
{
char aBuf[128];
str_format(aBuf, sizeof(aBuf), "Kicked by (%s)", pResult->GetString(0));
((CServer *)pUser)->Kick(Victim, aBuf);
}
else
((CServer *)pUser)->Kick(Victim, "Kicked by console");
2010-05-29 07:25:38 +00:00
}
void CServer::ConBan(IConsole::IResult *pResult, void *pUser, int ClientID)
2010-05-29 07:25:38 +00:00
{
NETADDR Addr;
CServer *pServer = (CServer *)pUser;
2010-05-29 07:25:38 +00:00
const char *pStr = pResult->GetString(0);
2010-10-11 01:50:43 +00:00
int Minutes = 30;
2011-01-12 09:10:57 +00:00
const char *pReason = "No reason given";
2010-10-11 01:50:43 +00:00
2010-05-29 07:25:38 +00:00
if(pResult->NumArguments() > 1)
2010-10-11 01:50:43 +00:00
Minutes = pResult->GetInteger(1);
if(pResult->NumArguments() > 2)
pReason = pResult->GetString(2);
2010-05-29 07:25:38 +00:00
if(net_addr_from_str(&Addr, pStr) == 0)
{
if(pServer->m_RconClientID >= 0 && pServer->m_RconClientID < MAX_CLIENTS && pServer->m_aClients[pServer->m_RconClientID].m_State != CClient::STATE_EMPTY)
{
NETADDR AddrCheck = pServer->m_NetServer.ClientAddr(pServer->m_RconClientID);
Addr.port = AddrCheck.port = 0;
if(net_addr_comp(&Addr, &AddrCheck) == 0)
{
2011-01-14 15:10:48 +00:00
pServer->Console()->PrintResponse(IConsole::OUTPUT_LEVEL_STANDARD, "server", "you can\'t ban yourself");
return;
}
}
for(int i=0;i<MAX_CLIENTS;i++)
{
NETADDR Temp = pServer->m_NetServer.ClientAddr(i);
Addr.port = Temp.port = 0;
if(net_addr_comp(&Addr, &Temp) == 0)
{
if ((((CServer *)pUser)->m_aClients[ClientID].m_Authed > 0) && ((CServer *)pUser)->m_aClients[ClientID].m_Authed <= ((CServer *)pUser)->m_aClients[i].m_Authed)
{
2011-01-14 15:10:48 +00:00
pServer->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", "you can\'t ban a player with higher or same level");
return;
}
}
}
pServer->BanAdd(Addr, Minutes*60, pReason);
}
2010-05-29 07:25:38 +00:00
else if(StrAllnum(pStr))
{
int TempClientID = str_toint(pStr);
2010-05-29 07:25:38 +00:00
if(TempClientID < 0 || TempClientID >= MAX_CLIENTS || pServer->m_aClients[TempClientID].m_State == CClient::STATE_EMPTY)
2010-05-29 07:25:38 +00:00
{
pServer->Console()->PrintResponse(IConsole::OUTPUT_LEVEL_STANDARD, "server", "invalid client id");
2010-05-29 07:25:38 +00:00
return;
}
2010-10-18 18:46:00 +00:00
if (ClientID != -1 && ((CServer *)pUser)->m_aClients[ClientID].m_Authed <= ((CServer *)pUser)->m_aClients[TempClientID].m_Authed)
2010-10-18 18:46:00 +00:00
return;
else if(pServer->m_RconClientID == TempClientID)
2010-05-29 07:25:38 +00:00
{
pServer->Console()->PrintResponse(IConsole::OUTPUT_LEVEL_STANDARD, "server", "you can't ban yourself");
2010-05-29 07:25:38 +00:00
return;
}
Addr = pServer->m_NetServer.ClientAddr(TempClientID);
pServer->BanAdd(Addr, Minutes*60, pReason);
2010-05-29 07:25:38 +00:00
}
else
{
pServer->Console()->PrintResponse(IConsole::OUTPUT_LEVEL_STANDARD, "server", "invalid network address to ban");
return;
}
2010-05-29 07:25:38 +00:00
}
void CServer::ConUnban(IConsole::IResult *pResult, void *pUser, int ClientID)
2010-05-29 07:25:38 +00:00
{
NETADDR Addr;
CServer *pServer = (CServer *)pUser;
2010-05-29 07:25:38 +00:00
const char *pStr = pResult->GetString(0);
if(net_addr_from_str(&Addr, pStr) == 0 && !pServer->BanRemove(Addr))
{
char aBuf[256];
str_format(aBuf, sizeof(aBuf), "unbanned %d.%d.%d.%d", Addr.ip[0], Addr.ip[1], Addr.ip[2], Addr.ip[3]);
pServer->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf);
}
else if(StrAllnum(pStr))
{
int BanIndex = str_toint(pStr);
CNetServer::CBanInfo Info;
if(BanIndex < 0 || !pServer->m_NetServer.BanGet(BanIndex, &Info))
pServer->Console()->PrintResponse(IConsole::OUTPUT_LEVEL_STANDARD, "server", "invalid ban index");
else if(!pServer->BanRemove(Info.m_Addr))
{
char aBuf[256];
str_format(aBuf, sizeof(aBuf), "unbanned %d.%d.%d.%d", Info.m_Addr.ip[0], Info.m_Addr.ip[1], Info.m_Addr.ip[2], Info.m_Addr.ip[3]);
pServer->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf);
}
}
2010-05-29 07:25:38 +00:00
else
pServer->Console()->PrintResponse(IConsole::OUTPUT_LEVEL_STANDARD, "server", "invalid network address");
2010-05-29 07:25:38 +00:00
}
void CServer::ConBans(IConsole::IResult *pResult, void *pUser, int ClientID)
2010-05-29 07:25:38 +00:00
{
unsigned Now = time_timestamp();
char aBuf[1024];
CServer* pServer = (CServer *)pUser;
2010-05-29 07:25:38 +00:00
int Num = pServer->m_NetServer.BanNum();
for(int i = 0; i < Num; i++)
{
CNetServer::CBanInfo Info;
pServer->m_NetServer.BanGet(i, &Info);
NETADDR Addr = Info.m_Addr;
2010-05-29 07:25:38 +00:00
if(Info.m_Expires == -1)
{
str_format(aBuf, sizeof(aBuf), "#%d %d.%d.%d.%d for life", i, Addr.ip[0], Addr.ip[1], Addr.ip[2], Addr.ip[3]);
}
else
{
unsigned t = Info.m_Expires - Now;
str_format(aBuf, sizeof(aBuf), "#%d %d.%d.%d.%d for %d minutes and %d seconds", i, Addr.ip[0], Addr.ip[1], Addr.ip[2], Addr.ip[3], t/60, t%60);
}
pServer->Console()->PrintResponse(IConsole::OUTPUT_LEVEL_STANDARD, "Server", aBuf);
2010-05-29 07:25:38 +00:00
}
str_format(aBuf, sizeof(aBuf), "%d ban(s)", Num);
pServer->Console()->PrintResponse(IConsole::OUTPUT_LEVEL_STANDARD, "Server", aBuf);
2010-05-29 07:25:38 +00:00
}
void CServer::ConStatus(IConsole::IResult *pResult, void *pUser, int ClientID)
2010-05-29 07:25:38 +00:00
{
int i;
NETADDR Addr;
char aBuf[1024];
CServer* pServer = (CServer *)pUser;
for(i = 0; i < MAX_CLIENTS; i++)
{
if(pServer->m_aClients[i].m_State != CClient::STATE_EMPTY)
2010-05-29 07:25:38 +00:00
{
Addr = pServer->m_NetServer.ClientAddr(i);
if(pServer->m_aClients[i].m_State == CClient::STATE_INGAME)
str_format(aBuf, sizeof(aBuf), "id=%d addr=%d.%d.%d.%d:%d name='%s' level=%d",
i, Addr.ip[0], Addr.ip[1], Addr.ip[2], Addr.ip[3], Addr.port,
pServer->m_aClients[i].m_aName, pServer->m_aClients[i].m_Authed);
else
str_format(aBuf, sizeof(aBuf), "id=%d addr=%d.%d.%d.%d:%d connecting",
i, Addr.ip[0], Addr.ip[1], Addr.ip[2], Addr.ip[3], Addr.port);
pServer->Console()->PrintResponse(IConsole::OUTPUT_LEVEL_STANDARD, "Server", aBuf);
2010-05-29 07:25:38 +00:00
}
}
}
void CServer::ConShutdown(IConsole::IResult *pResult, void *pUser, int ClientID)
2010-05-29 07:25:38 +00:00
{
((CServer *)pUser)->m_RunServer = 0;
}
void CServer::ConRecord(IConsole::IResult *pResult, void *pUser, int ClientID)
2010-05-29 07:25:38 +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, int ClientID)
2010-05-29 07:25:38 +00:00
{
((CServer *)pUser)->m_DemoRecorder.Stop();
}
void CServer::ConMapReload(IConsole::IResult *pResult, void *pUser, int ClientID)
{
((CServer *)pUser)->m_MapReload = 1;
}
2010-05-29 07:25:38 +00:00
void CServer::ConchainSpecialInfoupdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData)
{
pfnCallback(pResult, pCallbackUserData, -1);
2010-05-29 07:25:38 +00:00
if(pResult->NumArguments())
((CServer *)pUserData)->UpdateServerInfo();
}
void CServer::ConchainMaxclientsperipUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData)
{
pfnCallback(pResult, pCallbackUserData, -1);
if(pResult->NumArguments())
((CServer *)pUserData)->m_NetServer.SetMaxClientsPerIP(pResult->GetInteger(0));
}
2010-05-29 07:25:38 +00:00
void CServer::RegisterCommands()
{
m_pConsole = Kernel()->RequestInterface<IConsole>();
Console()->Register("kick", "v?t", CFGFLAG_SERVER, ConKick, this, "", IConsole::CONSOLELEVEL_MODERATOR);
Console()->Register("ban", "s?ir", CFGFLAG_SERVER|CFGFLAG_STORE, ConBan, this, "", IConsole::CONSOLELEVEL_MODERATOR);
Console()->Register("unban", "s", CFGFLAG_SERVER|CFGFLAG_STORE, ConUnban, this, "", IConsole::CONSOLELEVEL_MODERATOR);
Console()->Register("bans", "", CFGFLAG_SERVER|CFGFLAG_STORE, ConBans, this, "", IConsole::CONSOLELEVEL_MODERATOR);
Console()->Register("status", "", CFGFLAG_SERVER, ConStatus, this, "", IConsole::CONSOLELEVEL_MODERATOR);
Console()->Register("shutdown", "", CFGFLAG_SERVER, ConShutdown, this, "", IConsole::CONSOLELEVEL_ADMIN);
Console()->Register("record", "?s", CFGFLAG_SERVER|CFGFLAG_STORE, ConRecord, this, "", IConsole::CONSOLELEVEL_ADMIN);
Console()->Register("stoprecord", "", CFGFLAG_SERVER, ConStopRecord, this, "", IConsole::CONSOLELEVEL_ADMIN);
Console()->Register("add_banmaster", "s", CFGFLAG_SERVER, ConAddBanmaster, this, "", IConsole::CONSOLELEVEL_ADMIN);
Console()->Register("banmasters", "", CFGFLAG_SERVER, ConBanmasters, this, "", IConsole::CONSOLELEVEL_ADMIN);
Console()->Register("clear_banmasters", "", CFGFLAG_SERVER, ConClearBanmasters, this, "", IConsole::CONSOLELEVEL_ADMIN);
2011-02-07 06:25:38 +00:00
Console()->Register("reload", "", CFGFLAG_SERVER, ConMapReload, this, "", IConsole::CONSOLELEVEL_ADMIN);
2010-05-29 07:25:38 +00:00
Console()->Chain("sv_name", ConchainSpecialInfoupdate, this);
Console()->Chain("password", ConchainSpecialInfoupdate, this);
Console()->Chain("sv_max_clients_per_ip", ConchainMaxclientsperipUpdate, this);
Console()->Register("login", "?s", CFGFLAG_SERVER, ConLogin, this, "Allows you access to rcon if no password is given, or changes your level if a password is given", IConsole::CONSOLELEVEL_USER);
Console()->Register("auth", "?s", CFGFLAG_SERVER, ConLogin, this, "Allows you access to rcon if no password is given, or changes your level if a password is given", IConsole::CONSOLELEVEL_USER);
2010-11-12 16:56:31 +00:00
Console()->Register("cmdlist", "?i", CFGFLAG_SERVER, ConCmdList, this, "Shows you the commands available for your remote console access. Specify the level if you want to see other level's commands", IConsole::CONSOLELEVEL_USER);
}
2010-05-29 07:25:38 +00:00
int CServer::SnapNewID()
{
return m_IDPool.NewID();
}
void CServer::SnapFreeID(int ID)
{
m_IDPool.FreeID(ID);
}
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");
dbg_assert(ID >= 0 && ID <=0xffff, "incorrect id");
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
2010-05-29 07:25:38 +00:00
// init the engine
dbg_msg("server", "starting...");
CServer *pServer = CreateServer();
pServer->InitEngine("Teeworlds");
2010-05-29 07:25:38 +00:00
IKernel *pKernel = IKernel::Create();
// create the components
IEngineMap *pEngineMap = CreateEngineMap();
IGameServer *pGameServer = CreateGameServer();
IConsole *pConsole = CreateConsole(CFGFLAG_SERVER);
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();
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
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));
2010-05-29 07:25:38 +00:00
if(RegisterFail)
return -1;
}
2010-05-29 07:25:38 +00:00
pConfig->Init();
pEngineMasterServer->Init(pServer->Engine());
pEngineMasterServer->Load();
2010-05-29 07:25:38 +00:00
// register all console commands
pServer->RegisterCommands();
pGameServer->OnConsoleInit();
pConsole->ExecuteLine("tune_reset", IConsole::CONSOLELEVEL_CONFIG, -1);
pConsole->ExecuteLine("tune gun_speed 1400", IConsole::CONSOLELEVEL_CONFIG, -1);
pConsole->ExecuteLine("tune shotgun_curvature 0", IConsole::CONSOLELEVEL_CONFIG, -1);
pConsole->ExecuteLine("tune shotgun_speed 500", IConsole::CONSOLELEVEL_CONFIG, -1);
pConsole->ExecuteLine("tune shotgun_speeddiff 0", IConsole::CONSOLELEVEL_CONFIG, -1);
pConsole->ExecuteLine("tune gun_curvature 0", IConsole::CONSOLELEVEL_CONFIG, -1);
pConsole->ExecuteLine("sv_hit 1", IConsole::CONSOLELEVEL_CONFIG, -1);
pConsole->ExecuteLine("sv_npc 0", IConsole::CONSOLELEVEL_CONFIG, -1);
pConsole->ExecuteLine("sv_phook 1", IConsole::CONSOLELEVEL_CONFIG, -1);
pConsole->ExecuteLine("sv_endless_drag 0", IConsole::CONSOLELEVEL_CONFIG, -1);
pConsole->ExecuteLine("sv_old_laser 0", IConsole::CONSOLELEVEL_CONFIG, -1);
2010-05-29 07:25:38 +00:00
// execute autoexec file
pConsole->ExecuteFile("autoexec.cfg", 0, 0, 0, 0, 4);
2010-05-29 07:25:38 +00:00
// parse the command line arguments
if(argc > 1) // ignore_convention
pConsole->ParseArguments(argc-1, &argv[1]); // ignore_convention
// restore empty config strings to their defaults
pConfig->RestoreStrings();
2010-08-06 18:38:13 +00:00
pServer->Engine()->InitLogfile();
2010-05-29 07:25:38 +00:00
// run the server
pServer->Run();
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;
}
void CServer::SetRconLevel(int ClientID, int Level)
{
Level = clamp(Level, (int)IConsole::CONSOLELEVEL_USER, (int)IConsole::CONSOLELEVEL_ADMIN);
if(Level > IConsole::CONSOLELEVEL_USER)
{
dbg_msg("server", "%s set to level %d. ClientID=%x ip=%d.%d.%d.%d",ClientName(ClientID), Level, ClientID, m_aClients[ClientID].m_Addr.ip[0], m_aClients[ClientID].m_Addr.ip[1], m_aClients[ClientID].m_Addr.ip[2], m_aClients[ClientID].m_Addr.ip[3]);
CMsgPacker Msg(NETMSG_RCON_AUTH_STATUS);
Msg.AddInt(1);
SendMsgEx(&Msg, MSGFLAG_VITAL, ClientID, true);
m_aClients[ClientID].m_Authed = Level;
GameServer()->OnSetAuthed(ClientID, m_aClients[ClientID].m_Authed);
}
}
void CServer::CheckPass(int ClientID, const char *pPassword)
{
if(pPassword[0] != 0)
{
if(g_Config.m_SvRconPasswordHelper[0] == 0 &&
g_Config.m_SvRconPasswordModer[0] == 0 &&
g_Config.m_SvRconPasswordAdmin[0] == 0)
{
SendRconLine(ClientID, "No rcon password set on server. Set sv_admin_pass/sv_mod_pass/sv_helper_pass to enable the remote console.");
}
else
{
/*else if(str_comp(pPw, g_Config.m_SvRconPassword) == 0)
{
CMsgPacker Msg(NETMSG_RCON_AUTH_STATUS);
Msg.AddInt(1);
SendMsgEx(&Msg, MSGFLAG_VITAL, ClientID, true);
m_aClients[ClientID].m_Authed = 1;
SendRconLine(ClientID, "Authentication successful. Remote console access granted.");
dbg_msg("server", "ClientID=%d authed", ClientID);
}*/
int Level = IConsole::CONSOLELEVEL_USER;
if(str_comp(pPassword, g_Config.m_SvRconPasswordModer) == 0)
{
Level = IConsole::CONSOLELEVEL_MODERATOR;
}
else if(str_comp(pPassword, g_Config.m_SvRconPasswordAdmin) == 0)
{
Level = IConsole::CONSOLELEVEL_ADMIN;
}
if(Level > IConsole::CONSOLELEVEL_USER)
{
char buf[128]="Authentication successful. Remote console access granted for ClientID=%d with level=%d";
SetRconLevel(ClientID, Level);
str_format(buf,sizeof(buf),buf,ClientID,Level);
SendRconLine(ClientID, buf);
dbg_msg("server", "'%s' ClientID=%d authed with Level=%d", ClientName(ClientID), ClientID, Level);
}
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)
{
if(!g_Config.m_SvRconBantime)
m_NetServer.Drop(ClientID, "Too many remote console authentication tries");
else
{
NETADDR Addr = m_NetServer.ClientAddr(ClientID);
2011-01-06 03:46:10 +00:00
BanAdd(Addr, g_Config.m_SvRconBantime*60, "Too many remote console authentication tries");
}
}
}
}
}
/*else if(str_comp(pPw, g_Config.m_SvRconPassword) == 0)
{
CMsgPacker Msg(NETMSG_RCON_AUTH_STATUS);
Msg.AddInt(1);
SendMsgEx(&Msg, MSGFLAG_VITAL, ClientID, true);
m_aClients[ClientID].m_Authed = 1;
SendRconLine(ClientID, "Authentication successful. Remote console access granted.");
char aBuf[256];
str_format(aBuf, sizeof(aBuf), "ClientID=%d authed", ClientID);
Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf);
}*/
else
{
char aBuf[128]="Authentication successful. Remote console access granted for ClientID=%d with level=%d";
SetRconLevel(ClientID,0);
str_format(aBuf, sizeof(aBuf), aBuf, ClientID, 0);
SendRconLine(ClientID, aBuf);
dbg_msg("server", "'%s' ClientID=%d authed with Level=%d", ClientName(ClientID), ClientID, 0);
}
}
2010-11-22 10:59:25 +00:00
char *CServer::GetAnnouncementLine(char const *pFileName)
2010-11-22 10:59:25 +00:00
{
IOHANDLE File = m_pStorage->OpenFile(pFileName, IOFLAG_READ, IStorage::TYPE_ALL);
2010-11-22 10:59:25 +00:00
if(File)
{
std::vector<char*> v;
char *pLine;
CLineReader *lr = new CLineReader();
lr->Init(File);
while((pLine = lr->Get()))
2010-11-22 10:59:25 +00:00
if(str_length(pLine))
if(pLine[0]!='#')
2010-11-29 04:21:04 +00:00
v.push_back(pLine);
2010-12-06 02:27:35 +00:00
if(v.size() == 1)
{
m_AnnouncementLastLine = 0;
}
else if(!g_Config.m_SvAnnouncementRandom)
{
if(m_AnnouncementLastLine >= v.size())
m_AnnouncementLastLine %= v.size();
}
else
{
unsigned Rand;
2010-12-06 02:27:35 +00:00
do
Rand = rand() % v.size();
while(Rand == m_AnnouncementLastLine);
m_AnnouncementLastLine = Rand;
}
return v[m_AnnouncementLastLine];
2010-11-22 10:59:25 +00:00
}
return 0;
}
2010-12-19 05:25:01 +00:00
NETADDR CServer::GetClientIP(int ClientID)//this may exist already but i couldn't find it cause i am tired :D
{
return m_NetServer.ClientAddr(ClientID);
}
2011-01-06 03:46:10 +00:00
void CServer::ConAddBanmaster(IConsole::IResult *pResult, void *pUser, int ClientID)
2011-02-07 06:25:38 +00:00
{
CServer *pServer = (CServer *)pUser;
int Result = pServer->m_NetServer.BanmasterAdd(pResult->GetString(0));
if(Result == 0)
pServer->Console()->PrintResponse(IConsole::OUTPUT_LEVEL_STANDARD, "server/banmaster", "succesfully added banmaster");
else if (Result == 1)
pServer->Console()->PrintResponse(IConsole::OUTPUT_LEVEL_STANDARD, "server/banmaster", "invalid address for banmaster / net lookup failed");
else
pServer->Console()->PrintResponse(IConsole::OUTPUT_LEVEL_STANDARD, "server/banmaster", "too many banmasters");
}
void CServer::ConBanmasters(IConsole::IResult *pResult, void *pUser, int ClientID)
2011-02-07 06:25:38 +00:00
{
CServer *pServer = (CServer *)pUser;
int NumBanmasters = pServer->m_NetServer.BanmasterNum();
char aBuf[128];
char aIpString[64];
for(int i = 0; i < NumBanmasters; i++)
{
NETADDR *pBanmaster = pServer->m_NetServer.BanmasterGet(i);
net_addr_str(pBanmaster, aIpString, sizeof(aIpString));
str_format(aBuf, sizeof(aBuf), "%d: %s", i, aIpString);
pServer->Console()->PrintResponse(IConsole::OUTPUT_LEVEL_STANDARD, "server/banmaster", aBuf);
}
}
void CServer::ConClearBanmasters(IConsole::IResult *pResult, void *pUser, int ClientID)
2011-02-07 06:25:38 +00:00
{
CServer *pServer = (CServer *)pUser;
pServer->m_NetServer.BanmastersClear();
}
void CServer::SendRconResponse(const char *pLine, void *pUser)
{
RconResponseInfo *pInfo = (RconResponseInfo *)pUser;
CServer *pThis = pInfo->m_Server;
static volatile int ReentryGuard = 0;
if(ReentryGuard)
return;
ReentryGuard++;
if(pThis->m_aClients[pInfo->m_ClientID].m_State != CClient::STATE_EMPTY)
pThis->SendRconLine(pInfo->m_ClientID, pLine);
ReentryGuard--;
}
void CServer::ConCmdList(IConsole::IResult *pResult, void *pUserData, int ClientID)
{
CServer *pSelf = (CServer *)pUserData;
if(pResult->NumArguments() == 0)
pSelf->Console()->List((pSelf->m_aClients[ClientID].m_Authed != 0) ? pSelf->m_aClients[ClientID].m_Authed : -1, CFGFLAG_SERVER);
else if (pResult->GetInteger(0) == 0)
pSelf->Console()->List(-1, CFGFLAG_SERVER);
else
pSelf->Console()->List(pResult->GetInteger(0), CFGFLAG_SERVER);
}
void CServer::ConLogin(IConsole::IResult *pResult, void *pUser, int ClientID)
{
if(pResult->NumArguments())
((CServer *)pUser)->CheckPass(ClientID, pResult->GetString(0));
else
((CServer *)pUser)->SetRconLevel(ClientID, 0);
}
bool CServer::CompareClients(int ClientLevel, int Victim, void *pUser)
{
CServer* pSelf = (CServer *)pUser;
if(!ClientOnline(Victim, pSelf))
return false;
return clamp(ClientLevel, 0, 4) > clamp(pSelf->m_aClients[Victim].m_Authed, 0, 4);
}
bool CServer::ClientOnline(int ClientID, void *pUser)
{
CServer* pSelf = (CServer *)pUser;
if(ClientID < 0 || ClientID >= MAX_CLIENTS)
return false;
return pSelf->m_aClients[ClientID].m_State != CClient::STATE_EMPTY;
}
void CServer::SetClientAuthed(int ClientID, int Level) {
if(ClientID < 0 || ClientID >= MAX_CLIENTS || m_aClients[ClientID].m_State == CClient::STATE_READY)
{
return;
}
m_aClients[ClientID].m_Authed = Level;
}
2011-02-07 06:25:38 +00:00