about summary refs log tree commit diff
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
parentf0e8502050ce27b9cef72fa385619d13faa35c27 (diff)
downloadzcatch-9e4aea97bdb5a903150df57f8d546733ce4b4db2.tar.gz
zcatch-9e4aea97bdb5a903150df57f8d546733ce4b4db2.zip
lots of cool new features :D new master server
-rw-r--r--datasrc/teewars.ds10
-rw-r--r--default.bam64
-rw-r--r--masterserver/default.bam18
-rw-r--r--masterserver/include/common.h6
-rw-r--r--masterserver/include/masterserver.h48
-rw-r--r--masterserver/include/network.h13
-rw-r--r--masterserver/include/serverinfo.h69
-rw-r--r--masterserver/src/main.cpp29
-rw-r--r--masterserver/src/masterserver.cpp176
-rw-r--r--masterserver/src/network.cpp39
-rwxr-xr-xscripts/compiler.py2
-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
-rw-r--r--src/game/client/game_client.cpp21
-rw-r--r--src/game/client/menu.cpp182
-rw-r--r--src/game/game.h5
-rw-r--r--src/game/server/game_server.cpp109
-rw-r--r--src/game/server/game_server.h25
-rw-r--r--src/mastersrv/mastersrv.cpp118
-rw-r--r--src/mastersrv/mastersrv.h19
23 files changed, 860 insertions, 905 deletions
diff --git a/datasrc/teewars.ds b/datasrc/teewars.ds
index 9923a512..8c6deb12 100644
--- a/datasrc/teewars.ds
+++ b/datasrc/teewars.ds
@@ -238,6 +238,16 @@ weapons {
 		reloadtime 100
 		visual_size 96
 	}
