about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMagnus Auvinen <magnus.auvinen@gmail.com>2008-02-10 21:54:52 +0000
committerMagnus Auvinen <magnus.auvinen@gmail.com>2008-02-10 21:54:52 +0000
commit548a919ea379a3b9d1d9e41cf4dad6b4779fd3e6 (patch)
tree85666198fed3d4803f3ec3373c134d12bde9329b
parent2f969d9d6fece689e05857580ffb1843439e5fbb (diff)
downloadzcatch-548a919ea379a3b9d1d9e41cf4dad6b4779fd3e6.tar.gz
zcatch-548a919ea379a3b9d1d9e41cf4dad6b4779fd3e6.zip
merged 0.3.4 changes to trunk
-rw-r--r--src/engine/client/ec_client.c33
-rw-r--r--src/engine/client/ec_srvbrowse.c20
-rw-r--r--src/engine/e_config.c72
-rw-r--r--src/engine/e_config_variables.h2
-rw-r--r--src/engine/e_engine.c215
-rw-r--r--src/engine/e_engine.h17
-rw-r--r--src/engine/e_linereader.c62
-rw-r--r--src/engine/e_linereader.h14
-rw-r--r--src/engine/e_system.c47
-rw-r--r--src/engine/e_system.h13
-rw-r--r--src/engine/server/es_register.c258
-rw-r--r--src/engine/server/es_server.c100
-rw-r--r--src/game/server/gs_server.cpp3
-rw-r--r--src/mastersrv/mastersrv.cpp28
-rw-r--r--src/mastersrv/mastersrv.h3
-rw-r--r--src/tools/fake_server.c29
16 files changed, 736 insertions, 180 deletions
diff --git a/src/engine/client/ec_client.c b/src/engine/client/ec_client.c
index 9c63f7cb..f93cb5e5 100644
--- a/src/engine/client/ec_client.c
+++ b/src/engine/client/ec_client.c
@@ -1098,12 +1098,36 @@ static void client_update()
 	}
 
 	/* STRESS TEST: join the server again */
-	if(client_state() == CLIENTSTATE_OFFLINE && config.dbg_stress && (frames%100) == 0)
-		client_connect(config.dbg_stress_server);
+	if(config.dbg_stress)
+	{
+		static int64 action_taken = 0;
+		int64 now = time_get();
+		if(client_state() == CLIENTSTATE_OFFLINE)
+		{
+			if(now > action_taken+time_freq()*2)
+			{
+				dbg_msg("stress", "reconnecting!");
+				client_connect(config.dbg_stress_server);
+				action_taken = now;
+			}
+		}
+		else
+		{
+			if(now > action_taken+time_freq()*(10+config.dbg_stress))
+			{
+				dbg_msg("stress", "disconnecting!");
+				client_disconnect();
+				action_taken = now;
+			}
+		}
+	}
 	
 	/* pump the network */
 	client_pump_network();
 	
+	/* update the maser server registry */
+	mastersrv_update();
+	
 	/* update the server browser */
 	client_serverbrowse_update();
 }
@@ -1128,12 +1152,15 @@ static void client_run()
 	if(!gfx_init())
 		return;
 
+	/* start refreshing addresses while we load */
+	mastersrv_refresh_addresses();
+	
 	/* init the editor */
 	editor_init();
 
 	/* sound is allowed to fail */
 	snd_init();
-	
+
 	/* load data */
 	if(!client_load_data())
 		return;
diff --git a/src/engine/client/ec_srvbrowse.c b/src/engine/client/ec_srvbrowse.c
index 74d09b8d..0a6ebbf8 100644
--- a/src/engine/client/ec_srvbrowse.c
+++ b/src/engine/client/ec_srvbrowse.c
@@ -4,6 +4,7 @@
 #include <engine/e_client_interface.h>
 #include <engine/e_config.h>
 #include <engine/e_memheap.h>
+#include <engine/e_engine.h>
 
 #include <mastersrv/mastersrv.h>
 
