about summary refs log tree commit diff
path: root/src/engine/server/server.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/engine/server/server.cpp')
-rw-r--r--src/engine/server/server.cpp441
1 files changed, 212 insertions, 229 deletions
diff --git a/src/engine/server/server.cpp b/src/engine/server/server.cpp
index 54267a4a..fca9e045 100644
--- a/src/engine/server/server.cpp
+++ b/src/engine/server/server.cpp
@@ -19,6 +19,7 @@
 #include <engine/shared/econ.h>
 #include <engine/shared/filecollection.h>
 #include <engine/shared/mapchecker.h>
+#include <engine/shared/netban.h>
 #include <engine/shared/network.h>
 #include <engine/shared/packer.h>
 #include <engine/shared/protocol.h>
@@ -39,37 +40,48 @@
 
 static const char SERVER_BANMASTERFILE[] = "banmasters.cfg";
 
-static const char *StrLtrim(const char *pStr)
+static const char *StrUTF8Ltrim(const char *pStr)
 {
-	while(*pStr && *pStr >= 0 && *pStr <= 32)
-		pStr++;
-	return pStr;
-}
-
-static void StrRtrim(char *pStr)
-{
-	int i = str_length(pStr);
-	while(i >= 0)
+	while(*pStr)
 	{
-		if(pStr[i] < 0 || pStr[i] > 32)
-			break;
-		pStr[i] = 0;
-		i--;
+		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;
+		}
 	}
+	return pStr;
 }
 
-
-static int StrAllnum(const char *pStr)
+static void StrUTF8Rtrim(char *pStr)
 {
-	while(*pStr)
+	const char *p = pStr;
+	const char *pEnd = 0;
+	while(*p)
 	{
-		if(!(*pStr >= '0' && *pStr <= '9'))
-			return 0;
-		pStr++;
+		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;
 	}
-	return 1;
+	if(pEnd != 0)
+		*(const_cast<char *>(pEnd)) = 0;
 }
 
+
 CSnapIDPool::CSnapIDPool()
 {
 	Reset();
@@ -158,6 +170,115 @@ void CSnapIDPool::FreeID(int ID)
 	}
 }
 
+
+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;
+			}
+		}
+	}
+	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;
+			}
+		}
+	}
+
+	int Result = Ban(pBanPool, pData, Seconds, pReason);
+	if(Result != 0)
+		return Result;
+
+	// drop banned clients
+	typename T::CDataType Data = *pData;
+	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);
+}
+
+
 void CServer::CClient::Reset()
 {
 	// reset input
@@ -187,7 +308,7 @@ CServer::CServer() : m_DemoRecorder(&m_SnapshotDelta)
 
 	m_MapReload = 0;
 
-	m_RconClientID = -1;
+	m_RconClientID = IServer::RCON_CID_SERV;
 	m_RconAuthLevel = AUTHED_ADMIN;
 
 	Init();
@@ -199,8 +320,8 @@ int CServer::TrySetClientName(int ClientID, const char *pName)
 	char aTrimmedName[64];
 
 	// trim the name
-	str_copy(aTrimmedName, StrLtrim(pName), sizeof(aTrimmedName));
-	StrRtrim(aTrimmedName);
+	str_copy(aTrimmedName, StrUTF8Ltrim(pName), sizeof(aTrimmedName));
+	StrUTF8Rtrim(aTrimmedName);
 
 	// check if new and old name are the same
 	if(m_aClients[ClientID].m_aName[0] && str_comp(m_aClients[ClientID].m_aName, aTrimmedName) == 0)
@@ -328,6 +449,11 @@ int CServer::Init()
 	return 0;
 }
 