+	
+	rocket_backpack {
+		sprite_body sprites.weapons.weapon_hammer_body
+		sprite_cursor sprites.weapons.weapon_hammer_cursor
+		sprite_proj sprites.weapons.weapon_hammer_proj
+		
+		recoil 10
+		reloadtime 100
+		visual_size 64
+	}
 }
 	
 sprites {
diff --git a/default.bam b/default.bam
index 2f375ac3..839c2cba 100644
--- a/default.bam
+++ b/default.bam
@@ -91,43 +91,57 @@ function DataCompile(datafile, scriptfile, headerfile, sourcefile, outputdatafil
 	return {data = outputdatafile, header=headerfile, source=sourcefile}
 end
 
---baselib = Import("src/baselib/baselib.bam")
-baselib = Import("../baselib/baselib.bam")
+config_name = "debug"
+config_ext = ""
+
 settings = NewSettings()
+settings.cc.output = function(input, extention)
+	return Path("objs/" .. PathFilename(input) .. config_ext .. extention)
+end
+
+baselib_options = {}
+baselib_options.settings = settings
+baselib = Import("../baselib/baselib.bam", baselib_options)
 baselib.apply(settings, "all")
 
 server_settings = NewSettings()
 baselib.apply(server_settings, "network")
 
-settings.cc.debug = 1
-settings.cc.optimize = 0
 if family == "windows" then
 	settings.cc.flags = "/wd4244"
 else
 	settings.cc.flags = "-Wall"
 end
+
 settings.cc.includes:add("src")
 settings.cc.includes:add("../baselib/src/external/zlib")
 
-serverdata = DataCompile("datasrc/teewars.ds", "datasrc/server.dts", "src/game/server/data.h", "src/game/server/data/data.cpp", "data/server.dat")
-clientdata = DataCompile("datasrc/teewars.ds", "datasrc/client.dts", "src/game/client/data.h", "src/game/client/data/data.cpp", "data/client.dat")
-
-engine = Compile(settings, Collect("src/engine/*.cpp"))
-client = Compile(settings, Collect("src/engine/client/*.cpp", "src/engine/client/pnglite/*.c"))
-server = Compile(settings, Collect("src/engine/server/*.cpp"))
-game_shared = Compile(settings, Collect("src/game/*.cpp"))
-game_client = Compile(settings, Collect("src/game/client/*.cpp"), clientdata.source)
-game_server = Compile(settings, Collect("src/game/server/*.cpp"), serverdata.source)
-editor = Compile(settings, Collect("src/editor/*.cpp"))
-
-crapnet = Compile(settings, Collect("src/crapnet/*.cpp"))
-
-client_exe = Link(settings, "teewars", engine, client, editor, game_shared, game_client)
-server_exe = Link(server_settings, "teewars_srv", engine, server, game_shared, game_server)
--- editor_exe = Link(settings, "editor", engine, game_shared, editor)
-crapnet_exe = Link(server_settings, "crapnet", crapnet)
+serverdata = DataCompile("datasrc/teewars.ds", "datasrc/server.dts", "src/game/server/data.h", "src/game/server/data/server_data.cpp", "data/server.dat")
+clientdata = DataCompile("datasrc/teewars.ds", "datasrc/client.dts", "src/game/client/data.h", "src/game/client/data/client_data.cpp", "data/client.dat")
+
+function build(config)
+	engine = Compile(settings, Collect("src/engine/*.cpp"))
+	client = Compile(settings, Collect("src/engine/client/*.cpp", "src/engine/client/pnglite/*.c"))
+	server = Compile(settings, Collect("src/engine/server/*.cpp"))
+	masterserver = Compile(settings, Collect("src/mastersrv/*.cpp"))
+	game_shared = Compile(settings, Collect("src/game/*.cpp"))
+	game_client = Compile(settings, Collect("src/game/client/*.cpp"), clientdata.source)
+	game_server = Compile(settings, Collect("src/game/server/*.cpp"), serverdata.source)
+	editor = Compile(settings, Collect("src/editor/*.cpp"))
+
+	crapnet = Compile(settings, Collect("src/crapnet/*.cpp"))
+
+	client_exe = Link(settings, "teewars"..config_ext, engine, client, editor, game_shared, game_client)
+	server_exe = Link(server_settings, "teewars_srv"..config_ext, engine, server, game_shared, game_server)
+	masterserver_exe = Link(server_settings, "mastersrv"..config_ext, masterserver, engine)
+	-- editor_exe = Link(settings, "editor", engine, game_shared, editor)
+	crapnet_exe = Link(server_settings, "crapnet"..config_ext, crapnet)
+
+	Target(PseudoTarget("client", client_exe, clientdata.data))
+	Target(PseudoTarget("server", server_exe, serverdata.data))
+	Target(PseudoTarget("masterserver", masterserver_exe))
+	Target(PseudoTarget("tools", crapnet_exe))
+	-- Target(PseudoTarget("editor", editor_exe))
+end
 
-Target(PseudoTarget("client", client_exe, clientdata.data))
-Target(PseudoTarget("server", server_exe, serverdata.data))
-Target(PseudoTarget("tools", crapnet_exe))
--- Target(PseudoTarget("editor", editor_exe))
+build("debug")
diff --git a/masterserver/default.bam b/masterserver/default.bam
deleted file mode 100644
index 06492b70..00000000
--- a/masterserver/default.bam
+++ /dev/null
@@ -1,18 +0,0 @@
-baselib = Import("../../baselib/baselib.bam")
---scheme = Import("scheme/scheme.bam")
-
-settings = NewSettings()
-baselib.use(settings)
---scheme.use(settings)
-settings.cc.includes:add("include")
---settings.cc.includes:add("include/python")
---settings.cc.frameworks:add("Python")
---//settings.linker.libs:add("/opt/local/lib/python2.5/config/libpython2.5.a")
---settings.linker.frameworks:add("Python")
-
-src = Collect("src/*.cpp")
-objs = Compile(settings, src)
-exe = Link(settings, "server", objs)
-
---Target(baselib.lib)
-Target(exe)
diff --git a/masterserver/include/common.h b/masterserver/include/common.h
deleted file mode 100644
index ff03a61b..00000000
--- a/masterserver/include/common.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef _COMMON_H
-#define _COMMON_H
-
-typedef int int32;
-
-#endif
diff --git a/masterserver/include/masterserver.h b/masterserver/include/masterserver.h
deleted file mode 100644
index 7e042aaa..00000000
--- a/masterserver/include/masterserver.h
+++ /dev/null
@@ -1,48 +0,0 @@
-#ifndef _MASTERSERVER_H
-#define _MASTERSERVER_H
-
-#include <baselib/network.h>
-#include "serverinfo.h"
-
-using namespace baselib;
-
-#define HEARTBEAT_SIZE 216
-#define HEARTBEAT_SIGNATURE 'TWHB'
-#define HEARTBEAT_LIFETIME 10
-#define MAXSERVERS 1024
-#define SERVERINFOOUT_SIZE 212
-#define SERVERINFOHEADER_SIZE 12
-#define MASTERSERVER_VERSION 0
-
-class CMasterServer
-{
-	CServerInfo m_Servers[MAXSERVERS];
-	int m_ServerCount;
-	socket_udp4 m_UDPSocket;
-	socket_tcp4 m_TCPSocket;
-	int m_CurrentTime;
-	char m_ServerListPacket[MAXSERVERS * SERVERINFOOUT_SIZE + SERVERINFOHEADER_SIZE];
-	int m_ServerListPacketSize;
-	bool m_ServerListPacketIsOld;
-
-	void ListenForServerListPolls();
-	void BuildServerListPacket();
-    void ListenForHeartBeats();
-    void ProcessHeartBeat(CServerInfo info);
-    CServerInfo *FindServerInfo(int32 ip, int32 port);
-    CServerInfo *GetUnusedSlot();
-    void CleanUpServerList();
-public:
-	CMasterServer()
-	{
-		m_ServerCount = 0;
-		m_ServerListPacketIsOld = true;
-	}
-
-	void Init(int port);
-	void Shutdown(); 
-		
-	void Tick();
-};
-
-#endif
diff --git a/masterserver/include/network.h b/masterserver/include/network.h
deleted file mode 100644
index 455fe681..00000000
--- a/masterserver/include/network.h
+++ /dev/null
@@ -1,13 +0,0 @@
-#ifndef _NETWORK_H
-#define _NETWORK_H
-
-#include <cstring>
-#include "common.h"
-
-char *WriteInt32(char *buffer, int32 value);
-char *WriteFixedString(char *buffer, const char *string, int strlen);
-
-char *ReadInt32(char *buffer, int32 *value);
-char *ReadFixedString(char *buffer, char *string, int strlen);
-
-#endif
diff --git a/masterserver/include/serverinfo.h b/masterserver/include/serverinfo.h
deleted file mode 100644
index c63e11ba..00000000
--- a/masterserver/include/serverinfo.h
+++ /dev/null
@@ -1,69 +0,0 @@
-#ifndef _SERVERINFO_H
-#define _SERVERINFO_H
-
-#include <baselib/network.h>
-
-#include "common.h"
-#include "network.h"
-
-class CServerInfo
-{
-	int32 m_Version;
-	int32 m_IP;
-	int32 m_Port;
-	int32 m_Players;
-	int32 m_MaxPlayers;
-	char m_Name[128];
-	char m_Map[64];
-
-	int m_LastRefresh;
-
-public:
-	int32 IP() const { return m_IP; }
-	int32 Port() const { return m_Port; }
-	int32 Players() const { return m_Players; }
-	int32 MaxPlayers() const { return m_MaxPlayers; };
-	const char *Name() const { return m_Name; }
-	const char *Map() const { return m_Map; }
-
-	void Refresh(int time) { m_LastRefresh = time; }
-	int LastRefresh() { return m_LastRefresh; }
-
-	void SetAddress(baselib::netaddr4 *addr)
-	{
-		m_IP = addr->ip[0] << 24;
-		m_IP |= addr->ip[1] << 16;
-		m_IP |= addr->ip[2] << 8;
-		m_IP |= addr->ip[3];
-
-		m_Port = addr->port;
-	}
-	
-	char *Serialize(char *buffer) const
-	{
-		buffer = WriteInt32(buffer, m_Version);
-		buffer = WriteInt32(buffer, m_IP);
-		buffer = WriteInt32(buffer, m_Port);
-		buffer = WriteInt32(buffer, m_Players);
-		buffer = WriteInt32(buffer, m_MaxPlayers);
-		buffer = WriteFixedString(buffer, m_Name, sizeof(m_Name));
-		buffer = WriteFixedString(buffer, m_Map, sizeof(m_Map));
-
-		return buffer;
-	}
-
-	char *Deserialize(char *buffer)
-	{
-		buffer = ReadInt32(buffer, &m_Version);
-		buffer = ReadInt32(buffer, &m_IP);
-		buffer = ReadInt32(buffer, &m_Port);
-		buffer = ReadInt32(buffer, &m_Players);
-		buffer = ReadInt32(buffer, &m_MaxPlayers);
-		buffer = ReadFixedString(buffer, m_Name, sizeof(m_Name));
-		buffer = ReadFixedString(buffer, m_Map, sizeof(m_Map));
-
-		return buffer;
-	}
-};
-
-#endif
diff --git a/masterserver/src/main.cpp b/masterserver/src/main.cpp
deleted file mode 100644
index 380830ad..00000000
--- a/masterserver/src/main.cpp
+++ /dev/null
@@ -1,29 +0,0 @@
-#include <cstdio>
-#include <cstdlib>
-
-#include "masterserver.h"
-
-int main(int argc, char *argv[])
-{
-	if (argc != 2)
-	{
-		puts("Usage: masterserver <port>    (this will bind the server to the port specified (both udp and tcp).");
-		return -1;
-	}
-
-	int port = atoi(argv[1]);
-
-	CMasterServer masterServer;
-	masterServer.Init(port);
-
-	while (1)
-	{
-		masterServer.Tick();
-
-		thread_sleep(10);
-	}
-
-	masterServer.Shutdown();
-	
-	return 0;
-}
diff --git a/masterserver/src/masterserver.cpp b/masterserver/src/masterserver.cpp
deleted file mode 100644
index 383c696e..00000000
--- a/masterserver/src/masterserver.cpp
+++ /dev/null
@@ -1,176 +0,0 @@
-#include <ctime>
-
-#include "masterserver.h"
-
-void CMasterServer::Init(int port)
-{
-	netaddr4 addr(0, 0, 0, 0, port);
-	addr.port = port;
-	
-	net_init();
-	m_UDPSocket.open(port);
-	m_TCPSocket.open(&addr);
-	m_TCPSocket.set_non_blocking();
-	m_TCPSocket.listen();
-}
-
-void CMasterServer::Shutdown()
-{
-	m_UDPSocket.close();
-}
-
-void CMasterServer::Tick()
-{
-	m_CurrentTime = time(NULL);
-	
-	ListenForHeartBeats();
-	ListenForServerListPolls();
-
-	CleanUpServerList();
-}
-
-void CMasterServer::ListenForHeartBeats()
-{
-	netaddr4 from;
-	char data[1024];
-	int dataSize;
-
-	// read udp data while there is data to read :)
-	while ((dataSize = m_UDPSocket.recv(&from, (char *)data, sizeof(data))) > 0)
-	{
-		// compare the received data size to the expected size
-		if (dataSize == HEARTBEAT_SIZE)
-		{
-			char *d = data;
-			int32 signature;
-			d = ReadInt32(d, &signature);
-		
-			// make sure the signature is correct
-			if (signature == HEARTBEAT_SIGNATURE)
-			{
-				CServerInfo info;
-				info.Deserialize(d);
-
-				from.port = 8303;
-				info.SetAddress(&from);
-
-				dbg_msg("got heartbeat", "IP: %i.%i.%i.%i:%i", (int)from.ip[0], (int)from.ip[1], (int)from.ip[2], (int)from.ip[3], from.port);
-
-				dbg_msg("refresh", "okay. server count: %i", m_ServerCount);
-
-				ProcessHeartBeat(info);	
-			}
-			else
-			{}	// unexpected signature
-		}
-		else
-		{}	// unknown data received
-	}
-}
-
-void CMasterServer::ProcessHeartBeat(CServerInfo info)
-{
-	// find the corresponding server info
-	CServerInfo *serverInfo = FindServerInfo(info.IP(), info.Port());
-
-	// if it isn't in the list already, try to get an unused slot
-	if (!serverInfo)
-		serverInfo = GetUnusedSlot();
-
-	// if we STILL don't have one, we're out of luck.
-	if (!serverInfo)
-		return;
-	
-	*serverInfo = info;
-	serverInfo->Refresh(m_CurrentTime);
-
-	// mark the server list packet as old
-	m_ServerListPacketIsOld = true;
-}
-
-CServerInfo *CMasterServer::FindServerInfo(int32 ip, int32 port)
-{
-	// for now, just loop over the array
-	for (int i = 0; i < m_ServerCount; i++)
-	{
-		CServerInfo *info = &m_Servers[i];
-		if (info->IP() == ip && info->Port() == port)
-			return info;
-	}
-
-	return 0x0;
-}
-
-CServerInfo *CMasterServer::GetUnusedSlot()
-{
-	if (m_ServerCount == MAXSERVERS)
-		return 0x0;
-	else
-		return &m_Servers[m_ServerCount++];
-}
-
-void CMasterServer::CleanUpServerList()
-{
-	for (int i = 0; i < m_ServerCount; i++)
-	{
-		CServerInfo *serverInfo = &m_Servers[i];
-		
-		// check if it's time to remove it from the list
-		if (serverInfo->LastRefresh() + HEARTBEAT_LIFETIME < m_CurrentTime)
-		{
-			if (i + 1 == m_ServerCount)
-			{
-				// if we're at the last one, just decrease m_ServerCount
-				--m_ServerCount;
-			}
-			else
-			{
-				// otherwise, copy the last slot here and then decrease i and m_ServerCount
-				*serverInfo = m_Servers[m_ServerCount - 1];
-				--i;
-				--m_ServerCount;
-			}
-
-			// mark the server list packet as old and outdated
-			m_ServerListPacketIsOld = true;
-		}
-	}
-}
-
-void CMasterServer::ListenForServerListPolls()
-{
-	socket_tcp4 client;
-
-	// accept clients while there are clients to be accepted
-	while (m_TCPSocket.accept(&client))
-	{
-		// maybe we've prepared the packet already... it'd be silly to do it twice
-		if (m_ServerListPacketIsOld)
-		{
-			BuildServerListPacket();
-		}
-		
-		// send the server list and then close the socket
-		client.send(m_ServerListPacket, m_ServerListPacketSize);
-		client.close();
-	}
-}
-
-void CMasterServer::BuildServerListPacket()
-{
-	char *d = m_ServerListPacket;
-
-	d = WriteInt32(d, 'TWSL');
-	d = WriteInt32(d, MASTERSERVER_VERSION);
-
-	d = WriteInt32(d, m_ServerCount);
-	
-	for (int i = 0; i < m_ServerCount; i++)
-	{
-		CServerInfo *info = &m_Servers[i];
-		d = info->Serialize(d);
-	}
-
-	m_ServerListPacketSize = d - m_ServerListPacket;
-	m_ServerListPacketIsOld = false;
-}
diff --git a/masterserver/src/network.cpp b/masterserver/src/network.cpp
deleted file mode 100644
index 7d557cdf..00000000
--- a/masterserver/src/network.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-#include <cstring>
-#include "common.h"
-#include "network.h"
-
-char *WriteInt32(char *buffer, int32 value)
-{
-	buffer[0] = value >> 24;
-	buffer[1] = value >> 16;
-	buffer[2] = value >> 8;
-	buffer[3] = value;
-
-	return buffer + sizeof(int32);
-}
-
-char *WriteFixedString(char *buffer, const char *string, int strlen)
-{
-	memcpy(buffer, string, strlen);
-
-	return buffer + strlen;
-}
-
-
-
-char *ReadInt32(char *buffer, int32 *value)
-{
-	*value = buffer[0] << 24;
-	*value |= buffer[1] << 16;
-	*value |= buffer[2] << 8;
-	*value |= buffer[3];
-
-	return buffer + sizeof(int32);
-}
-
-char *ReadFixedString(char *buffer, char *string, int strlen)
-{
-	memcpy(string, buffer, strlen);
-
-	return buffer + strlen;
-}
diff --git a/scripts/compiler.py b/scripts/compiler.py
index 5d90e882..5d6a814a 100755
--- a/scripts/compiler.py
+++ b/scripts/compiler.py
@@ -476,7 +476,7 @@ class translator:
 #include <stdio.h>
 #include <stdlib.h>
 
-void patch_ptr(char **ptr, char *base)
+static void patch_ptr(char **ptr, char *base)
 {
 	*ptr = base+(size_t)(*ptr);
 }
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);
diff --git a/src/game/client/game_client.cpp b/src/game/client/game_client.cpp
index e3a0ab76..359bb880 100644
--- a/src/game/client/game_client.cpp
+++ b/src/game/client/game_client.cpp
@@ -201,7 +201,7 @@ public:
 		{
 			i->pos = pos;
 			i->life = 0.75f;
-			i->dir = dir;
+			i->dir = dir*-1;
 			i->startangle = (( (float)rand()/(float)RAND_MAX) - 1.0f) * 2.0f * pi;
 		}
 	}