@@ -341,18 +342,27 @@ void client_serverbrowse_refresh(int lan)
 	}
 	else
 	{
-		NETADDR4 master_server;
+		NETADDR4 addr;
 		NETPACKET p;
-
-		net_host_lookup(config.masterserver, MASTERSERVER_PORT, &master_server);
+		int i;
+		
+		/*net_host_lookup(config.masterserver, MASTERSERVER_PORT, &master_server);*/
 
 		mem_zero(&p, sizeof(p));
 		p.client_id = -1;
-		p.address = master_server;
 		p.flags = PACKETFLAG_CONNLESS;
 		p.data_size = sizeof(SERVERBROWSE_GETLIST);
 		p.data = SERVERBROWSE_GETLIST;
-		netclient_send(net, &p);	
+		
+		for(i = 0; i < MAX_MASTERSERVERS; i++)
+		{
+			addr = mastersrv_get(i);
+			if(!addr.ip[0] && !addr.ip[1] && !addr.ip[2] && !addr.ip[3])
+				continue;
+			
+			p.address = addr;
+			netclient_send(net, &p);	
+		}
 
 		if(config.debug)
 			dbg_msg("client", "requesting server list");
diff --git a/src/engine/e_config.c b/src/engine/e_config.c
index 44008b77..ddb6c742 100644
--- a/src/engine/e_config.c
+++ b/src/engine/e_config.c
@@ -6,77 +6,7 @@
 
 #include "e_system.h"
 #include "e_config.h"
-
-/* buffered stream for reading lines, should perhaps be something smaller */
-typedef struct
-{
-	char buffer[4*1024];
-	unsigned buffer_pos;
-	unsigned buffer_size;
-	unsigned buffer_max_size;
-	IOHANDLE io;
-} LINEREADER;
-
-void linereader_init(LINEREADER *lr, IOHANDLE io)
-{
-	lr->buffer_max_size = 4*1024;
-	lr->buffer_size = 0;
-	lr->buffer_pos = 0;
-	lr->io = io;
-}
-
-char *linereader_get(LINEREADER *lr)
-{
-	unsigned line_start = lr->buffer_pos;
-
-	while(1)
-	{
-		if(lr->buffer_pos >= lr->buffer_size)
-		{
-			/* fetch more */
-
-			/* move the remaining part to the front */
-			unsigned read;
-			unsigned left = lr->buffer_size - line_start;
-
-			if(line_start > lr->buffer_size)
-				left = 0;
-			if(left)
-				mem_move(lr->buffer, &lr->buffer[line_start], left);
-			lr->buffer_pos = left;
-
-			/* fill the buffer */
-			read = io_read(lr->io, &lr->buffer[lr->buffer_pos], lr->buffer_max_size-lr->buffer_pos);
-			lr->buffer_size = left + read;
-			line_start = 0;
-
-			if(!read)
-			{
-				if(left)
-				{
-					lr->buffer[left] = 0; /* return the last line */
-					lr->buffer_pos = left;
-					lr->buffer_size = left;
-					return lr->buffer;
-				}
-				else
-					return 0x0; /* we are done! */
-			}
-		}
-		else
-		{
-			if(lr->buffer[lr->buffer_pos] == '\n' || lr->buffer[lr->buffer_pos] == '\r')
-			{
-				/* line found */
-				lr->buffer[lr->buffer_pos] = 0;
-				lr->buffer_pos++;
-				return &lr->buffer[line_start];
-			}
-			else
-				lr->buffer_pos++;
-		}
-	}
-}
+#include "e_linereader.h"
 
 CONFIGURATION config;
 
diff --git a/src/engine/e_config_variables.h b/src/engine/e_config_variables.h
index 7271c530..01b35dc0 100644
--- a/src/engine/e_config_variables.h
+++ b/src/engine/e_config_variables.h
@@ -46,7 +46,7 @@ MACRO_CONFIG_INT(gfx_debug_resizable, 0, 0, 0)
 MACRO_CONFIG_INT(key_screenshot, 267, 32, 512)
 MACRO_CONFIG_INT(inp_mousesens, 100, 5, 100000)
 
-MACRO_CONFIG_STR(masterserver, 128, "master.teewars.com")
+/*MACRO_CONFIG_STR(masterserver, 128, "master.teewars.com")*/
 
 MACRO_CONFIG_STR(sv_name, 128, "unnamed server")
 MACRO_CONFIG_STR(sv_bindaddr, 128, "")
diff --git a/src/engine/e_engine.c b/src/engine/e_engine.c
index f4f1ed3b..b85f1eb3 100644
--- a/src/engine/e_engine.c
+++ b/src/engine/e_engine.c
@@ -7,7 +7,8 @@
 /*#include <engine/e_client_interface.h>*/
 #include <engine/e_config.h>
 #include <engine/e_console.h>
-
+#include <engine/e_engine.h>
+#include "e_linereader.h"
 
 static void con_dbg_dumpmem(void *result, void *user_data)
 {
@@ -24,7 +25,7 @@ const char *engine_savepath(const char *filename, char *buffer, int max)
 }
 
 
-void engine_init(const char *appname, int argc, char **argv)
+void engine_init(const char *appname)
 {
 	dbg_msg("engine", "running on %s-%s-%s", CONF_FAMILY_STRING, CONF_PLATFORM_STRING, CONF_ARCH_STRING);
 #ifdef CONF_ARCH_ENDIAN_LITTLE
@@ -37,7 +38,7 @@ void engine_init(const char *appname, int argc, char **argv)
 
 	/* init the network */
 	net_init();
-
+	
 	/* create storage location */
 	{
 		char path[1024] = {0};
@@ -92,6 +93,10 @@ void engine_parse_arguments(int argc, char **argv)
 		for(i = 1; i < argc; i++)
 			config_set(argv[i]);
 	}
+	
+	/* set default servers and load from disk*/
+	mastersrv_default();
+	mastersrv_load();
 }
 
 void engine_writeconfig()
@@ -175,3 +180,207 @@ void perf_dump(PERFORMACE_INFO *top)
 {
 	perf_dump_imp(top, 0);
 }
+
+/* master server functions */
+enum
+{
+	NUM_LOOKUP_THREADS=4,
+	
+	STATE_PROCESSED=0,
+	STATE_RESULT,
+	STATE_QUERYING
+};
+
+typedef struct
+{
+	char hostname[128];
+	NETADDR4 addr;
+	
+	/* these are used for lookups */
+	struct {
+		NETADDR4 addr;
+		int result;
+		void *thread;
+		volatile int state;
+	} lookup;
+} MASTER_INFO;
+
+typedef struct
+{
+	int start;
+	int num;
+} THREAD_INFO;
+
+static MASTER_INFO master_servers[MAX_MASTERSERVERS] = {{{0}}};
+static THREAD_INFO thread_info[NUM_LOOKUP_THREADS];
+static int needs_update = 0;
+
+void lookup_thread(void *user)
+{
+	THREAD_INFO *info = (THREAD_INFO *)user;
+	int i;
+	
+	for(i = 0; i < info->num; i++)
+	{
+		int index = info->start+i;
+		master_servers[index].lookup.result = net_host_lookup(master_servers[index].hostname, 8300, &master_servers[index].lookup.addr);
+		master_servers[index].lookup.state = STATE_RESULT;
+	}
+}
+
+int mastersrv_refresh_addresses()
+{
+	int i;
+	dbg_msg("engine/mastersrv", "refreshing master server addresses");
+	
+	/* spawn threads that does the lookups */
+	for(i = 0; i < NUM_LOOKUP_THREADS; i++)	
+	{
+		thread_info[i].start = MAX_MASTERSERVERS/NUM_LOOKUP_THREADS * i;
+		thread_info[i].num = MAX_MASTERSERVERS/NUM_LOOKUP_THREADS;
+		master_servers[i].lookup.state = STATE_QUERYING;
+		master_servers[i].lookup.thread = thread_create(lookup_thread, &thread_info[i]);
+	}
+	
+	needs_update = 1;
+	return 0;
+}
+
+void mastersrv_update()
+{
+	int i;
+	
+	/* check if we need to update */
+	if(!needs_update)
+		return;
+	needs_update = 0;
+	
+	for(i = 0; i < MAX_MASTERSERVERS; i++)
+	{
+		if(master_servers[i].lookup.state == STATE_RESULT)
+		{
+			/* we got a result from the lookup ready */
+			if(master_servers[i].lookup.result == 0)
+				master_servers[i].addr = master_servers[i].lookup.addr;
+			master_servers[i].lookup.state = STATE_PROCESSED;
+		}
+
+		/* set the needs_update flag if we isn't done */		
+		if(master_servers[i].lookup.state != STATE_PROCESSED)
+			needs_update = 1;
+	}
+	
+	if(!needs_update)
+	{
+		dbg_msg("engine/mastersrv", "saving addresses");
+		mastersrv_save();
+	}
+}
+
+int mastersrv_refreshing()
+{
+	return needs_update;
+}
+
+NETADDR4 mastersrv_get(int index) 
+{
+	return master_servers[index].addr;
+}
+
+const char *mastersrv_name(int index) 
+{
+	return master_servers[index].hostname;
+}
+
+void mastersrv_dump_servers()
+{
+	int i;
+	for(i = 0; i < MAX_MASTERSERVERS; i++)
+	{
+		dbg_msg("mastersrv", "#%d = %d.%d.%d.%d", i,
+			master_servers[i].addr.ip[0], master_servers[i].addr.ip[1],
+			master_servers[i].addr.ip[2], master_servers[i].addr.ip[3]);
+	}
+}
+
+void mastersrv_default()
+{
+	int i;
+	mem_zero(master_servers, sizeof(master_servers));
+	for(i = 0; i < MAX_MASTERSERVERS; i++)
+		sprintf(master_servers[i].hostname, "master%d.teewars.com", i+1);
+}
+
+int mastersrv_load()
+{
+	LINEREADER lr;
+	IOHANDLE file;
+	int count = 0;
+	char filename[1024];
+	
+	engine_savepath("masters.cfg", filename, sizeof(filename));
+	
+	/* try to open file */
+	file = io_open(filename, IOFLAG_READ);
+	if(!file)
+		return -1;
+	
+	linereader_init(&lr, file);
+	while(1)
+	{
+		MASTER_INFO info = {{0}};
+		int ip[4];
+		const char *line = linereader_get(&lr);
+		if(!line)
+			break;
+
+		/* parse line */		
+		if(sscanf(line, "%s %d.%d.%d.%d", info.hostname, &ip[0], &ip[1], &ip[2], &ip[3]) == 5)
+		{
+			info.addr.ip[0] = (unsigned char)ip[0];
+			info.addr.ip[1] = (unsigned char)ip[1];
+			info.addr.ip[2] = (unsigned char)ip[2];
+			info.addr.ip[3] = (unsigned char)ip[3];
+			info.addr.port = 8300;
+			if(count != MAX_MASTERSERVERS)
+			{
+				master_servers[count] = info;
+				count++;
+			}
+			else
+				dbg_msg("engine/mastersrv", "warning: skipped master server '%s' due to limit of %d", line, MAX_MASTERSERVERS);
+		}
+		else
+			dbg_msg("engine/mastersrv", "warning: couldn't parse master server '%s'", line);
+	}
+	
+	io_close(file);
+	return 0;
+}
+
+int mastersrv_save()
+{
+	IOHANDLE file;
+	int i;
+	char filename[1024];
+
+	engine_savepath("masters.cfg", filename, sizeof(filename));
+	
+	/* try to open file */
+	file = io_open(filename, IOFLAG_WRITE);
+	if(!file)
+		return -1;
+
+	for(i = 0; i < MAX_MASTERSERVERS; i++)
+	{
+		char buf[1024];
+		sprintf(buf, "%s %d.%d.%d.%d\n", master_servers[i].hostname,
+			master_servers[i].addr.ip[0], master_servers[i].addr.ip[1],
+			master_servers[i].addr.ip[2], master_servers[i].addr.ip[3]);
+			
+		io_write(file, buf, strlen(buf));
+	}
+	
+	io_close(file);
+	return 0;
+}
diff --git a/src/engine/e_engine.h b/src/engine/e_engine.h
index ba880bb0..6974db1d 100644
--- a/src/engine/e_engine.h
+++ b/src/engine/e_engine.h
@@ -4,3 +4,20 @@ const char *engine_savepath(const char *filename, char *buffer, int max);
 void engine_init(const char *appname);
 void engine_parse_arguments(int argc, char **argv);
 void engine_writeconfig();
+
+
+enum
+{
+	MAX_MASTERSERVERS=16
+};
+
+void mastersrv_default();
+int mastersrv_load();
+int mastersrv_save();
+
+int mastersrv_refresh_addresses();
+void mastersrv_update();
+int mastersrv_refreshing();
+void mastersrv_dump_servers();
+NETADDR4 mastersrv_get(int index);
+const char *mastersrv_name(int index);
diff --git a/src/engine/e_linereader.c b/src/engine/e_linereader.c
new file mode 100644
index 00000000..57ba9a85
--- /dev/null
+++ b/src/engine/e_linereader.c
@@ -0,0 +1,62 @@
+#include "e_linereader.h"
+
+void linereader_init(LINEREADER *lr, IOHANDLE io)
+{
+	lr->buffer_max_size = 4*1024;
+	lr->buffer_size = 0;
+	lr->buffer_pos = 0;
+	lr->io = io;
+}
+
+char *linereader_get(LINEREADER *lr)
+{
+	unsigned line_start = lr->buffer_pos;
+
+	while(1)
+	{
+		if(lr->buffer_pos >= lr->buffer_size)
+		{
+			/* fetch more */
+
+			/* move the remaining part to the front */
+			unsigned read;
+			unsigned left = lr->buffer_size - line_start;
+
+			if(line_start > lr->buffer_size)
+				left = 0;
+			if(left)
+				mem_move(lr->buffer, &lr->buffer[line_start], left);
+			lr->buffer_pos = left;
+
+			/* fill the buffer */
+			read = io_read(lr->io, &lr->buffer[lr->buffer_pos], lr->buffer_max_size-lr->buffer_pos);
+			lr->buffer_size = left + read;
+			line_start = 0;
+
+			if(!read)
+			{
+				if(left)
+				{
+					lr->buffer[left] = 0; /* return the last line */
+					lr->buffer_pos = left;
+					lr->buffer_size = left;
+					return lr->buffer;
+				}
+				else
+					return 0x0; /* we are done! */
+			}
+		}
+		else
+		{
+			if(lr->buffer[lr->buffer_pos] == '\n' || lr->buffer[lr->buffer_pos] == '\r')
+			{
+				/* line found */
+				lr->buffer[lr->buffer_pos] = 0;
+				lr->buffer_pos++;
+				return &lr->buffer[line_start];
+			}
+			else
+				lr->buffer_pos++;
+		}
+	}
+}
diff --git a/src/engine/e_linereader.h b/src/engine/e_linereader.h
new file mode 100644
index 00000000..d9d050d0
--- /dev/null
+++ b/src/engine/e_linereader.h
@@ -0,0 +1,14 @@
+#include "e_system.h"
+
+/* buffered stream for reading lines, should perhaps be something smaller */
+typedef struct
+{
+	char buffer[4*1024];
+	unsigned buffer_pos;
+	unsigned buffer_size;
+	unsigned buffer_max_size;
+	IOHANDLE io;
+} LINEREADER;
+
+void linereader_init(LINEREADER *lr, IOHANDLE io);
+char *linereader_get(LINEREADER *lr);
diff --git a/src/engine/e_system.c b/src/engine/e_system.c
index c175f7c9..9a8b2a94 100644
--- a/src/engine/e_system.c
+++ b/src/engine/e_system.c
@@ -268,6 +268,45 @@ int io_close(IOHANDLE io)
 	return 1;
 }
 
