about summary refs log tree commit diff
path: root/src/engine
diff options
context:
space:
mode:
authorMagnus Auvinen <magnus.auvinen@gmail.com>2007-07-21 18:07:27 +0000
committerMagnus Auvinen <magnus.auvinen@gmail.com>2007-07-21 18:07:27 +0000
commit9e4aea97bdb5a903150df57f8d546733ce4b4db2 (patch)
tree7e18776bd7ace981895bca01ac753c9c289a71a5 /src/engine
parentf0e8502050ce27b9cef72fa385619d13faa35c27 (diff)
downloadzcatch-9e4aea97bdb5a903150df57f8d546733ce4b4db2.tar.gz
zcatch-9e4aea97bdb5a903150df57f8d546733ce4b4db2.zip
lots of cool new features :D new master server
Diffstat (limited to 'src/engine')
-rw-r--r--src/engine/client/client.cpp481
-rw-r--r--src/engine/interface.h16
-rw-r--r--src/engine/network.cpp209
-rw-r--r--src/engine/network.h5
-rw-r--r--src/engine/server/server.cpp101
5 files changed, 505 insertions, 307 deletions
diff --git a/src/engine/client/client.cpp b/src/engine/client/client.cpp
index 57b3bd57..816a30e7 100644
--- a/src/engine/client/client.cpp
+++ b/src/engine/client/client.cpp
@@ -20,6 +20,8 @@
 #include <engine/config.h>
 #include <engine/network.h>
 
+#include <mastersrv/mastersrv.h>
+
 using namespace baselib;
 
 // --- input wrappers ---
@@ -156,15 +158,92 @@ int client_send_msg()
 	return 0;
 }
 
+static struct 
+{
+	server_info infos[MAX_SERVERS];
+	int64 request_times[MAX_SERVERS];
+	netaddr4 addresses[MAX_SERVERS];
+	int num;
+} servers;
+
+static netaddr4 master_server;
+
+int client_serverbrowse_getlist(server_info **serverlist)
+{
+	*serverlist = servers.infos;
+	return servers.num;
+}
+
+void client_serverbrowse_refresh()
+{
+	dbg_msg("client", "requesting server list");
+	NETPACKET packet;
+	packet.client_id = -1;
+	packet.address = master_server;
+	packet.flags = PACKETFLAG_CONNLESS;
+	packet.data_size = sizeof(SERVERBROWSE_GETLIST);
+	packet.data = SERVERBROWSE_GETLIST;
+	net.send(&packet);	
+	
+	// reset the list
+	servers.num = 0;
+}
+
+enum
+{
+	STATE_OFFLINE,
+	STATE_CONNECTING,
+	STATE_LOADING,
+	STATE_ONLINE,
+	STATE_BROKEN,
+	STATE_QUIT,
+};
+
+static netaddr4 server_address;
+
+static int state;
+static int get_state() { return state; }
+static void set_state(int s)
+{
+	dbg_msg("game", "state change. last=%d current=%d", state, s);
+	state = s;
+}
+
+
+void client_connect(const char *server_address_str)
+{
+	char buf[512];
+	strncpy(buf, server_address_str, 512);
+	
+	const char *port_str = 0;
+	for(int k = 0; buf[k]; k++)
+	{
+		if(buf[k] == ':')
+		{
+			port_str = &(buf[k+1]);
+			buf[k] = 0;
+			break;
+		}
+	}
+	
+	int port = 8303;
+	if(port_str)
+		port = atoi(port_str);
+		
+	if(net_host_lookup(buf, port, &server_address) != 0)
+		dbg_msg("client", "could not find the address of %s, connecting to localhost", buf);
+	
+	net.connect(&server_address);
+	set_state(STATE_CONNECTING);	
+}
+
 // --- client ---
 // TODO: remove this class
 class client
 {
 public:
-	
-	//socket_udp4 socket;
-	//connection conn;
-	int64 reconnect_timer;
+	int info_request_begin;
+	int info_request_end;
 	
 	int snapshot_part;
 
@@ -173,24 +252,6 @@ public:
 	// data to hold three snapshots
 	// previous, 
 
-	enum
-	{
-		STATE_OFFLINE,
-		STATE_CONNECTING,
-		STATE_LOADING,
-		STATE_ONLINE,
-		STATE_BROKEN,
-		STATE_QUIT,
-	};
-	
-	int state;
-	int get_state() { return state; }
-	void set_state(int s)
-	{
-		dbg_msg("game", "state change. last=%d current=%d", state, s);
-		state = s;
-	}
-
 	void send_info()
 	{
 		recived_snapshots = 0;
@@ -242,12 +303,6 @@ public:
 		map_unload();
 	}
 	
-	void connect(netaddr4 *server_address)
-	{
-		net.connect(server_address);
-		set_state(STATE_CONNECTING);
-	}
-	
 	bool load_data()
 	{
 		debug_font = gfx_load_texture("data/debug_font.png");
@@ -293,13 +348,11 @@ public:
 		}
 		else if (get_state() != STATE_CONNECTING && get_state() != STATE_LOADING)
 		{
-			netaddr4 server_address;
-			int status = modmenu_render(&server_address);
+			//netaddr4 server_address;
+			int status = modmenu_render();
 
 			if (status == -1)
 				set_state(STATE_QUIT);
-			else if (status)
-				connect(&server_address);
 		}
 		else if (get_state() == STATE_CONNECTING || get_state() == STATE_LOADING)
 		{
@@ -332,7 +385,7 @@ public:
 		}
 	}
 	