+void CServer::SetRconCID(int ClientID)
+{
+	m_RconClientID = ClientID;
+}
+
 bool CServer::IsAuthed(int ClientID)
 {
 	return m_aClients[ClientID].m_Authed;
@@ -350,11 +476,7 @@ int CServer::GetClientInfo(int ClientID, CClientInfo *pInfo)
 void CServer::GetClientAddr(int ClientID, char *pAddrStr, int Size)
 {
 	if(ClientID >= 0 && ClientID < MAX_CLIENTS && m_aClients[ClientID].m_State == CClient::STATE_INGAME)
-	{
-		NETADDR Addr = m_NetServer.ClientAddr(ClientID);
-		Addr.port = 0;
-		net_addr_str(&Addr, pAddrStr, Size);
-	}
+		net_addr_str(m_NetServer.ClientAddr(ClientID), pAddrStr, Size, false);
 }
 
 
@@ -394,6 +516,11 @@ bool CServer::ClientIngame(int ClientID)
 	return ClientID >= 0 && ClientID < MAX_CLIENTS && m_aClients[ClientID].m_State == CServer::CClient::STATE_INGAME;
 }
 
+int CServer::MaxClients() const
+{
+	return m_NetServer.MaxClients();
+}
+
 int CServer::SendMsg(CMsgPacker *pMsg, int Flags, int ClientID)
 {
 	return SendMsgEx(pMsg, Flags, ClientID, false);
@@ -595,9 +722,8 @@ int CServer::DelClientCallback(int ClientID, const char *pReason, void *pUser)
 {
 	CServer *pThis = (CServer *)pUser;
 
-	NETADDR Addr = pThis->m_NetServer.ClientAddr(ClientID);
 	char aAddrStr[NETADDR_MAXSTRSIZE];
-	net_addr_str(&Addr, aAddrStr, sizeof(aAddrStr));
+	net_addr_str(pThis->m_NetServer.ClientAddr(ClientID), aAddrStr, sizeof(aAddrStr), true);
 	char aBuf[256];
 	str_format(aBuf, sizeof(aBuf), "client dropped. cid=%d addr=%s reason='%s'", ClientID, aAddrStr,	pReason);
 	pThis->Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "server", aBuf);
@@ -676,7 +802,7 @@ void CServer::SendRconCmdRem(const IConsole::CCommandInfo *pCommandInfo, int Cli
 void CServer::UpdateClientRconCommands()
 {
 	int ClientID = Tick() % MAX_CLIENTS;
-		
+
 	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;
@@ -691,7 +817,6 @@ void CServer::UpdateClientRconCommands()
 void CServer::ProcessClientPacket(CNetChunk *pPacket)
 {
 	int ClientID = pPacket->m_ClientID;
-	NETADDR Addr;
 	CUnpacker Unpacker;
 	Unpacker.Reset(pPacket->m_pData, pPacket->m_DataSize);
 
@@ -770,9 +895,8 @@ void CServer::ProcessClientPacket(CNetChunk *pPacket)
 		{
 			if(m_aClients[ClientID].m_State == CClient::STATE_CONNECTING)
 			{
-				Addr = m_NetServer.ClientAddr(ClientID);
 				char aAddrStr[NETADDR_MAXSTRSIZE];
-				net_addr_str(&Addr, aAddrStr, sizeof(aAddrStr));
+				net_addr_str(m_NetServer.ClientAddr(ClientID), aAddrStr, sizeof(aAddrStr), true);
 
 				char aBuf[256];
 				str_format(aBuf, sizeof(aBuf), "player is ready. ClientID=%x addr=%s", ClientID, aAddrStr);
@@ -786,9 +910,8 @@ void CServer::ProcessClientPacket(CNetChunk *pPacket)
 		{
 			if(m_aClients[ClientID].m_State == CClient::STATE_READY && GameServer()->IsClientReady(ClientID))
 			{
-				Addr = m_NetServer.ClientAddr(ClientID);
 				char aAddrStr[NETADDR_MAXSTRSIZE];
-				net_addr_str(&Addr, aAddrStr, sizeof(aAddrStr));
+				net_addr_str(m_NetServer.ClientAddr(ClientID), aAddrStr, sizeof(aAddrStr), true);
 
 				char aBuf[256];
 				str_format(aBuf, sizeof(aBuf), "player has entered the game. ClientID=%x addr=%s", ClientID, aAddrStr);
@@ -861,9 +984,9 @@ void CServer::ProcessClientPacket(CNetChunk *pPacket)
 				m_RconClientID = ClientID;
 				m_RconAuthLevel = m_aClients[ClientID].m_Authed;
 				Console()->SetAccessLevel(m_aClients[ClientID].m_Authed == AUTHED_ADMIN ? IConsole::ACCESS_LEVEL_ADMIN : IConsole::ACCESS_LEVEL_MOD);
-				Console()->ExecuteLine(pCmd);
+				Console()->ExecuteLineFlag(pCmd, CFGFLAG_SERVER);
 				Console()->SetAccessLevel(IConsole::ACCESS_LEVEL_ADMIN);
-				m_RconClientID = -1;
+				m_RconClientID = IServer::RCON_CID_SERV;
 				m_RconAuthLevel = AUTHED_ADMIN;
 			}
 		}
@@ -922,10 +1045,7 @@ void CServer::ProcessClientPacket(CNetChunk *pPacket)
 						if(!g_Config.m_SvRconBantime)
 							m_NetServer.Drop(ClientID, "Too many remote console authentication tries");
 						else
-						{
-							NETADDR Addr = m_NetServer.ClientAddr(ClientID);
-							BanAdd(Addr, g_Config.m_SvRconBantime*60, "Too many remote console authentication tries");
-						}
+							m_ServerBan.BanAddr(m_NetServer.ClientAddr(ClientID), g_Config.m_SvRconBantime*60, "Too many remote console authentication tries");
 					}
 				}
 				else
@@ -969,7 +1089,7 @@ void CServer::ProcessClientPacket(CNetChunk *pPacket)
 	}
 }
 
-void CServer::SendServerInfo(NETADDR *pAddr, int Token)
+void CServer::SendServerInfo(const NETADDR *pAddr, int Token)
 {
 	CNetChunk Packet;
 	CPacker p;
@@ -1038,33 +1158,10 @@ 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);
-		}
+			SendServerInfo(m_NetServer.ClientAddr(i), -1);
 	}
 }
 