+void *thread_create(void (*threadfunc)(void *), void *u)
+{
+#if defined(CONF_FAMILY_UNIX)
+	pthread_t id;
+	pthread_create(&id, NULL, (void *(*)(void*))threadfunc, u);
+	return (void*)id;
+#elif defined(CONF_FAMILY_WINDOWS)
+	return CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)threadfunc, u, 0, NULL);
+#else
+	#error not implemented
+#endif
+}
+
+void thread_wait(void *thread)
+{
+#if defined(CONF_FAMILY_UNIX)
+	pthread_join((pthread_t)thread, NULL);
+#elif defined(CONF_FAMILY_WINDOWS)
+	WaitForSingleObject((HANDLE)thread, INFINITE);
+#else
+	#error not implemented
+#endif
+}
+
+void thread_destroy(void *thread)
+{
+}
+
+void thread_yield()
+{
+#if defined(CONF_FAMILY_UNIX)
+	sched_yield();
+#elif defined(CONF_FAMILY_WINDOWS)
+	Sleep(0);
+#else
+	#error not implemented
+#endif
+}
+
 void thread_sleep(int milliseconds)
 {
 #if defined(CONF_FAMILY_UNIX)
@@ -279,6 +318,9 @@ void thread_sleep(int milliseconds)
 #endif
 }
 