-	void run(netaddr4 *server_address)
+	void run(const char *direct_connect_server)
 	{
 		local_start_time = time_get();
 		snapshot_part = 0;
@@ -356,20 +409,16 @@ public:
 		// init menu
 		modmenu_init();
 		
-		net.open(0);
-		
 		// open socket
-		/*
-		if(!socket.open(0))
-		{
-			dbg_msg("network/client", "failed to open socket");
-			return;
-		}*/
+		net.open(0, 0);
+		
+		//
+		net_host_lookup(MASTERSERVER_ADDRESS, MASTERSERVER_PORT, &master_server);
 
 		// connect to the server if wanted
-		if(server_address)
-			connect(server_address);
-		
+		if(direct_connect_server)
+			client_connect(direct_connect_server);
+			
 		//int64 inputs_per_second = 50;
 		//int64 time_per_input = time_freq()/inputs_per_second;
 		int64 game_starttime = time_get();
@@ -413,6 +462,9 @@ public:
 			// pump the network
 			pump_network();
 			
+			// update the server browser
+			serverbrowse_update();
+			
 			// render
 			render();
 			
@@ -457,146 +509,235 @@ public:
 		send_error(msg);
 		set_state(STATE_BROKEN);
 	}
+	
+	void serverbrowse_request(int id)
+	{
+		dbg_msg("client", "requesting server info from %d.%d.%d.%d:%d",
+			servers.addresses[id].ip[0], servers.addresses[id].ip[1], servers.addresses[id].ip[2],
+			servers.addresses[id].ip[3], servers.addresses[id].port);
+		NETPACKET packet;
+		packet.client_id = -1;
+		packet.address = servers.addresses[id];
+		packet.flags = PACKETFLAG_CONNLESS;
+		packet.data_size = sizeof(SERVERBROWSE_GETINFO);
+		packet.data = SERVERBROWSE_GETINFO;
+		net.send(&packet);
+		servers.request_times[id] = time_get();
+	}
+	
+	void serverbrowse_update()
+	{
+		int64 timeout = time_freq();
+		int64 now = time_get();
+		int max_requests = 10;
+		
+		// timeout old requests
+		while(info_request_begin < servers.num && info_request_begin < info_request_end)
+		{
+			if(now > servers.request_times[info_request_begin]+timeout)
+				info_request_begin++;
+			else
+				break;
+		}
+		
+		// send new requests
+		while(info_request_end < servers.num && info_request_end-info_request_begin < max_requests)
+		{
+			serverbrowse_request(info_request_end);
+			info_request_end++;
+		}
+	}
 
 	void process_packet(NETPACKET *packet)
 	{
-		int sys;
-		int msg = msg_unpack_start(packet->data, packet->data_size, &sys);
-		if(sys)
+		if(packet->client_id == -1)
 		{
-			// system message
-			if(msg == NETMSG_MAP)
+			// connectionlesss
+			if(packet->data_size >= (int)sizeof(SERVERBROWSE_LIST) &&
+				memcmp(packet->data, SERVERBROWSE_LIST, sizeof(SERVERBROWSE_LIST)) == 0)
 			{
-				const char *map = msg_unpack_string();
-				dbg_msg("client/network", "connection accepted, map=%s", map);
-				set_state(STATE_LOADING);
-				
-				if(map_load(map))
+				// server listing
+				int size = packet->data_size-sizeof(SERVERBROWSE_LIST);
+				mem_copy(servers.addresses, (char*)packet->data+sizeof(SERVERBROWSE_LIST), size);
+				servers.num = size/sizeof(NETADDR4);
+
+				info_request_begin = 0;
+				info_request_end = 0;
+
+				for(int i = 0; i < servers.num; i++)
 				{
-					modc_entergame();
-					send_entergame();
-					dbg_msg("client/network", "loading done");
-					// now we will wait for two snapshots
-					// to finish the connection
+					servers.infos[i].num_players = 0;
+					servers.infos[i].max_players = 0;
+					servers.infos[i].latency = 999;
+					sprintf(servers.infos[i].address, "%d.%d.%d.%d:%d",
+						servers.addresses[i].ip[0], servers.addresses[i].ip[1], servers.addresses[i].ip[2],
+						servers.addresses[i].ip[3], servers.addresses[i].port);
 				}
-				else
+			}
+
+			if(packet->data_size >= (int)sizeof(SERVERBROWSE_INFO) &&
+				memcmp(packet->data, SERVERBROWSE_INFO, sizeof(SERVERBROWSE_INFO)) == 0)
+			{
+				// we got ze info
+				data_unpacker unpacker;
+				unpacker.reset((unsigned char*)packet->data+sizeof(SERVERBROWSE_INFO), packet->data_size-sizeof(SERVERBROWSE_INFO));
+				
+				for(int i = 0; i < servers.num; i++)
 				{
-					error("failure to load map");
+					if(net_addr4_cmp(&servers.addresses[i], &packet->address) == 0)
+					{
+						strncpy(servers.infos[i].name, unpacker.get_string(), 128);
+						strncpy(servers.infos[i].map, unpacker.get_string(), 128);
+						servers.infos[i].max_players = unpacker.get_int();
+						servers.infos[i].num_players = unpacker.get_int();
+						servers.infos[i].latency = ((time_get() - servers.request_times[i])*1000)/time_freq();
+						dbg_msg("client", "got server info");
+						break;
+					}
 				}
 			}
-			else if(msg == NETMSG_SNAP || msg == NETMSG_SNAPSMALL || msg == NETMSG_SNAPEMPTY)
+		}
+		else
+		{
+			
+			int sys;
+			int msg = msg_unpack_start(packet->data, packet->data_size, &sys);
+			if(sys)
 			{
-				//dbg_msg("client/network", "got snapshot");
-				int game_tick = msg_unpack_int();
-				int delta_tick = game_tick-msg_unpack_int();
-				int num_parts = 1;
-				int part = 0;
-				int part_size = 0;
-				
-				if(msg == NETMSG_SNAP)
+				// system message
+				if(msg == NETMSG_MAP)
 				{
-					num_parts = msg_unpack_int();
-					part = msg_unpack_int();
+					const char *map = msg_unpack_string();
+					dbg_msg("client/network", "connection accepted, map=%s", map);
+					set_state(STATE_LOADING);
+					
+					if(map_load(map))
+					{
+						modc_entergame();
+						send_entergame();
+						dbg_msg("client/network", "loading done");
+						// now we will wait for two snapshots
+						// to finish the connection
+					}
+					else
+					{
+						error("failure to load map");
+					}
 				}
-				
-				if(msg != NETMSG_SNAPEMPTY)
-					part_size = msg_unpack_int();
-				
-				if(snapshot_part == part)
+				else if(msg == NETMSG_SNAP || msg == NETMSG_SNAPSMALL || msg == NETMSG_SNAPEMPTY)
 				{
-					// TODO: clean this up abit
-					const char *d = (const char *)msg_unpack_raw(part_size);
-					mem_copy((char*)snapshots[SNAP_INCOMMING] + part*MAX_SNAPSHOT_PACKSIZE, d, part_size);
-					snapshot_part++;
-				
-					if(snapshot_part == num_parts)
+					//dbg_msg("client/network", "got snapshot");
+					int game_tick = msg_unpack_int();
+					int delta_tick = game_tick-msg_unpack_int();
+					int num_parts = 1;
+					int part = 0;
+					int part_size = 0;
+					
+					if(msg == NETMSG_SNAP)
 					{
-						snapshot *tmp = snapshots[SNAP_PREV];
-						snapshots[SNAP_PREV] = snapshots[SNAP_CURRENT];
-						snapshots[SNAP_CURRENT] = tmp;
-						current_tick = game_tick;
-
-						// decompress snapshot
-						void *deltadata = snapshot_empty_delta();
-						int deltasize = sizeof(int)*3;
-
-						unsigned char tmpbuffer[MAX_SNAPSHOT_SIZE];
-						unsigned char tmpbuffer2[MAX_SNAPSHOT_SIZE];
-						if(part_size)
+						num_parts = msg_unpack_int();
+						part = msg_unpack_int();
+					}
+					
+					if(msg != NETMSG_SNAPEMPTY)
+						part_size = msg_unpack_int();
+					
+					if(snapshot_part == part)
+					{
+						// TODO: clean this up abit
+						const char *d = (const char *)msg_unpack_raw(part_size);
+						mem_copy((char*)snapshots[SNAP_INCOMMING] + part*MAX_SNAPSHOT_PACKSIZE, d, part_size);
+						snapshot_part++;
+					
+						if(snapshot_part == num_parts)
 						{
-							int compsize = zerobit_decompress(snapshots[SNAP_INCOMMING], part_size, tmpbuffer);
-							int intsize = intpack_decompress(tmpbuffer, compsize, tmpbuffer2);
-							deltadata = tmpbuffer2;
-							deltasize = intsize;
-						}
+							snapshot *tmp = snapshots[SNAP_PREV];
+							snapshots[SNAP_PREV] = snapshots[SNAP_CURRENT];
+							snapshots[SNAP_CURRENT] = tmp;
+							current_tick = game_tick;
+
+							// decompress snapshot
+							void *deltadata = snapshot_empty_delta();
+							int deltasize = sizeof(int)*3;
+
+							unsigned char tmpbuffer[MAX_SNAPSHOT_SIZE];
+							unsigned char tmpbuffer2[MAX_SNAPSHOT_SIZE];
+							if(part_size)
+							{
+								int compsize = zerobit_decompress(snapshots[SNAP_INCOMMING], part_size, tmpbuffer);
+								int intsize = intpack_decompress(tmpbuffer, compsize, tmpbuffer2);
+								deltadata = tmpbuffer2;
+								deltasize = intsize;
+							}
 
-						// find snapshot that we should use as delta 
-						static snapshot emptysnap;
-						emptysnap.data_size = 0;
-						emptysnap.num_items = 0;
-						
-						snapshot *deltashot = &emptysnap;
-						int deltashot_size;
+							// find snapshot that we should use as delta 
+							static snapshot emptysnap;
+							emptysnap.data_size = 0;
+							emptysnap.num_items = 0;
+							
+							snapshot *deltashot = &emptysnap;
+							int deltashot_size;
 
-						if(delta_tick >= 0)
-						{
-							void *delta_data;
-							deltashot_size = snapshots_new.get(delta_tick, &delta_data);
-							if(deltashot_size >= 0)
+							if(delta_tick >= 0)
 							{
-								deltashot = (snapshot *)delta_data;
+								void *delta_data;
+								deltashot_size = snapshots_new.get(delta_tick, &delta_data);
+								if(deltashot_size >= 0)
+								{
+									deltashot = (snapshot *)delta_data;
+								}
+								else
+								{
+									// TODO: handle this
+									dbg_msg("client", "error, couldn't find the delta snapshot");
+								}
 							}
-							else
+
+							int snapsize = snapshot_unpack_delta(deltashot, (snapshot*)snapshots[SNAP_CURRENT], deltadata, deltasize);
+							//snapshot *shot = (snapshot *)snapshots[SNAP_CURRENT];
+
+							// purge old snapshots					
+							snapshots_new.purge_until(delta_tick);
+							snapshots_new.purge_until(game_tick-50); // TODO: change this to server tickrate
+							
+							// add new
+							snapshots_new.add(game_tick, snapsize, snapshots[SNAP_CURRENT]);
+							
+							// apply snapshot, cycle pointers
+							recived_snapshots++;
+							snapshot_start_time = time_get();
+							
+							// we got two snapshots until we see us self as connected
+							if(recived_snapshots == 2)
 							{
-								// TODO: handle this
-								dbg_msg("client", "error, couldn't find the delta snapshot");
+								local_start_time = time_get();
+								set_state(STATE_ONLINE);
 							}
+							
+							if(recived_snapshots > 2)
+								modc_newsnapshot();
+							
+							snapshot_part = 0;
+							
+							// ack snapshot
+							msg_pack_start_system(NETMSG_SNAPACK, 0);
+							msg_pack_int(game_tick);
+							msg_pack_end();
+							client_send_msg();
 						}
-
-						int snapsize = snapshot_unpack_delta(deltashot, (snapshot*)snapshots[SNAP_CURRENT], deltadata, deltasize);
-						//snapshot *shot = (snapshot *)snapshots[SNAP_CURRENT];
-
-						// purge old snapshots					
-						snapshots_new.purge_until(delta_tick);
-						snapshots_new.purge_until(game_tick-50); // TODO: change this to server tickrate
-						
-						// add new
-						snapshots_new.add(game_tick, snapsize, snapshots[SNAP_CURRENT]);
-						
-						// apply snapshot, cycle pointers
-						recived_snapshots++;
-						snapshot_start_time = time_get();
-						
-						// we got two snapshots until we see us self as connected
-						if(recived_snapshots == 2)
-						{
-							local_start_time = time_get();
-							set_state(STATE_ONLINE);
-						}
-						
-						if(recived_snapshots > 2)
-							modc_newsnapshot();
-						
+					}
+					else
+					{
+						dbg_msg("client", "snapshot reset!");
 						snapshot_part = 0;
-						
-						// ack snapshot
-						msg_pack_start_system(NETMSG_SNAPACK, 0);
-						msg_pack_int(game_tick);
-						msg_pack_end();
-						client_send_msg();
 					}
 				}
-				else
-				{
-					dbg_msg("client", "snapshot reset!");
-					snapshot_part = 0;
-				}
 			}
-		}
-		else
-		{
-			// game message
-			modc_message(msg);
+			else
+			{
+				// game message
+				modc_message(msg);
+			}
 		}
 	}
 	