-int CServer::BanAdd(NETADDR Addr, int Seconds, const char *pReason)
-{
-	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);
-}
-
-int CServer::BanRemove(NETADDR Addr)
-{
-	return m_NetServer.BanRemove(Addr);
-}
-
 
 void CServer::PumpNetwork()
 {
@@ -1092,7 +1189,7 @@ void CServer::PumpNetwork()
 				{
 				}*/
 
-				if(Packet.m_DataSize >= sizeof(BANMASTER_IPBAN) &&
+				if(Packet.m_DataSize >= (int)sizeof(BANMASTER_IPBAN) &&
 				  mem_comp(Packet.m_pData, BANMASTER_IPBAN, sizeof(BANMASTER_IPBAN)) == 0)
 				{
 					if(!g_Config.m_SvGlobalBantime)
@@ -1114,7 +1211,7 @@ void CServer::PumpNetwork()
 						return;
 					}
 
-					m_NetServer.BanAdd(Addr, g_Config.m_SvGlobalBantime * 60, aReason);
+					m_ServerBan.BanAddr(&Addr, g_Config.m_SvGlobalBantime * 60, aReason);
 					dbg_msg("globalbans", "added ban, ip=%s, reason='%s'", aIp, aReason);
 				}
 			}
@@ -1123,6 +1220,7 @@ void CServer::PumpNetwork()
 			ProcessClientPacket(&Packet);
 	}
 
+	m_ServerBan.Update();
 	m_Econ.Update();
 }
 
@@ -1173,7 +1271,7 @@ int CServer::LoadMap(const char *pMapName)
 	str_copy(m_aCurrentMap, pMapName, sizeof(m_aCurrentMap));
 	//map_set(df);
 