+
+
+
 #if defined(CONF_FAMILY_UNIX)
 typedef pthread_mutex_t LOCKINTERNAL;
 #elif defined(CONF_FAMILY_WINDOWS)
@@ -757,6 +799,11 @@ int net_socket_read_wait(NETSOCKET sock, int time)
 #endif*/
 }
 
+unsigned time_timestamp()
+{
+	return time(0);
+}
+
 #if defined(__cplusplus)
 }
 #endif
diff --git a/src/engine/e_system.h b/src/engine/e_system.h
index f271d71a..bfd8cd8c 100644
--- a/src/engine/e_system.h
+++ b/src/engine/e_system.h
@@ -268,13 +268,6 @@ int io_close(IOHANDLE io);
 
 /**** Group: Threads ****/
 
-int thread_create(); /* NOT IMPLEMENTED */
-int thread_destory(); /* NOT IMPLEMENTED */
-
-int thread_run(); /* NOT IMPLEMENTED */
-int thread_pause(); /* NOT IMPLEMENTED */
-int thread_wait(); /* NOT IMPLEMENTED */
-
 /*****
 	Function: thread_sleep
 	
@@ -517,6 +510,12 @@ int net_socket_read_wait(NETSOCKET sock, int time);
 void mem_debug_dump();
 int mem_allocated();
 
+void *thread_create(void (*threadfunc)(void *), void *user);
+void thread_wait(void *thread);
+void thread_destroy(void *thread);
+void thread_yield();
+unsigned time_timestamp();
+
 void swap_endian(void *data, unsigned elem_size, unsigned num);
 
 /* #define cache_prefetch(addr) __builtin_prefetch(addr) */