@@ -636,11 +777,8 @@ int main(int argc, char **argv)
 	config_reset();
 	config_load("teewars.cfg");
 
+	const char *direct_connect_server = 0x0;
 	snd_set_master_volume(config.volume / 255.0f);
-
-	netaddr4 server_address(127, 0, 0, 1, 8303);
-	//const char *name = "nameless jerk";
-	bool connect_at_once = false;
 	bool editor = false;
 
 	// init network, need to be done first so we can do lookups
@@ -653,24 +791,7 @@ int main(int argc, char **argv)
 		{
 			// -c SERVER:PORT
 			i++;
-			const char *port_str = 0;
-			for(int k = 0; argv[i][k]; k++)
-			{
-				if(argv[i][k] == ':')
-				{
-					port_str = &(argv[i][k+1]);
-					argv[i][k] = 0;
-					break;
-				}
-			}
-			int port = 8303;
-			if(port_str)
-				port = atoi(port_str);
-				
-			if(net_host_lookup(argv[i], port, &server_address) != 0)
-				dbg_msg("main", "could not find the address of %s, connecting to localhost", argv[i]);
-			else
-				connect_at_once = true;
+			direct_connect_server = argv[i];
 		}
 		else if(argv[i][0] == '-' && argv[i][1] == 'n' && argv[i][2] == 0 && argc - i > 1)
 		{
@@ -695,7 +816,7 @@ int main(int argc, char **argv)
 	{
 		// start the client
 		client c;
-		c.run(connect_at_once ? &server_address : 0x0);
+		c.run(direct_connect_server);
 	}
 	return 0;
 }
