about summary refs log tree commit diff
path: root/src/engine/server
diff options
context:
space:
mode:
Diffstat (limited to 'src/engine/server')
-rw-r--r--src/engine/server/es_register.cpp271
-rw-r--r--src/engine/server/es_server.cpp1969
-rw-r--r--src/engine/server/register.cpp264
-rw-r--r--src/engine/server/register.h48
-rw-r--r--src/engine/server/server.cpp1400
-rw-r--r--src/engine/server/server.h195
6 files changed, 1907 insertions, 2240 deletions
diff --git a/src/engine/server/es_register.cpp b/src/engine/server/es_register.cpp
deleted file mode 100644
index 0eb5dba5..00000000
--- a/src/engine/server/es_register.cpp
+++ /dev/null
@@ -1,271 +0,0 @@
-#include <string.h>
-#include <base/system.h>
-#include <engine/e_network.h>
-#include <engine/e_config.h>
-#include <engine/e_engine.h>
-
-#include <mastersrv/mastersrv.h>
-
-extern CNetServer *m_pNetServer;
-
-enum
-{
-	REGISTERSTATE_START=0,
-	REGISTERSTATE_UPDATE_ADDRS,
-	REGISTERSTATE_QUERY_COUNT,
-	REGISTERSTATE_HEARTBEAT,
-	REGISTERSTATE_REGISTERED,
-	REGISTERSTATE_ERROR
-};
-
-static int register_state = REGISTERSTATE_START;
-static int64 register_state_start = 0;
-static int register_first = 1;
-static int register_count = 0;
-
-static void register_new_state(int state)
-{
-	register_state = state;
-	register_state_start = time_get();
-}
-
-static void register_send_fwcheckresponse(NETADDR *pAddr)
-{
-	CNetChunk Packet;
-	Packet.m_ClientID = -1;
-	Packet.m_Address = *pAddr;
-	Packet.m_Flags = NETSENDFLAG_CONNLESS;
-	Packet.m_DataSize = sizeof(SERVERBROWSE_FWRESPONSE);
-	Packet.m_pData = SERVERBROWSE_FWRESPONSE;
-	m_pNetServer->Send(&Packet);
-}
-	
-static void register_send_heartbeat(NETADDR addr)
-{
-	static unsigned char data[sizeof(SERVERBROWSE_HEARTBEAT) + 2];
-	unsigned short port = config.sv_port;
-	CNetChunk Packet;
-	
-	mem_copy(data, SERVERBROWSE_HEARTBEAT, sizeof(SERVERBROWSE_HEARTBEAT));
-	
-	Packet.m_ClientID = -1;
-	Packet.m_Address = addr;
-	Packet.m_Flags = NETSENDFLAG_CONNLESS;
-	Packet.m_DataSize = sizeof(SERVERBROWSE_HEARTBEAT) + 2;
-	Packet.m_pData = &data;
-
-	/* supply the set port that the master can use if it has problems */	
-	if(config.sv_external_port)
-		port = config.sv_external_port;
-	data[sizeof(SERVERBROWSE_HEARTBEAT)] = port >> 8;
-	data[sizeof(SERVERBROWSE_HEARTBEAT)+1] = port&0xff;
-	m_pNetServer->Send(&Packet);
-}
-
-static void register_send_count_request(NETADDR Addr)
-{
-	CNetChunk Packet;
-	Packet.m_ClientID = -1;
-	Packet.m_Address = Addr;
-	Packet.m_Flags = NETSENDFLAG_CONNLESS;
-	Packet.m_DataSize = sizeof(SERVERBROWSE_GETCOUNT);
-	Packet.m_pData = SERVERBROWSE_GETCOUNT;
-	m_pNetServer->Send(&Packet);
-}
-
-typedef struct
-{
-	NETADDR addr;
-	int count;
-	int valid;
-	int64 last_send;
-} MASTERSERVER_INFO;
-
-static MASTERSERVER_INFO masterserver_info[MAX_MASTERSERVERS] = {{{0}}};
-static int register_registered_server = -1;
-
-void register_update()
-{
-	int64 now = time_get();
-	int64 freq = time_freq();
-	
-	if(!config.sv_register)
-		return;
-	
-	mastersrv_update();
-	
-	if(register_state == REGISTERSTATE_START)
-	{
-		register_count = 0;
-		register_first = 1;
-		register_new_state(REGISTERSTATE_UPDATE_ADDRS);
-		mastersrv_refresh_addresses();
-		dbg_msg("register", "refreshing ip addresses");
-	}
-	else if(register_state == REGISTERSTATE_UPDATE_ADDRS)
-	{
-		register_registered_server = -1;
-		
-		if(!mastersrv_refreshing())
-		{
-			int i;
-			for(i = 0; i < MAX_MASTERSERVERS; i++)
-			{
-				NETADDR addr = mastersrv_get(i);
-				masterserver_info[i].addr = addr;
-				masterserver_info[i].count = 0;
-				
-				if(!addr.ip[0] && !addr.ip[1] && !addr.ip[2] && !addr.ip[3])
-					masterserver_info[i].valid = 0;
-				else
-				{
-					masterserver_info[i].valid = 1;
-					masterserver_info[i].count = -1;
-					masterserver_info[i].last_send = 0;
-				}
-			}
-			
-			dbg_msg("register", "fetching server counts");
-			register_new_state(REGISTERSTATE_QUERY_COUNT);
-		}
-	}
-	else if(register_state == REGISTERSTATE_QUERY_COUNT)
-	{
-		int i;
-		int left = 0;
-		for(i = 0; i < MAX_MASTERSERVERS; i++)
-		{
-			if(!masterserver_info[i].valid)
-				continue;
-				
-			if(masterserver_info[i].count == -1)
-			{
-				left++;
-				if(masterserver_info[i].last_send+freq < now)
-				{
-					masterserver_info[i].last_send = now;
-					register_send_count_request(masterserver_info[i].addr);
-				}
-			}
-		}
-		
-		/* check if we are done or timed out */
-		if(left == 0 || now > register_state_start+freq*3)
-		{
-			/* choose server */
-			int best = -1;
-			int i;
-			for(i = 0; i < MAX_MASTERSERVERS; i++)
-			{
-				if(!masterserver_info[i].valid || masterserver_info[i].count == -1)
-					continue;
-					
-				if(best == -1 || masterserver_info[i].count < masterserver_info[best].count)
-					best = i;
-			}
-
-			/* server chosen */
-			register_registered_server = best;
-			if(register_registered_server == -1)
-			{
-				dbg_msg("register", "WARNING: No master servers. Retrying in 60 seconds");
-				register_new_state(REGISTERSTATE_ERROR);
-			}
-			else
-			{			
-				dbg_msg("register", "choosen '%s' as master, sending heartbeats", mastersrv_name(register_registered_server));
-				masterserver_info[register_registered_server].last_send = 0;
-				register_new_state(REGISTERSTATE_HEARTBEAT);
-			}
-		}
-	}
-	else if(register_state == REGISTERSTATE_HEARTBEAT)
-	{
-		/* check if we should send heartbeat */
-		if(now > masterserver_info[register_registered_server].last_send+freq*15)
-		{
-			masterserver_info[register_registered_server].last_send = now;
-			register_send_heartbeat(masterserver_info[register_registered_server].addr);
-		}
-		
-		if(now > register_state_start+freq*60)
-		{
-			dbg_msg("register", "WARNING: Master server is not responding, switching master");
-			register_new_state(REGISTERSTATE_START);
-		}
-	}
-	else if(register_state == REGISTERSTATE_REGISTERED)
-	{
-		if(register_first)
-			dbg_msg("register", "server registered");
-			
-		register_first = 0;
-		
-		/* check if we should send new heartbeat again */
-		if(now > register_state_start+freq)
-		{
-			if(register_count == 120) /* redo the whole process after 60 minutes to balance out the master servers */
-				register_new_state(REGISTERSTATE_START);
-			else
-			{
-				register_count++;
-				register_new_state(REGISTERSTATE_HEARTBEAT);
-			}
-		}
-	}
-	else if(register_state == REGISTERSTATE_ERROR)
-	{
-		/* check for restart */
-		if(now > register_state_start+freq*60)
-			register_new_state(REGISTERSTATE_START);
-	}
-}
-
-static void register_got_count(CNetChunk *pChunk)
-{
-	unsigned char *pData = (unsigned char *)pChunk->m_pData;
-	int Count = (pData[sizeof(SERVERBROWSE_COUNT)]<<8) | pData[sizeof(SERVERBROWSE_COUNT)+1];
-
-	for(int i = 0; i < MAX_MASTERSERVERS; i++)
-	{
-		if(net_addr_comp(&masterserver_info[i].addr, &pChunk->m_Address) == 0)
-		{
-			masterserver_info[i].count = Count;
-			break;
-		}
-	}
-}
-
-int register_process_packet(CNetChunk *pPacket)
-{
-	if(pPacket->m_DataSize == sizeof(SERVERBROWSE_FWCHECK) &&
-		memcmp(pPacket->m_pData, SERVERBROWSE_FWCHECK, sizeof(SERVERBROWSE_FWCHECK)) == 0)
-	{
-		register_send_fwcheckresponse(&pPacket->m_Address);
-		return 1;
-	}
-	else if(pPacket->m_DataSize == sizeof(SERVERBROWSE_FWOK) &&
-		memcmp(pPacket->m_pData, SERVERBROWSE_FWOK, sizeof(SERVERBROWSE_FWOK)) == 0)
-	{
-		if(register_first)
-			dbg_msg("register", "no firewall/nat problems detected");
-		register_new_state(REGISTERSTATE_REGISTERED);
-		return 1;
-	}
-	else if(pPacket->m_DataSize == sizeof(SERVERBROWSE_FWERROR) &&
-		memcmp(pPacket->m_pData, SERVERBROWSE_FWERROR, sizeof(SERVERBROWSE_FWERROR)) == 0)
-	{
-		dbg_msg("register", "ERROR: the master server reports that clients can not connect to this server.");
-		dbg_msg("register", "ERROR: configure your firewall/nat to let through udp on port %d.", config.sv_port);
-		register_new_state(REGISTERSTATE_ERROR);
-		return 1;
-	}
-	else if(pPacket->m_DataSize == sizeof(SERVERBROWSE_COUNT)+2 &&
-		memcmp(pPacket->m_pData, SERVERBROWSE_COUNT, sizeof(SERVERBROWSE_COUNT)) == 0)
-	{
-		register_got_count(pPacket);
-		return 1;
-	}
-
-	return 0;
-}
diff --git a/src/engine/server/es_server.cpp b/src/engine/server/es_server.cpp
deleted file mode 100644
index cc0e6e0f..00000000
--- a/src/engine/server/es_server.cpp
+++ /dev/null
@@ -1,1969 +0,0 @@
-/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-
-#include <base/system.h>
-
-#include <engine/e_config.h>
-#include <engine/e_engine.h>
-#include <engine/e_server_interface.h>
-
-#include <engine/e_protocol.h>
-#include <engine/e_snapshot.h>
-
-#include <engine/e_compression.h>
-
-#include <engine/e_network.h>
-#include <engine/e_config.h>
-#include <engine/e_packer.h>
-#include <engine/e_datafile.h>
-#include <engine/e_demorec.h>
-
-#include <mastersrv/mastersrv.h>
-
-#if defined(CONF_FAMILY_WINDOWS) 
-	#define _WIN32_WINNT 0x0500 
-	#include <windows.h> 
-#endif
-
-
-extern int register_process_packet(CNetChunk *pPacket);
-extern int register_update();
-
-static const char *str_ltrim(const char *str)
-{
-	while(*str && *str <= 32)
-		str++;
-	return str;
-}
-
-static void str_rtrim(char *str)
-{
-	int i = str_length(str);
-	while(i >= 0)
-	{
-		if(str[i] > 32)
-			break;
-		str[i] = 0;
-		i--;
-	}
-}
-
-
-class CSnapIDPool
-{
-	enum
-	{
-		MAX_IDS = 16*1024,
-	};
-
-	class CID
-	{
-	public:
-		short m_Next;
-		short m_State; /* 0 = free, 1 = alloced, 2 = timed */
-		int m_Timeout;
-	};
-
-	CID m_aIDs[MAX_IDS];
-		
-	int m_FirstFree;
-	int m_FirstTimed;
-	int m_LastTimed;
-	int m_Usage;
-	int m_InUsage;
-
-public:	
-
-	CSnapIDPool()
-	{
-		Reset();
-	}
-	
-	void Reset()
-	{
-		for(int i = 0; i < MAX_IDS; i++)
-		{
-			m_aIDs[i].m_Next = i+1;
-			m_aIDs[i].m_State = 0;
-		}
-			
-		m_aIDs[MAX_IDS-1].m_Next = -1;
-		m_FirstFree = 0;
-		m_FirstTimed = -1;
-		m_LastTimed = -1;
-		m_Usage = 0;
-		m_InUsage = 0;
-	}
-	
-
-	void RemoveFirstTimeout()
-	{
-		int NextTimed = m_aIDs[m_FirstTimed].m_Next;
-		
-		/* 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;
-		
-		/* remove it from the timed list */
-		m_FirstTimed = NextTimed;
-		if(m_FirstTimed == -1)
-			m_LastTimed = -1;
-			
-		m_Usage--;
-	}
-
-	int 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");
-		m_FirstFree = m_aIDs[m_FirstFree].m_Next;
-		m_aIDs[id].m_State = 1;
-		m_Usage++;
-		m_InUsage++;
-		return id;
-	}
-
-	void TimeoutIDs()
-	{
-		/* process timed ids */
-		while(m_FirstTimed != -1)
-			RemoveFirstTimeout();
-	}
-
-	void FreeID(int id)
-	{
-		dbg_assert(m_aIDs[id].m_State == 1, "id is not alloced");
-
-		m_InUsage--;
-		m_aIDs[id].m_State = 2;
-		m_aIDs[id].m_Timeout = time_get()+time_freq()*5;
-		m_aIDs[id].m_Next = -1;
-		
-		if(m_LastTimed != -1)
-		{
-			m_aIDs[m_LastTimed].m_Next = id;
-			m_LastTimed = id;
-		}
-		else
-		{
-			m_FirstTimed = id;
-			m_LastTimed = id;
-		}
-	}
-};
-
-#if 0
-class IGameServer
-{
-public:
-	/*
-		Function: mods_console_init
-			TODO
-	*/
-	virtual void ConsoleInit();
-
-	/*
-		Function: Init
-			Called when the server is started.
-		
-		Remarks:
-			It's called after the map is loaded so all map items are available.
-	*/
-	void Init() = 0;
-
-	/*
-		Function: Shutdown
-			Called when the server quits.
-		
-		Remarks:
-			Should be used to clean up all resources used.
-	*/
-	void Shutdown() = 0;
-
-	/*
-		Function: ClientEnter
-			Called when a client has joined the game.
-			
-		Arguments:
-			cid - Client ID. Is 0 - MAX_CLIENTS.
-		
-		Remarks:
-			It's called when the client is finished loading and should enter gameplay.
-	*/
-	void ClientEnter(int cid) = 0;
-
-	/*
-		Function: ClientDrop
-			Called when a client drops from the server.
-
-		Arguments:
-			cid - Client ID. Is 0 - MAX_CLIENTS
-	*/
-	void ClientDrop(int cid) = 0;
-
-	/*
-		Function: ClientDirectInput
-			Called when the server recives new input from a client.
-
-		Arguments:
-			cid - Client ID. Is 0 - MAX_CLIENTS.
-			input - Pointer to the input data.
-			size - Size of the data. (NOT IMPLEMENTED YET)
-	*/
-	void ClientDirectInput(int cid, void *input) = 0;
-
-	/*
-		Function: ClientPredictedInput
-			Called when the server applys the predicted input on the client.
-
-		Arguments:
-			cid - Client ID. Is 0 - MAX_CLIENTS.
-			input - Pointer to the input data.
-			size - Size of the data. (NOT IMPLEMENTED YET)
-	*/
-	void ClientPredictedInput(int cid, void *input) = 0;
-
-
-	/*
-		Function: Tick
-			Called with a regular interval to progress the gameplay.
-			
-		Remarks:
-			The SERVER_TICK_SPEED tells the number of ticks per second.
-	*/
-	void Tick() = 0;
-
-	/*
-		Function: Presnap
-			Called before the server starts to construct snapshots for the clients.
-	*/
-	void Presnap() = 0;
-
-	/*
-		Function: Snap
-			Called to create the snapshot for a client.
-		
-		Arguments:
-			cid - Client ID. Is 0 - MAX_CLIENTS.
-		
-		Remarks:
-			The game should make a series of calls to <snap_new_item> to construct
-			the snapshot for the client.
-	*/
-	void Snap(int cid) = 0;
-
-	/*
-		Function: PostSnap
-			Called after the server is done sending the snapshots.
-	*/
-	void PostSnap() = 0;
-
-	/*
-		Function: ClientConnected
-			TODO
-		
-		Arguments:
-			arg1 - desc
-		
-		Returns:
-
-		See Also:
-			<other_func>
-	*/
-	void ClientConnected(int client_id) = 0;
-
-
-	/*
-		Function: NetVersion
-			TODO
-		
-		Arguments:
-			arg1 - desc
-		
-		Returns:
-
-		See Also:
-			<other_func>
-	*/
-	const char *NetVersion() = 0;
-
-	/*
-		Function: Version
-			TODO
-		
-		Arguments:
-			arg1 - desc
-		
-		Returns:
-
-		See Also:
-			<other_func>
-	*/
-	const char *Version() = 0;
-
-	/*
-		Function: Message
-			TODO
-		
-		Arguments:
-			arg1 - desc
-		
-		Returns:
-
-		See Also:
-			<other_func>
-	*/
-	void Message(int msg, int client_id) = 0;
-};
-
-#endif
-/*
-class IServer
-{
-public:
-	BanAdd
-	BanRemove
-	TickSpeed
-	Tick
-	Kick
-	SetBrowseInfo
-	SetClientScore
-	SetClientName
-	GetClientInfo
-	LatestInput
-	ClientName
-	
-	SendMessage()
-	
-	Map
-	
-	virtual int NewSnapID() = 0;
-	virtual int FreeSnapID(int i) = 0;
-};*/
-
-class CServer
-{
-public:
-	/* */
-	class CClient
-	{
-	public:
-	
-		enum
-		{
-			STATE_EMPTY = 0,
-			STATE_AUTH,
-			STATE_CONNECTING,
-			STATE_READY,
-			STATE_INGAME,
-			
-			SNAPRATE_INIT=0,
-			SNAPRATE_FULL,
-			SNAPRATE_RECOVER
-		};
-	
-		class CInput
-		{
-		public:
-			int m_aData[MAX_INPUT_SIZE];
-			int m_GameTick; /* the tick that was chosen for the input */
-		};
-	
-		/* connection state info */
-		int m_State;
-		int m_Latency;
-		int m_SnapRate;
-		
-		int m_LastAckedSnapshot;
-		int m_LastInputTick;
-		CSnapshotStorage m_Snapshots;
-		
-		CInput m_LatestInput;
-		CInput m_aInputs[200]; /* TODO: handle input better */
-		int m_CurrentInput;
-		
-		char m_aName[MAX_NAME_LENGTH];
-		char m_aClan[MAX_CLANNAME_LENGTH];
-		int m_Score;
-		int m_Authed;
-		
-		void 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;
-		}
-	};
-	
-	CClient m_aClients[MAX_CLIENTS];
-
-	CSnapshotBuilder m_SnapshotBuilder;
-	CSnapIDPool m_IDPool;
-	CNetServer m_NetServer;
-
-	int64 m_GameStartTime;
-	int m_CurrentTick;
-	int m_RunServer;
-
-	char m_aBrowseinfoGametype[16];
-	int m_BrowseinfoProgression;
-
-	int64 m_Lastheartbeat;
-	/*static NETADDR4 master_server;*/
-
-	char m_aCurrentMap[64];
-	int m_CurrentMapCrc;
-	unsigned char *m_pCurrentMapData;
-	int m_CurrentMapSize;	
-	
-	CServer()
-	{
-		m_CurrentTick = 0;
-		m_RunServer = 1;
-
-		mem_zero(m_aBrowseinfoGametype, sizeof(m_aBrowseinfoGametype));
-		m_BrowseinfoProgression = -1;
-
-		m_pCurrentMapData = 0;
-		m_CurrentMapSize = 0;	
-	}
-	
-
-	int TrySetClientName(int ClientID, const char *pName)
-	{
-		int i;
-		char aTrimmedName[64];
-
-		/* trim the name */
-		str_copy(aTrimmedName, str_ltrim(pName), sizeof(aTrimmedName));
-		str_rtrim(aTrimmedName);
-		dbg_msg("", "'%s' -> '%s'", pName, aTrimmedName);
-		pName = aTrimmedName;
-		
-		
-		/* check for empty names */
-		if(!pName[0])
-			return -1;
-		
-		/* make sure that two clients doesn't have the same name */
-		for(i = 0; i < MAX_CLIENTS; i++)
-			if(i != ClientID && m_aClients[i].m_State >= CClient::STATE_READY)
-			{
-				if(strcmp(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 SetClientName(int ClientID, const char *pName)
-	{
-		if(ClientID < 0 || ClientID > MAX_CLIENTS || m_aClients[ClientID].m_State < CClient::STATE_READY)
-			return;
-			
-		if(!pName)
-			return;
-			
-		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;
-			}
-		}
-	}
-
-	void 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 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 Kick(int ClientID, const char *pReason)
-	{
-		if(ClientID < 0 || ClientID > MAX_CLIENTS)
-			return;
-			
-		if(m_aClients[ClientID].m_State != CClient::STATE_EMPTY)
-			m_NetServer.Drop(ClientID, pReason);
-	}
-
-	int Tick()
-	{
-		return m_CurrentTick;
-	}
-
-	int64 TickStartTime(int Tick)
-	{
-		return m_GameStartTime + (time_freq()*Tick)/SERVER_TICK_SPEED;
-	}
-
-	int TickSpeed()
-	{
-		return SERVER_TICK_SPEED;
-	}
-
-	int Init()
-	{
-		int i;
-		for(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_CurrentTick = 0;
-
-		return 0;
-	}
-
-	int GetClientInfo(int ClientID, CLIENT_INFO *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->name = m_aClients[ClientID].m_aName;
-			pInfo->latency = m_aClients[ClientID].m_Latency;
-			return 1;
-		}
-		return 0;
-	}
-
-	int SendMsg(int ClientID)
-	{
-		const MSG_INFO *pInfo = msg_get_info();
-		CNetChunk Packet;
-		if(!pInfo)
-			return -1;
-			
-		mem_zero(&Packet, sizeof(CNetChunk));
-		
-		Packet.m_ClientID = ClientID;
-		Packet.m_pData = pInfo->data;
-		Packet.m_DataSize = pInfo->size;
-
-		if(pInfo->flags&MSGFLAG_VITAL)
-			Packet.m_Flags |= NETSENDFLAG_VITAL;
-		if(pInfo->flags&MSGFLAG_FLUSH)
-			Packet.m_Flags |= NETSENDFLAG_FLUSH;
-		
-		/* write message to demo recorder */
-		if(!(pInfo->flags&MSGFLAG_NORECORD))
-			demorec_record_message(pInfo->data, pInfo->size);
-
-		if(!(pInfo->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 DoSnapshot()
-	{
-		int i;
-		
-		{
-			static PERFORMACE_INFO scope = {"presnap", 0};
-			perf_start(&scope);
-			mods_presnap();
-			perf_end();
-		}
-		
-		/* create snapshot for demo recording */
-		if(demorec_isrecording())
-		{
-			char data[CSnapshot::MAX_SIZE];
-			int snapshot_size;
-
-			/* build snap and possibly add some messages */
-			m_SnapshotBuilder.Init();
-			mods_snap(-1);
-			snapshot_size = m_SnapshotBuilder.Finish(data);
-			
-			/* write snapshot */
-			demorec_record_snapshot(Tick(), data, snapshot_size);
-		}
-
-		/* create snapshots for all clients */
-		for(i = 0; i < MAX_CLIENTS; i++)
-		{
-			/* client must be ingame to recive snapshots */
-			if(m_aClients[i].m_State != CClient::STATE_INGAME)
-				continue;
-				
-			/* this client is trying to recover, don't spam snapshots */
-			if(m_aClients[i].m_SnapRate == CClient::SNAPRATE_RECOVER && (Tick()%50) != 0)
-				continue;
-				
-			/* this client is trying to recover, don't spam snapshots */
-			if(m_aClients[i].m_SnapRate == CClient::SNAPRATE_INIT && (Tick()%10) != 0)
-				continue;
-				
-			{
-				char data[CSnapshot::MAX_SIZE];
-				char deltadata[CSnapshot::MAX_SIZE];
-				char compdata[CSnapshot::MAX_SIZE];
-				int snapshot_size;
-				int crc;
-				static CSnapshot emptysnap;
-				CSnapshot *deltashot = &emptysnap;
-				int deltashot_size;
-				int delta_tick = -1;
-				int deltasize;
-				static PERFORMACE_INFO scope = {"build", 0};
-				perf_start(&scope);
-
-				m_SnapshotBuilder.Init();
-
-				{
-					static PERFORMACE_INFO scope = {"modsnap", 0};
-					perf_start(&scope);
-					mods_snap(i);
-					perf_end();
-				}
-
-				/* finish snapshot */
-				snapshot_size = m_SnapshotBuilder.Finish(data);
-				crc = ((CSnapshot*)data)->Crc();
-
-				/* remove old snapshos */
-				/* keep 3 seconds worth of snapshots */
-				m_aClients[i].m_Snapshots.PurgeUntil(m_CurrentTick-SERVER_TICK_SPEED*3);
-				
-				/* save it the snapshot */
-				m_aClients[i].m_Snapshots.Add(m_CurrentTick, time_get(), snapshot_size, data, 0);
-				
-				/* find snapshot that we can preform delta against */
-				emptysnap.m_DataSize = 0;
-				emptysnap.m_NumItems = 0;
-				
-				{
-					deltashot_size = m_aClients[i].m_Snapshots.Get(m_aClients[i].m_LastAckedSnapshot, 0, &deltashot, 0);
-					if(deltashot_size >= 0)
-						delta_tick = 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;
-					}
-				}
-				
-				/* create delta */
-				{
-					static PERFORMACE_INFO scope = {"delta", 0};
-					perf_start(&scope);
-					deltasize = CSnapshot::CreateDelta(deltashot, (CSnapshot*)data, deltadata);
-					perf_end();
-				}
-
-				
-				if(deltasize)
-				{
-					/* compress it */
-					int snapshot_size;
-					const int max_size = MAX_SNAPSHOT_PACKSIZE;
-					int numpackets;
-					int n, left;
-
-					{				
-						static PERFORMACE_INFO scope = {"compress", 0};
-						/*char buffer[512];*/
-						perf_start(&scope);
-						snapshot_size = intpack_compress(deltadata, deltasize, compdata);
-						
-						/*str_hex(buffer, sizeof(buffer), compdata, snapshot_size);
-						dbg_msg("", "deltasize=%d -> %d : %s", deltasize, snapshot_size, buffer);*/
-						
-						perf_end();
-					}
-
-					numpackets = (snapshot_size+max_size-1)/max_size;
-					
-					for(n = 0, left = snapshot_size; left; n++)
-					{
-						int chunk = left < max_size ? left : max_size;
-						left -= chunk;
-
-						if(numpackets == 1)
-							msg_pack_start_system(NETMSG_SNAPSINGLE, MSGFLAG_FLUSH);
-						else
-							msg_pack_start_system(NETMSG_SNAP, MSGFLAG_FLUSH);
-							
-						msg_pack_int(m_CurrentTick);
-						msg_pack_int(m_CurrentTick-delta_tick); /* compressed with */
-						
-						if(numpackets != 1)
-						{
-							msg_pack_int(numpackets);
-							msg_pack_int(n);
-						}
-						
-						msg_pack_int(crc);
-						msg_pack_int(chunk);
-						msg_pack_raw(&compdata[n*max_size], chunk);
-						msg_pack_end();
-						SendMsg(i);
-					}
-				}
-				else
-				{
-					msg_pack_start_system(NETMSG_SNAPEMPTY, MSGFLAG_FLUSH);
-					msg_pack_int(m_CurrentTick);
-					msg_pack_int(m_CurrentTick-delta_tick); /* compressed with */
-					msg_pack_end();
-					SendMsg(i);
-				}
-				
-				perf_end();
-			}
-		}
-
-		mods_postsnap();
-	}
-
-
-	static int NewClientCallback(int cid, void *pUser)
-	{
-		CServer *pThis = (CServer *)pUser;
-		pThis->m_aClients[cid].m_State = CClient::STATE_AUTH;
-		pThis->m_aClients[cid].m_aName[0] = 0;
-		pThis->m_aClients[cid].m_aClan[0] = 0;
-		pThis->m_aClients[cid].m_Authed = 0;
-		pThis->m_aClients[cid].Reset();
-		return 0;
-	}
-
-	static int DelClientCallback(int cid, void *pUser)
-	{
-		CServer *pThis = (CServer *)pUser;
-		
-		/* notify the mod about the drop */
-		if(pThis->m_aClients[cid].m_State >= CClient::STATE_READY)
-			mods_client_drop(cid);
-		
-		pThis->m_aClients[cid].m_State = CClient::STATE_EMPTY;
-		pThis->m_aClients[cid].m_aName[0] = 0;
-		pThis->m_aClients[cid].m_aClan[0] = 0;
-		pThis->m_aClients[cid].m_Authed = 0;
-		pThis->m_aClients[cid].m_Snapshots.PurgeAll();
-		return 0;
-	}
-
-	void SendMap(int cid)
-	{
-		msg_pack_start_system(NETMSG_MAP_CHANGE, MSGFLAG_VITAL|MSGFLAG_FLUSH);
-		msg_pack_string(config.sv_map, 0);
-		msg_pack_int(m_CurrentMapCrc);
-		msg_pack_end();
-		server_send_msg(cid);
-	}
-
-	void SendRconLine(int cid, const char *pLine)
-	{
-		msg_pack_start_system(NETMSG_RCON_LINE, MSGFLAG_VITAL);
-		msg_pack_string(pLine, 512);
-		msg_pack_end();
-		server_send_msg(cid);
-	}
-
-	static void SendRconLineAuthed(const char *pLine, void *pUser)
-	{
-		CServer *pThis = (CServer *)pUser;
-		static volatile int reentry_guard = 0;
-		int i;
-		
-		if(reentry_guard) return;
-		reentry_guard++;
-		
-		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);
-		}
-		
-		reentry_guard--;
-	}
-	
-	void ProcessClientPacket(CNetChunk *pPacket)
-	{
-		int cid = pPacket->m_ClientID;
-		NETADDR addr;
-		
-		int sys;
-		int msg = msg_unpack_start(pPacket->m_pData, pPacket->m_DataSize, &sys);
-		
-		if(m_aClients[cid].m_State == CClient::STATE_AUTH)
-		{
-			if(sys && msg == NETMSG_INFO)
-			{
-				char version[64];
-				const char *password;
-				str_copy(version, msg_unpack_string(), 64);
-				if(strcmp(version, mods_net_version()) != 0)
-				{
-					/* OH FUCK! wrong version, drop him */
-					char reason[256];
-					str_format(reason, sizeof(reason), "wrong version. server is running '%s' and client '%s'.", mods_net_version(), version);
-					m_NetServer.Drop(cid, reason);
-					return;
-				}
-				
-				str_copy(m_aClients[cid].m_aName, msg_unpack_string(), MAX_NAME_LENGTH);
-				str_copy(m_aClients[cid].m_aClan, msg_unpack_string(), MAX_CLANNAME_LENGTH);
-				password = msg_unpack_string();
-				
-				if(config.password[0] != 0 && strcmp(config.password, password) != 0)
-				{
-					/* wrong password */
-					m_NetServer.Drop(cid, "wrong password");
-					return;
-				}
-				
-				m_aClients[cid].m_State = CClient::STATE_CONNECTING;
-				SendMap(cid);
-			}
-		}
-		else
-		{
-			if(sys)
-			{
-				/* system message */
-				if(msg == NETMSG_REQUEST_MAP_DATA)
-				{
-					int chunk = msg_unpack_int();
-					int chunk_size = 1024-128;
-					int offset = chunk * chunk_size;
-					int last = 0;
-					
-					/* drop faulty map data requests */
-					if(chunk < 0 || offset > m_CurrentMapSize)
-						return;
-					
-					if(offset+chunk_size >= m_CurrentMapSize)
-					{
-						chunk_size = m_CurrentMapSize-offset;
-						if(chunk_size < 0)
-							chunk_size = 0;
-						last = 1;
-					}
-					
-					msg_pack_start_system(NETMSG_MAP_DATA, MSGFLAG_VITAL|MSGFLAG_FLUSH);
-					msg_pack_int(last);
-					msg_pack_int(m_CurrentMapSize);
-					msg_pack_int(chunk_size);
-					msg_pack_raw(&m_pCurrentMapData[offset], chunk_size);
-					msg_pack_end();
-					server_send_msg(cid);
-					
-					if(config.debug)
-						dbg_msg("server", "sending chunk %d with size %d", chunk, chunk_size);
-				}
-				else if(msg == NETMSG_READY)
-				{
-					if(m_aClients[cid].m_State == CClient::STATE_CONNECTING)
-					{
-						addr = m_NetServer.ClientAddr(cid);
-						
-						dbg_msg("server", "player is ready. cid=%x ip=%d.%d.%d.%d",
-							cid,
-							addr.ip[0], addr.ip[1], addr.ip[2], addr.ip[3]
-							);
-						m_aClients[cid].m_State = CClient::STATE_READY;
-						mods_connected(cid);
-					}
-				}
-				else if(msg == NETMSG_ENTERGAME)
-				{
-					if(m_aClients[cid].m_State == CClient::STATE_READY)
-					{
-						addr = m_NetServer.ClientAddr(cid);
-						
-						dbg_msg("server", "player has entered the game. cid=%x ip=%d.%d.%d.%d",
-							cid,
-							addr.ip[0], addr.ip[1], addr.ip[2], addr.ip[3]
-							);
-						m_aClients[cid].m_State = CClient::STATE_INGAME;
-						mods_client_enter(cid);
-					}
-				}
-				else if(msg == NETMSG_INPUT)
-				{
-					int tick, size, i;
-					CClient::CInput *pInput;
-					int64 tagtime;
-					
-					m_aClients[cid].m_LastAckedSnapshot = msg_unpack_int();
-					tick = msg_unpack_int();
-					size = msg_unpack_int();
-					
-					/* check for errors */
-					if(msg_unpack_error() || size/4 > MAX_INPUT_SIZE)
-						return;
-
-					if(m_aClients[cid].m_LastAckedSnapshot > 0)
-						m_aClients[cid].m_SnapRate = CClient::SNAPRATE_FULL;
-						
-					if(m_aClients[cid].m_Snapshots.Get(m_aClients[cid].m_LastAckedSnapshot, &tagtime, 0, 0) >= 0)
-						m_aClients[cid].m_Latency = (int)(((time_get()-tagtime)*1000)/time_freq());
-
-					/* add message to report the input timing */
-					/* skip packets that are old */
-					if(tick > m_aClients[cid].m_LastInputTick)
-					{
-						int time_left = ((TickStartTime(tick)-time_get())*1000) / time_freq();
-						msg_pack_start_system(NETMSG_INPUTTIMING, 0);
-						msg_pack_int(tick);
-						msg_pack_int(time_left);
-						msg_pack_end();
-						server_send_msg(cid);
-					}
-
-					m_aClients[cid].m_LastInputTick = tick;
-
-					pInput = &m_aClients[cid].m_aInputs[m_aClients[cid].m_CurrentInput];
-					
-					if(tick <= server_tick())
-						tick = server_tick()+1;
-
-					pInput->m_GameTick = tick;
-					
-					for(i = 0; i < size/4; i++)
-						pInput->m_aData[i] = msg_unpack_int();
-					
-					mem_copy(m_aClients[cid].m_LatestInput.m_aData, pInput->m_aData, MAX_INPUT_SIZE*sizeof(int));
-					
-					m_aClients[cid].m_CurrentInput++;
-					m_aClients[cid].m_CurrentInput %= 200;
-				
-					/* call the mod with the fresh input data */
-					if(m_aClients[cid].m_State == CClient::STATE_INGAME)
-						mods_client_direct_input(cid, m_aClients[cid].m_LatestInput.m_aData);
-				}
-				else if(msg == NETMSG_RCON_CMD)
-				{
-					const char *cmd = msg_unpack_string();
-					
-					if(msg_unpack_error() == 0 && m_aClients[cid].m_Authed)
-					{
-						dbg_msg("server", "cid=%d rcon='%s'", cid, cmd);
-						console_execute_line(cmd);
-					}
-				}
-				else if(msg == NETMSG_RCON_AUTH)
-				{
-					const char *pw;
-					msg_unpack_string(); /* login name, not used */
-					pw = msg_unpack_string();
-					
-					if(msg_unpack_error() == 0)
-					{
-						if(config.sv_rcon_password[0] == 0)
-						{
-							SendRconLine(cid, "No rcon password set on server. Set sv_rcon_password to enable the remote console.");
-						}
-						else if(strcmp(pw, config.sv_rcon_password) == 0)
-						{
-							msg_pack_start_system(NETMSG_RCON_AUTH_STATUS, MSGFLAG_VITAL);
-							msg_pack_int(1);
-							msg_pack_end();
-							server_send_msg(cid);
-							
-							m_aClients[cid].m_Authed = 1;
-							SendRconLine(cid, "Authentication successful. Remote console access granted.");
-							dbg_msg("server", "cid=%d authed", cid);
-						}
-						else
-						{
-							SendRconLine(cid, "Wrong password.");
-						}
-					}
-				}
-				else if(msg == NETMSG_PING)
-				{
-					msg_pack_start_system(NETMSG_PING_REPLY, 0);
-					msg_pack_end();
-					server_send_msg(cid);
-				}
-				else
-				{
-					char hex[] = "0123456789ABCDEF";
-					char buf[512];
-					int b;
-
-					for(b = 0; b < pPacket->m_DataSize && b < 32; b++)
-					{
-						buf[b*3] = hex[((const unsigned char *)pPacket->m_pData)[b]>>4];
-						buf[b*3+1] = hex[((const unsigned char *)pPacket->m_pData)[b]&0xf];
-						buf[b*3+2] = ' ';
-						buf[b*3+3] = 0;
-					}
-
-					dbg_msg("server", "strange message cid=%d msg=%d data_size=%d", cid, msg, pPacket->m_DataSize);
-					dbg_msg("server", "%s", buf);
-					
-				}
-			}
-			else
-			{
-				/* game message */
-				if(m_aClients[cid].m_State >= CClient::STATE_READY)
-					mods_message(msg, cid);
-			}
-		}
-	}
-		
-	void SendServerInfo(NETADDR *addr, int token)
-	{
-		CNetChunk Packet;
-		CPacker p;
-		char buf[128];
-
-		/* count the players */	
-		int player_count = 0;
-		int i;
-		for(i = 0; i < MAX_CLIENTS; i++)
-		{
-			if(m_aClients[i].m_State != CClient::STATE_EMPTY)
-				player_count++;
-		}
-		
-		p.Reset();
-
-		if(token >= 0)
-		{
-			/* new token based format */
-			p.AddRaw(SERVERBROWSE_INFO, sizeof(SERVERBROWSE_INFO));
-			str_format(buf, sizeof(buf), "%d", token);
-			p.AddString(buf, 6);
-		}
-		else
-		{
-			/* old format */
-			p.AddRaw(SERVERBROWSE_OLD_INFO, sizeof(SERVERBROWSE_OLD_INFO));
-		}
-		
-		p.AddString(mods_version(), 32);
-		p.AddString(config.sv_name, 64);
-		p.AddString(config.sv_map, 32);
-
-		/* gametype */
-		p.AddString(m_aBrowseinfoGametype, 16);
-
-		/* flags */
-		i = 0;
-		if(config.password[0])   /* password set */
-			i |= SRVFLAG_PASSWORD;
-		str_format(buf, sizeof(buf), "%d", i);
-		p.AddString(buf, 2);
-
-		/* progression */
-		str_format(buf, sizeof(buf), "%d", m_BrowseinfoProgression);
-		p.AddString(buf, 4);
-		
-		str_format(buf, sizeof(buf), "%d", player_count); p.AddString(buf, 3);  /* num players */
-		str_format(buf, sizeof(buf), "%d", m_NetServer.MaxClients()); p.AddString(buf, 3); /* max players */
-
-		for(i = 0; i < MAX_CLIENTS; i++)
-		{
-			if(m_aClients[i].m_State != CClient::STATE_EMPTY)
-			{
-				p.AddString(m_aClients[i].m_aName, 48);  /* player name */
-				str_format(buf, sizeof(buf), "%d", m_aClients[i].m_Score); p.AddString(buf, 6);  /* player score */
-			}
-		}
-		
-		
-		Packet.m_ClientID = -1;
-		Packet.m_Address = *addr;
-		Packet.m_Flags = NETSENDFLAG_CONNLESS;
-		Packet.m_DataSize = p.Size();
-		Packet.m_pData = p.Data();
-		m_NetServer.Send(&Packet);
-	}
-
-	int BanAdd(NETADDR Addr, int Seconds)
-	{
-		return m_NetServer.BanAdd(Addr, Seconds);	
-	}
-
-	int BanRemove(NETADDR Addr)
-	{
-		return m_NetServer.BanRemove(Addr);
-	}
-		
-
-	void PumpNetwork()
-	{
-		CNetChunk Packet;
-
-		m_NetServer.Update();
-		
-		/* process packets */
-		while(m_NetServer.Recv(&Packet))
-		{
-			if(Packet.m_ClientID == -1)
-			{
-				/* stateless */
-				if(!register_process_packet(&Packet))
-				{
-					if(Packet.m_DataSize == sizeof(SERVERBROWSE_GETINFO)+1 &&
-						memcmp(Packet.m_pData, SERVERBROWSE_GETINFO, sizeof(SERVERBROWSE_GETINFO)) == 0)
-					{
-						SendServerInfo(&Packet.m_Address, ((unsigned char *)Packet.m_pData)[sizeof(SERVERBROWSE_GETINFO)]);
-					}
-					
-					
-					if(Packet.m_DataSize == sizeof(SERVERBROWSE_OLD_GETINFO) &&
-						memcmp(Packet.m_pData, SERVERBROWSE_OLD_GETINFO, sizeof(SERVERBROWSE_OLD_GETINFO)) == 0)
-					{
-						SendServerInfo(&Packet.m_Address, -1);
-					}
-				}
-			}
-			else
-				ProcessClientPacket(&Packet);
-		}
-	}
-
-	int LoadMap(const char *pMapName)
-	{
-		DATAFILE *df;
-		char buf[512];
-		str_format(buf, sizeof(buf), "maps/%s.map", pMapName);
-		df = datafile_load(buf);
-		if(!df)
-			return 0;
-		
-		/* stop recording when we change map */
-		demorec_record_stop();
-		
-		/* reinit snapshot ids */
-		m_IDPool.TimeoutIDs();
-		
-		/* get the crc of the map */
-		m_CurrentMapCrc = datafile_crc(buf);
-		dbg_msg("server", "%s crc is %08x", buf, m_CurrentMapCrc);
-			
-		str_copy(m_aCurrentMap, pMapName, sizeof(m_aCurrentMap));
-		map_set(df);
-		
-		/* load compelate map into memory for download */
-		{
-			IOHANDLE file = engine_openfile(buf, IOFLAG_READ);
-			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;
-	}
-
-	int Run()
-	{
-		NETADDR bindaddr;
-
-		//snap_init_id();
-		net_init();
-		
-		/* */
-		console_register_print_callback(SendRconLineAuthed, this);
-
-		/* load map */
-		if(!LoadMap(config.sv_map))
-		{
-			dbg_msg("server", "failed to load map. mapname='%s'", config.sv_map);
-			return -1;
-		}
-		
-		/* start server */
-		/* TODO: IPv6 support */
-		if(config.sv_bindaddr[0] && net_host_lookup(config.sv_bindaddr, &bindaddr, NETTYPE_IPV4) == 0)
-		{
-			/* sweet! */
-			bindaddr.port = config.sv_port;
-		}
-		else
-		{
-			mem_zero(&bindaddr, sizeof(bindaddr));
-			bindaddr.port = config.sv_port;
-		}
-		
-		
-		if(!m_NetServer.Open(bindaddr, config.sv_max_clients, 0))
-		{
-			dbg_msg("server", "couldn't open socket. port might already be in use");
-			return -1;
-		}
-
-		m_NetServer.SetCallbacks(NewClientCallback, DelClientCallback, this);
-		
-		dbg_msg("server", "server name is '%s'", config.sv_name);
-		
-		mods_init();
-		dbg_msg("server", "version %s", mods_net_version());
-
-		/* start game */
-		{
-			int64 reporttime = time_get();
-			int reportinterval = 3;
-		
-			m_Lastheartbeat = 0;
-			m_GameStartTime = time_get();
-		
-			if(config.debug)
-				dbg_msg("server", "baseline memory usage %dk", mem_stats()->allocated/1024);
-
-			while(m_RunServer)
-			{
-				static PERFORMACE_INFO rootscope = {"root", 0};
-				int64 t = time_get();
-				int new_ticks = 0;
-				
-				perf_start(&rootscope);
-				
-				/* load new map TODO: don't poll this */
-				if(strcmp(config.sv_map, m_aCurrentMap) != 0 || config.sv_map_reload)
-				{
-					config.sv_map_reload = 0;
-					
-					/* load map */
-					if(LoadMap(config.sv_map))
-					{
-						int c;
-						
-						/* new map loaded */
-						mods_shutdown();
-						
-						for(c = 0; c < MAX_CLIENTS; c++)
-						{
-							if(m_aClients[c].m_State == CClient::STATE_EMPTY)
-								continue;
-							
-							SendMap(c);
-							m_aClients[c].Reset();
-							m_aClients[c].m_State = CClient::STATE_CONNECTING;
-						}
-						
-						m_GameStartTime = time_get();
-						m_CurrentTick = 0;
-						mods_init();
-					}
-					else
-					{
-						dbg_msg("server", "failed to load map. mapname='%s'", config.sv_map);
-						config_set_sv_map(&config, m_aCurrentMap);
-					}
-				}
-				
-				while(t > TickStartTime(m_CurrentTick+1))
-				{
-					m_CurrentTick++;
-					new_ticks++;
-					
-					/* apply new input */
-					{
-						static PERFORMACE_INFO scope = {"input", 0};
-						int c, i;
-						
-						perf_start(&scope);
-						
-						for(c = 0; c < MAX_CLIENTS; c++)
-						{
-							if(m_aClients[c].m_State == CClient::STATE_EMPTY)
-								continue;
-							for(i = 0; i < 200; i++)
-							{
-								if(m_aClients[c].m_aInputs[i].m_GameTick == server_tick())
-								{
-									if(m_aClients[c].m_State == CClient::STATE_INGAME)
-										mods_client_predicted_input(c, m_aClients[c].m_aInputs[i].m_aData);
-									break;
-								}
-							}
-						}
-						
-						perf_end();
-					}
-					
-					/* progress game */
-					{
-						static PERFORMACE_INFO scope = {"tick", 0};
-						perf_start(&scope);
-						mods_tick();
-						perf_end();
-					}
-				}
-				
-				/* snap game */
-				if(new_ticks)
-				{
-					if(config.sv_high_bandwidth || (m_CurrentTick%2) == 0)
-					{
-						static PERFORMACE_INFO scope = {"snap", 0};
-						perf_start(&scope);
-						DoSnapshot();
-						perf_end();
-					}
-				}
-				
-				/* master server stuff */
-				register_update();
-		
-
-				{
-					static PERFORMACE_INFO scope = {"net", 0};
-					perf_start(&scope);
-					PumpNetwork();
-					perf_end();
-				}
-
-				perf_end();
-		
-				if(reporttime < time_get())
-				{
-					if(config.debug)
-					{
-						/*
-						static NETSTATS prev_stats;
-						NETSTATS stats;
-						netserver_stats(net, &stats);
-						
-						perf_next();
-						
-						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);
-							
-						prev_stats = stats;
-						*/
-					}
-		
-					reporttime += time_freq()*reportinterval;
-				}
-				
-				/* wait for incomming data */
-				net_socket_read_wait(m_NetServer.Socket(), 5);
-			}
-		}
-
-		mods_shutdown();
-		map_unload();
-
-		if(m_pCurrentMapData)
-			mem_free(m_pCurrentMapData);
-		return 0;
-	}
-
-	static void ConKick(void *pResult, void *pUser)
-	{
-		((CServer *)pUser)->Kick(console_arg_int(pResult, 0), "kicked by console");
-	}
-
-	static int str_allnum(const char *str)
-	{
-		while(*str)
-		{
-			if(!(*str >= '0' && *str <= '9'))
-				return 0;
-			str++;
-		}
-		return 1;
-	}
-
-	static void ConBan(void *pResult, void *pUser)
-	{
-		NETADDR addr;
-		char addrstr[128];
-		const char *str = console_arg_string(pResult, 0);
-		int minutes = 30;
-		
-		if(console_arg_num(pResult) > 1)
-			minutes = console_arg_int(pResult, 1);
-		
-		if(net_addr_from_str(&addr, str) == 0)
-			((CServer *)pUser)->BanAdd(addr, minutes*60);
-		else if(str_allnum(str))
-		{
-			NETADDR addr;
-			int cid = atoi(str);
-
-			if(cid < 0 || cid > MAX_CLIENTS || ((CServer *)pUser)->m_aClients[cid].m_State == CClient::STATE_EMPTY)
-			{
-				dbg_msg("server", "invalid client id");
-				return;
-			}
-
-			addr = ((CServer *)pUser)->m_NetServer.ClientAddr(cid);
-			((CServer *)pUser)->BanAdd(addr, minutes*60);
-		}
-		
-		addr.port = 0;
-		net_addr_str(&addr, addrstr, sizeof(addrstr));
-		
-		if(minutes)
-			dbg_msg("server", "banned %s for %d minutes", addrstr, minutes);
-		else
-			dbg_msg("server", "banned %s for life", addrstr);
-	}
-
-	static void ConUnban(void *result, void *pUser)
-	{
-		NETADDR addr;
-		const char *str = console_arg_string(result, 0);
-		
-		if(net_addr_from_str(&addr, str) == 0)
-			((CServer *)pUser)->BanRemove(addr);
-		else
-			dbg_msg("server", "invalid network address");
-	}
-
-	static void ConBans(void *pResult, void *pUser)
-	{
-		unsigned now = time_timestamp();
-		
-		int num = ((CServer *)pUser)->m_NetServer.BanNum();
-		for(int i = 0; i < num; i++)
-		{
-			CNetServer::CBanInfo Info;
-			((CServer *)pUser)->m_NetServer.BanGet(i, &Info);
-			NETADDR Addr = Info.m_Addr;
-			
-			if(Info.m_Expires == -1)
-			{
-				dbg_msg("server", "#%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;
-				dbg_msg("server", "#%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);
-			}
-		}
-		dbg_msg("server", "%d ban(s)", num);
-	}
-
-	static void ConStatus(void *pResult, void *pUser)
-	{
-		int i;
-		NETADDR addr;
-		for(i = 0; i < MAX_CLIENTS; i++)
-		{
-			if(((CServer *)pUser)->m_aClients[i].m_State == CClient::STATE_INGAME)
-			{
-				addr = ((CServer *)pUser)->m_NetServer.ClientAddr(i);
-				dbg_msg("server", "id=%d addr=%d.%d.%d.%d:%d name='%s' score=%d",
-					i, addr.ip[0], addr.ip[1], addr.ip[2], addr.ip[3], addr.port,
-					((CServer *)pUser)->m_aClients[i].m_aName, ((CServer *)pUser)->m_aClients[i].m_Score);
-			}
-		}
-	}
-
-	static void ConShutdown(void *pResult, void *pUser)
-	{
-		((CServer *)pUser)->m_RunServer = 0;
-	}
-
-	static void ConRecord(void *pResult, void *pUser)
-	{
-		char filename[512];
-		str_format(filename, sizeof(filename), "demos/%s.demo", console_arg_string(pResult, 0));
-		demorec_record_start(filename, mods_net_version(), ((CServer *)pUser)->m_aCurrentMap, ((CServer *)pUser)->m_CurrentMapCrc, "server");
-	}
-
-	static void ConStopRecord(void *pResult, void *pUser)
-	{
-		demorec_record_stop();
-	}
-	
-
-	void RegisterCommands()
-	{
-		MACRO_REGISTER_COMMAND("kick", "i", CFGFLAG_SERVER, ConKick, this, "");
-		MACRO_REGISTER_COMMAND("ban", "s?i", CFGFLAG_SERVER, ConBan, this, "");
-		MACRO_REGISTER_COMMAND("unban", "s", CFGFLAG_SERVER, ConUnban, this, "");
-		MACRO_REGISTER_COMMAND("bans", "", CFGFLAG_SERVER, ConBans, this, "");
-		MACRO_REGISTER_COMMAND("status", "", CFGFLAG_SERVER, ConStatus, this, "");
-		MACRO_REGISTER_COMMAND("shutdown", "", CFGFLAG_SERVER, ConShutdown, this, "");
-
-		MACRO_REGISTER_COMMAND("record", "s", CFGFLAG_SERVER, ConRecord, this, "");
-		MACRO_REGISTER_COMMAND("stoprecord", "", CFGFLAG_SERVER, ConStopRecord, this, "");
-	}	
-	
-};
-
-
-// UGLY UGLY HACK for now
-CServer g_Server;
-CNetServer *m_pNetServer;
-
-int server_tick() { return g_Server.Tick(); }
-int server_tickspeed() { return g_Server.TickSpeed(); }
-int snap_new_id() { return g_Server.m_IDPool.NewID(); }
-void snap_free_id(int id) { return g_Server.m_IDPool.FreeID(id); }
-int server_send_msg(int client_id) { return g_Server.SendMsg(client_id); }
-void server_setbrowseinfo(const char *game_type, int progression) { g_Server.SetBrowseInfo(game_type, progression); }
-void server_setclientname(int client_id, const char *name) { g_Server.SetClientName(client_id, name); }
-void server_setclientscore(int client_id, int score) { g_Server.SetClientScore(client_id, score); }
-
-int server_getclientinfo(int client_id, CLIENT_INFO *info) { return g_Server.GetClientInfo(client_id, info); }
-
-void *snap_new_item(int type, int id, int size)
-{
-	dbg_assert(type >= 0 && type <=0xffff, "incorrect type");
-	dbg_assert(id >= 0 && id <=0xffff, "incorrect id");
-	return g_Server.m_SnapshotBuilder.NewItem(type, id, size);
-}
-
-int *server_latestinput(int client_id, int *size)
-{
-	if(client_id < 0 || client_id > MAX_CLIENTS || g_Server.m_aClients[client_id].m_State < CServer::CClient::STATE_READY)
-		return 0;
-	return g_Server.m_aClients[client_id].m_LatestInput.m_aData;
-}
-
-const char *server_clientname(int client_id)
-{
-	if(client_id < 0 || client_id > MAX_CLIENTS || g_Server.m_aClients[client_id].m_State < CServer::CClient::STATE_READY)
-		return "(invalid client)";
-	return g_Server.m_aClients[client_id].m_aName;
-}
-
-#if 0
-
-
-static void reset_client(int cid)
-{
-	/* reset input */
-	int i;
-	for(i = 0; i < 200; i++)
-		m_aClients[cid].m_aInputs[i].m_GameTick = -1;
-	m_aClients[cid].m_CurrentInput = 0;
-	mem_zero(&m_aClients[cid].m_Latestinput, sizeof(m_aClients[cid].m_Latestinput));
-
-	m_aClients[cid].m_Snapshots.PurgeAll();
-	m_aClients[cid].m_LastAckedSnapshot = -1;
-	m_aClients[cid].m_LastInputTick = -1;
-	m_aClients[cid].m_SnapRate = CClient::SNAPRATE_INIT;
-	m_aClients[cid].m_Score = 0;
-
-}
-
-
-static int new_client_callback(int cid, void *user)
-{
-	m_aClients[cid].state = CClient::STATE_AUTH;
-	m_aClients[cid].name[0] = 0;
-	m_aClients[cid].clan[0] = 0;
-	m_aClients[cid].authed = 0;
-	reset_client(cid);
-	return 0;
-}
-
-static int del_client_callback(int cid, void *user)
-{
-	/* notify the mod about the drop */
-	if(m_aClients[cid].state >= CClient::STATE_READY)
-		mods_client_drop(cid);
-	
-	m_aClients[cid].state = CClient::STATE_EMPTY;
-	m_aClients[cid].name[0] = 0;
-	m_aClients[cid].clan[0] = 0;
-	m_aClients[cid].authed = 0;
-	m_aClients[cid].snapshots.PurgeAll();
-	return 0;
-}
-static void server_send_map(int cid)
-{
-	msg_pack_start_system(NETMSG_MAP_CHANGE, MSGFLAG_VITAL|MSGFLAG_FLUSH);
-	msg_pack_string(config.sv_map, 0);
-	msg_pack_int(current_map_crc);
-	msg_pack_end();
-	server_send_msg(cid);
-}
-
-static void server_send_rcon_line(int cid, const char *line)
-{
-	msg_pack_start_system(NETMSG_RCON_LINE, MSGFLAG_VITAL);
-	msg_pack_string(line, 512);
-	msg_pack_end();
-	server_send_msg(cid);
-}
-
-static void server_send_rcon_line_authed(const char *line, void *user_data)
-{
-	static volatile int reentry_guard = 0;
-	int i;
-	
-	if(reentry_guard) return;
-	reentry_guard++;
-	
-	for(i = 0; i < MAX_CLIENTS; i++)
-	{
-		if(m_aClients[i].state != CClient::STATE_EMPTY && m_aClients[i].authed)
-			server_send_rcon_line(i, line);
-	}
-	
-	reentry_guard--;
-}
-
-static void server_pump_network()
-{
-	CNetChunk Packet;
-
-	m_NetServer.Update();
-	
-	/* process packets */
-	while(m_NetServer.Recv(&Packet))
-	{
-		if(Packet.m_ClientID == -1)
-		{
-			/* stateless */
-			if(!register_process_packet(&Packet))
-			{
-				if(Packet.m_DataSize == sizeof(SERVERBROWSE_GETINFO)+1 &&
-					memcmp(Packet.m_pData, SERVERBROWSE_GETINFO, sizeof(SERVERBROWSE_GETINFO)) == 0)
-				{
-					server_send_serverinfo(&Packet.m_Address, ((unsigned char *)Packet.m_pData)[sizeof(SERVERBROWSE_GETINFO)]);
-				}
-				
-				
-				if(Packet.m_DataSize == sizeof(SERVERBROWSE_OLD_GETINFO) &&
-					memcmp(Packet.m_pData, SERVERBROWSE_OLD_GETINFO, sizeof(SERVERBROWSE_OLD_GETINFO)) == 0)
-				{
-					server_send_serverinfo(&Packet.m_Address, -1);
-				}
-			}
-		}
-		else
-			server_process_client_packet(&Packet);
-	}
-}
-
-static int server_load_map(const char *mapname)
-{
-	DATAFILE *df;
-	char buf[512];
-	str_format(buf, sizeof(buf), "maps/%s.map", mapname);
-	df = datafile_load(buf);
-	if(!df)
-		return 0;
-	
-	/* stop recording when we change map */
-	demorec_record_stop();
-	
-	/* reinit snapshot ids */
-	snap_timeout_ids();
-	
-	/* get the crc of the map */
-	current_map_crc = datafile_crc(buf);
-	dbg_msg("server", "%s crc is %08x", buf, current_map_crc);
-		
-	str_copy(current_map, mapname, sizeof(current_map));
-	map_set(df);
-	
-	/* load compelate map into memory for download */
-	{
-		IOHANDLE file = engine_openfile(buf, IOFLAG_READ);
-		current_map_size = (int)io_length(file);
-		if(current_map_data)
-			mem_free(current_map_data);
-		current_map_data = (unsigned char *)mem_alloc(current_map_size, 1);
-		io_read(file, current_map_data, current_map_size);
-		io_close(file);
-	}
-	return 1;
-}
-
-static int server_run()
-{
-	NETADDR bindaddr;
-
-	snap_init_id();
-	net_init();
-	
-	/* */
-	console_register_print_callback(server_send_rcon_line_authed, 0);
-
-	/* load map */
-	if(!server_load_map(config.sv_map))
-	{
-		dbg_msg("server", "failed to load map. mapname='%s'", config.sv_map);
-		return -1;
-	}
-	
-	/* start server */
-	/* TODO: IPv6 support */
-	if(config.sv_bindaddr[0] && net_host_lookup(config.sv_bindaddr, &bindaddr, NETTYPE_IPV4) == 0)
-	{
-		/* sweet! */
-		bindaddr.port = config.sv_port;
-	}
-	else
-	{
-		mem_zero(&bindaddr, sizeof(bindaddr));
-		bindaddr.port = config.sv_port;
-	}
-	
-	
-	if(!m_NetServer.Open(bindaddr, config.sv_max_clients, 0))
-	{
-		dbg_msg("server", "couldn't open socket. port might already be in use");
-		return -1;
-	}
-
-	m_NetServer.SetCallbacks(new_client_callback, del_client_callback, this);
-	
-	dbg_msg("server", "server name is '%s'", config.sv_name);
-	
-	mods_init();
-	dbg_msg("server", "version %s", mods_net_version());
-
-	/* start game */
-	{
-		int64 reporttime = time_get();
-		int reportinterval = 3;
-	
-		lastheartbeat = 0;
-		game_start_time = time_get();
-	
-		if(config.debug)
-			dbg_msg("server", "baseline memory usage %dk", mem_stats()->allocated/1024);
-
-		while(run_server)
-		{
-			static PERFORMACE_INFO rootscope = {"root", 0};
-			int64 t = time_get();
-			int new_ticks = 0;
-			
-			perf_start(&rootscope);
-			
-			/* load new map TODO: don't poll this */
-			if(strcmp(config.sv_map, current_map) != 0 || config.sv_map_reload)
-			{
-				config.sv_map_reload = 0;
-				
-				/* load map */
-				if(server_load_map(config.sv_map))
-				{
-					int c;
-					
-					/* new map loaded */
-					mods_shutdown();
-					
-					for(c = 0; c < MAX_CLIENTS; c++)
-					{
-						if(m_aClients[c].state == CClient::STATE_EMPTY)
-							continue;
-						
-						server_send_map(c);
-						reset_client(c);
-						m_aClients[c].state = CClient::STATE_CONNECTING;
-					}
-					
-					game_start_time = time_get();
-					current_tick = 0;
-					mods_init();
-				}
-				else
-				{
-					dbg_msg("server", "failed to load map. mapname='%s'", config.sv_map);
-					config_set_sv_map(&config, current_map);
-				}
-			}
-			
-			while(t > server_tick_start_time(current_tick+1))
-			{
-				current_tick++;
-				new_ticks++;
-				
-				/* apply new input */
-				{
-					static PERFORMACE_INFO scope = {"input", 0};
-					int c, i;
-					
-					perf_start(&scope);
-					
-					for(c = 0; c < MAX_CLIENTS; c++)
-					{
-						if(m_aClients[c].state == CClient::STATE_EMPTY)
-							continue;
-						for(i = 0; i < 200; i++)
-						{
-							if(m_aClients[c].inputs[i].game_tick == server_tick())
-							{
-								if(m_aClients[c].state == CClient::STATE_INGAME)
-									mods_client_predicted_input(c, m_aClients[c].inputs[i].data);
-								break;
-							}
-						}
-					}
-					
-					perf_end();
-				}
-				
-				/* progress game */
-				{
-					static PERFORMACE_INFO scope = {"tick", 0};
-					perf_start(&scope);
-					mods_tick();
-					perf_end();
-				}
-			}
-			
-			/* snap game */
-			if(new_ticks)
-			{
-				if(config.sv_high_bandwidth || (current_tick%2) == 0)
-				{
-					static PERFORMACE_INFO scope = {"snap", 0};
-					perf_start(&scope);
-					server_do_snap();
-					perf_end();
-				}
-			}
-			
-			/* master server stuff */
-			register_update();
-	
-
-			{
-				static PERFORMACE_INFO scope = {"net", 0};
-				perf_start(&scope);
-				server_pump_network();
-				perf_end();
-			}
-
-			perf_end();
-	
-			if(reporttime < time_get())
-			{
-				if(config.debug)
-				{
-					/*
-					static NETSTATS prev_stats;
-					NETSTATS stats;
-					netserver_stats(net, &stats);
-					
-					perf_next();
-					
-					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);
-						
-					prev_stats = stats;
-					*/
-				}
-	
-				reporttime += time_freq()*reportinterval;
-			}
-			
-			/* wait for incomming data */
-			net_socket_read_wait(m_NetServer.Socket(), 5);
-		}
-	}
-
-	mods_shutdown();
-	map_unload();
-
-	if(current_map_data)
-		mem_free(current_map_data);
-	return 0;
-}
-
-
-#endif
-
-int main(int argc, char **argv)
-{
-	
-	m_pNetServer = &g_Server.m_NetServer;
-#if defined(CONF_FAMILY_WINDOWS)
-	int i;
-	for(i = 1; i < argc; i++)
-	{
-		if(strcmp("-s", argv[i]) == 0 || strcmp("--silent", argv[i]) == 0)
-		{
-			ShowWindow(GetConsoleWindow(), SW_HIDE);
-			break;
-		}
-	}
-#endif
-
-	/* init the engine */
-	dbg_msg("server", "starting...");
-	engine_init("Teeworlds");
-	
-	/* register all console commands */
-	
-	g_Server.RegisterCommands();
-	mods_console_init();
-	
-	/* parse the command line arguments */
-	engine_parse_arguments(argc, argv);
-	
-	/* run the server */
-	g_Server.Run();
-	return 0;
-}
-
diff --git a/src/engine/server/register.cpp b/src/engine/server/register.cpp
new file mode 100644
index 00000000..959b9288
--- /dev/null
+++ b/src/engine/server/register.cpp
@@ -0,0 +1,264 @@
+#include <base/system.h>
+#include <engine/shared/network.h>
+#include <engine/shared/config.h>
+#include <engine/shared/engine.h>
+#include <engine/masterserver.h>
+
+#include <mastersrv/mastersrv.h>
+
+#include "register.h"
+
+CRegister::CRegister()
+{
+	m_pNetServer = 0;
+	m_pMasterServer = 0;
+
+	m_RegisterState = REGISTERSTATE_START;
+	m_RegisterStateStart = 0;
+	m_RegisterFirst = 1;
+	m_RegisterCount = 0;
+
+	mem_zero(m_aMasterserverInfo, sizeof(m_aMasterserverInfo));
+	m_RegisterRegisteredServer = -1;
+}
+
+void CRegister::RegisterNewState(int State)
+{
+	m_RegisterState = State;
+	m_RegisterStateStart = time_get();
+}
+
+void CRegister::RegisterSendFwcheckresponse(NETADDR *pAddr)
+{
+	CNetChunk Packet;
+	Packet.m_ClientID = -1;
+	Packet.m_Address = *pAddr;
+	Packet.m_Flags = NETSENDFLAG_CONNLESS;
+	Packet.m_DataSize = sizeof(SERVERBROWSE_FWRESPONSE);
+	Packet.m_pData = SERVERBROWSE_FWRESPONSE;
+	m_pNetServer->Send(&Packet);
+}
+	
+void CRegister::RegisterSendHeartbeat(NETADDR Addr)
+{
+	static unsigned char aData[sizeof(SERVERBROWSE_HEARTBEAT) + 2];
+	unsigned short Port = g_Config.m_SvPort;
+	CNetChunk Packet;
+	
+	mem_copy(aData, SERVERBROWSE_HEARTBEAT, sizeof(SERVERBROWSE_HEARTBEAT));
+	
+	Packet.m_ClientID = -1;
+	Packet.m_Address = Addr;
+	Packet.m_Flags = NETSENDFLAG_CONNLESS;
+	Packet.m_DataSize = sizeof(SERVERBROWSE_HEARTBEAT) + 2;
+	Packet.m_pData = &aData;
+
+	// supply the set port that the master can use if it has problems
+	if(g_Config.m_SvExternalPort)
+		Port = g_Config.m_SvExternalPort;
+	aData[sizeof(SERVERBROWSE_HEARTBEAT)] = Port >> 8;
+	aData[sizeof(SERVERBROWSE_HEARTBEAT)+1] = Port&0xff;
+	m_pNetServer->Send(&Packet);
+}
+
+void CRegister::RegisterSendCountRequest(NETADDR Addr)
+{
+	CNetChunk Packet;
+	Packet.m_ClientID = -1;
+	Packet.m_Address = Addr;
+	Packet.m_Flags = NETSENDFLAG_CONNLESS;
+	Packet.m_DataSize = sizeof(SERVERBROWSE_GETCOUNT);
+	Packet.m_pData = SERVERBROWSE_GETCOUNT;
+	m_pNetServer->Send(&Packet);
+}
+
+void CRegister::RegisterGotCount(CNetChunk *pChunk)
+{
+	unsigned char *pData = (unsigned char *)pChunk->m_pData;
+	int Count = (pData[sizeof(SERVERBROWSE_COUNT)]<<8) | pData[sizeof(SERVERBROWSE_COUNT)+1];
+
+	for(int i = 0; i < IMasterServer::MAX_MASTERSERVERS; i++)
+	{
+		if(net_addr_comp(&m_aMasterserverInfo[i].m_Addr, &pChunk->m_Address) == 0)
+		{
+			m_aMasterserverInfo[i].m_Count = Count;
+			break;
+		}
+	}
+}
+
+void CRegister::Init(CNetServer *pNetServer, IEngineMasterServer *pMasterServer)
+{
+	m_pNetServer = pNetServer;
+	m_pMasterServer = pMasterServer;
+}
+
+void CRegister::RegisterUpdate()
+{
+	int64 Now = time_get();
+	int64 Freq = time_freq();
+
+	if(!g_Config.m_SvRegister)
+		return;
+
+	m_pMasterServer->Update();
+
+	if(m_RegisterState == REGISTERSTATE_START)
+	{
+		m_RegisterCount = 0;
+		m_RegisterFirst = 1;
+		RegisterNewState(REGISTERSTATE_UPDATE_ADDRS);
+		m_pMasterServer->RefreshAddresses();
+		dbg_msg("register", "refreshing ip addresses");
+	}
+	else if(m_RegisterState == REGISTERSTATE_UPDATE_ADDRS)
+	{
+		m_RegisterRegisteredServer = -1;
+	
+		if(!m_pMasterServer->IsRefreshing())
+		{
+			int i;
+			for(i = 0; i < IMasterServer::MAX_MASTERSERVERS; i++)
+			{
+				NETADDR addr = m_pMasterServer->GetAddr(i);
+				m_aMasterserverInfo[i].m_Addr = addr;
+				m_aMasterserverInfo[i].m_Count = 0;
+			
+				if(!addr.ip[0] && !addr.ip[1] && !addr.ip[2] && !addr.ip[3])
+					m_aMasterserverInfo[i].m_Valid = 0;
+				else
+				{
+					m_aMasterserverInfo[i].m_Valid = 1;
+					m_aMasterserverInfo[i].m_Count = -1;
+					m_aMasterserverInfo[i].m_LastSend = 0;
+				}
+			}
+			
+			dbg_msg("register", "fetching server counts");
+			RegisterNewState(REGISTERSTATE_QUERY_COUNT);
+		}
+	}
+	else if(m_RegisterState == REGISTERSTATE_QUERY_COUNT)
+	{
+		int Left = 0;
+		for(int i = 0; i < IMasterServer::MAX_MASTERSERVERS; i++)
+		{
+			if(!m_aMasterserverInfo[i].m_Valid)
+				continue;
+				
+			if(m_aMasterserverInfo[i].m_Count == -1)
+			{
+				Left++;
+				if(m_aMasterserverInfo[i].m_LastSend+Freq < Now)
+				{
+					m_aMasterserverInfo[i].m_LastSend = Now;
+					RegisterSendCountRequest(m_aMasterserverInfo[i].m_Addr);
+				}
+			}
+		}
+		
+		// check if we are done or timed out
+		if(Left == 0 || Now > m_RegisterStateStart+Freq*3)
+		{
+			// choose server
+			int Best = -1;
+			int i;
+			for(i = 0; i < IMasterServer::MAX_MASTERSERVERS; i++)
+			{
+				if(!m_aMasterserverInfo[i].m_Valid || m_aMasterserverInfo[i].m_Count == -1)
+					continue;
+					
+				if(Best == -1 || m_aMasterserverInfo[i].m_Count < m_aMasterserverInfo[Best].m_Count)
+					Best = i;
+			}
+
+			// server chosen
+			m_RegisterRegisteredServer = Best;
+			if(m_RegisterRegisteredServer == -1)
+			{
+				dbg_msg("register", "WARNING: No master servers. Retrying in 60 seconds");
+				RegisterNewState(REGISTERSTATE_ERROR);
+			}
+			else
+			{			
+				dbg_msg("register", "choosen '%s' as master, sending heartbeats", m_pMasterServer->GetName(m_RegisterRegisteredServer));
+				m_aMasterserverInfo[m_RegisterRegisteredServer].m_LastSend = 0;
+				RegisterNewState(REGISTERSTATE_HEARTBEAT);
+			}
+		}
+	}
+	else if(m_RegisterState == REGISTERSTATE_HEARTBEAT)
+	{
+		// check if we should send heartbeat
+		if(Now > m_aMasterserverInfo[m_RegisterRegisteredServer].m_LastSend+Freq*15)
+		{
+			m_aMasterserverInfo[m_RegisterRegisteredServer].m_LastSend = Now;
+			RegisterSendHeartbeat(m_aMasterserverInfo[m_RegisterRegisteredServer].m_Addr);
+		}
+		
+		if(Now > m_RegisterStateStart+Freq*60)
+		{
+			dbg_msg("register", "WARNING: Master server is not responding, switching master");
+			RegisterNewState(REGISTERSTATE_START);
+		}
+	}
+	else if(m_RegisterState == REGISTERSTATE_REGISTERED)
+	{
+		if(m_RegisterFirst)
+			dbg_msg("register", "server registered");
+			
+		m_RegisterFirst = 0;
+		
+		// check if we should send new heartbeat again
+		if(Now > m_RegisterStateStart+Freq)
+		{
+			if(m_RegisterCount == 120) // redo the whole process after 60 minutes to balance out the master servers
+				RegisterNewState(REGISTERSTATE_START);
+			else
+			{
+				m_RegisterCount++;
+				RegisterNewState(REGISTERSTATE_HEARTBEAT);
+			}
+		}
+	}
+	else if(m_RegisterState == REGISTERSTATE_ERROR)
+	{
+		// check for restart
+		if(Now > m_RegisterStateStart+Freq*60)
+			RegisterNewState(REGISTERSTATE_START);
+	}
+}
+
+int CRegister::RegisterProcessPacket(CNetChunk *pPacket)
+{
+	if(pPacket->m_DataSize == sizeof(SERVERBROWSE_FWCHECK) &&
+		mem_comp(pPacket->m_pData, SERVERBROWSE_FWCHECK, sizeof(SERVERBROWSE_FWCHECK)) == 0)
+	{
+		RegisterSendFwcheckresponse(&pPacket->m_Address);
+		return 1;
+	}
+	else if(pPacket->m_DataSize == sizeof(SERVERBROWSE_FWOK) &&
+		mem_comp(pPacket->m_pData, SERVERBROWSE_FWOK, sizeof(SERVERBROWSE_FWOK)) == 0)
+	{
+		if(m_RegisterFirst)
+			dbg_msg("register", "no firewall/nat problems detected");
+		RegisterNewState(REGISTERSTATE_REGISTERED);
+		return 1;
+	}
+	else if(pPacket->m_DataSize == sizeof(SERVERBROWSE_FWERROR) &&
+		mem_comp(pPacket->m_pData, SERVERBROWSE_FWERROR, sizeof(SERVERBROWSE_FWERROR)) == 0)
+	{
+		dbg_msg("register", "ERROR: the master server reports that clients can not connect to this server.");
+		dbg_msg("register", "ERROR: configure your firewall/nat to let through udp on port %d.", g_Config.m_SvPort);
+		RegisterNewState(REGISTERSTATE_ERROR);
+		return 1;
+	}
+	else if(pPacket->m_DataSize == sizeof(SERVERBROWSE_COUNT)+2 &&
+		mem_comp(pPacket->m_pData, SERVERBROWSE_COUNT, sizeof(SERVERBROWSE_COUNT)) == 0)
+	{
+		RegisterGotCount(pPacket);
+		return 1;
+	}
+
+	return 0;
+}
diff --git a/src/engine/server/register.h b/src/engine/server/register.h
new file mode 100644
index 00000000..a800ec1e
--- /dev/null
+++ b/src/engine/server/register.h
@@ -0,0 +1,48 @@
+#ifndef ENGINE_SERVER_REGISTER_H
+#define ENGINE_SERVER_REGISTER_H
+

+class CRegister

+{

+	enum

+	{

+		REGISTERSTATE_START=0,

+		REGISTERSTATE_UPDATE_ADDRS,

+		REGISTERSTATE_QUERY_COUNT,

+		REGISTERSTATE_HEARTBEAT,

+		REGISTERSTATE_REGISTERED,

+		REGISTERSTATE_ERROR

+	};

+

+	struct CMasterserverInfo

+	{

+		NETADDR m_Addr;

+		int m_Count;

+		int m_Valid;

+		int64 m_LastSend;

+	};

+

+	class CNetServer *m_pNetServer;

+	class IEngineMasterServer *m_pMasterServer;

+

+	int m_RegisterState;

+	int64 m_RegisterStateStart;

+	int m_RegisterFirst;

+	int m_RegisterCount;

+

+	class CMasterserverInfo m_aMasterserverInfo[IMasterServer::MAX_MASTERSERVERS];

+	int m_RegisterRegisteredServer;

+

+	void RegisterNewState(int State);

+	void RegisterSendFwcheckresponse(NETADDR *pAddr);

+	void RegisterSendHeartbeat(NETADDR Addr);

+	void RegisterSendCountRequest(NETADDR Addr);

+	void RegisterGotCount(class CNetChunk *pChunk);

+

+public:

+	CRegister();

+	void Init(class CNetServer *pNetServer, class IEngineMasterServer *pMasterServer);

+	void RegisterUpdate();

+	int RegisterProcessPacket(class CNetChunk *pPacket);

+};

+

+#endif

diff --git a/src/engine/server/server.cpp b/src/engine/server/server.cpp
new file mode 100644
index 00000000..45cec1e4
--- /dev/null
+++ b/src/engine/server/server.cpp
@@ -0,0 +1,1400 @@
+// copyright (c) 2007 magnus auvinen, see licence.txt for more info
+
+#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>
+#include <engine/shared/demorec.h>
+
+#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>
+
+#include "register.h"
+#include "server.h"
+
+#if defined(CONF_FAMILY_WINDOWS) 
+	#define _WIN32_WINNT 0x0500
+	#define NOGDI
+	#include <windows.h>
+#endif
+
+static const char *StrLtrim(const char *pStr)
+{
+	while(*pStr && *pStr <= 32)
+		pStr++;
+	return pStr;
+}
+
+static void StrRtrim(char *pStr)
+{
+	int i = str_length(pStr);
+	while(i >= 0)
+	{
+		if(pStr[i] > 32)
+			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;
+	}
+		
+	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;
+	
+	// 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;
+	
+	// remove it from the timed list
+	m_FirstTimed = NextTimed;
+	if(m_FirstTimed == -1)
+		m_LastTimed = -1;
+		
+	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");
+	m_FirstFree = m_aIDs[m_FirstFree].m_Next;
+	m_aIDs[Id].m_State = 1;
+	m_Usage++;
+	m_InUsage++;
+	return Id;
+}
+
+void CSnapIDPool::TimeoutIDs()
+{
+	// process timed ids
+	while(m_FirstTimed != -1)
+		RemoveFirstTimeout();
+}
+
+void CSnapIDPool::FreeID(int Id)
+{
+	dbg_assert(m_aIDs[Id].m_State == 1, "id is not alloced");
+
+	m_InUsage--;
+	m_aIDs[Id].m_State = 2;
+	m_aIDs[Id].m_Timeout = time_get()+time_freq()*5;
+	m_aIDs[Id].m_Next = -1;
+	
+	if(m_LastTimed != -1)
+	{
+		m_aIDs[m_LastTimed].m_Next = Id;
+		m_LastTimed = Id;
+	}
+	else
+	{
+		m_FirstTimed = Id;
+		m_LastTimed = Id;
+	}
+}
+	
+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;
+	
+	m_pGameServer = 0;
+	
+	m_CurrentGameTick = 0;
+	m_RunServer = 1;
+
+	mem_zero(m_aBrowseinfoGametype, sizeof(m_aBrowseinfoGametype));
+	m_BrowseinfoProgression = -1;
+
+	m_pCurrentMapData = 0;
+	m_CurrentMapSize = 0;
+
+	Init();
+}
+
+
+int CServer::TrySetClientName(int ClientID, const char *pName)
+{
+	char aTrimmedName[64];
+
+	// trim the name
+	str_copy(aTrimmedName, StrLtrim(pName), sizeof(aTrimmedName));
+	StrRtrim(aTrimmedName);
+	dbg_msg("", "'%s' -> '%s'", pName, aTrimmedName);
+	pName = aTrimmedName;
+	
+	
+	// check for empty names
+	if(!pName[0])
+		return -1;
+	
+	// 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;
+		
+	if(!pName)
+		return;
+		
+	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;
+		}
+	}
+}
+
+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)
+		return;
+		
+	if(m_aClients[ClientID].m_State != CClient::STATE_EMPTY)
+		m_NetServer.Drop(ClientID, pReason);
+}
+
+/*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;
+
+	return 0;
+}
+
+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]);
+	}
+}
+	
+
+int *CServer::LatestInput(int ClientId, int *size)
+{
+	if(ClientId < 0 || ClientId >= MAX_CLIENTS || m_aClients[ClientId].m_State < CServer::CClient::STATE_READY)
+		return 0;
+	return m_aClients[ClientId].m_LatestInput.m_aData;
+}
+
+const char *CServer::ClientName(int ClientId)
+{
+	if(ClientId < 0 || ClientId >= MAX_CLIENTS || m_aClients[ClientId].m_State < CServer::CClient::STATE_READY)
+		return "(invalid client)";
+	return m_aClients[ClientId].m_aName;
+}	
+
+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)
+{
+	return SendMsgEx(pMsg, Flags, ClientId, false);
+}
+
+int CServer::SendMsgEx(CMsgPacker *pMsg, int Flags, int ClientID, bool System)
+{
+	CNetChunk Packet;
+	if(!pMsg)
+		return -1;
+		
+	mem_zero(&Packet, sizeof(CNetChunk));
+	
+	Packet.m_ClientID = ClientID;
+	Packet.m_pData = pMsg->Data();
+	Packet.m_DataSize = pMsg->Size();
+	
+	// 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;
+	
+	// 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();
+	
+	// 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);
+		
+		// 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;
+			
+		// this client is trying to recover, don't spam snapshots
+		if(m_aClients[i].m_SnapRate == CClient::SNAPRATE_RECOVER && (Tick()%50) != 0)
+			continue;
+			
+		// this client is trying to recover, don't spam snapshots
+		if(m_aClients[i].m_SnapRate == CClient::SNAPRATE_INIT && (Tick()%10) != 0)
+			continue;
+			
+		{
+			char aData[CSnapshot::MAX_SIZE];
+			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(aData);
+			Crc = ((CSnapshot*)aData)->Crc();
+
+			// remove old snapshos
+			// keep 3 seconds worth of snapshots
+			m_aClients[i].m_Snapshots.PurgeUntil(m_CurrentGameTick-SERVER_TICK_SPEED*3);
+			
+			// save it the snapshot
+			m_aClients[i].m_Snapshots.Add(m_CurrentGameTick, time_get(), SnapshotSize, aData, 0);
+			
+			// find snapshot that we can preform delta against
+			EmptySnap.Clear();
+			
+			{
+				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;
+				}
+			}
+			
+			// create delta
+			DeltaSize = m_SnapshotDelta.CreateDelta(pDeltashot, (CSnapshot*)aData, aDeltaData);
+			
+			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;
+				
+				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);							
+						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)
+{
+	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].Reset();
+	return 0;
+}
+
+int CServer::DelClientCallback(int ClientId, void *pUser)
+{
+	CServer *pThis = (CServer *)pUser;
+	
+	// notify the mod about the drop
+	if(pThis->m_aClients[ClientId].m_State >= CClient::STATE_READY)
+		pThis->GameServer()->OnClientDrop(ClientId);
+	
+	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_Snapshots.PurgeAll();
+	return 0;
+}
+
+void CServer::SendMap(int ClientId)
+{
+	CMsgPacker Msg(NETMSG_MAP_CHANGE);
+	Msg.AddString(g_Config.m_SvMap, 0);
+	Msg.AddInt(m_CurrentMapCrc);
+	SendMsgEx(&Msg, MSGFLAG_VITAL|MSGFLAG_FLUSH, ClientId, true);
+}
+
+void CServer::SendRconLine(int ClientId, const char *pLine)
+{
+	CMsgPacker Msg(NETMSG_RCON_LINE);
+	Msg.AddString(pLine, 512);
+	SendMsgEx(&Msg, MSGFLAG_VITAL, ClientId, true);
+}
+
+void CServer::SendRconLineAuthed(const char *pLine, void *pUser)
+{
+	CServer *pThis = (CServer *)pUser;
+	static volatile int ReentryGuard = 0;
+	int i;
+	
+	if(ReentryGuard) return;
+	ReentryGuard++;
+	
+	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);
+	}
+	
+	ReentryGuard--;
+}
+
+void CServer::ProcessClientPacket(CNetChunk *pPacket)
+{
+	int ClientId = pPacket->m_ClientID;
+	NETADDR Addr;
+	CUnpacker Unpacker;
+	Unpacker.Reset(pPacket->m_pData, pPacket->m_DataSize);
+	
+	// unpack msgid and system flag
+	int Msg = Unpacker.GetInt();
+	int Sys = Msg&1;
+	Msg >>= 1;
+	
+	if(Unpacker.Error())
+		return;
+	
+	if(m_aClients[ClientId].m_State == CClient::STATE_AUTH)
+	{
+		if(Sys && Msg == NETMSG_INFO)
+		{
+			char aVersion[64];
+			const char *pPassword;
+			str_copy(aVersion, Unpacker.GetString(), 64);
+			if(str_comp(aVersion, GameServer()->NetVersion()) != 0)
+			{
+				// OH FUCK! wrong version, drop him
+				char aReason[256];
+				str_format(aReason, sizeof(aReason), "wrong version. server is running '%s' and client '%s'.", GameServer()->NetVersion(), aVersion);
+				m_NetServer.Drop(ClientId, aReason);
+				return;
+			}
+			
+			str_copy(m_aClients[ClientId].m_aName, Unpacker.GetString(), MAX_NAME_LENGTH);
+			str_copy(m_aClients[ClientId].m_aClan, Unpacker.GetString(), MAX_CLANNAME_LENGTH);
+			pPassword = Unpacker.GetString();
+			
+			if(g_Config.m_Password[0] != 0 && str_comp(g_Config.m_Password, pPassword) != 0)
+			{
+				// wrong password
+				m_NetServer.Drop(ClientId, "wrong password");
+				return;
+			}
+			
+			m_aClients[ClientId].m_State = CClient::STATE_CONNECTING;
+			SendMap(ClientId);
+		}
+	}
+	else
+	{
+		if(Sys)
+		{
+			// system message
+			if(Msg == NETMSG_REQUEST_MAP_DATA)
+			{
+				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)
+					dbg_msg("server", "sending chunk %d with size %d", Chunk, ChunkSize);
+			}
+			else if(Msg == NETMSG_READY)
+			{
+				if(m_aClients[ClientId].m_State == CClient::STATE_CONNECTING)
+				{
+					Addr = m_NetServer.ClientAddr(ClientId);
+					
+					dbg_msg("server", "player is ready. ClientId=%x ip=%d.%d.%d.%d",
+						ClientId, Addr.ip[0], Addr.ip[1], Addr.ip[2], Addr.ip[3]);
+					m_aClients[ClientId].m_State = CClient::STATE_READY;
+					GameServer()->OnClientConnected(ClientId);
+				}
+			}
+			else if(Msg == NETMSG_ENTERGAME)
+			{
+				if(m_aClients[ClientId].m_State == CClient::STATE_READY)
+				{
+					Addr = m_NetServer.ClientAddr(ClientId);
+					
+					dbg_msg("server", "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]);
+					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);
+				}
+
+				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);
+			}
+			else if(Msg == NETMSG_RCON_CMD)
+			{
+				const char *pCmd = Unpacker.GetString();
+				
+				if(Unpacker.Error() == 0 && m_aClients[ClientId].m_Authed)
+				{
+					dbg_msg("server", "ClientId=%d rcon='%s'", ClientId, pCmd);
+					Console()->ExecuteLine(pCmd);
+				}
+			}
+			else if(Msg == NETMSG_RCON_AUTH)
+			{
+				const char *pPw;
+				Unpacker.GetString(); // login name, not used
+				pPw = Unpacker.GetString();
+				
+				if(Unpacker.Error() == 0)
+				{
+					if(g_Config.m_SvRconPassword[0] == 0)
+					{
+						SendRconLine(ClientId, "No rcon password set on server. Set sv_rcon_password to enable the remote console.");
+					}
+					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);
+					}
+					else
+					{
+						SendRconLine(ClientId, "Wrong password.");
+					}
+				}
+			}
+			else if(Msg == NETMSG_PING)
+			{
+				CMsgPacker Msg(NETMSG_PING_REPLY);
+				SendMsgEx(&Msg, 0, ClientId, true);
+			}
+			else
+			{
+				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;
+				}
+
+				dbg_msg("server", "strange message ClientId=%d msg=%d data_size=%d", ClientId, Msg, pPacket->m_DataSize);
+				dbg_msg("server", "%s", aBuf);
+				
+			}
+		}
+		else
+		{
+			// game message
+			if(m_aClients[ClientId].m_State >= CClient::STATE_READY)
+				GameServer()->OnMessage(Msg, &Unpacker, ClientId);
+		}
+	}
+}
+	
+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++;
+	}
+	
+	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));
+	}
+	
+	p.AddString(GameServer()->Version(), 32);
+	p.AddString(g_Config.m_SvName, 64);
+	p.AddString(g_Config.m_SvMap, 32);
+
+	// gametype
+	p.AddString(m_aBrowseinfoGametype, 16);
+
+	// flags
+	int i = 0;
+	if(g_Config.m_Password[0])   // password set
+		i |= SERVER_FLAG_PASSWORD;
+	str_format(aBuf, sizeof(aBuf), "%d", i);
+	p.AddString(aBuf, 2);
+
+	// progression
+	str_format(aBuf, sizeof(aBuf), "%d", m_BrowseinfoProgression);
+	p.AddString(aBuf, 4);
+	
+	str_format(aBuf, sizeof(aBuf), "%d", PlayerCount); p.AddString(aBuf, 3);  // num players
+	str_format(aBuf, sizeof(aBuf), "%d", m_NetServer.MaxClients()); p.AddString(aBuf, 3); // max players
+
+	for(i = 0; i < MAX_CLIENTS; i++)
+	{
+		if(m_aClients[i].m_State != CClient::STATE_EMPTY)
+		{
+			p.AddString(m_aClients[i].m_aName, 48);  // player name
+			str_format(aBuf, sizeof(aBuf), "%d", m_aClients[i].m_Score); p.AddString(aBuf, 6);  // player score
+		}
+	}
+	
+	
+	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)
+{
+	return m_NetServer.BanAdd(Addr, Seconds);	
+}
+
+int CServer::BanRemove(NETADDR Addr)
+{
+	return m_NetServer.BanRemove(Addr);
+}
+	
+
+void CServer::PumpNetwork()
+{
+	CNetChunk Packet;
+
+	m_NetServer.Update();
+	
+	// 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)]);
+				}
+				
+				
+				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);
+				}
+			}
+		}
+		else
+			ProcessClientPacket(&Packet);
+	}
+}
+
+int CServer::LoadMap(const char *pMapName)
+{
+	//DATAFILE *df;
+	char aBuf[512];
+	str_format(aBuf, sizeof(aBuf), "maps/%s.map", pMapName);
+	
+	/*df = datafile_load(buf);
+	if(!df)
+		return 0;*/
+		
+	if(!m_pMap->Load(aBuf))
+		return 0;
+	
+	// stop recording when we change map
+	m_DemoRecorder.Stop();
+	
+	// reinit snapshot ids
+	m_IDPool.TimeoutIDs();
+	
+	// get the crc of the map
+	m_CurrentMapCrc = m_pMap->Crc();
+	dbg_msg("server", "%s crc is %08x", aBuf, m_CurrentMapCrc);
+		
+	str_copy(m_aCurrentMap, pMapName, sizeof(m_aCurrentMap));
+	//map_set(df);
+	
+	// load compelate map into memory for download
+	{
+		IOHANDLE File = Storage()->OpenFile(aBuf, IOFLAG_READ);
+		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;
+}
+
+void CServer::InitEngine(const char *pAppname)
+{
+	m_Engine.Init(pAppname);
+}
+
+void CServer::InitRegister(CNetServer *pNetServer, IEngineMasterServer *pMasterServer)
+{
+	m_Register.Init(pNetServer, pMasterServer);
+}
+
+int CServer::Run()
+{
+	m_pGameServer = Kernel()->RequestInterface<IGameServer>();
+	m_pMap = Kernel()->RequestInterface<IEngineMap>();
+	m_pStorage = Kernel()->RequestInterface<IStorage>();
+
+	//snap_init_id();
+	net_init();
+	
+	//
+	Console()->RegisterPrintCallback(SendRconLineAuthed, this);
+
+	// load map
+	if(!LoadMap(g_Config.m_SvMap))
+	{
+		dbg_msg("server", "failed to load map. mapname='%s'", g_Config.m_SvMap);
+		return -1;
+	}
+	
+	// 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, 0))
+	{
+		dbg_msg("server", "couldn't open socket. port might already be in use");
+		return -1;
+	}
+
+	m_NetServer.SetCallbacks(NewClientCallback, DelClientCallback, this);
+	
+	dbg_msg("server", "server name is '%s'", g_Config.m_SvName);
+	
+	GameServer()->OnInit();
+	dbg_msg("server", "version %s", GameServer()->NetVersion());
+
+	// start game
+	{
+		int64 ReportTime = time_get();
+		int ReportInterval = 3;
+	
+		m_Lastheartbeat = 0;
+		m_GameStartTime = time_get();
+	
+		if(g_Config.m_Debug)
+			dbg_msg("server", "baseline memory usage %dk", mem_stats()->allocated/1024);
+
+		while(m_RunServer)
+		{
+			int64 t = time_get();
+			int NewTicks = 0;
+			
+			// load new map TODO: don't poll this
+			if(str_comp(g_Config.m_SvMap, m_aCurrentMap) != 0 || g_Config.m_SvMapReload)
+			{
+				g_Config.m_SvMapReload = 0;
+				
+				// load map
+				if(LoadMap(g_Config.m_SvMap))
+				{
+					// new map loaded
+					GameServer()->OnShutdown();
+					
+					for(int c = 0; c < MAX_CLIENTS; c++)
+					{
+						if(m_aClients[c].m_State == CClient::STATE_EMPTY)
+							continue;
+						
+						SendMap(c);
+						m_aClients[c].Reset();
+						m_aClients[c].m_State = CClient::STATE_CONNECTING;
+					}
+					
+					m_GameStartTime = time_get();
+					m_CurrentGameTick = 0;
+					Kernel()->ReregisterInterface(GameServer());
+					GameServer()->OnInit();
+				}
+				else
+				{
+					dbg_msg("server", "failed to load map. mapname='%s'", g_Config.m_SvMap);
+					str_copy(g_Config.m_SvMap, m_aCurrentMap, sizeof(g_Config.m_SvMap));
+				}
+			}
+			
+			while(t > TickStartTime(m_CurrentGameTick+1))
+			{
+				m_CurrentGameTick++;
+				NewTicks++;
+				
+				// 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();
+			}
+			
+			// snap game
+			if(NewTicks)
+			{
+				if(g_Config.m_SvHighBandwidth || (m_CurrentGameTick%2) == 0)
+					DoSnapshot();
+			}
+			
+			// master server stuff
+			m_Register.RegisterUpdate();
+	
+			PumpNetwork();
+	
+			if(ReportTime < time_get())
+			{
+				if(g_Config.m_Debug)
+				{
+					/*
+					static NETSTATS prev_stats;
+					NETSTATS stats;
+					netserver_stats(net, &stats);
+					
+					perf_next();
+					
+					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);
+						
+					prev_stats = stats;
+					*/
+				}
+	
+				ReportTime += time_freq()*ReportInterval;
+			}
+			
+			// 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");
+	}
+
+	GameServer()->OnShutdown();
+	m_pMap->Unload();
+
+	if(m_pCurrentMapData)
+		mem_free(m_pCurrentMapData);
+	return 0;
+}
+
+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;
+	char aAddrStr[128];
+	const char *pStr = pResult->GetString(0);
+	int Minutes = 30;
+	
+	if(pResult->NumArguments() > 1)
+		Minutes = pResult->GetInteger(1);
+	
+	if(net_addr_from_str(&Addr, pStr) == 0)
+		((CServer *)pUser)->BanAdd(Addr, Minutes*60);
+	else if(StrAllnum(pStr))
+	{
+		int ClientId = str_toint(pStr);
+
+		if(ClientId < 0 || ClientId >= MAX_CLIENTS || ((CServer *)pUser)->m_aClients[ClientId].m_State == CClient::STATE_EMPTY)
+		{
+			dbg_msg("server", "invalid client id");
+			return;
+		}
+
+		NETADDR Addr = ((CServer *)pUser)->m_NetServer.ClientAddr(ClientId);
+		((CServer *)pUser)->BanAdd(Addr, Minutes*60);
+	}
+	
+	Addr.port = 0;
+	net_addr_str(&Addr, aAddrStr, sizeof(aAddrStr));
+	
+	if(Minutes)
+		dbg_msg("server", "banned %s for %d minutes", aAddrStr, Minutes);
+	else
+		dbg_msg("server", "banned %s for life", aAddrStr);
+}
+
+void CServer::ConUnban(IConsole::IResult *pResult, void *pUser)
+{
+	NETADDR Addr;
+	const char *pStr = pResult->GetString(0);
+	
+	if(net_addr_from_str(&Addr, pStr) == 0)
+		((CServer *)pUser)->BanRemove(Addr);
+	else
+		dbg_msg("server", "invalid network address");
+}
+
+void CServer::ConBans(IConsole::IResult *pResult, void *pUser)
+{
+	unsigned Now = time_timestamp();
+	char aBuf[1024];
+	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;
+		
+		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()->Print(aBuf);
+		dbg_msg("server", "%s", aBuf);
+	}
+	str_format(aBuf, sizeof(aBuf), "%d ban(s)", Num);
+	pServer->Console()->Print(aBuf);
+	dbg_msg("server", "%s", aBuf);
+}
+
+void CServer::ConStatus(IConsole::IResult *pResult, void *pUser)
+{
+	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_INGAME)
+		{
+			Addr = pServer->m_NetServer.ClientAddr(i);
+			str_format(aBuf, sizeof(aBuf), "id=%d addr=%d.%d.%d.%d:%d name='%s' score=%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_Score);
+			pServer->Console()->Print(aBuf);
+			dbg_msg("server", "%s", aBuf);
+		}
+	}
+}
+
+void CServer::ConShutdown(IConsole::IResult *pResult, void *pUser)
+{
+	((CServer *)pUser)->m_RunServer = 0;
+}
+
+void CServer::ConRecord(IConsole::IResult *pResult, void *pUser)
+{
+	char aFilename[512];
+	str_format(aFilename, sizeof(aFilename), "demos/%s.demo", pResult->GetString(0));
+	((CServer *)pUser)->m_DemoRecorder.Start(((CServer *)pUser)->Storage(), aFilename, ((CServer *)pUser)->GameServer()->NetVersion(), ((CServer *)pUser)->m_aCurrentMap, ((CServer *)pUser)->m_CurrentMapCrc, "server");
+}
+
+void CServer::ConStopRecord(IConsole::IResult *pResult, void *pUser)
+{
+	((CServer *)pUser)->m_DemoRecorder.Stop();
+}
+
+void CServer::ConchainSpecialInfoupdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData)
+{
+	pfnCallback(pResult, pCallbackUserData);
+	if(pResult->NumArguments())
+		((CServer *)pUserData)->UpdateServerInfo();
+}
+
+void CServer::RegisterCommands()
+{
+	m_pConsole = Kernel()->RequestInterface<IConsole>();
+	
+	Console()->Register("kick", "i", CFGFLAG_SERVER, ConKick, this, "");
+	Console()->Register("ban", "s?i", CFGFLAG_SERVER, ConBan, this, "");
+	Console()->Register("unban", "s", CFGFLAG_SERVER, ConUnban, this, "");
+	Console()->Register("bans", "", CFGFLAG_SERVER, ConBans, this, "");
+	Console()->Register("status", "", CFGFLAG_SERVER, ConStatus, this, "");
+	Console()->Register("shutdown", "", CFGFLAG_SERVER, ConShutdown, this, "");
+
+	Console()->Register("record", "s", CFGFLAG_SERVER, ConRecord, this, "");
+	Console()->Register("stoprecord", "", CFGFLAG_SERVER, ConStopRecord, this, "");
+
+	Console()->Chain("sv_name", ConchainSpecialInfoupdate, this);
+	Console()->Chain("password", ConchainSpecialInfoupdate, this);
+}	
+
+
+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)
+{
+	dbg_assert(Type >= 0 && Type <=0xffff, "incorrect type");
+	dbg_assert(Id >= 0 && Id <=0xffff, "incorrect id");
+	return m_SnapshotBuilder.NewItem(Type, Id, Size);		
+}
+
+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
+
+	// init the engine
+	dbg_msg("server", "starting...");
+	CServer *pServer = CreateServer();
+	pServer->InitEngine("Teeworlds");
+	
+	IKernel *pKernel = IKernel::Create();
+
+	// create the components
+	IEngineMap *pEngineMap = CreateEngineMap();
+	IGameServer *pGameServer = CreateGameServer();
+	IConsole *pConsole = CreateConsole();
+	IEngineMasterServer *pEngineMasterServer = CreateEngineMasterServer();
+	IStorage *pStorage = CreateStorage("Teeworlds", argv[0]); // ignore_convention
+	IConfig *pConfig = CreateConfig();
+	
+	pServer->InitRegister(&pServer->m_NetServer, pEngineMasterServer);
+
+	{
+		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));
+		
+		if(RegisterFail)
+			return -1;
+	}
+	
+	pConfig->Init();
+	pEngineMasterServer->Init(pServer->Engine());
+	pEngineMasterServer->Load();
+		
+	// register all console commands
+	pServer->RegisterCommands();
+	pGameServer->OnConsoleInit();
+	
+	// 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
+	
+	// run the server
+	pServer->Run();
+	
+	// free
+	delete pServer;
+	delete pKernel;
+	delete pEngineMap;
+	delete pGameServer;
+	delete pConsole;
+	delete pEngineMasterServer;
+	delete pStorage;
+	delete pConfig;
+	return 0;
+}
+
diff --git a/src/engine/server/server.h b/src/engine/server/server.h
new file mode 100644
index 00000000..6904085a
--- /dev/null
+++ b/src/engine/server/server.h
@@ -0,0 +1,195 @@
+#ifndef ENGINE_SERVER_SERVER_H
+#define ENGINE_SERVER_SERVER_H
+
+#include <engine/server.h>
+
+class CSnapIDPool
+{
+	enum
+	{
+		MAX_IDS = 16*1024,
+	};
+
+	class CID
+	{
+	public:
+		short m_Next;
+		short m_State; // 0 = free, 1 = alloced, 2 = timed
+		int m_Timeout;
+	};
+
+	CID m_aIDs[MAX_IDS];
+		
+	int m_FirstFree;
+	int m_FirstTimed;
+	int m_LastTimed;
+	int m_Usage;
+	int m_InUsage;
+
+public:	
+
+	CSnapIDPool();
+	
+	void Reset();
+	void RemoveFirstTimeout();
+	int NewID();
+	void TimeoutIDs();
+	void FreeID(int Id);
+};
+
+class CServer : public IServer
+{
+	class IGameServer *m_pGameServer;
+	class IConsole *m_pConsole;
+	class IStorage *m_pStorage;
+public:
+	class IGameServer *GameServer() { return m_pGameServer; }
+	class IConsole *Console() { return m_pConsole; }
+	class IStorage *Storage() { return m_pStorage; }
+	class CEngine *Engine() { return &m_Engine; }
+
+	class CClient
+	{
+	public:
+	
+		enum
+		{
+			STATE_EMPTY = 0,
+			STATE_AUTH,
+			STATE_CONNECTING,
+			STATE_READY,
+			STATE_INGAME,
+			
+			SNAPRATE_INIT=0,
+			SNAPRATE_FULL,
+			SNAPRATE_RECOVER
+		};
+	
+		class CInput
+		{
+		public:
+			int m_aData[MAX_INPUT_SIZE];
+			int m_GameTick; // the tick that was chosen for the input
+		};
+	
+		// connection state info
+		int m_State;
+		int m_Latency;
+		int m_SnapRate;
+		
+		int m_LastAckedSnapshot;
+		int m_LastInputTick;
+		CSnapshotStorage m_Snapshots;
+		
+		CInput m_LatestInput;
+		CInput m_aInputs[200]; // TODO: handle input better
+		int m_CurrentInput;
+		
+		char m_aName[MAX_NAME_LENGTH];
+		char m_aClan[MAX_CLANNAME_LENGTH];
+		int m_Score;
+		int m_Authed;
+		
+		void Reset();
+	};
+	
+	CClient m_aClients[MAX_CLIENTS];
+
+	CSnapshotDelta m_SnapshotDelta;
+	CSnapshotBuilder m_SnapshotBuilder;
+	CSnapIDPool m_IDPool;
+	CNetServer m_NetServer;
+	
+	IEngineMap *m_pMap;
+
+	int64 m_GameStartTime;
+	//int m_CurrentGameTick;
+	int m_RunServer;
+
+	char m_aBrowseinfoGametype[16];
+	int m_BrowseinfoProgression;
+
+	int64 m_Lastheartbeat;
+	//static NETADDR4 master_server;
+
+	char m_aCurrentMap[64];
+	int m_CurrentMapCrc;
+	unsigned char *m_pCurrentMapData;
+	int m_CurrentMapSize;	
+	
+	CDemoRecorder m_DemoRecorder;
+	CEngine m_Engine;
+	CRegister m_Register;
+	
+	CServer();
+	
+	int TrySetClientName(int ClientID, const char *pName);
+
+	virtual void SetClientName(int ClientID, const char *pName);
+	virtual void SetClientScore(int ClientID, int Score);
+	virtual void SetBrowseInfo(const char *pGameType, int Progression);
+
+	void Kick(int ClientID, const char *pReason);
+
+	//int Tick()
+	int64 TickStartTime(int Tick);
+	//int TickSpeed()
+
+	int Init();
+
+	int GetClientInfo(int ClientID, CClientInfo *pInfo);
+	void GetClientIP(int ClientID, char *pIPString, int Size);
+	const char *ClientName(int ClientId);
+	bool ClientIngame(int ClientID);
+
+	int *LatestInput(int ClientId, int *size);
+
+	virtual int SendMsg(CMsgPacker *pMsg, int Flags, int ClientId);
+	int SendMsgEx(CMsgPacker *pMsg, int Flags, int ClientID, bool System);
+
+	void DoSnapshot();
+
+	static int NewClientCallback(int ClientId, void *pUser);
+	static int DelClientCallback(int ClientId, void *pUser);
+
+	void SendMap(int ClientId);
+	void SendRconLine(int ClientId, const char *pLine);
+	static void SendRconLineAuthed(const char *pLine, void *pUser);
+	
+	void ProcessClientPacket(CNetChunk *pPacket);
+		
+	void SendServerInfo(NETADDR *pAddr, int Token);
+	void UpdateServerInfo();
+
+	int BanAdd(NETADDR Addr, int Seconds);
+	int BanRemove(NETADDR Addr);
+		
+
+	void PumpNetwork();
+
+	int LoadMap(const char *pMapName);
+
+	void InitEngine(const char *pAppname);
+	void InitRegister(CNetServer *pNetServer, IEngineMasterServer *pMasterServer);
+	int Run();
+
+	static void ConKick(IConsole::IResult *pResult, void *pUser);
+	static void ConBan(IConsole::IResult *pResult, void *pUser);
+	static void ConUnban(IConsole::IResult *pResult, void *pUser);
+	static void ConBans(IConsole::IResult *pResult, void *pUser);
+ 	static void ConStatus(IConsole::IResult *pResult, void *pUser);
+	static void ConShutdown(IConsole::IResult *pResult, void *pUser);
+	static void ConRecord(IConsole::IResult *pResult, void *pUser);
+	static void ConStopRecord(IConsole::IResult *pResult, void *pUser);
+	static void ConchainSpecialInfoupdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
+
+	void RegisterCommands();
+	
+	
+	virtual int SnapNewID();
+	virtual void SnapFreeID(int ID);
+	virtual void *SnapNewItem(int Type, int Id, int Size);
+	void SnapSetStaticsize(int ItemType, int Size);
+};
+
+#endif