-	// load compelate map into memory for download
+	// load complete map into memory for download
 	{
 		IOHANDLE File = Storage()->OpenFile(aBuf, IOFLAG_READ, IStorage::TYPE_ALL);
 		m_CurrentMapSize = (int)io_length(File);
@@ -1209,7 +1307,7 @@ int CServer::Run()
 
 	// start server
 	NETADDR BindAddr;
-	if(g_Config.m_SvBindaddr[0] && net_host_lookup(g_Config.m_SvBindaddr, &BindAddr, NETTYPE_ALL) == 0)
+	if(g_Config.m_Bindaddr[0] && net_host_lookup(g_Config.m_Bindaddr, &BindAddr, NETTYPE_ALL) == 0)
 	{
 		// sweet!
 		BindAddr.port = g_Config.m_SvPort;
@@ -1221,15 +1319,16 @@ int CServer::Run()
 		BindAddr.port = g_Config.m_SvPort;
 	}
 
-	if(!m_NetServer.Open(BindAddr, g_Config.m_SvMaxClients, g_Config.m_SvMaxClientsPerIP, 0))
+	if(!m_NetServer.Open(BindAddr, &m_ServerBan, g_Config.m_SvMaxClients, g_Config.m_SvMaxClientsPerIP, 0))
 	{
-		dbg_msg("server", "couldn't open socket. port might already be in use");
+		dbg_msg("server", "couldn't open socket. port %d might already be in use", g_Config.m_SvPort);
 		return -1;
 	}
 
 	m_NetServer.SetCallbacks(NewClientCallback, DelClientCallback, this);
 
-	m_Econ.Init(Console());
+	m_ServerBan.Init(Console(), Storage(), this);
+	m_Econ.Init(Console(), &m_ServerBan);
 
 	Console()->ExecuteFile(SERVER_BANMASTERFILE);
 
@@ -1394,163 +1493,23 @@ void CServer::ConKick(IConsole::IResult *pResult, void *pUser)
 		((CServer *)pUser)->Kick(pResult->GetInteger(0), "Kicked by console");
 }
 