diff --git a/src/engine/interface.h b/src/engine/interface.h
index d23067fb..4cadb996 100644
--- a/src/engine/interface.h
+++ b/src/engine/interface.h
@@ -689,7 +689,7 @@ void modmenu_shutdown();
     Function: modmenu_render
         Called every frame to let the menu render it self.
 */
-int modmenu_render(void *server_address);
+int modmenu_render();
 
 
 
@@ -752,9 +752,19 @@ float gfx_pretty_text_width(float size, const char *text);
 void mods_message(int msg, int client_id);
 void modc_message(int msg);
 
-#define MASTER_SERVER_ADDRESS "master.teewars.com"
-#define MASTER_SERVER_PORT 8300
+struct server_info
+{
+	int max_players;
+	int num_players;
+	int latency; // in ms
+	char name[128];
+	char map[128];
+	char address[128];
+};
 
+void client_connect(const char *address);
 
+void client_serverbrowse_refresh();
+int client_serverbrowse_getlist(server_info **servers);
 
 #endif
diff --git a/src/engine/network.cpp b/src/engine/network.cpp
index 3abe44b0..c6b8207f 100644
--- a/src/engine/network.cpp
+++ b/src/engine/network.cpp
@@ -18,20 +18,6 @@
 		unsigned char crc[2];		6
 */
 
