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.cpp (renamed from src/engine/server/es_register.c)83
-rw-r--r--src/engine/server/es_server.c1380
-rw-r--r--src/engine/server/es_server.cpp1969
3 files changed, 2010 insertions, 1422 deletions
diff --git a/src/engine/server/es_register.c b/src/engine/server/es_register.cpp
index b3ac70a6..0eb5dba5 100644
--- a/src/engine/server/es_register.c
+++ b/src/engine/server/es_register.cpp
@@ -6,7 +6,7 @@
 
 #include <mastersrv/mastersrv.h>
 
-extern NETSERVER *net;
+extern CNetServer *m_pNetServer;
 
 enum
 {
@@ -29,48 +29,48 @@ static void register_new_state(int state)
 	register_state_start = time_get();
 }
 
-static void register_send_fwcheckresponse(NETADDR *addr)
+static void register_send_fwcheckresponse(NETADDR *pAddr)
 {
-	NETCHUNK packet;
-	packet.client_id = -1;
-	packet.address = *addr;
-	packet.flags = NETSENDFLAG_CONNLESS;
-	packet.data_size = sizeof(SERVERBROWSE_FWRESPONSE);
-	packet.data = SERVERBROWSE_FWRESPONSE;
-	netserver_send(net, &packet);
+	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;
-	NETCHUNK packet;
+	CNetChunk Packet;
 	
 	mem_copy(data, SERVERBROWSE_HEARTBEAT, sizeof(SERVERBROWSE_HEARTBEAT));
 	
-	packet.client_id = -1;
-	packet.address = addr;
-	packet.flags = NETSENDFLAG_CONNLESS;
-	packet.data_size = sizeof(SERVERBROWSE_HEARTBEAT) + 2;
-	packet.data = &data;
+	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;
-	netserver_send(net, &packet);
+	m_pNetServer->Send(&Packet);
 }
 
-static void register_send_count_request(NETADDR addr)
+static void register_send_count_request(NETADDR Addr)
 {
-	NETCHUNK packet;
-	packet.client_id = -1;
-	packet.address = addr;
-	packet.flags = NETSENDFLAG_CONNLESS;
-	packet.data_size = sizeof(SERVERBROWSE_GETCOUNT);
-	packet.data = SERVERBROWSE_GETCOUNT;
-	netserver_send(net, &packet);
+	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
@@ -221,50 +221,49 @@ void register_update()
 	}
 }
 
-static void register_got_count(NETCHUNK *p)
+static void register_got_count(CNetChunk *pChunk)
 {
-	unsigned char *data = (unsigned char *)p->data;
-	int count = (data[sizeof(SERVERBROWSE_COUNT)]<<8) | data[sizeof(SERVERBROWSE_COUNT)+1];
-	int i;
+	unsigned char *pData = (unsigned char *)pChunk->m_pData;
+	int Count = (pData[sizeof(SERVERBROWSE_COUNT)]<<8) | pData[sizeof(SERVERBROWSE_COUNT)+1];
 
-	for(i = 0; i < MAX_MASTERSERVERS; i++)
+	for(int i = 0; i < MAX_MASTERSERVERS; i++)
 	{
-		if(net_addr_comp(&masterserver_info[i].addr, &p->address) == 0)
+		if(net_addr_comp(&masterserver_info[i].addr, &pChunk->m_Address) == 0)
 		{
-			masterserver_info[i].count = count;
+			masterserver_info[i].count = Count;
 			break;
 		}
 	}
 }
 