diff --git a/src/engine/server/es_register.c b/src/engine/server/es_register.c
new file mode 100644
index 00000000..c7f4bcc9
--- /dev/null
+++ b/src/engine/server/es_register.c
@@ -0,0 +1,258 @@
+#include <string.h>
+#include <engine/e_system.h>
+#include <engine/e_network.h>
+#include <engine/e_config.h>
+#include <engine/e_engine.h>
+
+#include <mastersrv/mastersrv.h>
+
+extern NETSERVER *net;
+
+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 void register_new_state(int state)
+{
+	register_state = state;
+	register_state_start = time_get();
+}
+
+static void register_send_fwcheckresponse(NETADDR4 *addr)
+{
+	NETPACKET packet;
+	packet.client_id = -1;
+	packet.address = *addr;
+	packet.flags = PACKETFLAG_CONNLESS;
+	packet.data_size = sizeof(SERVERBROWSE_FWRESPONSE);
+	packet.data = SERVERBROWSE_FWRESPONSE;
+	netserver_send(net, &packet);
+}
+	
+static void register_send_heartbeat(NETADDR4 addr)
+{
+	static unsigned char data[sizeof(SERVERBROWSE_HEARTBEAT) + 2];
+	unsigned short port = config.sv_port;
+	NETPACKET packet;
+	
+	mem_copy(data, SERVERBROWSE_HEARTBEAT, sizeof(SERVERBROWSE_HEARTBEAT));
+	
+	packet.client_id = -1;
+	packet.address = addr;
+	packet.flags = PACKETFLAG_CONNLESS;
+	packet.data_size = sizeof(SERVERBROWSE_HEARTBEAT) + 2;
+	packet.data = &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);
+}
+
+static void register_send_count_request(NETADDR4 addr)
+{
+	NETPACKET packet;
+	packet.client_id = -1;
+	packet.address = addr;
+	packet.flags = PACKETFLAG_CONNLESS;
+	packet.data_size = sizeof(SERVERBROWSE_GETCOUNT);
+	packet.data = SERVERBROWSE_GETCOUNT;
+	netserver_send(net, &packet);
+}
+
+typedef struct
+{
+	NETADDR4 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();
+	mastersrv_update();
+	
+	if(register_state == REGISTERSTATE_START)
+	{
+		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++)
+			{
+				NETADDR4 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*30)
+			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(NETPACKET *p)
+{
+	unsigned char *data = (unsigned char *)p->data;
+	int count = (data[sizeof(SERVERBROWSE_COUNT)]<<8) | data[sizeof(SERVERBROWSE_COUNT)+1];
+	int i;
+
+	for(i = 0; i < MAX_MASTERSERVERS; i++)
+	{
+		if(net_addr4_cmp(&masterserver_info[i].addr, &p->address) == 0)
+		{
+			masterserver_info[i].count = count;
+			break;
+		}
+	}
+}
+
+int register_process_packet(NETPACKET *packet)
+{
+	if(packet->data_size == sizeof(SERVERBROWSE_FWCHECK) &&
+		memcmp(packet->data, SERVERBROWSE_FWCHECK, sizeof(SERVERBROWSE_FWCHECK)) == 0)
+	{
+		register_send_fwcheckresponse(&packet->address);
+		return 1;
+	}
+	else if(packet->data_size == sizeof(SERVERBROWSE_FWOK) &&
+		memcmp(packet->data, 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)
+	{
+		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 trough 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)
+	{
+		register_got_count(packet);
+		return 1;
+	}
+
+	return 0;
+}
diff --git a/src/engine/server/es_server.c b/src/engine/server/es_server.c
index c677d154..415b67fe 100644
--- a/src/engine/server/es_server.c
+++ b/src/engine/server/es_server.c
@@ -29,7 +29,7 @@ static int browseinfo_gametype = -1;
 static int browseinfo_progression = -1;
 
 static int64 lastheartbeat;
-static NETADDR4 master_server;
+/*static NETADDR4 master_server;*/
 
 static char current_map[64];
 static int current_map_crc;
@@ -101,7 +101,7 @@ typedef struct
 } CLIENT;
 
 static CLIENT clients[MAX_CLIENTS];
-static NETSERVER *net;
+NETSERVER *net;
 
 static void snap_init_id()
 {
@@ -535,29 +535,6 @@ static void server_send_map(int cid)
 	msg_pack_end();
 	server_send_msg(cid);
 }
-	
-static void server_send_heartbeat()
-{
-	static unsigned char data[sizeof(SERVERBROWSE_HEARTBEAT) + 2];
-	unsigned short port = config.sv_port;
-	NETPACKET packet;
-	
-	mem_copy(data, SERVERBROWSE_HEARTBEAT, sizeof(SERVERBROWSE_HEARTBEAT));
-	
-	packet.client_id = -1;
-	packet.address = master_server;
-	packet.flags = PACKETFLAG_CONNLESS;
-	packet.data_size = sizeof(SERVERBROWSE_HEARTBEAT) + 2;
-	packet.data = &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);
-}
 
 static void server_process_client_packet(NETPACKET *packet)
 {
@@ -631,7 +608,7 @@ static void server_process_client_packet(NETPACKET *packet)
 		}
 		else if(msg == NETMSG_ENTERGAME)
 		{
-			if(clients[cid].state != SRVCLIENT_STATE_INGAME)
+			if(clients[cid].state == SRVCLIENT_STATE_READY)
 			{
 				dbg_msg("server", "player as entered the game. cid=%x", cid);
 				clients[cid].state = SRVCLIENT_STATE_INGAME;
@@ -783,18 +760,8 @@ static void server_dump_status()
 }
 			
 
-
-
-static void server_send_fwcheckresponse(NETADDR4 *addr)
-{
-	NETPACKET packet;
-	packet.client_id = -1;
-	packet.address = *addr;
-	packet.flags = PACKETFLAG_CONNLESS;
-	packet.data_size = sizeof(SERVERBROWSE_FWRESPONSE);
-	packet.data = SERVERBROWSE_FWRESPONSE;
-	netserver_send(net, &packet);
-}
+extern int register_process_packet(NETPACKET *packet);
+extern int register_update();
 
 static void server_pump_network()
 {
@@ -808,32 +775,18 @@ static void server_pump_network()
 		if(packet.client_id == -1)
 		{
 			/* stateless */
-			if(packet.data_size == sizeof(SERVERBROWSE_GETINFO) &&
-				memcmp(packet.data, SERVERBROWSE_GETINFO, sizeof(SERVERBROWSE_GETINFO)) == 0)
-			{
-				server_send_serverinfo(&packet.address, 0);
-			}
-			else if(packet.data_size == sizeof(SERVERBROWSE_GETINFO_LAN) &&
-				memcmp(packet.data, SERVERBROWSE_GETINFO_LAN, sizeof(SERVERBROWSE_GETINFO_LAN)) == 0)
+			if(!register_process_packet(&packet))
 			{
-				server_send_serverinfo(&packet.address, 1);
-			}
-			else if(packet.data_size == sizeof(SERVERBROWSE_FWCHECK) &&
-				memcmp(packet.data, SERVERBROWSE_FWCHECK, sizeof(SERVERBROWSE_FWCHECK)) == 0)
-			{
-				server_send_fwcheckresponse(&packet.address);
-			}
-			else if(packet.data_size == sizeof(SERVERBROWSE_FWOK) &&
-				memcmp(packet.data, SERVERBROWSE_FWOK, sizeof(SERVERBROWSE_FWOK)) == 0)
-			{
-				if(config.debug)
-					dbg_msg("server", "no firewall/nat problems detected");
-			}
-			else if(packet.data_size == sizeof(SERVERBROWSE_FWERROR) &&
-				memcmp(packet.data, SERVERBROWSE_FWERROR, sizeof(SERVERBROWSE_FWERROR)) == 0)
-			{
-				dbg_msg("server", "ERROR: the master server reports that clients can not connect to this server.");
-				dbg_msg("server", "ERROR: configure your firewall/nat to let trough udp on port %d.", config.sv_port);
+				if(packet.data_size == sizeof(SERVERBROWSE_GETINFO) &&
+					memcmp(packet.data, SERVERBROWSE_GETINFO, sizeof(SERVERBROWSE_GETINFO)) == 0)
+				{
+					server_send_serverinfo(&packet.address, 0);
+				}
+				else if(packet.data_size == sizeof(SERVERBROWSE_GETINFO_LAN) &&
+					memcmp(packet.data, SERVERBROWSE_GETINFO_LAN, sizeof(SERVERBROWSE_GETINFO_LAN)) == 0)
+				{
+					server_send_serverinfo(&packet.address, 1);
+				}
 			}
 		}
 		else
@@ -873,7 +826,6 @@ static int server_load_map(const char *mapname)
 	return 1;
 }
 
-
 static int server_run()
 {
 	NETADDR4 bindaddr;
@@ -910,19 +862,12 @@ static int server_run()
 	netserver_set_callbacks(net, new_client_callback, del_client_callback, 0);
 	
 	dbg_msg("server", "server name is '%s'", config.sv_name);
-	dbg_msg("server", "masterserver is '%s'", config.masterserver);
-	if(net_host_lookup(config.masterserver, MASTERSERVER_PORT, &master_server) != 0)
-	{
-		/* TODO: fix me */
-		/*master_server = netaddr4(0, 0, 0, 0, 0); */
-	}
-
+	
 	mods_init();
 	dbg_msg("server", "version %s", mods_net_version());
 
 	/* start game */
 	{
-		int64 time_per_heartbeat = time_freq() * 30;
 		int64 reporttime = time_get();
 		int reportinterval = 3;
 	
@@ -1025,15 +970,10 @@ static int server_run()
 					perf_end();
 				}
 			}
+			
+			/* master server stuff */
+			register_update();
 	
-			if(config.sv_sendheartbeats)
-			{
-				if (t > lastheartbeat+time_per_heartbeat)
-				{
-					server_send_heartbeat();
-					lastheartbeat = t+time_per_heartbeat;
-				}
-			}
 
 			{
 				static PERFORMACE_INFO scope = {"net", 0};
diff --git a/src/game/server/gs_server.cpp b/src/game/server/gs_server.cpp
index bcab00e5..53ad4c2b 100644
--- a/src/game/server/gs_server.cpp
+++ b/src/game/server/gs_server.cpp
@@ -576,6 +576,7 @@ player::player()
 void player::init()
 {
 	proximity_radius = phys_size;
+	dbg_msg("", "%p %d -> %d (init)", this, client_id, -1);
 	client_id = -1;
 	team = -1; // -1 == spectator
 
@@ -2116,6 +2117,7 @@ void mods_client_enter(int client_id)
 void mods_connected(int client_id)
 {
 	players[client_id].init();
+	dbg_msg("", "%p %d -> %d (mods_connected)", &players[client_id], players[client_id].client_id, client_id);
 	players[client_id].client_id = client_id;
 
 	//dbg_msg("game", "connected player='%d:%s'", client_id, server_clientname(client_id));
@@ -2144,6 +2146,7 @@ void mods_client_drop(int client_id)
 	gameobj->on_player_death(&players[client_id], 0, -1);
 	world->remove_entity(&players[client_id]);
 	world->core.players[client_id] = 0x0;
+	dbg_msg("", "%p %d -> %d (mods_client_drop)", &players[client_id], players[client_id].client_id, -1);
 	players[client_id].client_id = -1;
 }
 
diff --git a/src/mastersrv/mastersrv.cpp b/src/mastersrv/mastersrv.cpp
index aa799919..b83330a5 100644
--- a/src/mastersrv/mastersrv.cpp
+++ b/src/mastersrv/mastersrv.cpp
@@ -27,6 +27,14 @@ static struct packet_data
 	unsigned char header[sizeof(SERVERBROWSE_LIST)];
 	NETADDR4 servers[MAX_SERVERS];
 } data;
+
+static struct count_packet_data
+{
+	unsigned char header[sizeof(SERVERBROWSE_COUNT)];
+	unsigned char high;
+	unsigned char low;
+} count_data;
+
 static int64 server_expire[MAX_SERVERS];
 static int num_servers = 0;
 
@@ -41,6 +49,9 @@ void send_ok(NETADDR4 *addr)
 	p.flags = PACKETFLAG_CONNLESS;
 	p.data_size = sizeof(SERVERBROWSE_FWOK);
 	p.data = SERVERBROWSE_FWOK;
+	
+	// send on both to be sure
+	net_checker.send(&p);
 	net_op.send(&p);
 }
 
@@ -183,6 +194,8 @@ int main(int argc, char **argv)
 	// TODO: check socket for errors
 	
 	mem_copy(data.header, SERVERBROWSE_LIST, sizeof(SERVERBROWSE_LIST));
+	mem_copy(count_data.header, SERVERBROWSE_COUNT, sizeof(SERVERBROWSE_COUNT));
+	
 	dbg_msg("mastersrv", "started");
 	
 	while(1)
@@ -207,6 +220,21 @@ int main(int argc, char **argv)
 				// add it
 				add_checkserver(&packet.address, &alt);
 			}