-
-// move
-static int net_addr4_cmp(const NETADDR4 *a, const NETADDR4 *b)
-{
-	if(	a->ip[0] != b->ip[0] ||
-		a->ip[1] != b->ip[1] ||
-		a->ip[2] != b->ip[2] ||
-		a->ip[3] != b->ip[3] ||
-		a->port != b->port
-	)
-		return 1;
-	return 0;
-}
-
 enum
 {
 	NETWORK_VERSION = 1,
@@ -51,7 +37,7 @@ enum
 	NETWORK_PACKETFLAG_CLOSE=0x04,
 	NETWORK_PACKETFLAG_VITAL=0x08,
 	NETWORK_PACKETFLAG_RESEND=0x10,
-	//NETWORK_PACKETFLAG_STATELESS=0x20,
+	NETWORK_PACKETFLAG_CONNLESS=0x20,
 };
 
 struct NETPACKETDATA
@@ -208,7 +194,7 @@ static void conn_send(NETCONNECTION *conn, int flags, int data_size, const void
 	p.data_size = data_size;
 	p.data = (unsigned char *)data;
 	p.first_send_time = time_get();
-	
+
 	if(flags&NETWORK_PACKETFLAG_VITAL)
 	{
 		// save packet if we need to resend
@@ -321,7 +307,7 @@ static int conn_feed(NETCONNECTION *conn, NETPACKETDATA *p, NETADDR4 *addr)
 
 static void conn_update(NETCONNECTION *conn)
 {
-	if(conn->state == NETWORK_CONNSTATE_ERROR)
+	if(conn->state == NETWORK_CONNSTATE_OFFLINE || conn->state == NETWORK_CONNSTATE_ERROR)
 		return;
 	
 	// check for timeout
@@ -479,58 +465,71 @@ int net_server_recv(NETSERVER *s, NETPACKET *packet)
 		int r = check_packet(s->recv_buffer, bytes, &data);
 		if(r == 0)
 		{
-			// ok packet, process it
-			if(data.flags == NETWORK_PACKETFLAG_CONNECT)
+			if(data.flags&NETWORK_PACKETFLAG_CONNLESS)
 			{
-				int found = 0;
-				
-				// check if we already got this client
-				for(int i = 0; i < NETWORK_MAX_CLIENTS; i++)
-				{
-					if(s->slots[i].conn.state != NETWORK_CONNSTATE_OFFLINE &&
-						net_addr4_cmp(&s->slots[i].conn.peeraddr, &addr) == 0)
-					{
-						found = 1; // silent ignore.. we got this client already
-						break;
-					}
-				}
-				
-				// client that wants to connect
-				if(!found)
+				// connection less packets
+				packet->client_id = -1;
+				packet->address = addr;
+				packet->flags = PACKETFLAG_CONNLESS;
+				packet->data_size = data.data_size;
+				packet->data = data.data;
+				return 1;		
+			}
+			else
+			{
+				// ok packet, process it
+				if(data.flags == NETWORK_PACKETFLAG_CONNECT)
 				{
+					int found = 0;
+					
+					// check if we already got this client
 					for(int i = 0; i < NETWORK_MAX_CLIENTS; i++)
 					{
-						if(s->slots[i].conn.state == NETWORK_CONNSTATE_OFFLINE)
+						if(s->slots[i].conn.state != NETWORK_CONNSTATE_OFFLINE &&
+							net_addr4_cmp(&s->slots[i].conn.peeraddr, &addr) == 0)
 						{
-							conn_feed(&s->slots[i].conn, &data, &addr);
-							found = 1;
+							found = 1; // silent ignore.. we got this client already
 							break;
 						}
 					}
+					
+					// client that wants to connect
+					if(!found)
+					{
+						for(int i = 0; i < NETWORK_MAX_CLIENTS; i++)
+						{
+							if(s->slots[i].conn.state == NETWORK_CONNSTATE_OFFLINE)
+							{
+								conn_feed(&s->slots[i].conn, &data, &addr);
+								found = 1;
+								break;
+							}
+						}
+					}
+					
+					if(!found)
+					{
+						// TODO: send error
+					}
 				}
-				
-				if(!found)
-				{
-					// TODO: send error
-				}
-			}
-			else
-			{
-				// find matching slot
-				for(int i = 0; i < NETWORK_MAX_CLIENTS; i++)
+				else
 				{
-					if(net_addr4_cmp(&s->slots[i].conn.peeraddr, &addr) == 0)
+					// find matching slot
+					for(int i = 0; i < NETWORK_MAX_CLIENTS; i++)
 					{
-						if(conn_feed(&s->slots[i].conn, &data, &addr))
+						if(net_addr4_cmp(&s->slots[i].conn.peeraddr, &addr) == 0)
 						{
-							if(data.data_size)
+							if(conn_feed(&s->slots[i].conn, &data, &addr))
 							{
-								packet->client_id = i;	
-								packet->address = addr;
-								packet->flags = 0;
-								packet->data_size = data.data_size;
-								packet->data = data.data;
-								return 1;
+								if(data.data_size)
+								{
+									packet->client_id = i;	
+									packet->address = addr;
+									packet->flags = 0;
+									packet->data_size = data.data_size;
+									packet->data = data.data;
+									return 1;
+								}
 							}
 						}
 					}
@@ -540,6 +539,7 @@ int net_server_recv(NETSERVER *s, NETPACKET *packet)
 		else
 		{
 			// errornous packet, drop it
+			dbg_msg("server", "crazy packet");
 		}
 		
 		// read header
@@ -551,10 +551,27 @@ int net_server_recv(NETSERVER *s, NETPACKET *packet)
 
 int net_server_send(NETSERVER *s, NETPACKET *packet)
 {
-	// TODO: insert stuff for stateless stuff
-	dbg_assert(packet->client_id >= 0, "errornous client id");
-	dbg_assert(packet->client_id < NETWORK_MAX_CLIENTS, "errornous client id");
-	conn_send(&s->slots[packet->client_id].conn, 0, packet->data_size, packet->data);
+	if(packet->flags&PACKETFLAG_CONNLESS)
+	{
+		// send connectionless packet
+		NETPACKETDATA p;
+		p.ID[0] = 'T';
+		p.ID[1] = 'W';
+		p.version = NETWORK_VERSION;
+		p.flags = NETWORK_PACKETFLAG_CONNLESS;
+		p.seq = 0;
+		p.ack = 0;
+		p.crc = 0;
+		p.data_size = packet->data_size;
+		p.data = (unsigned char *)packet->data;
+		send_packet(s->socket, &packet->address, &p);
+	}
+	else
+	{
+		dbg_assert(packet->client_id >= 0, "errornous client id");
+		dbg_assert(packet->client_id < NETWORK_MAX_CLIENTS, "errornous client id");
+		conn_send(&s->slots[packet->client_id].conn, 0, packet->data_size, packet->data);
+	}
 	return 0;
 }
 
@@ -574,11 +591,11 @@ void net_server_stats(NETSERVER *s, NETSTATS *stats)
 }
 
 //
-NETCLIENT *net_client_open(int flags)
+NETCLIENT *net_client_open(int port, int flags)
 {
 	NETCLIENT *client = (NETCLIENT *)mem_alloc(sizeof(NETCLIENT), 1);
 	mem_zero(client, sizeof(NETCLIENT));
-	client->socket = net_udp4_create(0);
+	client->socket = net_udp4_create(port);
 	conn_init(&client->conn, client->socket);
 	return client;
 }
@@ -626,23 +643,37 @@ int net_client_recv(NETCLIENT *c, NETPACKET *packet)
 		
 		NETPACKETDATA data;
 		int r = check_packet(c->recv_buffer, bytes, &data);
-		if(r == 0 && conn_feed(&c->conn, &data, &addr))
-		{
-			// fill in packet
-			packet->client_id = 0;
-			packet->address = addr;
-			packet->flags = 0;
-			packet->data_size = data.data_size;
-			packet->data = data.data;
-			return 1;
-		}
-		else
+		
+		if(r == 0)
 		{
-			// errornous packet, drop it
+			if(data.flags&NETWORK_PACKETFLAG_CONNLESS)
+			{
+				// connection less packets
+				packet->client_id = -1;
+				packet->address = addr;
+				packet->flags = PACKETFLAG_CONNLESS;
+				packet->data_size = data.data_size;
+				packet->data = data.data;
+				return 1;		
+			}
+			else
+			{
+				if(conn_feed(&c->conn, &data, &addr))
+				{
+					// fill in packet
+					packet->client_id = 0;
+					packet->address = addr;
+					packet->flags = 0;
+					packet->data_size = data.data_size;
+					packet->data = data.data;
+					return 1;
+				}
+				else
+				{
+					// errornous packet, drop it
+				}
+			}			
 		}
-		
-		// read header
-		// do checksum
 	}
 	
 	return 0;
@@ -650,9 +681,27 @@ int net_client_recv(NETCLIENT *c, NETPACKET *packet)
 
 int net_client_send(NETCLIENT *c, NETPACKET *packet)
 {
-	// TODO: insert stuff for stateless stuff
-	dbg_assert(packet->client_id == 0, "errornous client id");
-	conn_send(&c->conn, 0, packet->data_size, packet->data);
+	if(packet->flags&PACKETFLAG_CONNLESS)
+	{
+		// send connectionless packet
+		NETPACKETDATA p;
+		p.ID[0] = 'T';
+		p.ID[1] = 'W';
+		p.version = NETWORK_VERSION;
+		p.flags = NETWORK_PACKETFLAG_CONNLESS;
+		p.seq = 0;
+		p.ack = 0;
+		p.crc = 0;
+		p.data_size = packet->data_size;
+		p.data = (unsigned char *)packet->data;
+		send_packet(c->socket, &packet->address, &p);
+	}
+	else
+	{
+		// TODO: insert stuff for stateless stuff
+		dbg_assert(packet->client_id == 0, "errornous client id");
+		conn_send(&c->conn, 0, packet->data_size, packet->data);
+	}
 	return 0;
 }
 
diff --git a/src/engine/network.h b/src/engine/network.h
index de035888..e251f13e 100644
--- a/src/engine/network.h
+++ b/src/engine/network.h
@@ -28,6 +28,7 @@ enum
 {
 	NETFLAG_ALLOWSTATELESS=1,
 	PACKETFLAG_VITAL=1,
+	PACKETFLAG_CONNLESS=2,
 	
 	NETSTATE_OFFLINE=0,
 	NETSTATE_CONNECTING,
@@ -46,7 +47,7 @@ int net_server_delclient(NETSERVER *s); // -1 when no more, else, client id
 void net_server_stats(NETSERVER *s, NETSTATS *stats);
 
 // client side
-NETCLIENT *net_client_open(int flags);
+NETCLIENT *net_client_open(int port, int flags);
 int net_client_disconnect(NETCLIENT *c, const char *reason);
 int net_client_connect(NETCLIENT *c, NETADDR4 *addr);
 int net_client_recv(NETCLIENT *c, NETPACKET *packet);
@@ -88,7 +89,7 @@ public:
 	net_client() : ptr(0) {}
 	~net_client() { close(); }
 	
-	int open(int flags) { ptr = net_client_open(flags); return ptr != 0; }
+	int open(int port, int flags) { ptr = net_client_open(port, flags); return ptr != 0; }
 	int close() { int r = net_client_close(ptr); ptr = 0; return r; }
 	
 	int connect(NETADDR4 *addr) { return net_client_connect(ptr, addr); }
diff --git a/src/engine/server/server.cpp b/src/engine/server/server.cpp
index 6fe84ade..87380cfa 100644
--- a/src/engine/server/server.cpp
+++ b/src/engine/server/server.cpp
@@ -15,6 +15,7 @@
 #include <engine/network.h>
 #include <engine/config.h>
 
+#include <mastersrv/mastersrv.h>
 
 namespace baselib {}
 using namespace baselib;
@@ -162,11 +163,14 @@ public:
 		//for(int i = 0; i < MAX_CLIENTS; i++)
 			//dbg_msg("network/server", "\t%d: %d", i, clients[i].state);
 
-		if (net_host_lookup(MASTER_SERVER_ADDRESS, MASTER_SERVER_PORT, &master_server) != 0)
+		if (net_host_lookup(MASTERSERVER_ADDRESS, MASTERSERVER_PORT, &master_server) != 0)
 		{
 			// TODO: fix me
 			//master_server = netaddr4(0, 0, 0, 0, 0);
 		}
+		
+		dbg_msg("server", "masterserver = %d.%d.%d.%d:%d",
+			master_server.ip[0], master_server.ip[1], master_server.ip[2], master_server.ip[3], master_server.port);
 
 		mods_init();
 
@@ -178,13 +182,15 @@ public:
 		lastheartbeat = 0;
 
 		int64 reporttime = time_get();
-		int64 reportinterval = time_freq()*3;
+		int reportinterval = 3;
+		//int64 reportinterval = time_freq()*3;
 
 		int64 simulationtime = 0;
 		int64 snaptime = 0;
 		int64 networktime = 0;
+		int64 totaltime = 0;
 
-		while(1)
+		while(true)
 		{
 			int64 t = time_get();
 			if(t-lasttick > time_per_tick)
@@ -204,23 +210,11 @@ public:
 				lasttick += time_per_tick;
 			}
 
-			if(send_heartbeats)
+			//if(send_heartbeats)
 			{
 				if (t > lastheartbeat+time_per_heartbeat)
 				{
-					if (master_server.port != 0)
-					{
-						int players = 0;
-
-						for (int i = 0; i < MAX_CLIENTS; i++)
-							if (!clients[i].is_empty())
-								players++;
-
-						// TODO: fix me
-						netaddr4 me(127, 0, 0, 0, 8303);
-						//send_heartbeat(0, &me, players, MAX_CLIENTS, server_name, mapname);
-					}
-
+					send_heartbeat();
 					lastheartbeat = t+time_per_heartbeat;
 				}
 			}
@@ -233,37 +227,21 @@ public:
 
 			if(reporttime < time_get())
 			{
-				int64 totaltime = simulationtime+snaptime+networktime;
 				dbg_msg("server/report", "sim=%.02fms snap=%.02fms net=%.02fms total=%.02fms load=%.02f%%",
-					simulationtime/(float)reportinterval*1000,
-					snaptime/(float)reportinterval*1000,
-					networktime/(float)reportinterval*1000,
-					totaltime/(float)reportinterval*1000,
-					(simulationtime+snaptime+networktime)/(float)reportinterval*100.0f);
-
-				unsigned sent_total=0, recv_total=0;
-				/*
-				for (int i = 0; i < MAX_CLIENTS; i++)
-					if (!clients[i].is_empty())
-					{
-						unsigned s,r;
-						clients[i].conn.counter_get(&s,&r);
-						clients[i].conn.counter_reset();
-						sent_total += s;
-						recv_total += r;
-					}
-				*/
-
-				dbg_msg("server/report", "biggestsnap=%d send=%d recv=%d",
-					biggest_snapshot, sent_total/3, recv_total/3);
+					(simulationtime/reportinterval)/(double)time_freq()*1000,
+					(snaptime/reportinterval)/(double)time_freq()*1000,
+					(networktime/reportinterval)/(double)time_freq()*1000,
+					(totaltime/reportinterval)/(double)time_freq()*1000,
+					(totaltime)/reportinterval/(double)time_freq()*100.0f);
 
 				simulationtime = 0;
 				snaptime = 0;
 				networktime = 0;
+				totaltime = 0;
 
-				reporttime += reportinterval;
+				reporttime += time_freq()*reportinterval;
 			}
-
+			totaltime += time_get()-t;
 			thread_sleep(1);
 		}
 
@@ -382,6 +360,18 @@ public:
 		msg_pack_end();
 		server_send_msg(cid);
 	}
+	
+	void send_heartbeat()
+	{
+		dbg_msg("server", "sending heartbeat");
+		NETPACKET packet;
+		packet.client_id = -1;
+		packet.address = master_server;
+		packet.flags = PACKETFLAG_CONNLESS;
+		packet.data_size = sizeof(SERVERBROWSE_HEARTBEAT);
+		packet.data = SERVERBROWSE_HEARTBEAT;
+		net.send(&packet);
+	}
 
 	void drop(int cid, const char *reason)
 	{
@@ -450,6 +440,27 @@ public:
 		drop(clientId, "client timedout");
 	}
 
+	void send_serverinfo(NETADDR4 *addr)
+	{
+		dbg_msg("server", "sending heartbeat");
+		NETPACKET packet;
+		
+		data_packer packer;
+		packer.reset();
+		packer.add_raw(SERVERBROWSE_INFO, sizeof(SERVERBROWSE_INFO));
+		packer.add_string(server_name, 128);
+		packer.add_string(map_name, 128);
+		packer.add_int(8); // max_players
+		packer.add_int(0); // num_players
+		
+		packet.client_id = -1;
+		packet.address = *addr;
+		packet.flags = PACKETFLAG_CONNLESS;
+		packet.data_size = packer.size();
+		packet.data = packer.data();
+		net.send(&packet);
+	}
+
 	void pump_network()
 	{
 		net.update();
@@ -458,10 +469,16 @@ public:
 		NETPACKET packet;
 		while(net.recv(&packet))
 		{
-			
 			if(packet.client_id == -1)
 			{
 				// stateless
+				if(packet.data_size == sizeof(SERVERBROWSE_GETINFO) &&
+					memcmp(packet.data, SERVERBROWSE_GETINFO, sizeof(SERVERBROWSE_GETINFO)) == 0)
+				{
+					dbg_msg("server", "info requested");
+					send_serverinfo(&packet.address);
+					
+				}
 			}
 			else
 				process_client_packet(&packet);