-int register_process_packet(NETCHUNK *packet)
+int register_process_packet(CNetChunk *pPacket)
 {
-	if(packet->data_size == sizeof(SERVERBROWSE_FWCHECK) &&
-		memcmp(packet->data, SERVERBROWSE_FWCHECK, sizeof(SERVERBROWSE_FWCHECK)) == 0)
+	if(pPacket->m_DataSize == sizeof(SERVERBROWSE_FWCHECK) &&
+		memcmp(pPacket->m_pData, SERVERBROWSE_FWCHECK, sizeof(SERVERBROWSE_FWCHECK)) == 0)
 	{
-		register_send_fwcheckresponse(&packet->address);
+		register_send_fwcheckresponse(&pPacket->m_Address);
 		return 1;
 	}
-	else if(packet->data_size == sizeof(SERVERBROWSE_FWOK) &&
-		memcmp(packet->data, SERVERBROWSE_FWOK, sizeof(SERVERBROWSE_FWOK)) == 0)
+	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(packet->data_size == sizeof(SERVERBROWSE_FWERROR) &&
-		memcmp(packet->data, SERVERBROWSE_FWERROR, sizeof(SERVERBROWSE_FWERROR)) == 0)
+	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(packet->data_size == sizeof(SERVERBROWSE_COUNT)+2 &&
-		memcmp(packet->data, SERVERBROWSE_COUNT, sizeof(SERVERBROWSE_COUNT)) == 0)
+	else if(pPacket->m_DataSize == sizeof(SERVERBROWSE_COUNT)+2 &&
+		memcmp(pPacket->m_pData, SERVERBROWSE_COUNT, sizeof(SERVERBROWSE_COUNT)) == 0)
 	{
-		register_got_count(packet);
+		register_got_count(pPacket);
 		return 1;
 	}
 
diff --git a/src/engine/server/es_server.c b/src/engine/server/es_server.c
deleted file mode 100644
index 8763351d..00000000
--- a/src/engine/server/es_server.c
+++ /dev/null
@@ -1,1380 +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 
-
-static SNAPBUILD builder;
-
-static int64 game_start_time;
-static int current_tick = 0;
-static int run_server = 1;
-
-static char browseinfo_gametype[16] = {0};
-static int browseinfo_progression = -1;
-
-static int64 lastheartbeat;
-/*static NETADDR4 master_server;*/
-
-static char current_map[64];
-static int current_map_crc;
-static unsigned char *current_map_data = 0;
-static int current_map_size = 0;
-
-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 snapbuild_new_item(&builder, type, id, size);
-}
-
-typedef struct
-{
-	short next;
-	short state; /* 0 = free, 1 = alloced, 2 = timed */
-	int timeout;
-} SNAP_ID;
-
-static const int MAX_IDS = 16*1024; /* should be lowered */
-static SNAP_ID snap_ids[16*1024];
-static int snap_first_free_id;
-static int snap_first_timed_id;
-static int snap_last_timed_id;
-static int snap_id_usage;
-static int snap_id_inusage;
-static int snap_id_inited = 0;
-
-enum
-{
-	SRVCLIENT_STATE_EMPTY = 0,
-	SRVCLIENT_STATE_AUTH,
-	SRVCLIENT_STATE_CONNECTING,
-	SRVCLIENT_STATE_READY,
-	SRVCLIENT_STATE_INGAME,
-	
-	SRVCLIENT_SNAPRATE_INIT=0,
-	SRVCLIENT_SNAPRATE_FULL,
-	SRVCLIENT_SNAPRATE_RECOVER
-};
-
-typedef struct 
-{
-	int data[MAX_INPUT_SIZE];
-	int game_tick; /* the tick that was chosen for the input */
-} CLIENT_INPUT;
-	
-/* */
-typedef struct
-{
-	/* connection state info */
-	int state;
-	int latency;
-	int snap_rate;
-	
-	int last_acked_snapshot;
-	int last_input_tick;
-	SNAPSTORAGE snapshots;
-	
-	CLIENT_INPUT latestinput;
-	CLIENT_INPUT inputs[200]; /* TODO: handle input better */
-	int current_input;
-	
-	char name[MAX_NAME_LENGTH];
-	char clan[MAX_CLANNAME_LENGTH];
-	int score;
-	int authed;
-} CLIENT;
-
-static CLIENT clients[MAX_CLIENTS];
-NETSERVER *net;
-
-static void snap_init_id()
-{
-	int i;
-	if(snap_id_inited)
-		return;
-	
-	for(i = 0; i < MAX_IDS; i++)
-	{
-		snap_ids[i].next = i+1;
-		snap_ids[i].state = 0;
-	}
-		
-	snap_ids[MAX_IDS-1].next = -1;
-	snap_first_free_id = 0;
-	snap_first_timed_id = -1;
-	snap_last_timed_id = -1;
-	snap_id_usage = 0;
-	snap_id_inusage = 0;
-	
-	snap_id_inited = 1;
-}
-
-static void snap_remove_first_timeout()
-{
-	int next_timed = snap_ids[snap_first_timed_id].next;
-	
-	/* add it to the free list */
-	snap_ids[snap_first_timed_id].next = snap_first_free_id;
-	snap_ids[snap_first_timed_id].state = 0;
-	snap_first_free_id = snap_first_timed_id;
-	
-	/* remove it from the timed list */
-	snap_first_timed_id = next_timed;
-	if(snap_first_timed_id == -1)
-		snap_last_timed_id = -1;
-		
-	snap_id_usage--;
-}
-
-int snap_new_id()
-{
-	int id;
-	int64 now = time_get();
-	if(!snap_id_inited)
-		snap_init_id();
-	
-	/* process timed ids */
-	while(snap_first_timed_id != -1 && snap_ids[snap_first_timed_id].timeout < now)
-		snap_remove_first_timeout();
-	
-	id = snap_first_free_id;
-	dbg_assert(id != -1, "id error");
-	snap_first_free_id = snap_ids[snap_first_free_id].next;
-	snap_ids[id].state = 1;
-	snap_id_usage++;
-	snap_id_inusage++;
-	return id;
-}
-
-void snap_timeout_ids()
-{
-	/* process timed ids */
-	while(snap_first_timed_id != -1)
-		snap_remove_first_timeout();
-}
-
-void snap_free_id(int id)
-{
-	dbg_assert(snap_ids[id].state == 1, "id is not alloced");
-
-	snap_id_inusage--;
-	snap_ids[id].state = 2;
-	snap_ids[id].timeout = time_get()+time_freq()*5;
-	snap_ids[id].next = -1;
-	
-	if(snap_last_timed_id != -1)
-	{
-		snap_ids[snap_last_timed_id].next = id;
-		snap_last_timed_id = id;
-	}
-	else
-	{
-		snap_first_timed_id = id;
-		snap_last_timed_id = id;
-	}
-}
-
-int *server_latestinput(int client_id, int *size)
-{
-	if(client_id < 0 || client_id > MAX_CLIENTS || clients[client_id].state < SRVCLIENT_STATE_READY)
-		return 0;
-	return clients[client_id].latestinput.data;
-}
-
-const char *server_clientname(int client_id)
-{
-	if(client_id < 0 || client_id > MAX_CLIENTS || clients[client_id].state < SRVCLIENT_STATE_READY)
-		return "(invalid client)";
-	return clients[client_id].name;
-}
-
-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--;
-	}
-}
-
-static int server_try_setclientname(int client_id, const char *name)
-{
-	int i;
-	char trimmed_name[64];
-
-	/* trim the name */
-	str_copy(trimmed_name, str_ltrim(name), sizeof(trimmed_name));
-	str_rtrim(trimmed_name);
-	dbg_msg("", "'%s' -> '%s'", name, trimmed_name);
-	name = trimmed_name;
-	
-	
-	/* check for empty names */
-	if(!name[0])
-		return -1;
-	
-	/* make sure that two clients doesn't have the same name */
-	for(i = 0; i < MAX_CLIENTS; i++)
-		if(i != client_id && clients[i].state >= SRVCLIENT_STATE_READY)
-		{
-			if(strcmp(name, clients[i].name) == 0)
-				return -1;
-		}
-
-	/* set the client name */
-	str_copy(clients[client_id].name, name, MAX_NAME_LENGTH);
-	return 0;
-}
-
-void server_setclientname(int client_id, const char *name)
-{
-	char nametry[MAX_NAME_LENGTH];
-	int i;
-	if(client_id < 0 || client_id > MAX_CLIENTS || clients[client_id].state < SRVCLIENT_STATE_READY)
-		return;
-		
-	if(!name)
-		return;
-		
-	str_copy(nametry, name, MAX_NAME_LENGTH);
-	if(server_try_setclientname(client_id, nametry))
-	{
-		/* auto rename */
-		for(i = 1;; i++)
-		{
-			str_format(nametry, MAX_NAME_LENGTH, "(%d)%s", i, name);
-			if(server_try_setclientname(client_id, nametry) == 0)
-				break;
-		}
-	}
-}
-
-void server_setclientscore(int client_id, int score)
-{
-	if(client_id < 0 || client_id > MAX_CLIENTS || clients[client_id].state < SRVCLIENT_STATE_READY)
-		return;
-	clients[client_id].score = score;
-}
-
-void server_setbrowseinfo(const char *game_type, int progression)
-{
-	str_copy(browseinfo_gametype, game_type, sizeof(browseinfo_gametype));
-	browseinfo_progression = progression;
-	if(browseinfo_progression > 100)
-		browseinfo_progression = 100;
-	if(browseinfo_progression < -1)
-		browseinfo_progression = -1;
-}
-
-void server_kick(int client_id, const char *reason)
-{
-	if(client_id < 0 || client_id > MAX_CLIENTS)
-		return;
-		
-	if(clients[client_id].state != SRVCLIENT_STATE_EMPTY)
-		netserver_drop(net, client_id, reason);
-}
-
-int server_tick()
-{
-	return current_tick;
-}
-
-int64 server_tick_start_time(int tick)
-{
-	return game_start_time + (time_freq()*tick)/SERVER_TICK_SPEED;
-}
-
-int server_tickspeed()
-{
-	return SERVER_TICK_SPEED;
-}
-
-int server_init()
-{
-	int i;
-	for(i = 0; i < MAX_CLIENTS; i++)
-	{
-		clients[i].state = SRVCLIENT_STATE_EMPTY;
-		clients[i].name[0] = 0;
-		clients[i].clan[0] = 0;
-		snapstorage_init(&clients[i].snapshots);
-	}
-
-	current_tick = 0;
-
-	return 0;
-}
-
-int server_getclientinfo(int client_id, CLIENT_INFO *info)
-{
-	dbg_assert(client_id >= 0 && client_id < MAX_CLIENTS, "client_id is not valid");
-	dbg_assert(info != 0, "info can not be null");
-
-	if(clients[client_id].state == SRVCLIENT_STATE_INGAME)
-	{
-		info->name = clients[client_id].name;
-		info->latency = clients[client_id].latency;
-		return 1;
-	}
-	return 0;
-}
-
-int server_send_msg(int client_id)
-{
-	const MSG_INFO *info = msg_get_info();
-	NETCHUNK packet;
-	if(!info)
-		return -1;
-		
-	mem_zero(&packet, sizeof(NETCHUNK));
-	
-	packet.client_id = client_id;
-	packet.data = info->data;
-	packet.data_size = info->size;
-
-	if(info->flags&MSGFLAG_VITAL)
-		packet.flags |= NETSENDFLAG_VITAL;
-	if(info->flags&MSGFLAG_FLUSH)
-		packet.flags |= NETSENDFLAG_FLUSH;
-	
-	/* write message to demo recorder */
-	if(!(info->flags&MSGFLAG_NORECORD))
-		demorec_record_message(info->data, info->size);
-
-	if(!(info->flags&MSGFLAG_NOSEND))
-	{
-		if(client_id == -1)
-		{
-			/* broadcast */
-			int i;
-			for(i = 0; i < MAX_CLIENTS; i++)
-				if(clients[i].state == SRVCLIENT_STATE_INGAME)
-				{
-					packet.client_id = i;
-					netserver_send(net, &packet);
-				}
-		}
-		else
-			netserver_send(net, &packet);
-	}
-	return 0;
-}
-
-static void server_do_snap()
-{
-	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[MAX_SNAPSHOT_SIZE];
-		int snapshot_size;
-
-		/* build snap and possibly add some messages */
-		snapbuild_init(&builder);
-		mods_snap(-1);
-		snapshot_size = snapbuild_finish(&builder, data);
-		
-		/* write snapshot */
-		demorec_record_snapshot(server_tick(), data, snapshot_size);
-	}
-
-	/* create snapshots for all clients */
-	for(i = 0; i < MAX_CLIENTS; i++)
-	{
-		/* client must be ingame to recive snapshots */
-		if(clients[i].state != SRVCLIENT_STATE_INGAME)
-			continue;
-			
-		/* this client is trying to recover, don't spam snapshots */
-		if(clients[i].snap_rate == SRVCLIENT_SNAPRATE_RECOVER && (server_tick()%50) != 0)
-			continue;
-			
-		/* this client is trying to recover, don't spam snapshots */
-		if(clients[i].snap_rate == SRVCLIENT_SNAPRATE_INIT && (server_tick()%10) != 0)
-			continue;
-			
-		{
-			char data[MAX_SNAPSHOT_SIZE];
-			char deltadata[MAX_SNAPSHOT_SIZE];
-			char compdata[MAX_SNAPSHOT_SIZE];
-			int snapshot_size;
-			int crc;
-			static SNAPSHOT emptysnap;
-			SNAPSHOT *deltashot = &emptysnap;
-			int deltashot_size;
-			int delta_tick = -1;
-			int deltasize;
-			static PERFORMACE_INFO scope = {"build", 0};
-			perf_start(&scope);
-
-			snapbuild_init(&builder);
-
-			{
-				static PERFORMACE_INFO scope = {"modsnap", 0};
-				perf_start(&scope);
-				mods_snap(i);
-				perf_end();
-			}
-
-			/* finish snapshot */
-			snapshot_size = snapbuild_finish(&builder, data);
-			crc = snapshot_crc((SNAPSHOT*)data);
-
-			/* remove old snapshos */
-			/* keep 3 seconds worth of snapshots */
-			snapstorage_purge_until(&clients[i].snapshots, current_tick-SERVER_TICK_SPEED*3);
-			
-			/* save it the snapshot */
-			snapstorage_add(&clients[i].snapshots, current_tick, time_get(), snapshot_size, data, 0);
-			
-			/* find snapshot that we can preform delta against */
-			emptysnap.data_size = 0;
-			emptysnap.num_items = 0;
-			
-			{
-				deltashot_size = snapstorage_get(&clients[i].snapshots, clients[i].last_acked_snapshot, 0, &deltashot, 0);
-				if(deltashot_size >= 0)
-					delta_tick = clients[i].last_acked_snapshot;
-				else
-				{
-					/* no acked package found, force client to recover rate */
-					if(clients[i].snap_rate == SRVCLIENT_SNAPRATE_FULL)
-						clients[i].snap_rate = SRVCLIENT_SNAPRATE_RECOVER;
-				}
-			}
-			
-			/* create delta */
-			{
-				static PERFORMACE_INFO scope = {"delta", 0};
-				perf_start(&scope);
-				deltasize = snapshot_create_delta(deltashot, (SNAPSHOT*)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(current_tick);
-					msg_pack_int(current_tick-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();
-					server_send_msg(i);
-				}
-			}
-			else
-			{
-				msg_pack_start_system(NETMSG_SNAPEMPTY, MSGFLAG_FLUSH);
-				msg_pack_int(current_tick);
-				msg_pack_int(current_tick-delta_tick); /* compressed with */
-				msg_pack_end();
-				server_send_msg(i);
-			}
-			
-			perf_end();
-		}
-	}
-
-	mods_postsnap();
-}
-
-
-static void reset_client(int cid)
-{
-	/* reset input */
-	int i;
-	for(i = 0; i < 200; i++)
-		clients[cid].inputs[i].game_tick = -1;
-	clients[cid].current_input = 0;
-	mem_zero(&clients[cid].latestinput, sizeof(clients[cid].latestinput));
-
-	snapstorage_purge_all(&clients[cid].snapshots);
-	clients[cid].last_acked_snapshot = -1;
-	clients[cid].last_input_tick = -1;
-	clients[cid].snap_rate = SRVCLIENT_SNAPRATE_INIT;
-	clients[cid].score = 0;
-
-}
-
-static int new_client_callback(int cid, void *user)
-{
-	clients[cid].state = SRVCLIENT_STATE_AUTH;
-	clients[cid].name[0] = 0;
-	clients[cid].clan[0] = 0;
-	clients[cid].authed = 0;
-	reset_client(cid);
-	return 0;
-}
-
-static int del_client_callback(int cid, void *user)
-{
-	/* notify the mod about the drop */
-	if(clients[cid].state >= SRVCLIENT_STATE_READY)
-		mods_client_drop(cid);
-	
-	clients[cid].state = SRVCLIENT_STATE_EMPTY;
-	clients[cid].name[0] = 0;
-	clients[cid].clan[0] = 0;
-	clients[cid].authed = 0;
-	snapstorage_purge_all(&clients[cid].snapshots);
-	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(clients[i].state != SRVCLIENT_STATE_EMPTY && clients[i].authed)
-			server_send_rcon_line(i, line);
-	}
-	
-	reentry_guard--;
-}
-
-static void server_process_client_packet(NETCHUNK *packet)
-{
-	int cid = packet->client_id;
-	NETADDR addr;
-	
-	int sys;
-	int msg = msg_unpack_start(packet->data, packet->data_size, &sys);
-	
-	if(clients[cid].state == SRVCLIENT_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);
-				netserver_drop(net, cid, reason);
-				return;
-			}
-			
-			str_copy(clients[cid].name, msg_unpack_string(), MAX_NAME_LENGTH);
-			str_copy(clients[cid].clan, msg_unpack_string(), MAX_CLANNAME_LENGTH);
-			password = msg_unpack_string();
-			
-			if(config.password[0] != 0 && strcmp(config.password, password) != 0)
-			{
-				/* wrong password */
-				netserver_drop(net, cid, "wrong password");
-				return;
-			}
-			
-			clients[cid].state = SRVCLIENT_STATE_CONNECTING;
-			server_send_map(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 > current_map_size)
-					return;
-				
-				if(offset+chunk_size >= current_map_size)
-				{
-					chunk_size = current_map_size-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(current_map_size);
-				msg_pack_int(chunk_size);
-				msg_pack_raw(&current_map_data[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(clients[cid].state == SRVCLIENT_STATE_CONNECTING)
-				{
-					netserver_client_addr(net, cid, &addr);
-					
-					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]
-						);
-					clients[cid].state = SRVCLIENT_STATE_READY;
-					mods_connected(cid);
-				}
-			}
-			else if(msg == NETMSG_ENTERGAME)
-			{
-				if(clients[cid].state == SRVCLIENT_STATE_READY)
-				{
-					netserver_client_addr(net, cid, &addr);
-					
-					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]
-						);
-					clients[cid].state = SRVCLIENT_STATE_INGAME;
-					mods_client_enter(cid);
-				}
-			}
-			else if(msg == NETMSG_INPUT)
-			{
-				int tick, size, i;
-				CLIENT_INPUT *input;
-				int64 tagtime;
-				
-				clients[cid].last_acked_snapshot = 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(clients[cid].last_acked_snapshot > 0)
-					clients[cid].snap_rate = SRVCLIENT_SNAPRATE_FULL;
-					
-				if(snapstorage_get(&clients[cid].snapshots, clients[cid].last_acked_snapshot, &tagtime, 0, 0) >= 0)
-					clients[cid].latency = (int)(((time_get()-tagtime)*1000)/time_freq());
-
-				/* add message to report the input timing */
-				/* skip packets that are old */
-				if(tick > clients[cid].last_input_tick)
-				{
-					int time_left = ((server_tick_start_time(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);
-				}
-
-				clients[cid].last_input_tick = tick;
-
-				input = &clients[cid].inputs[clients[cid].current_input];
-				
-				if(tick <= server_tick())
-					tick = server_tick()+1;
-
-				input->game_tick = tick;
-				
-				for(i = 0; i < size/4; i++)
-					input->data[i] = msg_unpack_int();
-				
-				mem_copy(clients[cid].latestinput.data, input->data, MAX_INPUT_SIZE*sizeof(int));
-				
-				clients[cid].current_input++;
-				clients[cid].current_input %= 200;
-			
-				/* call the mod with the fresh input data */
-				if(clients[cid].state == SRVCLIENT_STATE_INGAME)
-					mods_client_direct_input(cid, clients[cid].latestinput.data);
-			}
-			else if(msg == NETMSG_RCON_CMD)
-			{
-				const char *cmd = msg_unpack_string();
-				
-				if(msg_unpack_error() == 0 && clients[cid].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)
-					{
-						server_send_rcon_line(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);
-						
-						clients[cid].authed = 1;
-						server_send_rcon_line(cid, "Authentication successful. Remote console access granted.");
-						dbg_msg("server", "cid=%d authed", cid);
-					}
-					else
-					{
-						server_send_rcon_line(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 < packet->data_size && b < 32; b++)
-				{
-					buf[b*3] = hex[((const unsigned char *)packet->data)[b]>>4];
-					buf[b*3+1] = hex[((const unsigned char *)packet->data)[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, packet->data_size);
-				dbg_msg("server", "%s", buf);
-				
-			}
-		}
-		else
-		{
-			/* game message */
-			if(clients[cid].state >= SRVCLIENT_STATE_READY)
-				mods_message(msg, cid);
-		}
-	}
-}
-
-
-int server_ban_add(NETADDR addr, int seconds)
-{
-	return netserver_ban_add(net, addr, seconds);	
-}
-
-int server_ban_remove(NETADDR addr)
-{
-	return netserver_ban_remove(net, addr);
-}
-
-static void server_send_serverinfo(NETADDR *addr, int token)
-{
-	NETCHUNK packet;
-	PACKER p;
-	char buf[128];
-
-	/* count the players */	
-	int player_count = 0;
-	int i;
-	for(i = 0; i < MAX_CLIENTS; i++)
-	{
-		if(clients[i].state != SRVCLIENT_STATE_EMPTY)
-			player_count++;
-	}
-	
-	packer_reset(&p);
-
-	if(token >= 0)
-	{
-		/* new token based format */
-		packer_add_raw(&p, SERVERBROWSE_INFO, sizeof(SERVERBROWSE_INFO));
-		str_format(buf, sizeof(buf), "%d", token);
-		packer_add_string(&p, buf, 6);
-	}
-	else
-	{
-		/* old format */
-		packer_add_raw(&p, SERVERBROWSE_OLD_INFO, sizeof(SERVERBROWSE_OLD_INFO));
-	}
-	
-	packer_add_string(&p, mods_version(), 32);
-	packer_add_string(&p, config.sv_name, 64);
-	packer_add_string(&p, config.sv_map, 32);
-
-	/* gametype */
-	packer_add_string(&p, browseinfo_gametype, 16);
-
-	/* flags */
-	i = 0;
-	if(config.password[0])   /* password set */
-		i |= SRVFLAG_PASSWORD;
-	str_format(buf, sizeof(buf), "%d", i);
-	packer_add_string(&p, buf, 2);
-
-	/* progression */
-	str_format(buf, sizeof(buf), "%d", browseinfo_progression);
-	packer_add_string(&p, buf, 4);
-	
-	str_format(buf, sizeof(buf), "%d", player_count); packer_add_string(&p, buf, 3);  /* num players */
-	str_format(buf, sizeof(buf), "%d", netserver_max_clients(net)); packer_add_string(&p, buf, 3); /* max players */
-
-	for(i = 0; i < MAX_CLIENTS; i++)
-	{
-		if(clients[i].state != SRVCLIENT_STATE_EMPTY)
-		{
-			packer_add_string(&p, clients[i].name, 48);  /* player name */
-			str_format(buf, sizeof(buf), "%d", clients[i].score); packer_add_string(&p, buf, 6);  /* player score */
-		}
-	}
-	
-	
-	packet.client_id = -1;
-	packet.address = *addr;
-	packet.flags = NETSENDFLAG_CONNLESS;
-	packet.data_size = packer_size(&p);
-	packet.data = packer_data(&p);
-	netserver_send(net, &packet);
-}
-
-extern int register_process_packet(NETCHUNK *packet);
-extern int register_update();
-
-static void server_pump_network()
-{
-	NETCHUNK packet;
-
-	netserver_update(net);
-	
-	/* process packets */
-	while(netserver_recv(net, &packet))
-	{
-		if(packet.client_id == -1)
-		{
-			/* stateless */
-			if(!register_process_packet(&packet))
-			{
-				if(packet.data_size == sizeof(SERVERBROWSE_GETINFO)+1 &&
-					memcmp(packet.data, SERVERBROWSE_GETINFO, sizeof(SERVERBROWSE_GETINFO)) == 0)
-				{
-					server_send_serverinfo(&packet.address, ((unsigned char *)packet.data)[sizeof(SERVERBROWSE_GETINFO)]);
-				}
-				
-				
-				if(packet.data_size == sizeof(SERVERBROWSE_OLD_GETINFO) &&
-					memcmp(packet.data, SERVERBROWSE_OLD_GETINFO, sizeof(SERVERBROWSE_OLD_GETINFO)) == 0)
-				{
-					server_send_serverinfo(&packet.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;
-	}
-	
-	net = netserver_open(bindaddr, config.sv_max_clients, 0);
-	if(!net)
-	{
-		dbg_msg("server", "couldn't open socket. port might already be in use");
-		return -1;
-	}
-	
-	netserver_set_callbacks(net, new_client_callback, del_client_callback, 0);
-	
-	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(clients[c].state == SRVCLIENT_STATE_EMPTY)
-							continue;
-						
-						server_send_map(c);
-						reset_client(c);
-						clients[c].state = SRVCLIENT_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(clients[c].state == SRVCLIENT_STATE_EMPTY)
-							continue;
-						for(i = 0; i < 200; i++)
-						{
-							if(clients[c].inputs[i].game_tick == server_tick())
-							{
-								if(clients[c].state == SRVCLIENT_STATE_INGAME)
-									mods_client_predicted_input(c, clients[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(netserver_socket(net), 5);
-		}
-	}
-
-	mods_shutdown();
-	map_unload();
-
-	if(current_map_data)
-		mem_free(current_map_data);
-	return 0;
-}
-
-static void con_kick(void *result, void *user_data)
-{
-	server_kick(console_arg_int(result, 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 con_ban(void *result, void *user_data)
-{
-	NETADDR addr;
-	char addrstr[128];
-	const char *str = console_arg_string(result, 0);
-	int minutes = 30;
-	
-	if(console_arg_num(result) > 1)
-		minutes = console_arg_int(result, 1);
-	
-	if(net_addr_from_str(&addr, str) == 0)
-		server_ban_add(addr, minutes*60);
-	else if(str_allnum(str))
-	{
-		NETADDR addr;
-		int cid = atoi(str);
-
-		if(cid < 0 || cid > MAX_CLIENTS || clients[cid].state == SRVCLIENT_STATE_EMPTY)
-		{
-			dbg_msg("server", "invalid client id");
-			return;
-		}
-
-		netserver_client_addr(net, cid, &addr);
-		server_ban_add(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 con_unban(void *result, void *user_data)
-{
-	NETADDR addr;
-	const char *str = console_arg_string(result, 0);
-	
-	if(net_addr_from_str(&addr, str) == 0)
-		server_ban_remove(addr);
-	else
-		dbg_msg("server", "invalid network address");
-}
-
-static void con_bans(void *result, void *user_data)
-{
-	int i;
-	unsigned now = time_timestamp();
-	NETBANINFO info;
-	NETADDR addr;
-	int num = netserver_ban_num(net);
-	for(i = 0; i < num; i++)
-	{
-		unsigned t;
-		netserver_ban_get(net, i, &info);
-		addr = info.addr;
-		
-		if(info.expires == 0xffffffff)
-		{
-			dbg_msg("server", "#%d %d.%d.%d.%d for life", i, addr.ip[0], addr.ip[1], addr.ip[2], addr.ip[3]);
-		}
-		else
-		{
-			t = info.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 con_status(void *result, void *user_data)
-{
-	int i;
-	NETADDR addr;
-	for(i = 0; i < MAX_CLIENTS; i++)
-	{
-		if(clients[i].state == SRVCLIENT_STATE_INGAME)
-		{
-			netserver_client_addr(net, i, &addr);
-			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,
-				clients[i].name, clients[i].score);
-		}
-	}
-}
-
-static void con_shutdown(void *result, void *user_data)
-{
-	run_server = 0;
-}
-
-static void con_record(void *result, void *user_data)
-{
-	char filename[512];
-	str_format(filename, sizeof(filename), "demos/%s.demo", console_arg_string(result, 0));
-	demorec_record_start(filename, mods_net_version(), current_map, current_map_crc, "server");
-}
-
-static void con_stoprecord(void *result, void *user_data)
-{
-	demorec_record_stop();
-}
-
-static void server_register_commands()
-{
-	MACRO_REGISTER_COMMAND("kick", "i", CFGFLAG_SERVER, con_kick, 0, "");
-	MACRO_REGISTER_COMMAND("ban", "s?i", CFGFLAG_SERVER, con_ban, 0, "");
-	MACRO_REGISTER_COMMAND("unban", "s", CFGFLAG_SERVER, con_unban, 0, "");
-	MACRO_REGISTER_COMMAND("bans", "", CFGFLAG_SERVER, con_bans, 0, "");
-	MACRO_REGISTER_COMMAND("status", "", CFGFLAG_SERVER, con_status, 0, "");
-	MACRO_REGISTER_COMMAND("shutdown", "", CFGFLAG_SERVER, con_shutdown, 0, "");
-
-	MACRO_REGISTER_COMMAND("record", "s", CFGFLAG_SERVER, con_record, 0, "");
-	MACRO_REGISTER_COMMAND("stoprecord", "", CFGFLAG_SERVER, con_stoprecord, 0, "");
-}
-
-int main(int argc, char **argv)
-{
-#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 */
-	server_register_commands();
-	mods_console_init();
-	
-	/* parse the command line arguments */
-	engine_parse_arguments(argc, argv);
-	
-	/* run the server */
-	server_run();
-	return 0;
-}
-
diff --git a/src/engine/server/es_server.cpp b/src/engine/server/es_server.cpp
new file mode 100644
index 00000000..cc0e6e0f
--- /dev/null
+++ b/src/engine/server/es_server.cpp
@@ -0,0 +1,1969 @@
+/* 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;
+}
+