+			else if(packet.data_size == sizeof(SERVERBROWSE_GETCOUNT) &&
+				memcmp(packet.data, SERVERBROWSE_GETCOUNT, sizeof(SERVERBROWSE_GETCOUNT)) == 0)
+			{
+				dbg_msg("mastersrv", "count requested, responding with %d", num_servers);
+				
+				NETPACKET p;
+				p.client_id = -1;
+				p.address = packet.address;
+				p.flags = PACKETFLAG_CONNLESS;
+				p.data_size = sizeof(count_data);
+				p.data = &count_data;
+				count_data.high = (num_servers>>8)&0xff;
+				count_data.low = num_servers&0xff;
+				net_op.send(&p);
+			}
 			else if(packet.data_size == sizeof(SERVERBROWSE_GETLIST) &&
 				memcmp(packet.data, SERVERBROWSE_GETLIST, sizeof(SERVERBROWSE_GETLIST)) == 0)
 			{
diff --git a/src/mastersrv/mastersrv.h b/src/mastersrv/mastersrv.h
index 1de5b9ef..b5347914 100644
--- a/src/mastersrv/mastersrv.h
+++ b/src/mastersrv/mastersrv.h
@@ -10,6 +10,9 @@ static const unsigned char SERVERBROWSE_HEARTBEAT[] = {255, 255, 255, 255, 'b',
 static const unsigned char SERVERBROWSE_GETLIST[] = {255, 255, 255, 255, 'r', 'e', 'q', 't'};
 static const unsigned char SERVERBROWSE_LIST[] = {255, 255, 255, 255, 'l', 'i', 's', 't'};
 
+static const unsigned char SERVERBROWSE_GETCOUNT[] = {255, 255, 255, 255, 'c', 'o', 'u', 'n'};
+static const unsigned char SERVERBROWSE_COUNT[] = {255, 255, 255, 255, 's', 'i', 'z', 'e'};
+
 static const unsigned char SERVERBROWSE_GETINFO[] = {255, 255, 255, 255, 'g', 'i', 'e', 'f'};
 static const unsigned char SERVERBROWSE_INFO[] = {255, 255, 255, 255, 'i', 'n', 'f', 'o'};
 
diff --git a/src/tools/fake_server.c b/src/tools/fake_server.c
index c81dfaaa..a2af1a0a 100644
--- a/src/tools/fake_server.c
+++ b/src/tools/fake_server.c
@@ -45,20 +45,29 @@ int player_scores[16] = {0};
 int num_players = 0;
 int max_players = 0;
 
-static void send_heartbeats()
-{
-	int i = 0;
 
-	NETPACKET p;
-	p.client_id = -1;
-	p.flags = PACKETFLAG_CONNLESS;
-	p.data_size = sizeof(SERVERBROWSE_HEARTBEAT);
-	p.data = SERVERBROWSE_HEARTBEAT;
 
+static void send_heartbeats()
+{
+	static unsigned char data[sizeof(SERVERBROWSE_HEARTBEAT) + 2];
+	NETPACKET packet;
+	int i;
+	
+	mem_copy(data, SERVERBROWSE_HEARTBEAT, sizeof(SERVERBROWSE_HEARTBEAT));
+	
+	packet.client_id = -1;
+	packet.flags = PACKETFLAG_CONNLESS;
+	packet.data_size = sizeof(SERVERBROWSE_HEARTBEAT) + 2;
+	packet.data = &data;
+
+	/* supply the set port that the master can use if it has problems */	
+	data[sizeof(SERVERBROWSE_HEARTBEAT)] = 0;
+	data[sizeof(SERVERBROWSE_HEARTBEAT)+1] = 0;
+	
 	for(i = 0; i < num_masters; i++)
 	{
-		p.address = master_servers[i];
-		netserver_send(net, &p);
+		packet.address = master_servers[i];
+		netserver_send(net, &packet);
 	}
 }