@@ -382,7 +382,7 @@ void modc_init()
 {
 	// load the data container
 	data = load_data_container("data/client.dat");
-	
+
 	// load sounds
 	for(int s = 0; s < data->num_sounds; s++)
 		for(int i = 0; i < data->sounds[s].num_sounds; i++)
@@ -505,15 +505,17 @@ static void render_projectile(obj_projectile *prev, obj_projectile *current)
 	gfx_quads_begin();
 	
 	select_sprite(data->weapons[current->type%data->num_weapons].sprite_proj);
-	vec2 vel(current->vx, current->vy);
+	vec2 vel = mix(vec2(prev->vx, prev->vy), vec2(current->vx, current->vy), client_intratick());
+	vec2 pos = mix(vec2(prev->x, prev->y), vec2(current->x, current->y), client_intratick());
 	
-	// TODO: interpolare angle aswell
 	if(length(vel) > 0.00001f)
 		gfx_quads_setrotation(get_angle(vel));
 	else
 		gfx_quads_setrotation(0);
 	
-	vec2 pos = mix(vec2(prev->x, prev->y), vec2(current->x, current->y), client_intratick());
+	// TODO: do this, but nice
+	//temp_system.new_particle(pos, vec2(0,0), 0.3f, 14.0f, 0, 0.95f);
+
 	gfx_quads_draw(pos.x, pos.y,32,32);
 	gfx_quads_setrotation(0);
 	gfx_quads_end();
@@ -898,7 +900,8 @@ void modc_render()
 		if(mouse_pos.x < 0)
 			a = a+pi;
 			
-		input.angle = (int)(a*256.0f);
+		input.target_x = (int)mouse_pos.x; //(int)(a*256.0f);
+		input.target_y = (int)mouse_pos.y; //(int)(a*256.0f);
 		input.activeweapon = -1;
 		
 		if(!chat_active)
@@ -912,10 +915,8 @@ void modc_render()
 			input.blink = inp_key_pressed('S');
 			
 			// Weapon switching
-			input.activeweapon = inp_key_pressed('1') ? 0 : input.activeweapon;
-			input.activeweapon = inp_key_pressed('2') ? 1 : input.activeweapon;
-			input.activeweapon = inp_key_pressed('3') ? 2 : input.activeweapon;
-			input.activeweapon = inp_key_pressed('4') ? 3 : input.activeweapon;
+			for(int i = 0; i < 8; i++)
+				input.activeweapon = inp_key_pressed('1'+i) ? i : input.activeweapon;
 		}
 
 		snap_input(&input, sizeof(input));