-void CServer::ConBan(IConsole::IResult *pResult, void *pUser)
-{
-	NETADDR Addr;
-	CServer *pServer = (CServer *)pUser;
-	const char *pStr = pResult->GetString(0);
-	int Minutes = 30;
-	const char *pReason = "No reason given";
-
-	if(pResult->NumArguments() > 1)
-		Minutes = max(0, pResult->GetInteger(1));
-
-	if(pResult->NumArguments() > 2)
-		pReason = pResult->GetString(2);
-
-	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)
-			{
-				pServer->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", "you can't ban yourself");
-				return;
-			}
-
-			for(int i = 0; i < MAX_CLIENTS; ++i)
-			{
-				if(i == pServer->m_RconClientID)
-					continue;
-
-				AddrCheck = pServer->m_NetServer.ClientAddr(i);
-				AddrCheck.port = 0;
-				if(net_addr_comp(&Addr, &AddrCheck) == 0 && pServer->m_aClients[i].m_Authed > pServer->m_RconAuthLevel)
-				{
-					pServer->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", "ban command denied");
-					return;
-				}
-			}
-		}
-		pServer->BanAdd(Addr, Minutes*60, pReason);
-	}
-	else if(StrAllnum(pStr))
-	{
-		int ClientID = str_toint(pStr);
-
-		if(ClientID < 0 || ClientID >= MAX_CLIENTS || pServer->m_aClients[ClientID].m_State == CClient::STATE_EMPTY)
-		{
-			pServer->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", "invalid client id");
-			return;
-		}
-		else if(pServer->m_RconClientID == ClientID)
-		{
-			pServer->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", "you can't ban yourself");
-			return;
-		}
-		else if(pServer->m_aClients[ClientID].m_Authed > pServer->m_RconAuthLevel)
-		{
-			pServer->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", "ban command denied");
-			return;
-		}
-
-		Addr = pServer->m_NetServer.ClientAddr(ClientID);
-		pServer->BanAdd(Addr, Minutes*60, pReason);
-	}
-	else
-	{
-		pServer->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", "invalid network address to ban");
-		return;
- 	}
-}
-
-void CServer::ConUnban(IConsole::IResult *pResult, void *pUser)
-{
-	NETADDR Addr;
-	CServer *pServer = (CServer *)pUser;
-	const char *pStr = pResult->GetString(0);
-
-	if(net_addr_from_str(&Addr, pStr) == 0 && !pServer->BanRemove(Addr))
-	{
-		char aAddrStr[NETADDR_MAXSTRSIZE];
-		net_addr_str(&Addr, aAddrStr, sizeof(aAddrStr));
-
-		char aBuf[256];
-		str_format(aBuf, sizeof(aBuf), "unbanned %s", aAddrStr);
-		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()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", "invalid ban index");
-		else if(!pServer->BanRemove(Info.m_Addr))
-		{
-			char aAddrStr[NETADDR_MAXSTRSIZE];
-			net_addr_str(&Info.m_Addr, aAddrStr, sizeof(aAddrStr));
-
-			char aBuf[256];
-			str_format(aBuf, sizeof(aBuf), "unbanned %s", aAddrStr);
-			pServer->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf);
-		}
-	}
-	else
-		pServer->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", "invalid network address");
-}
-
-void CServer::ConBans(IConsole::IResult *pResult, void *pUser)
-{
-	unsigned Now = time_timestamp();
-	char aBuf[1024];
-	char aAddrStr[NETADDR_MAXSTRSIZE];
-	CServer* pServer = (CServer *)pUser;
-
-	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;
-		net_addr_str(&Addr, aAddrStr, sizeof(aAddrStr));
-
-		if(Info.m_Expires == -1)
-		{
-			str_format(aBuf, sizeof(aBuf), "#%i %s for life", i, aAddrStr);
-		}
-		else
-		{
-			unsigned t = Info.m_Expires - Now;
-			str_format(aBuf, sizeof(aBuf), "#%i %s for %d minutes and %d seconds", i, aAddrStr, t/60, t%60);
-		}
-		pServer->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "Server", aBuf);
-	}
-	str_format(aBuf, sizeof(aBuf), "%d ban(s)", Num);
-	pServer->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "Server", aBuf);
-}
-
 void CServer::ConStatus(IConsole::IResult *pResult, void *pUser)
 {
-	int i;
-	NETADDR Addr;
 	char aBuf[1024];
 	char aAddrStr[NETADDR_MAXSTRSIZE];
-	CServer* pServer = (CServer *)pUser;
+	CServer* pThis = static_cast<CServer *>(pUser);
 
-	for(i = 0; i < MAX_CLIENTS; i++)
+	for(int i = 0; i < MAX_CLIENTS; i++)
 	{
-		if(pServer->m_aClients[i].m_State != CClient::STATE_EMPTY)
+		if(pThis->m_aClients[i].m_State != CClient::STATE_EMPTY)
 		{
-			Addr = pServer->m_NetServer.ClientAddr(i);
-			net_addr_str(&Addr, aAddrStr, sizeof(aAddrStr));
-			if(pServer->m_aClients[i].m_State == CClient::STATE_INGAME)
+			net_addr_str(pThis->m_NetServer.ClientAddr(i), aAddrStr, sizeof(aAddrStr), true);
+			if(pThis->m_aClients[i].m_State == CClient::STATE_INGAME)
 				str_format(aBuf, sizeof(aBuf), "id=%d addr=%s name='%s' score=%d", i, aAddrStr,
-					pServer->m_aClients[i].m_aName, pServer->m_aClients[i].m_Score);
+					pThis->m_aClients[i].m_aName, pThis->m_aClients[i].m_Score);
 			else
 				str_format(aBuf, sizeof(aBuf), "id=%d addr=%s connecting", i, aAddrStr);
-			pServer->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "Server", aBuf);
+			pThis->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "Server", aBuf);
 		}
 	}
 }
@@ -1584,6 +1543,11 @@ void CServer::MapReload()
 	m_MapReload = 1;
 }
 