diff --git a/src/game/client/menu.cpp b/src/game/client/menu.cpp
index 0d10116f..96d75fd6 100644
--- a/src/game/client/menu.cpp
+++ b/src/game/client/menu.cpp
@@ -11,6 +11,7 @@
 #include <baselib/system.h>
 #include <baselib/input.h>
 #include <baselib/network.h>
+#include <baselib/math.h>
 
 #include <engine/interface.h>
 #include <engine/versions.h>
@@ -22,6 +23,7 @@
 #include <engine/config.h>
 
 #include "data.h"
+#include <mastersrv/mastersrv.h>
 
 extern data_container *data;
 
@@ -234,6 +236,7 @@ void draw_teewars_button(void *id, const char *text, int checked, float x, float
 	ui_do_label(x + w/2 - text_width/2, y, text, 46);
 }
 
+/*
 struct server_info
 {
 	int version;
@@ -242,11 +245,10 @@ struct server_info
 	netaddr4 address;
 	char name[129];
 	char map[65];
-};
+};*/
 
 struct server_list
 {   
-    server_info infos[10];
 	int active_count, info_count;
 	int scroll_index;
 	int selected_index;
@@ -555,7 +557,7 @@ int do_scroll_bar_vert(void *id, float x, float y, float height, int steps, int
 	return r;
 }
 
-static int do_server_list(server_list *list, float x, float y, int visible_items)
+static int do_server_list(float x, float y, int *scroll_index, int *selected_index, int visible_items)
 {
 	const float spacing = 3.f;
 	const float item_height = 28;
@@ -563,22 +565,27 @@ static int do_server_list(server_list *list, float x, float y, int visible_items
 	const float real_width = item_width + 20;
 	const float real_height = item_height * visible_items + spacing * (visible_items - 1);
 
+	server_info *servers;
+	int num_servers = client_serverbrowse_getlist(&servers);
+
 	int r = -1;
 
 	for (int i = 0; i < visible_items; i++)
 	{
-		int item_index = i + list->scroll_index;
-		if (item_index >= list->active_count);
+		int item_index = i + *scroll_index;
+		if (item_index >= num_servers)
+			;
 			//ui_do_image(empty_item_texture, x, y + i * item_height + i * spacing, item_width, item_height);
 		else
 		{
-			server_info *item = &list->infos[item_index];
+			server_info *item = &servers[item_index];
 
 			bool clicked = false;
-			clicked = ui_do_button(item, item->name, 0, x, y + i * item_height + i * spacing, item_width, item_height, draw_menu_button, (list->selected_index == item_index) ? (void *)1 : 0);
+			clicked = ui_do_button(item, item->name, 0, x, y + i * item_height + i * spacing, item_width, item_height,
+				draw_menu_button, (*selected_index == item_index) ? (void *)1 : 0);
 
 			char temp[64]; // plenty of extra room so we don't get sad :o
-			sprintf(temp, "%i/%i", item->players, item->max_players);
+			sprintf(temp, "%i/%i  %3d", item->num_players, item->max_players, item->latency);
 
 			ui_do_label(x + 600, y + i * item_height + i * spacing, temp, item_height);
 			ui_do_label(x + 360, y + i * item_height + i * spacing, item->map, item_height);
@@ -586,136 +593,17 @@ static int do_server_list(server_list *list, float x, float y, int visible_items
 			if (clicked)
 			{
 				r = item_index;
-				list->selected_index = item_index;
+				*selected_index = item_index;
 			}
 		}
 	}
 
-	list->scroll_index = do_scroll_bar_vert(&list->scroll_index, x + real_width - 16, y, real_height, list->active_count - visible_items, list->scroll_index);
+	*scroll_index = do_scroll_bar_vert(scroll_index, x + real_width - 16, y, real_height,
+		min(num_servers - visible_items, 0), *scroll_index);
 	
 	return r;
 }
 
-static char *read_int(char *buffer, int *value)
-{
-    *value = buffer[0] << 24;
-    *value |= buffer[1] << 16;
-    *value |= buffer[2] << 8;
-    *value |= buffer[3];
-
-	return buffer + 4;
-}
-
-static char *read_netaddr(char *buffer, netaddr4 *addr)
-{
-	addr->ip[0] = *buffer++;
-	addr->ip[1] = *buffer++;
-	addr->ip[2] = *buffer++;
-	addr->ip[3] = *buffer++;
-
-	int port;
-	buffer = read_int(buffer, &port);
-
-	addr->port = port;
-
-	return buffer;
-}
-
-static void refresh_list(server_list *list)
-{
-	netaddr4 addr;
-	netaddr4 me(0, 0, 0, 0, 0);
-
-	list->selected_index = -1;
-	
-	if (net_host_lookup(MASTER_SERVER_ADDRESS, MASTER_SERVER_PORT, &addr) == 0)
-    {
-        socket_tcp4 sock;
-        sock.open(&me);
-
-		//sock.set_non_blocking();
-
-		// try and connect with a timeout of 1 second
-        if (sock.connect_non_blocking(&addr))
-        {
-            char data[256];
-            int total_received = 0;
-            int received;
-
-            int master_server_version = -1;
-            int server_count = -1;
-
-            // read header
-            while (total_received < 12 && (received = sock.recv(data + total_received, 12 - total_received)) > 0)
-                total_received += received;
-
-            // see if we have the header
-            if (total_received == 12)
-            {
-                int signature;
-                read_int(data, &signature);
-    
-                // check signature
-                if(signature == 'TWSL')
-                {
-                    read_int(data + 4, &master_server_version);
-                    read_int(data + 8, &server_count);
-
-                    // TODO: handle master server version O.o
-                        
-                    const int server_info_size = 212;
-
-                    list->active_count = 0;
-    
-                    for (int i = 0; i < server_count; i++)
-                    { 
-                         total_received = 0;
-
-                        // read data for a server
-                        while (sock.is_connected() && total_received < server_info_size && (received = sock.recv(data + total_received, server_info_size - total_received)) > 0)
-                            total_received += received;
-
-                        // check if we got enough data
-                        if (total_received == server_info_size)
-                        {
-                            char *d = data;
-
-                            server_info *info = &list->infos[i];
-
-                            d = read_int(d, &info->version);
-                            d = read_netaddr(d, &info->address);
-
-							//dbg_msg("menu/got_serverinfo", "IP: %i.%i.%i.%i:%i", (int)info->address.ip[0], (int)info->address.ip[1], (int)info->address.ip[2], (int)info->address.ip[3], info->address.port);
-
-                            d = read_int(d, &info->players);
-                            d = read_int(d, &info->max_players);
-                            memcpy(info->name, d, 128);
-                            d += 128;
-							memcpy(info->map, d, 64);
-
-                            // let's be safe.
-                            info->name[128] = 0;
-                            info->map[64] = 0;
-
-                            ++list->active_count;
-                        }
-                        else
-                            break;
-                    }
-
-                    if (list->scroll_index >= list->active_count)
-                        list->scroll_index = list->active_count - 1;
-
-                    if (list->scroll_index < 0)
-                        list->scroll_index = 0;
-                }
-            }
-
-            sock.close();
-        }
-	}
-}
-
 enum
 {
 	SCREEN_MAIN,
@@ -737,41 +625,45 @@ const float row2_y = row1_y + 40;
 const float row3_y = row2_y + 40;
 const float row4_y = row3_y + 40;
 
-static int main_render(netaddr4 *server_address)
+static int main_render()
 {
-	static server_list list;
 	static bool inited = false;
 
 	if (!inited)
 	{
+		/*
 		list.info_count = 256;
 
 		list.scroll_index = 0;
 		list.selected_index = -1;
+		*/
 
 		inited = true;
 
-		refresh_list(&list);
+		client_serverbrowse_refresh();
 	}
 
-	do_server_list(&list, 20, 160, 8);
+	static int scoll_index = 0, selected_index = -1;
+	do_server_list(20, 160, &scoll_index, &selected_index, 8);
 
 	static int refresh_button, join_button, quit_button;
 
 	if (ui_do_button(&refresh_button, "Refresh", 0, 440, 420, 170, 48, draw_teewars_button))
-	{
-		refresh_list(&list);
-	} 
+		client_serverbrowse_refresh();
 
-	if (list.selected_index == -1)
+	if (selected_index == -1)
 	{
 		ui_do_button(&join_button, "Join", 0, 620, 420, 128, 48, draw_teewars_button, (void *)1);
 	}
 	else if (ui_do_button(&join_button, "Join", 0, 620, 420, 128, 48, draw_teewars_button))
 	{
-		*server_address = list.infos[list.selected_index].address;
+		// *server_address = list.infos[list.selected_index].address;
+		
+		server_info *servers;
+		int num_servers = client_serverbrowse_getlist(&servers);
 
-		dbg_msg("menu/join_button", "IP: %i.%i.%i.%i:%i", (int)server_address->ip[0], (int)server_address->ip[1], (int)server_address->ip[2], (int)server_address->ip[3], server_address->port);
+		client_connect(servers[selected_index].address);
+		//dbg_msg("menu/join_button", "IP: %i.%i.%i.%i:%i", (int)server_address->ip[0], (int)server_address->ip[1], (int)server_address->ip[2], (int)server_address->ip[3], server_address->port);
 
 		return 1;
 	}
@@ -1123,7 +1015,7 @@ static int kerning_render()
 	return 0;
 }
 
-static int menu_render(netaddr4 *server_address)
+static int menu_render()
 {
 	// background color
 	gfx_clear(0.65f,0.78f,0.9f);
@@ -1147,7 +1039,7 @@ static int menu_render(netaddr4 *server_address)
 
 	switch (screen)
 	{
-		case SCREEN_MAIN: return main_render(server_address);
+		case SCREEN_MAIN: return main_render();
 		case SCREEN_SETTINGS_GENERAL:
 		case SCREEN_SETTINGS_CONTROLS:
 		case SCREEN_SETTINGS_VIDEO:
@@ -1176,7 +1068,7 @@ void modmenu_shutdown()
 {
 }
 
-int modmenu_render(void *ptr)
+int modmenu_render()
 {
 	static int mouse_x = 0;
 	static int mouse_y = 0;
@@ -1187,8 +1079,6 @@ int modmenu_render(void *ptr)
 		music_menu_id = snd_play(music_menu, SND_LOOP);
 	}
 
-	netaddr4 *server_address = (netaddr4 *)ptr;	
-
     // handle mouse movement
     float mx, my;
     {
@@ -1214,7 +1104,7 @@ int modmenu_render(void *ptr)
     }
 
     //int r = menu_render(server_address, str, max_len);
-	int r = menu_render(server_address);
+	int r = menu_render();
 
     // render butt ugly mouse cursor
     // TODO: render nice cursor
diff --git a/src/game/game.h b/src/game/game.h
index 917abab1..5f69ef9e 100644
--- a/src/game/game.h
+++ b/src/game/game.h
@@ -58,7 +58,10 @@ struct player_input
 {
 	int left;
 	int right;
-	int angle;
+	
+	int target_x;
+	int target_y;
+	
 	int jump;
 	int fire;
 	int hook;
diff --git a/src/game/server/game_server.cpp b/src/game/server/game_server.cpp
index cf8cea90..12685fe8 100644
--- a/src/game/server/game_server.cpp
+++ b/src/game/server/game_server.cpp
@@ -464,7 +464,8 @@ void projectile::tick()
 	lifespan--;
 	
 	// check player intersection as well
-	entity *targetplayer = (entity*)intersect_player(oldpos, pos, oldpos, powner);
+	vec2 new_pos;
+	entity *targetplayer = (entity*)intersect_player(oldpos, pos, new_pos, powner);
 	if(targetplayer || lifespan < 0 || col_check_point((int)pos.x, (int)pos.y))
 	{
 		if (lifespan >= 0)
@@ -489,6 +490,74 @@ void projectile::snap(int snapping_client)
 	proj->type = type;
 }
 
+
+//////////////////////////////////////////////////
+// projectile_backpackrocket
+//////////////////////////////////////////////////
+projectile_backpackrocket::projectile_backpackrocket(baselib::vec2 pos, baselib::vec2 target, int owner, entity* powner)
+: projectile(WEAPON_PROJECTILETYPE_ROCKET, owner, pos, vec2(0,0), 100, powner, 0, 0, 0, -1, WEAPON_ROCKET_BACKPACK)
+{
+	stage = 0;
+	start_tick = server_tick();
+	vel = vec2(0,-10.0f);
+	this->target = target;
+	start = pos;
+	midpoint = pos;
+	midpoint.y = target.y;
+	direction = normalize(target-midpoint);
+	deply_ticks = (int)( distance(start, midpoint)/(float)server_tickspeed() * 5.0f );
+	dbg_msg("rocket_bp", "%f %d", distance(start, midpoint), deply_ticks);
+}
+
+void projectile_backpackrocket::tick()
+{
+	lifespan--;
+	if(!lifespan)
+		world.destroy_entity(this);
+		
+	vec2 oldpos = pos;
+		
+	if(stage == 0)
+	{
+		float time = (server_tick()-start_tick)/(float)(deply_ticks);
+		if(midpoint.y > start.y)
+			pos.y = mix(start.y, midpoint.y, 1-sinf((1-time)*pi/2));
+		else
+			pos.y = mix(start.y, midpoint.y, sinf(time*pi/2));
+
+		float a = (server_tick()-start_tick)/(float)server_tickspeed()*pi*7.5f;
+		vel.x = sinf(a)*30.0f;
+		vel.y = cosf(a)*30.0f;
+		
+		if(server_tick() > start_tick+deply_ticks)
+		{
+			pos = midpoint;
+			direction = normalize(target-pos);
+			vel = vec2(0,0);
+			stage = 1;
+		}
+	}
+	else if(stage == 1)
+	{
+		vel += direction*1.5f;
+		vel.x = clamp(vel.x, -20.0f, 20.0f);
+		pos += vel;
+	}
+
+	// check player intersection as well
+	vec2 new_pos;
+	entity *targetplayer = (entity*)intersect_player(oldpos, pos, new_pos, powner);
+	if(targetplayer || lifespan < 0 || col_check_point((int)pos.x, (int)pos.y))
+	{
+		if (lifespan >= 0)
+			create_sound(pos, sound_impact);
+			
+		create_explosion(oldpos, owner, weapon, false);
+			
+		world.destroy_entity(this);
+	}	
+}
+
 //////////////////////////////////////////////////
 // player
 //////////////////////////////////////////////////
@@ -556,6 +625,11 @@ void player::respawn()
 	weapons[WEAPON_HAMMER].ammo = -1;
 	weapons[WEAPON_GUN].got = true;
 	weapons[WEAPON_GUN].ammo = 10;
+
+	// TEMP DEBUG
+	weapons[WEAPON_ROCKET_BACKPACK].got = true;
+	weapons[WEAPON_ROCKET_BACKPACK].ammo = 10;
+
 	active_weapon = WEAPON_GUN;
 	reload_timer = 0;
 	
@@ -620,7 +694,7 @@ void player::handle_weapons()
 						break;
 
 					case WEAPON_GUN:
-						new projectile(WEAPON_PROJECTILETYPE_GUN,
+						new projectile(projectile::WEAPON_PROJECTILETYPE_GUN,
 							client_id,
 							pos+vec2(0,0),
 							direction*30.0f,
@@ -630,7 +704,7 @@ void player::handle_weapons()
 						create_sound(pos, SOUND_GUN_FIRE);
 						break;
 					case WEAPON_ROCKET:
-						new projectile(WEAPON_PROJECTILETYPE_ROCKET,
+						new projectile(projectile::WEAPON_PROJECTILETYPE_ROCKET,
 							client_id,
 							pos+vec2(0,0),
 							direction*15.0f,
@@ -644,7 +718,7 @@ void player::handle_weapons()
 						{
 							float a = get_angle(direction);
 							a += i*0.075f;
-							new projectile(WEAPON_PROJECTILETYPE_SHOTGUN,
+							new projectile(projectile::WEAPON_PROJECTILETYPE_SHOTGUN,
 								client_id,
 								pos+vec2(0,0),
 								vec2(cosf(a), sinf(a))*25.0f,
@@ -655,6 +729,13 @@ void player::handle_weapons()
 						}
 						create_sound(pos, SOUND_SHOTGUN_FIRE);
 						break;
+					case WEAPON_ROCKET_BACKPACK:
+						new projectile_backpackrocket(
+							pos+vec2(0,0),
+							pos+vec2(input.target_x,input.target_y),
+							client_id,
+							this);
+						break;						
 				}
 				
 				weapons[active_weapon].ammo--;
@@ -662,10 +743,11 @@ void player::handle_weapons()
 			else
 			{
 				// click!!! click
+				// TODO: make sound here
 			}
 			
 			attack_tick = server_tick();
-			reload_timer = 10; // make this variable depending on weapon
+			reload_timer = 10; // TODO: make this variable depending on weapon
 		}
 	}
 }
@@ -685,7 +767,7 @@ void player::tick()
 	
 	// fetch some info
 	bool grounded = is_grounded();
-	direction = get_direction(input.angle);
+	direction = normalize(vec2(input.target_x, input.target_y));
 	
 	float max_speed = grounded ? ground_control_speed : air_control_speed;
 	float accel = grounded ? ground_control_accel : air_control_accel;
@@ -950,8 +1032,13 @@ void player::snap(int snaping_client)
 	player->hook_active = hook_state>0?1:0;
 	player->hook_x = (int)hook_pos.x;
 	player->hook_y = (int)hook_pos.y;
+
+	float a = atan((float)input.target_y/(float)input.target_x);
+	if(input.target_x < 0)
+		a = a+pi;
 		
-	player->angle = input.angle;
+	player->angle = (int)(a*256.0f);
+	
 	player->score = score;
 }
 
@@ -1079,15 +1166,17 @@ void create_explosion(vec2 p, int owner, int weapon, bool bnodamage)
 		// deal damage
 		entity *ents[64];
 		const float radius = 128.0f;
+		const float innerradius = 42.0f;
 		int num = world.find_entities(p, radius, ents, 64);
 		for(int i = 0; i < num; i++)
 		{
 			vec2 diff = ents[i]->pos - p;
 			vec2 forcedir(0,1);
-			if (length(diff))
-				forcedir = normalize(diff);
 			float l = length(diff);
-			float dmg = 5 * (1 - (l/radius));
+			if(l)
+				forcedir = normalize(diff);
+			l = 1-clamp((l-innerradius)/(radius-innerradius), 0.0f, 1.0f);
+			float dmg = 6 * l;
 			if((int)dmg)
 				ents[i]->take_damage(forcedir*dmg*2, (int)dmg, owner, weapon);
 		}
diff --git a/src/game/server/game_server.h b/src/game/server/game_server.h
index 027a140f..ec3d597a 100644
--- a/src/game/server/game_server.h
+++ b/src/game/server/game_server.h
@@ -146,6 +146,10 @@ public:
 	enum
 	{
 		PROJECTILE_FLAGS_EXPLODE = 1 << 0,
+		
+		WEAPON_PROJECTILETYPE_GUN		= 0,
+		WEAPON_PROJECTILETYPE_ROCKET	= 1,
+		WEAPON_PROJECTILETYPE_SHOTGUN	= 2,
 	};
 	
 	baselib::vec2 vel;
@@ -166,19 +170,26 @@ public:
 	virtual void snap(int snapping_client);
 };
 
+class projectile_backpackrocket : public projectile
+{
+	int stage;
+	int start_tick;
+	int deply_ticks;
+	baselib::vec2 target;
+	baselib::vec2 start;
+	baselib::vec2 midpoint;
+	baselib::vec2 direction;
+public:
+	projectile_backpackrocket(baselib::vec2 pos, baselib::vec2 target, int owner, entity* powner);
+	virtual void tick();
+};
+
 // player entity
 class player : public entity
 {
 public:
 	static const int phys_size = 28;
 	
-	enum // what are these?
-	{
-		WEAPON_PROJECTILETYPE_GUN		= 0,
-		WEAPON_PROJECTILETYPE_ROCKET	= 1,
-		WEAPON_PROJECTILETYPE_SHOTGUN	= 2,
-	};
-	
 	// weapon info
 	struct weaponstat
 	{
diff --git a/src/mastersrv/mastersrv.cpp b/src/mastersrv/mastersrv.cpp
new file mode 100644
index 00000000..03c06b97
--- /dev/null
+++ b/src/mastersrv/mastersrv.cpp
@@ -0,0 +1,118 @@
+#include <string.h>
+#include <baselib/system.h>
+#include <engine/network.h>
+
+#include "mastersrv.h"
+
+enum {
+	MTU = 1400,
+	EXPIRE_TIME = 90
+};
+
+static struct packet_data
+{
+	unsigned char header[sizeof(SERVERBROWSE_LIST)];
+	NETADDR4 servers[MAX_SERVERS];
+} data;
+static int64 server_expire[MAX_SERVERS];
+static int num_servers = 0;
+
+static net_client net;
+
+void add_server(NETADDR4 *info)
+{
+	// see if server already exists in list
+	int i;
+	for(i = 0; i < num_servers; i++)
+	{
+		if(net_addr4_cmp(&data.servers[i], info) == 0)
+		{
+			dbg_msg("mastersrv", "updated: %d.%d.%d.%d:%d",
+				info->ip[0], info->ip[1], info->ip[2], info->ip[3], info->port);
+			server_expire[i] = time_get()+time_freq()*EXPIRE_TIME;
+			return;
+		}
+	}
+	
+	// add server
+	if(num_servers == MAX_SERVERS)
+	{
+		dbg_msg("mastersrv", "error: mastersrv is full");
+		return;
+	}
+	
+	dbg_msg("mastersrv", "added: %d.%d.%d.%d:%d",
+		info->ip[0], info->ip[1], info->ip[2], info->ip[3], info->port);
+	data.servers[num_servers] = *info;
+	server_expire[num_servers] = time_get()+time_freq()*EXPIRE_TIME;
+	num_servers++;
+}
+
+void purge_servers()
+{
+	int64 now = time_get();
+	int i = 0;
+	while(i < num_servers)
+	{
+		if(server_expire[i] < now)
+		{
+			// remove server
+			dbg_msg("mastersrv", "expired: %d.%d.%d.%d:%d",
+				data.servers[i].ip[0], data.servers[i].ip[1],
+				data.servers[i].ip[2], data.servers[i].ip[3], data.servers[i].port);
+			data.servers[i] = data.servers[num_servers-1];
+			server_expire[i] = server_expire[num_servers-1];
+			num_servers--;
+		}
+		else
+			i++;
+	}
+}
+
+int main(int argc, char **argv)
+{
+	net.open(MASTERSERVER_PORT, 0);
+	// TODO: check socket for errors
+	
+	mem_copy(data.header, SERVERBROWSE_LIST, sizeof(SERVERBROWSE_LIST));
+	dbg_msg("mastersrv", "started");
+	
+	while(1)
+	{
+		net.update();
+		
+		// process packets
+		NETPACKET packet;
+		while(net.recv(&packet))
+		{
+			if(packet.data_size == sizeof(SERVERBROWSE_HEARTBEAT) &&
+				memcmp(packet.data, SERVERBROWSE_HEARTBEAT, sizeof(SERVERBROWSE_HEARTBEAT)) == 0)
+			{
+				// add it
+				add_server(&packet.address);
+			}
+			else if(packet.data_size == sizeof(SERVERBROWSE_GETLIST) &&
+				memcmp(packet.data, SERVERBROWSE_GETLIST, sizeof(SERVERBROWSE_GETLIST)) == 0)
+			{
+				// someone requested the list
+				dbg_msg("mastersrv", "requested, responding with %d servers", num_servers);
+				NETPACKET p;
+				p.client_id = -1;
+				p.address = packet.address;
+				p.flags = PACKETFLAG_CONNLESS;
+				p.data_size = num_servers*sizeof(NETADDR4)+sizeof(SERVERBROWSE_LIST);
+				p.data = &data;
+				net.send(&p);
+			}
+
+		}
+		
+		// TODO: shouldn't be done every fuckin frame
+		purge_servers();
+		
+		// be nice to the CPU
+		thread_sleep(1);
+	}
+	
+	return 0;
+}
diff --git a/src/mastersrv/mastersrv.h b/src/mastersrv/mastersrv.h
new file mode 100644
index 00000000..fd59808f
--- /dev/null
+++ b/src/mastersrv/mastersrv.h
@@ -0,0 +1,19 @@
+static const char *MASTERSERVER_ADDRESS = "localhost";
+static const int MASTERSERVER_PORT = 8383;
+
+static const int MAX_SERVERS = 200;
+
+/*enum {
+	SERVERBROWSE_NULL=0,
+	SERVERBROWSE_HEARTBEAT,
+	SERVERBROWSE_GETLIST,
+	SERVERBROWSE_LIST,
+	SERVERBROWSE_GETINFO,
+	SERVERBROWSE_INFO,
+};*/
+
+static const unsigned char SERVERBROWSE_HEARTBEAT[] = {255, 255, 255, 255, 'b', 'e', 'a', 't'};
+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_GETINFO[] = {255, 255, 255, 255, 'g', 'i', 'e', 'f'};
+static const unsigned char SERVERBROWSE_INFO[] = {255, 255, 255, 255, 'i', 'n', 'f', 'o'};