+bool CServer::DemoRecorder_IsRecording()
+{
+	return m_DemoRecorder.IsRecording();
+}
+
 void CServer::ConRecord(IConsole::IResult *pResult, void *pUser)
 {
 	CServer* pServer = (CServer *)pUser;
@@ -1635,7 +1599,7 @@ void CServer::ConBanmasters(IConsole::IResult *pResult, void *pUser)
 	for(int i = 0; i < NumBanmasters; i++)
 	{
 		NETADDR *pBanmaster = pServer->m_NetServer.BanmasterGet(i);
-		net_addr_str(pBanmaster, aIpString, sizeof(aIpString));
+		net_addr_str(pBanmaster, aIpString, sizeof(aIpString), 0);
 		str_format(aBuf, sizeof(aBuf), "%d: %s", i, aIpString);
 		pServer->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server/banmaster", aBuf);
 	}
@@ -1649,6 +1613,27 @@ void CServer::ConClearBanmasters(IConsole::IResult *pResult, void *pUser)
 	pServer->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server/banmaster", "cleared banmaster list");
 }
 
+void CServer::ConLogout(IConsole::IResult *pResult, void *pUser)
+{
+	CServer *pServer = (CServer *)pUser;
+
+	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);
+	}
+}
+
 void CServer::ConchainSpecialInfoupdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData)
 {
 	pfnCallback(pResult, pCallbackUserData);
@@ -1669,7 +1654,7 @@ void CServer::ConchainModCommandUpdate(IConsole::IResult *pResult, void *pUserDa
 	{
 		CServer *pThis = static_cast<CServer *>(pUserData);
 		const IConsole::CCommandInfo *pInfo = pThis->Console()->GetCommandInfo(pResult->GetString(0), CFGFLAG_SERVER, false);
-		int OldAccessLevel;
+		int OldAccessLevel = 0;
 		if(pInfo)
 			OldAccessLevel = pInfo->GetAccessLevel();
 		pfnCallback(pResult, pCallbackUserData);
@@ -1707,11 +1692,9 @@ void CServer::RegisterCommands()
 	m_pConsole = Kernel()->RequestInterface<IConsole>();
 
 	Console()->Register("kick", "i?r", CFGFLAG_SERVER, ConKick, this, "Kick player with specified id for any reason");
-	Console()->Register("ban", "s?ir", CFGFLAG_SERVER|CFGFLAG_STORE, ConBan, this, "Ban player with ip/id for x minutes for any reason");
-	Console()->Register("unban", "s", CFGFLAG_SERVER|CFGFLAG_STORE, ConUnban, this, "Unban ip");
-	Console()->Register("bans", "", CFGFLAG_SERVER|CFGFLAG_STORE, ConBans, this, "Show banlist");
 	Console()->Register("status", "", CFGFLAG_SERVER, ConStatus, this, "List players");
 	Console()->Register("shutdown", "", CFGFLAG_SERVER, ConShutdown, this, "Shut down");
+	Console()->Register("logout", "", CFGFLAG_SERVER, ConLogout, this, "Logout of rcon");
 
 	Console()->Register("record", "?s", CFGFLAG_SERVER|CFGFLAG_STORE, ConRecord, this, "Record to a file");
 	Console()->Register("stoprecord", "", CFGFLAG_SERVER, ConStopRecord, this, "Stop recording");
@@ -1776,9 +1759,9 @@ int main(int argc, const char **argv) // ignore_convention
 	IEngine *pEngine = CreateEngine("Teeworlds");
 	IEngineMap *pEngineMap = CreateEngineMap();
 	IGameServer *pGameServer = CreateGameServer();
-	IConsole *pConsole = CreateConsole(CFGFLAG_SERVER);
+	IConsole *pConsole = CreateConsole(CFGFLAG_SERVER|CFGFLAG_ECON);
 	IEngineMasterServer *pEngineMasterServer = CreateEngineMasterServer();
-	IStorage *pStorage = CreateStorage("Teeworlds", argc, argv); // ignore_convention
+	IStorage *pStorage = CreateStorage("Teeworlds", IStorage::STORAGETYPE_SERVER, argc, argv); // ignore_convention
 	IConfig *pConfig = CreateConfig();
 
 	pServer->InitRegister(&pServer->m_NetServer, pEngineMasterServer, pConsole);