about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorMagnus Auvinen <magnus.auvinen@gmail.com>2008-07-06 11:21:21 +0000
committerMagnus Auvinen <magnus.auvinen@gmail.com>2008-07-06 11:21:21 +0000
commit9d632dd826c8a312095de0f56df66b2580d336cb (patch)
tree3fdde543c94323d6c698d278a58bf18e3c385776 /src
parent3705064b109580103a3d13f44693503da9927281 (diff)
downloadzcatch-9d632dd826c8a312095de0f56df66b2580d336cb.tar.gz
zcatch-9d632dd826c8a312095de0f56df66b2580d336cb.zip
major update. server clean up and much added documentation
Diffstat (limited to 'src')
-rw-r--r--src/engine/client/ec_client.c63
-rw-r--r--src/engine/client/ec_srvbrowse.c10
-rw-r--r--src/engine/e_engine.c9
-rw-r--r--src/engine/e_engine.h2
-rw-r--r--src/engine/e_if_client.h5
-rw-r--r--src/engine/e_if_gfx.h4
-rw-r--r--src/engine/e_if_other.h14
-rw-r--r--src/engine/e_if_server.h2
-rw-r--r--src/engine/e_network.c48
-rw-r--r--src/engine/e_network.h16
-rw-r--r--src/engine/e_snapshot.c34
-rw-r--r--src/engine/e_system.c80
-rw-r--r--src/engine/e_system.h589
-rw-r--r--src/engine/server/es_register.c14
-rw-r--r--src/engine/server/es_server.c25
-rw-r--r--src/game/client/gc_client.cpp7
-rw-r--r--src/game/client/gc_client.hpp12
-rw-r--r--src/game/client/gc_hooks.cpp71
-rw-r--r--src/game/client/gc_menu.cpp2
-rw-r--r--src/game/client/gc_render.cpp8
-rw-r--r--src/game/client/gc_render.hpp2
-rw-r--r--src/game/client/gc_render_obj.cpp21
-rw-r--r--src/game/g_game.cpp20
-rw-r--r--src/game/g_game.hpp12
-rw-r--r--src/game/g_protocol.hpp217
-rw-r--r--src/game/server/gs_common.hpp411
-rw-r--r--src/game/server/gs_ent_pickup.cpp140
-rw-r--r--src/game/server/gs_ent_player.cpp1128
-rw-r--r--src/game/server/gs_game.cpp83
-rw-r--r--src/game/server/gs_game_ctf.cpp77
-rw-r--r--src/game/server/gs_game_ctf.hpp6
-rw-r--r--src/game/server/gs_game_tdm.cpp6
-rw-r--r--src/game/server/gs_game_tdm.hpp2
-rw-r--r--src/game/server/gs_server.cpp1641
-rw-r--r--src/mastersrv/mastersrv.cpp34
-rw-r--r--src/tools/crapnet.cpp18
-rw-r--r--src/tools/fake_server.c8
-rw-r--r--src/tools/packetgen.c10
38 files changed, 2602 insertions, 2249 deletions
diff --git a/src/engine/client/ec_client.c b/src/engine/client/ec_client.c
index 8872d5f4..b8439194 100644
--- a/src/engine/client/ec_client.c
+++ b/src/engine/client/ec_client.c
@@ -43,7 +43,7 @@ const int prediction_margin = 7; /* magic network prediction value */
 NETCLIENT *net;
 
 /* TODO: ugly, fix me */
-extern void client_serverbrowse_set(NETADDR4 *addr, int request, SERVER_INFO *info);
+extern void client_serverbrowse_set(NETADDR *addr, int request, SERVER_INFO *info);
 
 static int snapshot_part;
 static int64 local_start_time;
@@ -53,7 +53,7 @@ static float frametime = 0.0001f;
 static float frametime_low = 1.0f;
 static float frametime_high = 0.0f;
 static int frames = 0;
-static NETADDR4 server_address;
+static NETADDR server_address;
 static int window_must_refocus = 0;
 static int snaploss = 0;
 static int snapcrcerrors = 0;
@@ -489,11 +489,13 @@ void client_connect(const char *server_address_str)
 	
 	if(port_str)
 		port = atoi(port_str);
-		
-	if(net_host_lookup(buf, port, &server_address) != 0)
+	
+	/* TODO: IPv6 support */
+	if(net_host_lookup(buf, &server_address, NETTYPE_IPV4) != 0)
 		dbg_msg("client", "could not find the address of %s, connecting to localhost", buf);
 	
 	rcon_authed = 0;
+	server_address.port = port;
 	netclient_connect(net, &server_address);
 	client_set_state(CLIENTSTATE_CONNECTING);
 	
@@ -534,6 +536,8 @@ static int client_load_data()
 extern int snapshot_data_rate[0xffff];
 extern int snapshot_data_updates[0xffff];
 
+const char *modc_getitemname(int type);
+
 static void client_debug_render()
 {
 	static NETSTATS prev, current;
@@ -555,29 +559,50 @@ static void client_debug_render()
 		net_stats(&current);
 	}
 	
+	/*
+		eth = 14
+		ip = 20
+		udp = 8
+		total = 42
+	*/
+	
 	frametime_avg = frametime_avg*0.9f + frametime*0.1f;
-	str_format(buffer, sizeof(buffer), "ticks: %8d %8d send: %5d/%3d recv: %5d/%3d snaploss: %d  mem %dk   gfxmem: %dk  fps: %3d",
+	str_format(buffer, sizeof(buffer), "ticks: %8d %8d snaploss: %d  mem %dk   gfxmem: %dk  fps: %3d",
 		current_tick, current_predtick,
-		(current.sent_bytes-prev.sent_bytes),
-		(current.sent_packets-prev.sent_packets),
-		(current.recv_bytes-prev.recv_bytes),
-		(current.recv_packets-prev.recv_packets),
 		snaploss,
 		mem_allocated()/1024,
 		gfx_memory_usage()/1024,
 		(int)(1.0f/frametime_avg));
 	gfx_quads_text(2, 2, 16, buffer);
 
+	
+	{
+		int send_packets = (current.sent_packets-prev.sent_packets);
+		int send_bytes = (current.sent_bytes-prev.sent_bytes);
+		int send_total = send_bytes + send_packets*42;
+		int recv_packets = (current.recv_packets-prev.recv_packets);
+		int recv_bytes = (current.recv_bytes-prev.recv_bytes);
+		int recv_total = recv_bytes + recv_packets*42;
+		
+		if(!send_packets) send_packets++;
+		if(!recv_packets) recv_packets++;
+		str_format(buffer, sizeof(buffer), "send: %3d %5d+%4d=%5d (%3d kbps) avg: %5d\nrecv: %3d %5d+%4d=%5d (%3d kbps) avg: %5d",
+			send_packets, send_bytes, send_packets*42, send_total, (send_total*8)/1024, send_bytes/send_packets,
+			recv_packets, recv_bytes, recv_packets*42, recv_total, (recv_total*8)/1024, recv_bytes/recv_packets);
+		gfx_quads_text(2, 14, 16, buffer);
+	}
 	/* render rates */
 	{
+		int y = 0;
 		int i;
 		for(i = 0; i < 256; i++)
 		{
 			if(snapshot_data_rate[i])
 			{
-				str_format(buffer, sizeof(buffer), "%4d : %8d %8d %8d", i, snapshot_data_rate[i]/8, snapshot_data_updates[i],
+				str_format(buffer, sizeof(buffer), "%4d %20s: %8d %8d %8d", i, modc_getitemname(i), snapshot_data_rate[i]/8, snapshot_data_updates[i],
 					(snapshot_data_rate[i]/snapshot_data_updates[i])/8);
-				gfx_quads_text(2, 100+i*8, 16, buffer);
+				gfx_quads_text(2, 100+y*12, 16, buffer);
+				y++;
 			}
 		}
 	}
@@ -684,13 +709,13 @@ static void client_process_packet(NETCHUNK *packet)
 			memcmp(packet->data, SERVERBROWSE_LIST, sizeof(SERVERBROWSE_LIST)) == 0)
 		{
 			int size = packet->data_size-sizeof(SERVERBROWSE_LIST);
-			int num = size/sizeof(NETADDR4);
-			NETADDR4 *addrs = (NETADDR4 *)((char*)packet->data+sizeof(SERVERBROWSE_LIST));
+			int num = size/sizeof(NETADDR);
+			NETADDR *addrs = (NETADDR *)((char*)packet->data+sizeof(SERVERBROWSE_LIST));
 			int i;
 			
 			for(i = 0; i < num; i++)
 			{
-				NETADDR4 addr = addrs[i];
+				NETADDR addr = addrs[i];
 				SERVER_INFO info = {0};
 
 #if defined(CONF_ARCH_ENDIAN_BIG)
@@ -1016,12 +1041,16 @@ static void client_process_packet(NETCHUNK *packet)
 						purgetick = delta_tick;
 						snapsize = snapshot_unpack_delta(deltashot, (SNAPSHOT*)tmpbuffer3, deltadata, deltasize);
 						if(snapsize < 0)
+						{
+							dbg_msg("client", "delta unpack failed!");
 							return;
-							
+						}
+						
 						if(msg != NETMSG_SNAPEMPTY && snapshot_crc((SNAPSHOT*)tmpbuffer3) != crc)
 						{
 							if(config.debug)
-								dbg_msg("client", "snapshot crc error %d", snapcrcerrors);
+								dbg_msg("client", "snapshot crc error #%d - tick=%d wantedcrc=%d gotcrc=%d compressed_size=%d", snapcrcerrors, game_tick, crc, snapshot_crc((SNAPSHOT*)tmpbuffer3), complete_size);
+								
 							snapcrcerrors++;
 							if(snapcrcerrors > 10)
 							{
@@ -1248,7 +1277,7 @@ extern void editor_init();
 
 static void client_run()
 {
-	NETADDR4 bindaddr;
+	NETADDR bindaddr;
 	int64 reporttime = time_get();
 	int64 reportinterval = time_freq()*1;
 
diff --git a/src/engine/client/ec_srvbrowse.c b/src/engine/client/ec_srvbrowse.c
index d6f045f1..41c6a7a0 100644
--- a/src/engine/client/ec_srvbrowse.c
+++ b/src/engine/client/ec_srvbrowse.c
@@ -20,7 +20,7 @@ extern NETCLIENT *net;
 typedef struct SERVERENTRY_t SERVERENTRY;
 struct SERVERENTRY_t
 {
-	NETADDR4 addr;
+	NETADDR addr;
 	int64 request_time;
 	int got_info;
 	SERVER_INFO info;
@@ -264,7 +264,7 @@ static void client_serverbrowse_remove_request(SERVERENTRY *entry)
 	}
 }
 
-void client_serverbrowse_set(NETADDR4 *addr, int request, SERVER_INFO *info)
+void client_serverbrowse_set(NETADDR *addr, int request, SERVER_INFO *info)
 {
 	int hash = addr->ip[0];
 	SERVERENTRY *entry = 0;
@@ -272,7 +272,7 @@ void client_serverbrowse_set(NETADDR4 *addr, int request, SERVER_INFO *info)
 	entry = serverlist_ip[hash];
 	while(entry)
 	{
-		if(net_addr4_cmp(&entry->addr, addr) == 0)
+		if(net_addr_comp(&entry->addr, addr) == 0)
 		{
 			/* update the server that we already have */
 			if(!serverlist_lan)
@@ -377,7 +377,7 @@ void client_serverbrowse_refresh(int lan)
 	}
 	else
 	{
-		NETADDR4 addr;
+		NETADDR addr;
 		NETCHUNK p;
 		int i;
 		
@@ -396,7 +396,7 @@ void client_serverbrowse_refresh(int lan)
 				continue;
 			
 			p.address = addr;
-			netclient_send(net, &p);	
+			netclient_send(net, &p);
 		}
 
 		if(config.debug)
diff --git a/src/engine/e_engine.c b/src/engine/e_engine.c
index 6e1efa95..f71b7b6e 100644
--- a/src/engine/e_engine.c
+++ b/src/engine/e_engine.c
@@ -235,11 +235,11 @@ enum
 typedef struct
 {
 	char hostname[128];
-	NETADDR4 addr;
+	NETADDR addr;
 	
 	/* these are used for lookups */
 	struct {
-		NETADDR4 addr;
+		NETADDR addr;
 		int result;
 		void *thread;
 		volatile int state;
@@ -264,7 +264,7 @@ void lookup_thread(void *user)
 	for(i = 0; i < info->num; i++)
 	{
 		int index = info->start+i;
-		master_servers[index].lookup.result = net_host_lookup(master_servers[index].hostname, 8300, &master_servers[index].lookup.addr);
+		master_servers[index].lookup.result = net_host_lookup(master_servers[index].hostname, &master_servers[index].lookup.addr, NETTYPE_IPV4);
 		master_servers[index].lookup.state = STATE_RESULT;
 	}
 }
@@ -304,6 +304,7 @@ void mastersrv_update()
 			if(master_servers[i].lookup.result == 0)
 			{
 				master_servers[i].addr = master_servers[i].lookup.addr;
+				master_servers[i].addr.port = 8300;
 			}
 			master_servers[i].lookup.state = STATE_PROCESSED;
 		}
@@ -332,7 +333,7 @@ int mastersrv_refreshing()
 	return needs_update;
 }
 
-NETADDR4 mastersrv_get(int index) 
+NETADDR mastersrv_get(int index) 
 {
 	return master_servers[index].addr;
 }
diff --git a/src/engine/e_engine.h b/src/engine/e_engine.h
index 7b5baec9..1c247588 100644
--- a/src/engine/e_engine.h
+++ b/src/engine/e_engine.h
@@ -24,5 +24,5 @@ int mastersrv_refresh_addresses();
 void mastersrv_update();
 int mastersrv_refreshing();
 void mastersrv_dump_servers();
-NETADDR4 mastersrv_get(int index);
+NETADDR mastersrv_get(int index);
 const char *mastersrv_name(int index);
diff --git a/src/engine/e_if_client.h b/src/engine/e_if_client.h
index e06d3e6b..280edf1a 100644
--- a/src/engine/e_if_client.h
+++ b/src/engine/e_if_client.h
@@ -3,6 +3,10 @@
 #define ENGINE_IF_CLIENT_H
 
 /*
+	Title: Client Interface
+*/
+
+/*
 	Section: Constants
 */
 
@@ -21,7 +25,6 @@ enum
 	CLIENTSTATE_ONLINE,
 	CLIENTSTATE_QUITING,
 
-
 	/* Constants: Image Formats
 		IMG_AUTO - Lets the engine choose the format.
 		IMG_RGB - 8-Bit uncompressed RGB
diff --git a/src/engine/e_if_gfx.h b/src/engine/e_if_gfx.h
index 57af6072..b758ed30 100644
--- a/src/engine/e_if_gfx.h
+++ b/src/engine/e_if_gfx.h
@@ -3,6 +3,10 @@
 #define ENGINE_IF_GFX_H
 
 /*
+	Title: Graphics
+*/
+
+/*
 	Section: Structures
 */
 
diff --git a/src/engine/e_if_other.h b/src/engine/e_if_other.h
index e02a7fd1..3d43533d 100644
--- a/src/engine/e_if_other.h
+++ b/src/engine/e_if_other.h
@@ -281,6 +281,20 @@ void snap_invalidate_item(int snapid, int index);
 */
 void snap_input(void *data, int size);
 
+/*
+	Function: snap_set_staticsize
+		Tells the engine how big a specific item always will be. This
+		helps the engine to compress snapshots better.
+	
+	Arguments:
+		type - Item type
+		size - Size of the data.
+		
+	Remarks:
+		Size must be in a multiple of 4.
+*/
+void snap_set_staticsize(int type, int size);
+
 /* message packing */
 enum
 {
diff --git a/src/engine/e_if_server.h b/src/engine/e_if_server.h
index c9775d90..dd5e1403 100644
--- a/src/engine/e_if_server.h
+++ b/src/engine/e_if_server.h
@@ -3,7 +3,7 @@
 #define ENGINE_IF_SERVER_H
 
 /*
-	Section: Server Hooks
+	Section: Server Interface
 */
 
 /* server */
diff --git a/src/engine/e_network.c b/src/engine/e_network.c
index 51fcbd75..2eef9006 100644
--- a/src/engine/e_network.c
+++ b/src/engine/e_network.c
@@ -170,7 +170,7 @@ typedef struct
 	
 	NETPACKETCONSTRUCT construct;
 	
-	NETADDR4 peeraddr;
+	NETADDR peeraddr;
 	NETSOCKET socket;
 	NETSTATS stats;
 } NETCONNECTION;
@@ -182,7 +182,7 @@ typedef struct
 
 typedef struct
 {
-	NETADDR4 addr;
+	NETADDR addr;
 	NETCONNECTION *conn;
 	int current_chunk;
 	int client_id;
@@ -205,7 +205,7 @@ struct NETSERVER_t
 
 struct NETCLIENT_t
 {
-	NETADDR4 server_addr;
+	NETADDR server_addr;
 	NETSOCKET socket;
 	
 	NETRECVINFO recv;
@@ -215,7 +215,7 @@ struct NETCLIENT_t
 static IOHANDLE datalog = 0;
 static HUFFSTATE huffmanstate;
 
-#define COMPRESSION 0
+#define COMPRESSION 1
 
 typedef struct pcap_hdr_s {
         unsigned magic_number;   /* magic number */
@@ -235,7 +235,7 @@ typedef struct pcaprec_hdr_s {
 } pcaprec_hdr_t;
 
 /* packs the data tight and sends it */
-static void send_packet(NETSOCKET socket, NETADDR4 *addr, NETPACKETCONSTRUCT *packet)
+static void send_packet(NETSOCKET socket, NETADDR *addr, NETPACKETCONSTRUCT *packet)
 {
 	unsigned char buffer[NET_MAX_PACKETSIZE];
 	buffer[0] = ((packet->flags<<4)&0xf0)|((packet->ack>>8)&0xf);
@@ -249,13 +249,13 @@ static void send_packet(NETSOCKET socket, NETADDR4 *addr, NETPACKETCONSTRUCT *pa
 	
 	if(COMPRESSION)
 	{
-		int compressed_size = (huffman_compress(&huffmanstate, packet->chunk_data, packet->data_size, &buffer[4], NET_MAX_PACKETSIZE-4)+7)/8;
-		net_udp4_send(socket, addr, buffer, NET_PACKETHEADERSIZE+compressed_size);
+		int compressed_size = (huffman_compress(&huffmanstate, packet->chunk_data, packet->data_size, &buffer[3], NET_MAX_PACKETSIZE-4)+7)/8;
+		net_udp_send(socket, addr, buffer, NET_PACKETHEADERSIZE+compressed_size);
 	}
 	else
 	{
 		mem_copy(&buffer[3], packet->chunk_data, packet->data_size);
-		net_udp4_send(socket, addr, buffer, NET_PACKETHEADERSIZE+packet->data_size);
+		net_udp_send(socket, addr, buffer, NET_PACKETHEADERSIZE+packet->data_size);
 	}
 }
 
@@ -473,7 +473,7 @@ static void conn_resend(NETCONNECTION *conn)
 	dbg_msg("conn", "resent %d packets", resend_count);
 }
 
-static int conn_connect(NETCONNECTION *conn, NETADDR4 *addr)
+static int conn_connect(NETCONNECTION *conn, NETADDR *addr)
 {
 	if(conn->state != NET_CONNSTATE_OFFLINE)
 		return -1;
@@ -504,7 +504,7 @@ static void conn_disconnect(NETCONNECTION *conn, const char *reason)
 	conn_reset(conn);
 }
 
-static int conn_feed(NETCONNECTION *conn, NETPACKETCONSTRUCT *packet, NETADDR4 *addr)
+static int conn_feed(NETCONNECTION *conn, NETPACKETCONSTRUCT *packet, NETADDR *addr)
 {
 	int64 now = time_get();
 	conn->last_recv_time = now;
@@ -685,11 +685,11 @@ static int conn_update(NETCONNECTION *conn)
 	return 0;
 }
 
-NETSERVER *netserver_open(NETADDR4 bindaddr, int max_clients, int flags)
+NETSERVER *netserver_open(NETADDR bindaddr, int max_clients, int flags)
 {
 	int i;
 	NETSERVER *server;
-	NETSOCKET socket = net_udp4_create(bindaddr);
+	NETSOCKET socket = net_udp_create(bindaddr);
 	if(socket == NETSOCKET_INVALID)
 		return 0;
 	
@@ -756,7 +756,7 @@ static void recvinfo_clear(NETRECVINFO *info)
 	info->valid = 0;
 }
 
-static void recvinfo_start(NETRECVINFO *info, NETADDR4 *addr, NETCONNECTION *conn, int cid)
+static void recvinfo_start(NETRECVINFO *info, NETADDR *addr, NETCONNECTION *conn, int cid)
 {
 	info->addr = *addr;
 	info->conn = conn;
@@ -823,7 +823,7 @@ int netserver_recv(NETSERVER *s, NETCHUNK *chunk)
 {
 	while(1)
 	{
-		NETADDR4 addr;
+		NETADDR addr;
 		int i, bytes, found;
 			
 		/* check for a chunk */
@@ -831,7 +831,7 @@ int netserver_recv(NETSERVER *s, NETCHUNK *chunk)
 			return 1;
 		
 		/* TODO: empty the recvinfo */
-		bytes = net_udp4_recv(s->socket, &addr, s->recv.buffer, NET_MAX_PACKETSIZE);
+		bytes = net_udp_recv(s->socket, &addr, s->recv.buffer, NET_MAX_PACKETSIZE);
 
 		/* no more packets for now */
 		if(bytes <= 0)
@@ -849,7 +849,7 @@ int netserver_recv(NETSERVER *s, NETCHUNK *chunk)
 				for(i = 0; i < s->max_clients; i++)
 				{
 					if(s->slots[i].conn.state != NET_CONNSTATE_OFFLINE &&
-						net_addr4_cmp(&s->slots[i].conn.peeraddr, &addr) == 0)
+						net_addr_comp(&s->slots[i].conn.peeraddr, &addr) == 0)
 					{
 						found = 1; /* silent ignore.. we got this client already */
 						break;
@@ -884,7 +884,7 @@ int netserver_recv(NETSERVER *s, NETCHUNK *chunk)
 				/* normal packet, find matching slot */
 				for(i = 0; i < s->max_clients; i++)
 				{
-					if(net_addr4_cmp(&s->slots[i].conn.peeraddr, &addr) == 0)
+					if(net_addr_comp(&s->slots[i].conn.peeraddr, &addr) == 0)
 					{
 						if(conn_feed(&s->slots[i].conn, &s->recv.data, &addr))
 						{
@@ -952,17 +952,17 @@ NETSOCKET netserver_socket(NETSERVER *s)
 	return s->socket;
 }
 
-int netserver_client_addr(NETSERVER *s, int client_id, NETADDR4 *addr)
+int netserver_client_addr(NETSERVER *s, int client_id, NETADDR *addr)
 {
 	*addr = s->slots[client_id].conn.peeraddr;
 	return 1;
 }
 
-NETCLIENT *netclient_open(NETADDR4 bindaddr, int flags)
+NETCLIENT *netclient_open(NETADDR bindaddr, int flags)
 {
 	NETCLIENT *client = (NETCLIENT *)mem_alloc(sizeof(NETCLIENT), 1);
 	mem_zero(client, sizeof(NETCLIENT));
-	client->socket = net_udp4_create(bindaddr);
+	client->socket = net_udp_create(bindaddr);
 	conn_init(&client->conn, client->socket);
 	return client;
 }
@@ -988,7 +988,7 @@ int netclient_disconnect(NETCLIENT *c, const char *reason)
 	return 0;
 }
 
-int netclient_connect(NETCLIENT *c, NETADDR4 *addr)
+int netclient_connect(NETCLIENT *c, NETADDR *addr)
 {
 	conn_connect(&c->conn, addr);
 	return 0;
@@ -998,7 +998,7 @@ int netclient_recv(NETCLIENT *c, NETCHUNK *chunk)
 {
 	while(1)
 	{
-		NETADDR4 addr;
+		NETADDR addr;
 		int bytes;
 			
 		/* check for a chunk */
@@ -1006,7 +1006,7 @@ int netclient_recv(NETCLIENT *c, NETCHUNK *chunk)
 			return 1;
 		
 		/* TODO: empty the recvinfo */
-		bytes = net_udp4_recv(c->socket, &addr, c->recv.buffer, NET_MAX_PACKETSIZE);
+		bytes = net_udp_recv(c->socket, &addr, c->recv.buffer, NET_MAX_PACKETSIZE);
 
 		/* no more packets for now */
 		if(bytes <= 0)
@@ -1089,7 +1089,7 @@ void netcommon_openlog(const char *filename)
 
 
 static const int freq_table[256+1] = {
-31230,4545,2657,431,1950,919,444,482,2244,617,838,542,715,1814,304,240,754,212,647,186,
+1<<30,4545,2657,431,1950,919,444,482,2244,617,838,542,715,1814,304,240,754,212,647,186,
 283,131,146,166,543,164,167,136,179,859,363,113,157,154,204,108,137,180,202,176,
 872,404,168,134,151,111,113,109,120,126,129,100,41,20,16,22,18,18,17,19,
 16,37,13,21,362,166,99,78,95,88,81,70,83,284,91,187,77,68,52,68,
diff --git a/src/engine/e_network.h b/src/engine/e_network.h
index 746230a7..eaa9df39 100644
--- a/src/engine/e_network.h
+++ b/src/engine/e_network.h
@@ -5,7 +5,7 @@ typedef struct
 	/* -1 means that it's a stateless packet */
 	/* 0 on the client means the server */
 	int client_id;
-	NETADDR4 address; /* only used when client_id == -1 */
+	NETADDR address; /* only used when client_id == -1 */
 	int flags;
 	int data_size;
 	const void *data;
@@ -45,7 +45,7 @@ void netcommon_openlog(const char *filename);
 void netcommon_init();
 
 /* server side */
-NETSERVER *netserver_open(NETADDR4 bindaddr, int max_clients, int flags);
+NETSERVER *netserver_open(NETADDR bindaddr, int max_clients, int flags);
 int netserver_set_callbacks(NETSERVER *s, NETFUNC_NEWCLIENT new_client, NETFUNC_DELCLIENT del_client, void *user);
 int netserver_recv(NETSERVER *s, NETCHUNK *chunk);
 int netserver_send(NETSERVER *s, NETCHUNK *chunk);
@@ -53,14 +53,14 @@ int netserver_close(NETSERVER *s);
 int netserver_update(NETSERVER *s);
 NETSOCKET netserver_socket(NETSERVER *s);
 int netserver_drop(NETSERVER *s, int client_id, const char *reason);
-int netserver_client_addr(NETSERVER *s, int client_id, NETADDR4 *addr);
+int netserver_client_addr(NETSERVER *s, int client_id, NETADDR *addr);
 int netserver_max_clients(NETSERVER *s);
 /*void netserver_stats(NETSERVER *s, NETSTATS *stats);*/
 
 /* client side */
-NETCLIENT *netclient_open(NETADDR4 bindaddr, int flags);
+NETCLIENT *netclient_open(NETADDR bindaddr, int flags);
 int netclient_disconnect(NETCLIENT *c, const char *reason);
-int netclient_connect(NETCLIENT *c, NETADDR4 *addr);
+int netclient_connect(NETCLIENT *c, NETADDR *addr);
 int netclient_recv(NETCLIENT *c, NETCHUNK *chunk);
 int netclient_send(NETCLIENT *c, NETCHUNK *chunk);
 int netclient_close(NETCLIENT *c);
@@ -79,7 +79,7 @@ public:
 	net_server() : ptr(0) {}
 	~net_server() { close(); }
 	
-	int open(NETADDR4 bindaddr, int max, int flags) { ptr = netserver_open(bindaddr, max, flags); return ptr != 0; }
+	int open(NETADDR bindaddr, int max, int flags) { ptr = netserver_open(bindaddr, max, flags); return ptr != 0; }
 	int close() { int r = netserver_close(ptr); ptr = 0; return r; }
 	
 	int set_callbacks(NETFUNC_NEWCLIENT new_client, NETFUNC_DELCLIENT del_client, void *user)
@@ -103,10 +103,10 @@ public:
 	net_client() : ptr(0) {}
 	~net_client() { close(); }
 	
-	int open(NETADDR4 bindaddr, int flags) { ptr = netclient_open(bindaddr, flags); return ptr != 0; }
+	int open(NETADDR bindaddr, int flags) { ptr = netclient_open(bindaddr, flags); return ptr != 0; }
 	int close() { int r = netclient_close(ptr); ptr = 0; return r; }
 	
-	int connect(NETADDR4 *addr) { return netclient_connect(ptr, addr); }
+	int connect(NETADDR *addr) { return netclient_connect(ptr, addr); }
 	int disconnect(const char *reason) { return netclient_disconnect(ptr, reason); }
 	
 	int recv(NETCHUNK *chunk) { return netclient_recv(ptr, chunk); }
diff --git a/src/engine/e_snapshot.c b/src/engine/e_snapshot.c
index 9d06f59d..88949b65 100644
--- a/src/engine/e_snapshot.c
+++ b/src/engine/e_snapshot.c
@@ -6,6 +6,15 @@
 #include "e_common_interface.h"
 
 
+/* TODO: strange arbitrary number */
+static short item_sizes[64] = {0};
+
+void snap_set_staticsize(int itemtype, int size)
+{
+	dbg_msg("","%d = %d", itemtype, size);
+	item_sizes[itemtype] = size;
+}
+
 int *snapitem_data(SNAPSHOT_ITEM *item) { return (int *)(item+1); }
 int snapitem_type(SNAPSHOT_ITEM *item) { return item->type_and_id>>16; }
 int snapitem_id(SNAPSHOT_ITEM *item) { return item->type_and_id&0xffff; }
@@ -253,14 +262,21 @@ int snapshot_create_delta(SNAPSHOT *from, SNAPSHOT *to, void *dstdata)
 			if(pastindex != -1)
 			{
 				static PERFORMACE_INFO scope = {"diff", 0};
+				int *item_data_dst = data+3;
 				perf_start(&scope);
 		
 				pastitem = snapshot_get_item(from, pastindex);
-				if(diff_item((int*)snapitem_data(pastitem), (int*)snapitem_data(curitem), data+3, itemsize/4))
+				
+				if(item_sizes[snapitem_type(curitem)])
+					item_data_dst = data+2;
+				
+				if(diff_item((int*)snapitem_data(pastitem), (int*)snapitem_data(curitem), item_data_dst, itemsize/4))
 				{
-					*data++ = itemsize;
+					
 					*data++ = snapitem_type(curitem);
 					*data++ = snapitem_id(curitem);
+					if(!item_sizes[snapitem_type(curitem)])
+						*data++ = itemsize/4;
 					data += itemsize/4;
 					delta->num_update_items++;
 				}
@@ -271,9 +287,10 @@ int snapshot_create_delta(SNAPSHOT *from, SNAPSHOT *to, void *dstdata)
 				static PERFORMACE_INFO scope = {"copy", 0};
 				perf_start(&scope);
 				
-				*data++ = itemsize;
 				*data++ = snapitem_type(curitem);
 				*data++ = snapitem_id(curitem);
+				if(!item_sizes[snapitem_type(curitem)])
+					*data++ = itemsize/4;
 				
 				mem_copy(data, snapitem_data(curitem), itemsize);
 				size_count += itemsize;
@@ -370,12 +387,19 @@ int snapshot_unpack_delta(SNAPSHOT *from, SNAPSHOT *to, void *srcdata, int data_
 	/* unpack updated stuff */
 	for(i = 0; i < delta->num_update_items; i++)
 	{
-		if(data+3 > end)
+		if(data+2 > end)
 			return -1;
 		
-		itemsize = *data++;
 		type = *data++;
 		id = *data++;
+		if(item_sizes[type])
+			itemsize = item_sizes[type];
+		else
+		{
+			if(data+1 > end)
+				return -1;
+			itemsize = (*data++) * 4;
+		}
 		snapshot_current = type;
 		
 		if(range_check(end, data, itemsize) || itemsize < 0) return -1;
diff --git a/src/engine/e_system.c b/src/engine/e_system.c
index c0d3595c..f8fca864 100644
--- a/src/engine/e_system.c
+++ b/src/engine/e_system.c
@@ -465,8 +465,9 @@ int64 time_freq()
 }
 
 /* -----  network ----- */
-static void netaddr4_to_sockaddr(const NETADDR4 *src, struct sockaddr *dest)
+static void netaddr_to_sockaddr(const NETADDR *src, struct sockaddr *dest)
 {
+	/* TODO: IPv6 support */
 	struct sockaddr_in *p = (struct sockaddr_in *)dest;
 	mem_zero(p, sizeof(struct sockaddr_in));
 	p->sin_family = AF_INET;
@@ -474,9 +475,11 @@ static void netaddr4_to_sockaddr(const NETADDR4 *src, struct sockaddr *dest)
 	p->sin_addr.s_addr = htonl(src->ip[0]<<24|src->ip[1]<<16|src->ip[2]<<8|src->ip[3]);
 }
 
-static void sockaddr_to_netaddr4(const struct sockaddr *src, NETADDR4 *dst)
+static void sockaddr_to_netaddr(const struct sockaddr *src, NETADDR *dst)
 {
+	/* TODO: IPv6 support */
 	unsigned int ip = htonl(((struct sockaddr_in*)src)->sin_addr.s_addr);
+	dst->type = NETTYPE_IPV4;
 	dst->port = htons(((struct sockaddr_in*)src)->sin_port);
 	dst->ip[0] = (unsigned char)((ip>>24)&0xFF);
 	dst->ip[1] = (unsigned char)((ip>>16)&0xFF);
@@ -484,21 +487,15 @@ static void sockaddr_to_netaddr4(const struct sockaddr *src, NETADDR4 *dst)
 	dst->ip[3] = (unsigned char)(ip&0xFF);
 }
 
-int net_addr4_cmp(const NETADDR4 *a, const NETADDR4 *b)
+int net_addr_comp(const NETADDR *a, const NETADDR *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;
+	return mem_comp(a, b, sizeof(NETADDR));
 }
 
 
-int net_host_lookup(const char *hostname, unsigned short port, NETADDR4 *addr)
+int net_host_lookup(const char *hostname, NETADDR *addr, int types)
 {
+	/* TODO: IPv6 support */
 	struct addrinfo hints;
 	struct addrinfo *result;
 	int e;
@@ -510,14 +507,15 @@ int net_host_lookup(const char *hostname, unsigned short port, NETADDR4 *addr)
 	if(e != 0 || !result)
 		return -1;
 
-	sockaddr_to_netaddr4(result->ai_addr, addr);
+	sockaddr_to_netaddr(result->ai_addr, addr);
 	freeaddrinfo(result);
-	addr->port = port;
+	addr->port = 0;
 	return 0;
 }
 
-NETSOCKET net_udp4_create(NETADDR4 bindaddr)
+NETSOCKET net_udp_create(NETADDR bindaddr)
 {
+	/* TODO: IPv6 support */
 	struct sockaddr addr;
 	unsigned int mode = 1;
 	int broadcast = 1;
@@ -528,10 +526,10 @@ NETSOCKET net_udp4_create(NETADDR4 bindaddr)
 		return NETSOCKET_INVALID;
 	
 	/* bind, we should check for error */
-	netaddr4_to_sockaddr(&bindaddr, &addr);
+	netaddr_to_sockaddr(&bindaddr, &addr);
 	if(bind(sock, &addr, sizeof(addr)) != 0)
 	{
-		net_udp4_close(sock);
+		net_udp_close(sock);
 		return NETSOCKET_INVALID;
 	}
 	
@@ -549,12 +547,12 @@ NETSOCKET net_udp4_create(NETADDR4 bindaddr)
 	return sock;
 }
 
-int net_udp4_send(NETSOCKET sock, const NETADDR4 *addr, const void *data, int size)
+int net_udp_send(NETSOCKET sock, const NETADDR *addr, const void *data, int size)
 {
 	struct sockaddr sa;
 	int d;
 	mem_zero(&sa, sizeof(sa));
-	netaddr4_to_sockaddr(addr, &sa);
+	netaddr_to_sockaddr(addr, &sa);
 	d = sendto((int)sock, (const char*)data, size, 0, &sa, sizeof(sa));
 	if(d < 0)
 		dbg_msg("net", "sendto error %d %x", d, d);
@@ -563,7 +561,7 @@ int net_udp4_send(NETSOCKET sock, const NETADDR4 *addr, const void *data, int si
 	return d;
 }
 
-int net_udp4_recv(NETSOCKET sock, NETADDR4 *addr, void *data, int maxsize)
+int net_udp_recv(NETSOCKET sock, NETADDR *addr, void *data, int maxsize)
 {
 	struct sockaddr from;
 	int bytes;
@@ -571,7 +569,7 @@ int net_udp4_recv(NETSOCKET sock, NETADDR4 *addr, void *data, int maxsize)
 	bytes = recvfrom(sock, (char*)data, maxsize, 0, &from, &fromlen);
 	if(bytes > 0)
 	{
-		sockaddr_to_netaddr4(&from, addr);
+		sockaddr_to_netaddr(&from, addr);
 		network_stats.recv_bytes += bytes;
 		network_stats.recv_packets++;
 		return bytes;
@@ -581,7 +579,7 @@ int net_udp4_recv(NETSOCKET sock, NETADDR4 *addr, void *data, int maxsize)
 	return -1; /* error */
 }
 
-int net_udp4_close(NETSOCKET sock)
+int net_udp_close(NETSOCKET sock)
 {
 #if defined(CONF_FAMILY_WINDOWS)
 	closesocket(sock);
@@ -591,8 +589,9 @@ int net_udp4_close(NETSOCKET sock)
 	return 0;
 }
 
-NETSOCKET net_tcp4_create(const NETADDR4 *a)
+NETSOCKET net_tcp_create(const NETADDR *a)
 {
+	/* TODO: IPv6 support */
     struct sockaddr addr;
 
     /* create socket */
@@ -601,14 +600,14 @@ NETSOCKET net_tcp4_create(const NETADDR4 *a)
         return NETSOCKET_INVALID;
 
     /* bind, we should check for error */
-    netaddr4_to_sockaddr(a, &addr);
+    netaddr_to_sockaddr(a, &addr);
     bind(sock, &addr, sizeof(addr));
 
     /* return */
     return sock;
 }
 
-int net_tcp4_set_non_blocking(NETSOCKET sock)
+int net_tcp_set_non_blocking(NETSOCKET sock)
 {
 	unsigned int mode = 1;
 #if defined(CONF_FAMILY_WINDOWS)
@@ -618,7 +617,7 @@ int net_tcp4_set_non_blocking(NETSOCKET sock)
 #endif
 }
 
-int net_tcp4_set_blocking(NETSOCKET sock)
+int net_tcp_set_blocking(NETSOCKET sock)
 {
 	unsigned int mode = 0;
 #if defined(CONF_FAMILY_WINDOWS)
@@ -628,12 +627,12 @@ int net_tcp4_set_blocking(NETSOCKET sock)
 #endif
 }
 
-int net_tcp4_listen(NETSOCKET sock, int backlog)
+int net_tcp_listen(NETSOCKET sock, int backlog)
 {
 	return listen(sock, backlog);
 }
 
-int net_tcp4_accept(NETSOCKET sock, NETSOCKET *new_sock, NETADDR4 *a)
+int net_tcp_accept(NETSOCKET sock, NETSOCKET *new_sock, NETADDR *a)
 {
 	int s;
 	socklen_t sockaddr_len;
@@ -645,48 +644,48 @@ int net_tcp4_accept(NETSOCKET sock, NETSOCKET *new_sock, NETADDR4 *a)
 
 	if (s != -1)
 	{
-		sockaddr_to_netaddr4(&addr, a);
+		sockaddr_to_netaddr(&addr, a);
 		*new_sock = s;
 	}
 	return s;
 }
 
-int net_tcp4_connect(NETSOCKET sock, const NETADDR4 *a)
+int net_tcp_connect(NETSOCKET sock, const NETADDR *a)
 {
   struct sockaddr addr;
 
-  netaddr4_to_sockaddr(a, &addr);
+  netaddr_to_sockaddr(a, &addr);
   return connect(sock, &addr, sizeof(addr)); 
 }
 
-int net_tcp4_connect_non_blocking(NETSOCKET sock, const NETADDR4 *a)
+int net_tcp_connect_non_blocking(NETSOCKET sock, const NETADDR *a)
 {
 	struct sockaddr addr;
 	int res;
 
-	netaddr4_to_sockaddr(a, &addr);
-	net_tcp4_set_non_blocking(sock);
+	netaddr_to_sockaddr(a, &addr);
+	net_tcp_set_non_blocking(sock);
   	res = connect(sock, &addr, sizeof(addr));
-	net_tcp4_set_blocking(sock);
+	net_tcp_set_blocking(sock);
 
 	return res;
 }
 
-int net_tcp4_send(NETSOCKET sock, const void *data, int size)
+int net_tcp_send(NETSOCKET sock, const void *data, int size)
 {
   int d;
   d = send((int)sock, (const char*)data, size, 0);
   return d;
 }
 
-int net_tcp4_recv(NETSOCKET sock, void *data, int maxsize)
+int net_tcp_recv(NETSOCKET sock, void *data, int maxsize)
 {
   int bytes;
   bytes = recv((int)sock, (char*)data, maxsize, 0);
   return bytes;
 }
 
-int net_tcp4_close(NETSOCKET sock)
+int net_tcp_close(NETSOCKET sock)
 {
 #if defined(CONF_FAMILY_WINDOWS)
 	closesocket(sock);
@@ -958,6 +957,11 @@ void str_hex(char *dst, int dst_size, const void *data, int data_size)
 	}
 }
 
+int mem_comp(const void *a, const void *b, int size)
+{
+	return memcmp(a,b,size);
+}
+
 void net_stats(NETSTATS *stats_inout)
 {
 	*stats_inout = network_stats;
diff --git a/src/engine/e_system.h b/src/engine/e_system.h
index 38c2b076..0b1cbaab 100644
--- a/src/engine/e_system.h
+++ b/src/engine/e_system.h
@@ -1,4 +1,9 @@
 /* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
+
+/*
+	Title: OS Abstraction
+*/
+
 #ifndef BASE_SYSTEM_H
 #define BASE_SYSTEM_H
 
@@ -9,10 +14,9 @@ extern "C" {
 #endif
 
 /* Group: Debug */
-/**********
+/*
 	Function: dbg_assert
-	
-	Breaks into the debugger based on a test.
+		Breaks into the debugger based on a test.
 	
 	Parameters:
 		test - Result of the test.
@@ -23,25 +27,24 @@ extern "C" {
 	
 	See Also:
 		<dbg_break>
-**********/
+*/
 void dbg_assert(int test, const char *msg);
 #define dbg_assert(test,msg) dbg_assert_imp(__FILE__, __LINE__, test,  msg)
 void dbg_assert_imp(const char *filename, int line, int test, const char *msg);
 
-/**********
+/*
 	Function: dbg_break
-	
-	Breaks into the debugger.
+		Breaks into the debugger.
 	
 	Remarks:
 		Does nothing in release version of the library.
 	
 	See Also:
 		<dbg_assert>
-**********/
+*/
 void dbg_break();
 
-/**********
+/*
 	Function: dbg_msg
 	
 	Prints a debug message.
@@ -55,15 +58,14 @@ void dbg_break();
 		
 	See Also:
 		<dbg_assert>
-**********/
+*/
 void dbg_msg(const char *sys, const char *fmt, ...);
 
 /* Group: Memory */
 
-/**********
+/*
 	Function: mem_alloc
-	
-	Allocates memory.
+		Allocates memory.
 	
 	Parameters:
 		size - Size of the needed block.
@@ -79,14 +81,13 @@ void dbg_msg(const char *sys, const char *fmt, ...);
 
 	See Also:
 		<mem_free>
-**********/
+*/
 void *mem_alloc_debug(const char *filename, int line, unsigned size, unsigned alignment);
 #define mem_alloc(s,a) mem_alloc_debug(__FILE__, __LINE__, (s), (a))
 
-/**********
+/*
 	Function: mem_free
-	
-	Frees a block allocated through <mem_alloc>.
+		Frees a block allocated through <mem_alloc>.
 	
 	Remarks:
 		- In the debug version of the library the function will assert if
@@ -95,10 +96,10 @@ void *mem_alloc_debug(const char *filename, int line, unsigned size, unsigned al
 	
 	See Also:
 		<mem_alloc>
-**********/
+*/
 void mem_free(void *block);
 
-/**********
+/*
 	Function: mem_copy
 		Copies a a memory block.
 	
@@ -113,37 +114,54 @@ void mem_free(void *block);
 	
 	See Also:
 		<mem_move>
-**********/
+*/
 void mem_copy(void *dest, const void *source, unsigned size);
 
-/**********
+/*
 	Function: mem_move
-		Copies a a memory block.
+		Copies a a memory block
 	
 	Parameters:
-		dest - Destination.
-		source - Source to copy.
-		size - Size of the block to copy.
+		dest - Destination
+		source - Source to copy
+		size - Size of the block to copy
 	
 	Remarks:
-		- This functions handles cases where source and destination is overlapping.
+		- This functions handles cases where source and destination
+		is overlapping
 	
 	See Also:
 		<mem_copy>
-**********/
+*/
 void mem_move(void *dest, const void *source, unsigned size);
 
-/**********
+/*
 	Function: mem_zero
-		Sets a complete memory block to 0.
+		Sets a complete memory block to 0
 	
 	Parameters:
-		block - Pointer to the block to zero out.
-		size - Size of the block.
-**********/
+		block - Pointer to the block to zero out
+		size - Size of the block
+*/
 void mem_zero(void *block, unsigned size);
 
-/* ------- io ------- */
+/*
+	Function: mem_comp
+		Compares two blocks of memory
+	
+	Parameters:
+		a - First block of data
+		b - Second block of data
+		size - Size of the data to compare
+		
+	Returns:
+		<0 - Block a is lesser then block b
+		0 - Block a is equal to block b
+		>0 - Block a is greater then block b
+*/
+int mem_comp(const void *a, const void *b, int size);
+
+/* Group: File IO */
 enum {
 	IOFLAG_READ = 1,
 	IOFLAG_WRITE = 2,
@@ -156,9 +174,7 @@ enum {
 
 typedef struct IOINTERNAL *IOHANDLE;
 
-/**** Group: File IO ****/
-
-/****
+/*
 	Function: io_open
 		Opens a file.
 
@@ -169,10 +185,10 @@ typedef struct IOINTERNAL *IOHANDLE;
 	Returns:
 		Returns a handle to the file on success and 0 on failure.
 
-****/
+*/
 IOHANDLE io_open(const char *filename, int flags);
 
-/****
+/*
 	Function: io_read
 		Reads data into a buffer from a file.
 
@@ -184,10 +200,10 @@ IOHANDLE io_open(const char *filename, int flags);
 	Returns:
 		Number of bytes read.
 
-****/
+*/
 unsigned io_read(IOHANDLE io, void *buffer, unsigned size);
 
-/*****
+/*
 	Function: io_skip
 		Skips data in a file.
 	
@@ -197,13 +213,12 @@ unsigned io_read(IOHANDLE io, void *buffer, unsigned size);
 		
 	Returns:
 		Number of bytes skipped.
-****/
+*/
 unsigned io_skip(IOHANDLE io, unsigned size);
 
-/*****
+/*
 	Function: io_write
-	
-	Writes data from a buffer to file.
+		Writes data from a buffer to file.
 	
 	Parameters:
 		io - Handle to the file.
@@ -212,10 +227,10 @@ unsigned io_skip(IOHANDLE io, unsigned size);
 		
 	Returns:
 		Number of bytes written.
-*****/
+*/
 unsigned io_write(IOHANDLE io, const void *buffer, unsigned size);
 
-/*****
+/*
 	Function: io_seek
 		Seeks to a specified offset in the file.
 	
@@ -226,10 +241,10 @@ unsigned io_write(IOHANDLE io, const void *buffer, unsigned size);
 		
 	Returns:
 		Returns 0 on success.
-*****/
+*/
 int io_seek(IOHANDLE io, int offset, int origin);
 
-/*****
+/*
 	Function: io_tell
 		Gets the current position in the file.
 	
@@ -238,10 +253,10 @@ int io_seek(IOHANDLE io, int offset, int origin);
 		
 	Returns:
 		Returns the current position. -1L if an error occured.
-*****/
+*/
 long int io_tell(IOHANDLE io);
 
-/*****
+/*
 	Function: io_length
 		Gets the total length of the file. Resetting cursor to the beginning
 	
@@ -250,10 +265,10 @@ long int io_tell(IOHANDLE io);
 		
 	Returns:
 		Returns the total size. -1L if an error occured.
-*****/
+*/
 long int io_length(IOHANDLE io);
 
-/*****
+/*
 	Function: io_close
 		Closes a file.
 	
@@ -262,10 +277,10 @@ long int io_length(IOHANDLE io);
 		
 	Returns:
 		Returns 0 on success.
-*****/
+*/
 int io_close(IOHANDLE io);
 
-/*****
+/*
 	Function: io_flush
 		Empties all buffers and writes all pending data.
 	
@@ -274,22 +289,77 @@ int io_close(IOHANDLE io);
 		
 	Returns:
 		Returns 0 on success.
-*****/
+*/
 int io_flush(IOHANDLE io);
 
-/**** Group: Threads ****/
 
-/*****
+/*
+	Function: io_stdin
+		Returns an <IOHANDLE> to the standard input.
+*/
+IOHANDLE io_stdin();
+
+/*
+	Function: io_stdout
+		Returns an <IOHANDLE> to the standard output.
+*/
+IOHANDLE io_stdout();
+
+/*
+	Function: io_stderr
+		Returns an <IOHANDLE> to the standard error.
+*/
+IOHANDLE io_stderr();
+
+
+/* Group: Threads */
+
+/*
 	Function: thread_sleep
-	
-	Suspends the current thread for a given period.
+		Suspends the current thread for a given period.
 	
 	Parameters:
 		milliseconds - Number of milliseconds to sleep.
-*****/
+*/
 void thread_sleep(int milliseconds);
 
-/**** Group: Locks ****/
+/*
+	Function: thread_create
+		Creates a new thread.
+	
+	Parameters:
+		threadfunc - Entry point for the new thread.
+		user - Pointer to pass to the thread.
+		
+*/
+void *thread_create(void (*threadfunc)(void *), void *user);
+
+/*
+	Function: thread_wait
+		Waits for a thread to be done or destroyed.
+	
+	Parameters:
+		thread - Thread to wait for.
+*/
+void thread_wait(void *thread);
+
+/*
+	Function: thread_destoy
+		Destroys a thread.
+	
+	Parameters:
+		thread - Thread to destroy.
+*/
+void thread_destroy(void *thread);
+
+/*
+	Function: thread_yeild
+		Yeild the current threads execution slice.
+*/
+void thread_yield();
+
+
+/* Group: Locks */
 typedef void* LOCK;
 
 LOCK lock_create();
@@ -299,7 +369,7 @@ int lock_try(LOCK lock);
 void lock_wait(LOCK lock);
 void lock_release(LOCK lock);
 
-/**** Group: Timer ****/
+/* Group: Timer */
 #ifdef __GNUC__
 /* if compiled with -pedantic-errors it will complain about long
 	not being a C90 thing.
@@ -308,71 +378,88 @@ __extension__ typedef long long int64;
 #else
 typedef long long int64;
 #endif
-/*****
+/*
 	Function: time_get
-	
-	Fetches a sample from a high resolution timer.
+		Fetches a sample from a high resolution timer.
 	
 	Returns:
 		Current value of the timer.
 
 	Remarks:
 		To know how fast the timer is ticking, see <time_freq>.
-*****/
+*/
 int64 time_get();
 
-/*****
+/*
 	Function: time_freq
-	
-	Returns the frequency of the high resolution timer.
+		Returns the frequency of the high resolution timer.
 	
 	Returns:
 		Returns the frequency of the high resolution timer.
-*****/
+*/
 int64 time_freq();
 
-/**** Group: Network General ipv4 ****/
+/*
+	Function: time_timestamp
+		Retrives the current time as a UNIX timestamp
+	
+	Returns:
+		The time as a UNIX timestamp
+*/
+unsigned time_timestamp();
+
+/* Group: Network General ipv4 */
 typedef int NETSOCKET;
 enum
 {
-	NETSOCKET_INVALID = -1
+	NETSOCKET_INVALID = -1,
+	
+	NETTYPE_INVALID = 0,
+	NETTYPE_IPV4 = 1,
+	NETTYPE_IPV6 = 2,
+	NETTYPE_ALL = ~0
 };
 
-typedef struct 
+/*
+typedef struct
 {
 	unsigned char ip[4];
 	unsigned short port;
-} NETADDR4;
+} NETADDR4;*/
 
-/*****
+typedef struct
+{
+	unsigned int type;
+	unsigned char ip[16];
+	unsigned short port;
+} NETADDR;
+
+/*
 	Function: net_host_lookup
-	
-	Does a hostname lookup by name and fills out the passed NETADDE4 struct with the recieved details.
+		Does a hostname lookup by name and fills out the passed NETADDR struct with the recieved details.
 
 	Returns:
 		0 on success.
-*****/
-int net_host_lookup(const char *hostname, unsigned short port, NETADDR4 *addr);
+*/
+int net_host_lookup(const char *hostname, NETADDR *addr, int types);
 
-/**** Group: Network UDP4 ****/
+/* Group: Network UDP */
 
-/*****
-	Function: net_udp4_create
-	
-	Creates a UDP4 socket and binds it to a port.
+/*
+	Function: net_udp_create
+		Creates a UDP socket and binds it to a port.
 
 	Parameters:
 		port - Port to bind the socket to.
 	
 	Returns:
 		On success it returns an handle to the socket. On failure it returns NETSOCKET_INVALID.
-*****/
-NETSOCKET net_udp4_create(NETADDR4 bindaddr);
+*/
+NETSOCKET net_udp_create(NETADDR bindaddr);
 
-/*****
-	Function: net_udp4_send
-	
-	Sends a packet over an UDP4 socket.
+/*
+	Function: net_udp_send
+		Sends a packet over an UDP socket.
 
 	Parameters:
 		sock - Socket to use.
@@ -382,177 +469,322 @@ NETSOCKET net_udp4_create(NETADDR4 bindaddr);
 	
 	Returns:
 		On success it returns the number of bytes sent. Returns -1 on error.
-*****/
-int net_udp4_send(NETSOCKET sock, const NETADDR4 *addr, const void *data, int size);
+*/
+int net_udp_send(NETSOCKET sock, const NETADDR *addr, const void *data, int size);
 
-/*****
-	Function: net_udp4_recv
-	
-	Recives a packet over an UDP4 socket.
+/*
+	Function: net_udp_recv
+		Recives a packet over an UDP socket.
 
 	Parameters:
 		sock - Socket to use.
-		addr - Pointer to an NETADDR4 that will recive the address.
+		addr - Pointer to an NETADDR that will recive the address.
 		data - Pointer to a buffer that will recive the data.
 		maxsize - Maximum size to recive.
 	
 	Returns:
 		On success it returns the number of bytes recived. Returns -1 on error.
-*****/
-int net_udp4_recv(NETSOCKET sock, NETADDR4 *addr, void *data, int maxsize);
+*/
+int net_udp_recv(NETSOCKET sock, NETADDR *addr, void *data, int maxsize);
 
-/*****
-	Function: net_udp4_close
-	
-	Closes an UDP4 socket.
+/*
+	Function: net_udp_close
+		Closes an UDP socket.
 
 	Parameters:
 		sock - Socket to close.
 	
 	Returns:
 		Returns 0 on success. -1 on error.
-*****/
-int net_udp4_close(NETSOCKET sock);
+*/
+int net_udp_close(NETSOCKET sock);
 
 
-/**** Group: Network TCP4 ****/
+/* Group: Network TCP */
 
-/*****
-	Function: net_tcp4_create
+/*
+	Function: net_tcp_create
 	
 	DOCTODO: serp
-*****/
-NETSOCKET net_tcp4_create(const NETADDR4 *a);
+*/
+NETSOCKET net_tcp_create(const NETADDR *a);
 
-/*****
-	Function: net_tcp4_set_non_blocking
+/*
+	Function: net_tcp_set_non_blocking
 
 	DOCTODO: serp
-*****/
-int net_tcp4_set_non_blocking(NETSOCKET sock);
+*/
+int net_tcp_set_non_blocking(NETSOCKET sock);
 
-/*****
-	Function: net_tcp4_set_non_blocking
+/*
+	Function: net_tcp_set_non_blocking
 
 	DOCTODO: serp
-*****/
-int net_tcp4_set_blocking(NETSOCKET sock);
+*/
+int net_tcp_set_blocking(NETSOCKET sock);
 
-/*****
-	Function: net_tcp4_listen
+/*
+	Function: net_tcp_listen
 
 	DOCTODO: serp 
-*****/
-int net_tcp4_listen(NETSOCKET sock, int backlog);
+*/
+int net_tcp_listen(NETSOCKET sock, int backlog);
 
-/*****
-	Function: net_tcp4_accept
+/*
+	Function: net_tcp_accept
 	
 	DOCTODO: serp
-*****/
-int net_tcp4_accept(NETSOCKET sock, NETSOCKET *new_sock, NETADDR4 *a);
+*/
+int net_tcp_accept(NETSOCKET sock, NETSOCKET *new_sock, NETADDR *a);
 
-/*****
-	Function: net_tcp4_connect
+/*
+	Function: net_tcp_connect
 	
 	DOCTODO: serp
-*****/
-int net_tcp4_connect(NETSOCKET sock, const NETADDR4 *a);
+*/
+int net_tcp_connect(NETSOCKET sock, const NETADDR *a);
 
-/*****
-	Function: net_tcp4_connect_non_blocking
+/*
+	Function: net_tcp_connect_non_blocking
 	
 	DOCTODO: serp
-*****/
-int net_tcp4_connect_non_blocking(NETSOCKET sock, const NETADDR4 *a);
+*/
+int net_tcp_connect_non_blocking(NETSOCKET sock, const NETADDR *a);
 
-/*****
-	Function: net_tcp4_send
+/*
+	Function: net_tcp_send
 	
 	DOCTODO: serp
-*****/
-int net_tcp4_send(NETSOCKET sock, const void *data, int size);
+*/
+int net_tcp_send(NETSOCKET sock, const void *data, int size);
 
-/*****
-	Function: net_tcp4_recv
+/*
+	Function: net_tcp_recv
 	
 	DOCTODO: serp
-*****/
-int net_tcp4_recv(NETSOCKET sock, void *data, int maxsize);
+*/
+int net_tcp_recv(NETSOCKET sock, void *data, int maxsize);
 
-/*****
-	Function: net_tcp4_close
+/*
+	Function: net_tcp_close
 	
 	DOCTODO: serp
-*****/
-int net_tcp4_close(NETSOCKET sock);
+*/
+int net_tcp_close(NETSOCKET sock);
 
-/*****
+/*
 	Function: net_errno
 
 	DOCTODO: serp
-*****/
+*/
 int net_errno();
 
-/*****
+/*
 	Function: net_would_block
 
 	DOCTODO: serp
-*****/
+*/
 int net_would_block();
 
-/*****
+/*
 	Function: net_init
 
 	DOCTODO: serp
-*****/
+*/
 int net_init();
 
+/* Group: Strings */
 
+/*
+	Function: str_append
+		Appends a string to another.
+	
+	Parameters:
+		dst - Pointer to a buffer that contains a string.
+		src - String to append.
+		dst_size - Size of the buffer of the dst string.
+		
+	Remarks:
+		- The strings are treated as zero-termineted strings.
+		- Garantees that dst string will contain zero-termination.
+*/
+void str_append(char *dst, const char *src, int dst_size);
 
-/* NOT DOCUMENTED */
-typedef void (*fs_listdir_callback)(const char *name, int is_dir, void *user);
-int fs_listdir(const char *dir, fs_listdir_callback cb, void *user);
-int fs_storage_path(const char *appname, char *path, int max);
-int fs_makedir(const char *path);
-int net_addr4_cmp(const NETADDR4 *a, const NETADDR4 *b);
-int net_socket_read_wait(NETSOCKET sock, int time);
-
-void mem_debug_dump();
-int mem_allocated();
+/*
+	Function: str_copy
+		Copies a string to another.
+		
+	Parameters:
+		dst - Pointer to a buffer that shall recive the string.
+		src - String to be copied.
+		dst_size - Size of the buffer dst.
 
-void *thread_create(void (*threadfunc)(void *), void *user);
-void thread_wait(void *thread);
-void thread_destroy(void *thread);
-void thread_yield();
-unsigned time_timestamp();
+	Remarks:
+		- The strings are treated as zero-termineted strings.
+		- Garantees that dst string will contain zero-termination.
+*/
+void str_copy(char *dst, const char *src, int dst_size);
 
-void swap_endian(void *data, unsigned elem_size, unsigned num);
+/*
+	Function: str_format
+		Performs printf formating into a buffer.
+		
+	Parameters:
+		buffer - Pointer to the buffer to recive the formated string.
+		buffer_size - Size of the buffer.
+		format - printf formating string.
+		... - Parameters for the formating.
 
-/* #define cache_prefetch(addr) __builtin_prefetch(addr) */
+	Remarks:
+		- See the C manual for syntax for the printf formating string.
+		- The strings are treated as zero-termineted strings.
+		- Garantees that dst string will contain zero-termination.
+*/
+void str_format(char *buffer, int buffer_size, const char *format, ...);
 
-/*typedef unsigned char [256] pstr;
-void pstr_format(pstr *str, )*/
+/*
+	Function: str_sanitize_strong
+		Replaces all characters below 32 and above 127 with whitespace.
+	
+	Parameters:
+		str - String to sanitize.
 
-void str_append(char *dst, const char *src, int dst_size);
-void str_copy(char *dst, const char *src, int dst_size);
-void str_format(char *buffer, int buffer_size, const char *format, ...);
+	Remarks:
+		- The strings are treated as zero-termineted strings.
+*/
 void str_sanitize_strong(char *str);
+
+/*
+	Function: str_sanitize
+		Replaces all characters below 32 and above 127 with whitespace with
+		exception to \r, \n and \r.
+	
+	Parameters:
+		str - String to sanitize.
+
+	Remarks:
+		- The strings are treated as zero-termineted strings.
+*/
 void str_sanitize(char *str);
+
+/*
+	Function: str_comp_nocase
+		Compares to strings case insensitive.
+	
+	Parameters:
+		a - String to compare.
+		b - String to compare.
+	
+	Returns:	
+		<0 - String g a is lesser then string b
+		0 - String a is equal to string b
+		>0 - String a is greater then string b
+
+	Remarks:
+		- Only garanted to work with a-z/A-Z.
+		- The strings are treated as zero-termineted strings.
+*/
 int str_comp_nocase(const char *a, const char *b);
+
+/*
+	Function: str_find_nocase
+		Finds a string inside another string case insensitive.
+
+	Parameters:
+		haystack - String to search in
+		needle - String to search for
+		
+	Returns:
+		A pointer into haystack where the needle was found.
+		Returns NULL of needle could not be found.
+
+	Remarks:
+		- Only garanted to work with a-z/A-Z.
+		- The strings are treated as zero-termineted strings.
+*/
 const char *str_find_nocase(const char *haystack, const char *needle);
+
+
+/*
+	Function: str_hex
+		Takes a datablock and generates a hexstring of it.
+
+	Parameters:
+		dst - Buffer to fill with hex data
+		dst_size - size of the buffer
+		data - Data to turn into hex
+		data - Size of the data
+
+	Remarks:
+		- The desination buffer will be zero-terminated
+*/
 void str_hex(char *dst, int dst_size, const void *data, int data_size);
 
+
+/*
+	Function: fs_listdir
+		Lists the files in a directory
+		
+	Parameters:
+		dir - Directory to list
+		cb - Callback function to call for each entry
+		user - Pointer to give to the callback
+	
+	Returns:
+		DOCTODO
+*/
+typedef void (*fs_listdir_callback)(const char *name, int is_dir, void *user);
+int fs_listdir(const char *dir, fs_listdir_callback cb, void *user);
+
+/*
+	Function: fs_makedir
+		Creates a directory
+	
+	Parameters:
+		path - Directory to create
+	
+	Returns:
+		DOCTODO
+	
+	Remarks:
+		Does not create several directories if needed. "a/b/c" will result
+		in a failure if b or a does not exist.
+*/
+int fs_makedir(const char *path);
+
+/*
+	Function: fs_storage_path
+		Fetches per user configuration directory.
+	
+	Returns:
+		DOCTODO
+	
+	Remarks:
+		- Returns ~/.appname on UNIX based systems
+		- Returns %APPDATA%/Appname on Windows based systems
+*/
+int fs_storage_path(const char *appname, char *path, int max);
+
+
+/*
+	Group: Undocumented
+*/
+
+int net_addr_comp(const NETADDR *a, const NETADDR *b);
+int net_addr_str(const NETADDR *addr, char *string, int max_length);
+int net_socket_read_wait(NETSOCKET sock, int time);
+
+void mem_debug_dump();
+int mem_allocated();
+
+void swap_endian(void *data, unsigned elem_size, unsigned num);
+
 typedef void (*DBG_LOGGER)(const char *line);
 void dbg_logger(DBG_LOGGER logger);
 void dbg_logger_stdout();
 void dbg_logger_debugger();
 void dbg_logger_file(const char *filename);
 
-IOHANDLE io_stdin();
-IOHANDLE io_stdout();
-IOHANDLE io_stderr();
-
 typedef struct
 {
 	int sent_packets;
@@ -561,6 +793,7 @@ typedef struct
 	int recv_bytes;
 } NETSTATS;
 
+
 void net_stats(NETSTATS *stats);
 
 #ifdef __cplusplus
diff --git a/src/engine/server/es_register.c b/src/engine/server/es_register.c
index cec0fa19..a85900aa 100644
--- a/src/engine/server/es_register.c
+++ b/src/engine/server/es_register.c
@@ -29,7 +29,7 @@ static void register_new_state(int state)
 	register_state_start = time_get();
 }
 
-static void register_send_fwcheckresponse(NETADDR4 *addr)
+static void register_send_fwcheckresponse(NETADDR *addr)
 {
 	NETCHUNK packet;
 	packet.client_id = -1;
@@ -40,7 +40,7 @@ static void register_send_fwcheckresponse(NETADDR4 *addr)
 	netserver_send(net, &packet);
 }
 	
-static void register_send_heartbeat(NETADDR4 addr)
+static void register_send_heartbeat(NETADDR addr)
 {
 	static unsigned char data[sizeof(SERVERBROWSE_HEARTBEAT) + 2];
 	unsigned short port = config.sv_port;
@@ -62,7 +62,7 @@ static void register_send_heartbeat(NETADDR4 addr)
 	netserver_send(net, &packet);
 }
 
-static void register_send_count_request(NETADDR4 addr)
+static void register_send_count_request(NETADDR addr)
 {
 	NETCHUNK packet;
 	packet.client_id = -1;
@@ -75,13 +75,13 @@ static void register_send_count_request(NETADDR4 addr)
 
 typedef struct
 {
-	NETADDR4 addr;
+	NETADDR addr;
 	int count;
 	int valid;
 	int64 last_send;
 } MASTERSERVER_INFO;
 
-static MASTERSERVER_INFO masterserver_info[MAX_MASTERSERVERS] = {{{{0}}}};
+static MASTERSERVER_INFO masterserver_info[MAX_MASTERSERVERS] = {{{0}}};
 static int register_registered_server = -1;
 
 void register_update()
@@ -111,7 +111,7 @@ void register_update()
 			int i;
 			for(i = 0; i < MAX_MASTERSERVERS; i++)
 			{
-				NETADDR4 addr = mastersrv_get(i);
+				NETADDR addr = mastersrv_get(i);
 				masterserver_info[i].addr = addr;
 				masterserver_info[i].count = 0;
 				
@@ -229,7 +229,7 @@ static void register_got_count(NETCHUNK *p)
 
 	for(i = 0; i < MAX_MASTERSERVERS; i++)
 	{
-		if(net_addr4_cmp(&masterserver_info[i].addr, &p->address) == 0)
+		if(net_addr_comp(&masterserver_info[i].addr, &p->address) == 0)
 		{
 			masterserver_info[i].count = count;
 			break;
diff --git a/src/engine/server/es_server.c b/src/engine/server/es_server.c
index ac1a79a1..72da4f5d 100644
--- a/src/engine/server/es_server.c
+++ b/src/engine/server/es_server.c
@@ -147,8 +147,8 @@ int snap_new_id()
 {
 	int id;
 	int64 now = time_get();
-	dbg_assert(snap_id_inited == 1, "requesting id too soon");
-	
+	if(!snap_id_inited)
+		snap_init_id();
 	
 	/* process timed ids */
 	while(snap_first_timed_id != -1 && snap_ids[snap_first_timed_id].timeout < now)
@@ -433,7 +433,7 @@ static void server_do_snap()
 					break;
 				}
 			}
-			
+	
 			/* create delta */
 			{
 				static PERFORMACE_INFO scope = {"delta", 0};
@@ -453,16 +453,18 @@ static void server_do_snap()
 
 				{				
 					static PERFORMACE_INFO scope = {"compress", 0};
+					/*char buffer[512];*/
 					perf_start(&scope);
 					snapshot_size = intpack_compress(deltadata, deltasize, compdata);
+					
+					/*str_hex(buffer, sizeof(buffer), compdata, snapshot_size);
+					dbg_msg("", "deltasize=%d -> %d : %s", deltasize, snapshot_size, buffer);*/
+					
 					perf_end();
 				}
-				
 
 				numpackets = (snapshot_size+max_size-1)/max_size;
 				
-				
-				
 				for(n = 0, left = snapshot_size; left; n++)
 				{
 					int chunk = left < max_size ? left : max_size;
@@ -776,7 +778,7 @@ static void server_process_client_packet(NETCHUNK *packet)
 	}
 }
 
-static void server_send_serverinfo(NETADDR4 *addr, int lan)
+static void server_send_serverinfo(NETADDR *addr, int lan)
 {
 	NETCHUNK packet;
 	PACKER p;
@@ -904,10 +906,9 @@ static int server_load_map(const char *mapname)
 
 static int server_run()
 {
-	NETADDR4 bindaddr;
+	NETADDR bindaddr;
 
 	net_init();
-	snap_init_id();
 	
 	/* */
 	console_register_print_callback(server_send_rcon_line_authed);
@@ -920,9 +921,11 @@ static int server_run()
 	}
 	
 	/* start server */
-	if(config.sv_bindaddr[0] && net_host_lookup(config.sv_bindaddr, config.sv_port, &bindaddr) == 0)
+	/* TODO: IPv6 support */
+	if(config.sv_bindaddr[0] && net_host_lookup(config.sv_bindaddr, &bindaddr, NETTYPE_IPV4) == 0)
 	{
 		/* sweet! */
+		bindaddr.port = config.sv_port;
 	}
 	else
 	{
@@ -1108,7 +1111,7 @@ static void con_kick(void *result, void *user_data)
 static void con_status(void *result, void *user_data)
 {
 	int i;
-	NETADDR4 addr;
+	NETADDR addr;
 	for(i = 0; i < MAX_CLIENTS; i++)
 	{
 		if(clients[i].state == SRVCLIENT_STATE_INGAME)
diff --git a/src/game/client/gc_client.cpp b/src/game/client/gc_client.cpp
index c42c354e..f0195a47 100644
--- a/src/game/client/gc_client.cpp
+++ b/src/game/client/gc_client.cpp
@@ -880,7 +880,7 @@ void render_game()
 			// don't use predicted
 		}
 		else
-			local_character_pos = mix(predicted_prev_player.pos, predicted_player.pos, client_predintratick());
+			local_character_pos = mix(predicted_prev_char.pos, predicted_char.pos, client_predintratick());
 	}
 	else if(netobjects.local_character && netobjects.local_prev_character)
 	{
@@ -1663,3 +1663,8 @@ void render_game()
 	
 }
 
+
+extern "C" const char *modc_getitemname(int type)
+{
+	return netobj_get_name(type);
+}
diff --git a/src/game/client/gc_client.hpp b/src/game/client/gc_client.hpp
index 750154b3..f19766bf 100644
--- a/src/game/client/gc_client.hpp
+++ b/src/game/client/gc_client.hpp
@@ -1,5 +1,5 @@
 #include <game/g_vmath.hpp>
-#include <game/g_protocol.hpp>
+#include <game/generated/g_protocol.hpp>
 #include <game/g_game.hpp>
 
 #include <game/client/gc_render.hpp>
@@ -22,8 +22,8 @@ extern vec2 local_target_pos;
 // snap pointers
 struct SNAPSTATE
 {
-	const NETOBJ_PLAYER_CHARACTER *local_character;
-	const NETOBJ_PLAYER_CHARACTER *local_prev_character;
+	const NETOBJ_CHARACTER *local_character;
+	const NETOBJ_CHARACTER *local_prev_character;
 	const NETOBJ_PLAYER_INFO *local_info;
 	const NETOBJ_FLAG *flags[2];
 	const NETOBJ_GAME *gameobj;
@@ -46,8 +46,8 @@ extern const NETOBJ_GAME *gameobj;
 extern TUNING_PARAMS tuning;
 
 // predicted players
-extern PLAYER_CORE predicted_prev_player;
-extern PLAYER_CORE predicted_player;
+extern CHARACTER_CORE predicted_prev_char;
+extern CHARACTER_CORE predicted_char;
 
 // input
 extern NETOBJ_PLAYER_INPUT input_data;
@@ -148,7 +148,7 @@ struct CLIENT_DATA
 	int team;
 	int emoticon;
 	int emoticon_start;
-	PLAYER_CORE predicted;
+	CHARACTER_CORE predicted;
 	
 	TEE_RENDER_INFO skin_info; // this is what the server reports
 	TEE_RENDER_INFO render_info; // this is what we use
diff --git a/src/game/client/gc_hooks.cpp b/src/game/client/gc_hooks.cpp
index 2aaca900..5d9570fb 100644
--- a/src/game/client/gc_hooks.cpp
+++ b/src/game/client/gc_hooks.cpp
@@ -56,6 +56,9 @@ static void load_sounds_thread(void *do_render)
 
 extern "C" void modc_init()
 {
+	for(int i = 0; i < NUM_NETOBJTYPES; i++)
+		snap_set_staticsize(i, netobj_get_size(i));
+	
 	static FONT_SET default_font;
 	int64 start = time_get();
 	
@@ -128,17 +131,17 @@ extern "C" void modc_shutdown()
 }
 
 
-PLAYER_CORE predicted_prev_player;
-PLAYER_CORE predicted_player;
+CHARACTER_CORE predicted_prev_char;
+CHARACTER_CORE predicted_char;
 static int predicted_tick = 0;
 static int last_new_predicted_tick = -1;
 
 extern "C" void modc_predict()
 {
-	PLAYER_CORE before_prev_player = predicted_prev_player;
-	PLAYER_CORE before_player = predicted_player;
+	CHARACTER_CORE before_prev_char = predicted_prev_char;
+	CHARACTER_CORE before_char = predicted_char;
 
-	// repredict player
+	// repredict character
 	WORLD_CORE world;
 	world.tuning = tuning;
 	int local_cid = -1;
@@ -150,11 +153,11 @@ extern "C" void modc_predict()
 		const void *data = snap_get_item(SNAP_CURRENT, i, &item);
 		int client_id = item.id;
 
-		if(item.type == NETOBJTYPE_PLAYER_CHARACTER)
+		if(item.type == NETOBJTYPE_CHARACTER)
 		{
-			const NETOBJ_PLAYER_CHARACTER *character = (const NETOBJ_PLAYER_CHARACTER *)data;
+			const NETOBJ_CHARACTER *character = (const NETOBJ_CHARACTER *)data;
 			client_datas[client_id].predicted.world = &world;
-			world.players[client_id] = &client_datas[client_id].predicted;
+			world.characters[client_id] = &client_datas[client_id].predicted;
 
 			client_datas[client_id].predicted.read(character);
 		}
@@ -174,45 +177,45 @@ extern "C" void modc_predict()
 	for(int tick = client_tick()+1; tick <= client_predtick(); tick++)
 	{
 		// fetch the local
-		if(tick == client_predtick() && world.players[local_cid])
-			predicted_prev_player = *world.players[local_cid];
+		if(tick == client_predtick() && world.characters[local_cid])
+			predicted_prev_char = *world.characters[local_cid];
 		
 		// first calculate where everyone should move
 		for(int c = 0; c < MAX_CLIENTS; c++)
 		{
-			if(!world.players[c])
+			if(!world.characters[c])
 				continue;
 
-			mem_zero(&world.players[c]->input, sizeof(world.players[c]->input));
+			mem_zero(&world.characters[c]->input, sizeof(world.characters[c]->input));
 			if(local_cid == c)
 			{
 				// apply player input
 				int *input = client_get_input(tick);
 				if(input)
-					world.players[c]->input = *((NETOBJ_PLAYER_INPUT*)input);
+					world.characters[c]->input = *((NETOBJ_PLAYER_INPUT*)input);
 			}
 
-			world.players[c]->tick();
+			world.characters[c]->tick();
 		}
 
 		// move all players and quantize their data
 		for(int c = 0; c < MAX_CLIENTS; c++)
 		{
-			if(!world.players[c])
+			if(!world.characters[c])
 				continue;
 
-			world.players[c]->move();
-			world.players[c]->quantize();
+			world.characters[c]->move();
+			world.characters[c]->quantize();
 		}
 		
 		if(tick > last_new_predicted_tick)
 		{
 			last_new_predicted_tick = tick;
 			
-			if(local_cid != -1 && world.players[local_cid])
+			if(local_cid != -1 && world.characters[local_cid])
 			{
-				vec2 pos = world.players[local_cid]->pos;
-				int events = world.players[local_cid]->triggered_events;
+				vec2 pos = world.characters[local_cid]->pos;
+				int events = world.characters[local_cid]->triggered_events;
 				if(events&COREEVENT_GROUND_JUMP) snd_play_random(CHN_WORLD, SOUND_PLAYER_JUMP, 1.0f, pos);
 				if(events&COREEVENT_AIR_JUMP)
 				{
@@ -232,26 +235,26 @@ extern "C" void modc_predict()
 				(int)world.players[c]->vel.x, (int)world.players[c]->vel.y);*/
 		}
 		
-		if(tick == client_predtick() && world.players[local_cid])
-			predicted_player = *world.players[local_cid];
+		if(tick == client_predtick() && world.characters[local_cid])
+			predicted_char = *world.characters[local_cid];
 	}
 	
 	if(config.debug && predicted_tick == client_predtick())
 	{
-		if(predicted_player.pos.x != before_player.pos.x ||
-			predicted_player.pos.y != before_player.pos.y)
+		if(predicted_char.pos.x != before_char.pos.x ||
+			predicted_char.pos.y != before_char.pos.y)
 		{
 			dbg_msg("client", "prediction error, (%d %d) (%d %d)", 
-				(int)before_player.pos.x, (int)before_player.pos.y,
-				(int)predicted_player.pos.x, (int)predicted_player.pos.y);
+				(int)before_char.pos.x, (int)before_char.pos.y,
+				(int)predicted_char.pos.x, (int)predicted_char.pos.y);
 		}
 
-		if(predicted_prev_player.pos.x != before_prev_player.pos.x ||
-			predicted_prev_player.pos.y != before_prev_player.pos.y)
+		if(predicted_prev_char.pos.x != before_prev_char.pos.x ||
+			predicted_prev_char.pos.y != before_prev_char.pos.y)
 		{
 			dbg_msg("client", "prediction error, prev (%d %d) (%d %d)", 
-				(int)before_prev_player.pos.x, (int)before_prev_player.pos.y,
-				(int)predicted_prev_player.pos.x, (int)predicted_prev_player.pos.y);
+				(int)before_prev_char.pos.x, (int)before_prev_char.pos.y,
+				(int)predicted_prev_char.pos.x, (int)predicted_prev_char.pos.y);
 		}
 	}
 	
@@ -316,15 +319,15 @@ extern "C" void modc_newsnapshot()
 				if(info->local)
 				{
 					netobjects.local_info = info;
-					const void *data = snap_find_item(SNAP_CURRENT, NETOBJTYPE_PLAYER_CHARACTER, item.id);
+					const void *data = snap_find_item(SNAP_CURRENT, NETOBJTYPE_CHARACTER, item.id);
 					if(data)
 					{
-						netobjects.local_character = (const NETOBJ_PLAYER_CHARACTER *)data;
+						netobjects.local_character = (const NETOBJ_CHARACTER *)data;
 						local_character_pos = vec2(netobjects.local_character->x, netobjects.local_character->y);
 
-						const void *p = snap_find_item(SNAP_PREV, NETOBJTYPE_PLAYER_CHARACTER, item.id);
+						const void *p = snap_find_item(SNAP_PREV, NETOBJTYPE_CHARACTER, item.id);
 						if(p)
-							netobjects.local_prev_character = (NETOBJ_PLAYER_CHARACTER *)p;
+							netobjects.local_prev_character = (NETOBJ_CHARACTER *)p;
 					}
 				}
 			}
diff --git a/src/game/client/gc_menu.cpp b/src/game/client/gc_menu.cpp
index cb8324a7..3a1e9cee 100644
--- a/src/game/client/gc_menu.cpp
+++ b/src/game/client/gc_menu.cpp
@@ -15,7 +15,7 @@ extern "C" {
 }
 
 #include "../g_version.hpp"
-#include "../g_protocol.hpp"
+#include <game/generated/g_protocol.hpp>
 
 #include "../generated/gc_data.hpp"
 #include "gc_render.hpp"
diff --git a/src/game/client/gc_render.cpp b/src/game/client/gc_render.cpp
index 2e5a3d50..afb827fb 100644
--- a/src/game/client/gc_render.cpp
+++ b/src/game/client/gc_render.cpp
@@ -3,7 +3,7 @@
 #include <engine/e_client_interface.h>
 #include <engine/e_config.h>
 #include <game/generated/gc_data.hpp>
-#include <game/g_protocol.hpp>
+#include <game/generated/g_protocol.hpp>
 #include <game/g_math.hpp>
 #include <game/g_layers.hpp>
 #include "gc_render.hpp"
@@ -473,7 +473,7 @@ static void render_players()
 		SNAP_ITEM item;
 		const void *data = snap_get_item(SNAP_CURRENT, i, &item);
 
-		if(item.type == NETOBJTYPE_PLAYER_CHARACTER)
+		if(item.type == NETOBJTYPE_CHARACTER)
 		{
 			const void *prev = snap_find_item(SNAP_PREV, item.type, item.id);
 			const void *prev_info = snap_find_item(SNAP_PREV, NETOBJTYPE_PLAYER_INFO, item.id);
@@ -482,8 +482,8 @@ static void render_players()
 			if(prev && prev_info && info)
 			{
 				render_player(
-						(const NETOBJ_PLAYER_CHARACTER *)prev,
-						(const NETOBJ_PLAYER_CHARACTER *)data,
+						(const NETOBJ_CHARACTER *)prev,
+						(const NETOBJ_CHARACTER *)data,
 						(const NETOBJ_PLAYER_INFO *)prev_info,
 						(const NETOBJ_PLAYER_INFO *)info
 					);
diff --git a/src/game/client/gc_render.hpp b/src/game/client/gc_render.hpp
index 02bc224d..d819c194 100644
--- a/src/game/client/gc_render.hpp
+++ b/src/game/client/gc_render.hpp
@@ -66,7 +66,7 @@ void render_pickup(const struct NETOBJ_PICKUP *prev, const struct NETOBJ_PICKUP
 void render_projectile(const struct NETOBJ_PROJECTILE *current, int itemid);
 void render_laser(const struct NETOBJ_LASER *current);
 void render_player(
-	const struct NETOBJ_PLAYER_CHARACTER *prev_char, const struct NETOBJ_PLAYER_CHARACTER *player_char,
+	const struct NETOBJ_CHARACTER *prev_char, const struct NETOBJ_CHARACTER *player_char,
 	const struct NETOBJ_PLAYER_INFO *prev_info, const struct NETOBJ_PLAYER_INFO *player_info);
 	
 // map render methods (gc_render_map.cpp)
diff --git a/src/game/client/gc_render_obj.cpp b/src/game/client/gc_render_obj.cpp
index 119db4d6..daba5ceb 100644
--- a/src/game/client/gc_render_obj.cpp
+++ b/src/game/client/gc_render_obj.cpp
@@ -3,8 +3,8 @@
 #include <stdio.h>
 #include <engine/e_client_interface.h>
 #include <engine/e_config.h>
-#include "../generated/gc_data.hpp"
-#include "../g_protocol.hpp"
+#include <game/generated/gc_data.hpp>
+#include <game/generated/g_protocol.hpp>
 #include "../g_math.hpp"
 #include "gc_render.hpp"
 #include "gc_anim.hpp"
@@ -265,14 +265,14 @@ static void render_hand(TEE_RENDER_INFO *info, vec2 center_pos, vec2 dir, float
 }
 
 void render_player(
-	const NETOBJ_PLAYER_CHARACTER *prev_char,
-	const NETOBJ_PLAYER_CHARACTER *player_char,
+	const NETOBJ_CHARACTER *prev_char,
+	const NETOBJ_CHARACTER *player_char,
 	const NETOBJ_PLAYER_INFO *prev_info,
 	const NETOBJ_PLAYER_INFO *player_info
 	)
 {
-	NETOBJ_PLAYER_CHARACTER prev;
-	NETOBJ_PLAYER_CHARACTER player;
+	NETOBJ_CHARACTER prev;
+	NETOBJ_CHARACTER player;
 	prev = *prev_char;
 	player = *player_char;
 
@@ -330,8 +330,8 @@ void render_player(
 		else
 		{
 			// apply predicted results
-			predicted_player.write(&player);
-			predicted_prev_player.write(&prev);
+			predicted_char.write(&player);
+			predicted_prev_char.write(&prev);
 			intratick = client_predintratick();
 		}
 	}
@@ -403,8 +403,8 @@ void render_player(
 		{
 			if(netobjects.local_info && player_char->hooked_player == netobjects.local_info->cid)
 			{
-				hook_pos = mix(vec2(predicted_prev_player.pos.x, predicted_prev_player.pos.y),
-					vec2(predicted_player.pos.x, predicted_player.pos.y), client_predintratick());
+				hook_pos = mix(vec2(predicted_prev_char.pos.x, predicted_prev_char.pos.y),
+					vec2(predicted_char.pos.x, predicted_char.pos.y), client_predintratick());
 			}
 			else
 				hook_pos = mix(vec2(prev_char->hook_x, prev_char->hook_y), vec2(player_char->hook_x, player_char->hook_y), client_intratick());
@@ -541,7 +541,6 @@ void render_player(
 					vec2 diry(-dir.y,dir.x);
 					vec2 muzzlepos = p + dir * data->weapons.id[iw].muzzleoffsetx + diry * offsety;
 
-					dbg_msg("", "%d", data->weapons.id[iw].num_sprite_muzzles);
 					draw_sprite(muzzlepos.x, muzzlepos.y, data->weapons.id[iw].visual_size);
 				}
 			}
diff --git a/src/game/g_game.cpp b/src/game/g_game.cpp
index 6545a75d..2f14b25f 100644
--- a/src/game/g_game.cpp
+++ b/src/game/g_game.cpp
@@ -166,7 +166,7 @@ float velocity_ramp(float value, float start, float range, float curvature)
 	return 1.0f/pow(curvature, (value-start)/range);
 }
 
-void PLAYER_CORE::reset()
+void CHARACTER_CORE::reset()
 {
 	pos = vec2(0,0);
 	vel = vec2(0,0);
@@ -179,7 +179,7 @@ void PLAYER_CORE::reset()
 	triggered_events = 0;
 }
 
-void PLAYER_CORE::tick()
+void CHARACTER_CORE::tick()
 {
 	float phys_size = 28.0f;
 	triggered_events = 0;
@@ -273,7 +273,7 @@ void PLAYER_CORE::tick()
 			// Check against other players first
 			for(int i = 0; i < MAX_CLIENTS; i++)
 			{
-				PLAYER_CORE *p = world->players[i];
+				CHARACTER_CORE *p = world->characters[i];
 				if(!p || p == this)
 					continue;
 
@@ -312,7 +312,7 @@ void PLAYER_CORE::tick()
 	{
 		if(hooked_player != -1)
 		{
-			PLAYER_CORE *p = world->players[hooked_player];
+			CHARACTER_CORE *p = world->characters[hooked_player];
 			if(p)
 				hook_pos = p->pos;
 			else
@@ -366,7 +366,7 @@ void PLAYER_CORE::tick()
 	{
 		for(int i = 0; i < MAX_CLIENTS; i++)
 		{
-			PLAYER_CORE *p = world->players[i];
+			CHARACTER_CORE *p = world->characters[i];
 			if(!p)
 				continue;
 			
@@ -414,7 +414,7 @@ void PLAYER_CORE::tick()
 		vel = normalize(vel) * 6000;
 }
 
-void PLAYER_CORE::move()
+void CHARACTER_CORE::move()
 {
 	float rampvalue = velocity_ramp(length(vel)*50, world->tuning.velramp_start, world->tuning.velramp_range, world->tuning.velramp_curvature);
 	
@@ -423,7 +423,7 @@ void PLAYER_CORE::move()
 	vel.x = vel.x*(1.0f/rampvalue);
 }
 
-void PLAYER_CORE::write(NETOBJ_PLAYER_CORE *obj_core)
+void CHARACTER_CORE::write(NETOBJ_CHARACTER_CORE *obj_core)
 {
 	obj_core->x = (int)pos.x;
 	obj_core->y = (int)pos.y;
@@ -450,7 +450,7 @@ void PLAYER_CORE::write(NETOBJ_PLAYER_CORE *obj_core)
 	obj_core->angle = (int)(a*256.0f);
 }
 
-void PLAYER_CORE::read(const NETOBJ_PLAYER_CORE *obj_core)
+void CHARACTER_CORE::read(const NETOBJ_CHARACTER_CORE *obj_core)
 {
 	pos.x = obj_core->x;
 	pos.y = obj_core->y;
@@ -466,9 +466,9 @@ void PLAYER_CORE::read(const NETOBJ_PLAYER_CORE *obj_core)
 	jumped = obj_core->jumped;
 }
 
-void PLAYER_CORE::quantize()
+void CHARACTER_CORE::quantize()
 {
-	NETOBJ_PLAYER_CORE c;
+	NETOBJ_CHARACTER_CORE c;
 	write(&c);
 	read(&c);
 }
diff --git a/src/game/g_game.hpp b/src/game/g_game.hpp
index 7ab8df40..61342d76 100644
--- a/src/game/g_game.hpp
+++ b/src/game/g_game.hpp
@@ -7,7 +7,7 @@
 #include <math.h>
 #include "g_math.hpp"
 #include "g_collision.hpp"
-#include "g_protocol.hpp"
+#include <game/generated/g_protocol.hpp>
 
 struct TUNING_PARAMS
 {
@@ -114,14 +114,14 @@ class WORLD_CORE
 public:
 	WORLD_CORE()
 	{
-		mem_zero(players, sizeof(players));
+		mem_zero(characters, sizeof(characters));
 	}
 	
 	TUNING_PARAMS tuning;
-	class PLAYER_CORE *players[MAX_CLIENTS];
+	class CHARACTER_CORE *characters[MAX_CLIENTS];
 };
 
-class PLAYER_CORE
+class CHARACTER_CORE
 {
 public:
 	WORLD_CORE *world;
@@ -144,8 +144,8 @@ public:
 	void tick();
 	void move();
 	
-	void read(const NETOBJ_PLAYER_CORE *obj_core);
-	void write(NETOBJ_PLAYER_CORE *obj_core);
+	void read(const NETOBJ_CHARACTER_CORE *obj_core);
+	void write(NETOBJ_CHARACTER_CORE *obj_core);
 	void quantize();
 };
 
diff --git a/src/game/g_protocol.hpp b/src/game/g_protocol.hpp
deleted file mode 100644
index 8650b8be..00000000
--- a/src/game/g_protocol.hpp
+++ /dev/null
@@ -1,217 +0,0 @@
-/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
-// NOTE: Be very careful when editing this file as it will change the network version
-
-#ifndef GAME_PROTOCOL_H
-#define GAME_PROTOCOL_H
-
-#include <game/generated/g_protocol.hpp>
-
-// Network stuff
-/*
-enum
-{
-	OBJTYPE_NULL=0,
-	OBJTYPE_GAME,
-	OBJTYPE_PLAYER_INFO,
-	OBJTYPE_PLAYER_CHARACTER, // use this if you are searching for the player entity
-	OBJTYPE_PROJECTILE,
-	OBJTYPE_LASER,
-	OBJTYPE_POWERUP,
-	OBJTYPE_FLAG,
-	EVENT_EXPLOSION,
-	EVENT_DAMAGEINDICATION,
-	EVENT_SOUND_WORLD,
-	EVENT_SMOKE,
-	EVENT_PLAYERSPAWN,
-	EVENT_DEATH,
-	EVENT_AIR_JUMP,
-	
-	EVENT_DUMMY
-};
-
-enum
-{
-	MSG_NULL=0,
-	MSG_SAY, // client -> server
-	MSG_CHAT, // server -> client
-	MSG_SETINFO, // server -> client - contains name, skin and color info
-	MSG_KILLMSG, // server -> client
-	MSG_SETTEAM,
-	MSG_JOIN,
-	MSG_QUIT,
-	MSG_EMOTICON,
-	MSG_STARTINFO, // client -> server
-	MSG_CHANGEINFO, // client -> server
-	MSG_READY_TO_ENTER, // server -> client
-    MSG_WEAPON_PICKUP,
-    MSG_SOUND_GLOBAL,
-    MSG_TUNE_PARAMS,
-	MSG_KILL,
-    MSG_EXTRA_PROJECTILE, // server -> client
-	
-};
-
-enum
-{
-	EMOTE_NORMAL=0,
-	EMOTE_PAIN,
-	EMOTE_HAPPY,
-	EMOTE_SURPRISE,
-	EMOTE_ANGRY,
-	EMOTE_BLINK,
-};
-
-enum
-{
-	INPUT_STATE_MASK=0x1f,
-};
-
-enum
-{
-	PLAYERSTATE_UNKNOWN=0,
-	PLAYERSTATE_PLAYING,
-	PLAYERSTATE_IN_MENU,
-	PLAYERSTATE_CHATTING,
-
-	GAMETYPE_DM=0,
-	GAMETYPE_TDM,
-	GAMETYPE_CTF,
-};
-
-struct player_input
-{
-	int left;
-	int right;
-
-	int target_x;
-	int target_y;
-
-	int jump;
-	int fire;
-	int hook;
-	int blink;
-
-	int player_state;
-
-	int wanted_weapon;
-	int next_weapon;
-	int prev_weapon;
-};
-
-struct ev_common
-{
-	int x, y;
-};
-
-struct ev_explosion : public ev_common
-{
-};
-
-struct ev_spawn : public ev_common
-{
-};
-
-struct ev_death : public ev_common
-{
-};
-
-struct ev_sound : public ev_common
-{
-	int sound;
-};
-
-struct ev_damageind : public ev_common
-{
-	int angle;
-};
-
-struct obj_game
-{
-	int round_start_tick;
-	int game_over;
-	int sudden_death;
-	int paused;
-
-	int score_limit;
-	int time_limit;
-	int gametype;
-
-	int warmup;
-
-	int teamscore[2];
-};
-
-struct obj_projectile
-{
-	int x, y;
-	int vx, vy; // should be an angle instead
-	int type;
-	int start_tick;
-};
-
-struct obj_laser
-{
-	int x, y;
-	int from_x, from_y;
-	int eval_tick;
-};
-
-struct obj_powerup
-{
-	int x, y;
-	int type; // why do we need two types?
-	int subtype;
-};
-
-struct obj_flag
-{
-	int x, y;
-	int team;
-	int carried_by; // is set if the local player has the flag
-};
-
-// core object needed for physics
-struct obj_player_core
-{
-	int x, y;
-	int vx, vy;
-	int angle;
-	int jumped;
-
-	int hooked_player;
-	int hook_state;
-	int hook_tick;
-	int hook_x, hook_y;
-	int hook_dx, hook_dy;
-};
-
-// info about the player that is only needed when it's on screen
-struct obj_player_character : public obj_player_core
-{
-	int player_state;
-
-	int health;
-	int armor;
-	int ammocount;
-	int weaponstage;
-
-	int weapon; // current active weapon
-
-	int emote;
-
-	int attacktick; // num attack ticks left of current attack
-};
-
-// information about the player that is always needed
-struct obj_player_info
-{
-	int local;
-	int clientid;
-
-	int team;
-	int score;
-	int latency;
-	int latency_flux;
-};*/
-
-#endif
diff --git a/src/game/server/gs_common.hpp b/src/game/server/gs_common.hpp
index b6c809fa..ff939116 100644
--- a/src/game/server/gs_common.hpp
+++ b/src/game/server/gs_common.hpp
@@ -2,16 +2,15 @@
 #include "../g_game.hpp"
 #include "../generated/gs_data.hpp"
 
-
 extern TUNING_PARAMS tuning;
 
-void create_sound_global(int sound, int target=-1);
-
 inline int cmask_all() { return -1; }
 inline int cmask_one(int cid) { return 1<<cid; }
 inline int cmask_all_except_one(int cid) { return 0x7fffffff^cmask_one(cid); }
 inline bool cmask_is_set(int mask, int cid) { return (mask&cmask_one(cid)) != 0; }
 
+
+
 //
 class EVENT_HANDLER
 {
@@ -33,57 +32,94 @@ public:
 	void snap(int snapping_client);
 };
 
-extern EVENT_HANDLER events;
-
-// a basic entity
+/*
+	Class: Entity
+		Basic entity class.
+*/
 class ENTITY
 {
 private:
 	friend class GAMEWORLD; // thy these?
-	friend class PLAYER;
 	ENTITY *prev_entity;
 	ENTITY *next_entity;
 
 	ENTITY *prev_type_entity;
 	ENTITY *next_type_entity;
 protected:
+	bool marked_for_destroy;
 	int id;
-public:
-	float proximity_radius;
-	unsigned flags;
 	int objtype;
-	vec2 pos;
-
-	enum
-	{
-		FLAG_DESTROY=0x00000001,
-		FLAG_PHYSICS=0x00000002,
-	};
+public:
 	
 	ENTITY(int objtype);
 	virtual ~ENTITY();
 	
-	virtual void reset() {}
-	virtual void post_reset() {}
-	
-	void set_flag(unsigned flag) { flags |= flag; }
-	void clear_flag(unsigned flag) { flags &= ~flag; }
+	ENTITY *typenext() { return next_type_entity; }
+	ENTITY *typeprev() { return prev_type_entity; }
 
+	/*
+		Function: destroy
+		Destorys the entity.
+	*/
 	virtual void destroy() { delete this; }
+		
+	/*
+		Function: reset
+		Called when the game resets the map. Puts the entity
+		back to it's starting state or perhaps destroys it.
+	*/
+	virtual void reset() {}
+	
+	/*
+		Function: tick
+		Called progress the entity to the next tick. Updates
+		and moves the entity to it's new state and position.
+	*/
 	virtual void tick() {}
+
+	/*
+		Function: tick
+		Called after all entities tick() function has been called.
+	*/
 	virtual void tick_defered() {}
+	
+	/*
+		Function: snap
+		Called when a new snapshot is being generated for a specific
+		client.
 		
+		Arguments:
+			snapping_client - ID of the client which snapshot is
+				being generated. Could be -1 to create a complete
+				snapshot of everything in the game for demo
+				recording.
+	*/
 	virtual void snap(int snapping_client) {}
-		
-	virtual bool take_damage(vec2 force, int dmg, int from, int weapon) { return true; }
+
+	/*
+		Variable: proximity_radius
+		Contains the physical size of the entity.
+	*/
+	float proximity_radius;
+	
+	/*
+		Variable: pos
+		Contains the current posititon of the entity.
+	*/
+	vec2 pos;
 };
 
 
+/*
+	Class: Game World
+		Tracks all entities in the game. Propagates tick and
+		snap calls to all entities.
+*/
 class GAMEWORLD
 {
 	void reset();
 	void remove_entities();
-public:
+
 	enum
 	{
 		NUM_ENT_TYPES=10, // TODO: are more exact value perhaps? :)
@@ -92,30 +128,108 @@ public:
 	// TODO: two lists seams kinda not good, shouldn't be needed
 	ENTITY *first_entity;
 	ENTITY *first_entity_types[NUM_ENT_TYPES];
-	bool paused;
+
+public:
 	bool reset_requested;
-	
+	bool paused;
 	WORLD_CORE core;
 	
 	GAMEWORLD();
 	~GAMEWORLD();
-	int find_entities(vec2 pos, float radius, ENTITY **ents, int max);
-	int find_entities(vec2 pos, float radius, ENTITY **ents, int max, const int* types, int maxtypes);
-
-	void insert_entity(ENTITY *ent);
-	void destroy_entity(ENTITY *ent);
-	void remove_entity(ENTITY *ent);
 	
-	//
+	ENTITY *find_first() { return first_entity; }
+	ENTITY *find_first(int type);
+	
+	/*
+		Function: find_entities
+			Finds entities close to a position and returns them in a list.
+		Arguments:
+			pos - Position.
+			radius - How close the entities have to be.
+			ents - Pointer to a list that should be filled with the pointers
+				to the entities.
+			max - Number of entities that fits into the ents array.
+			type - Type of the entities to find. -1 for all types.
+		Returns:
+			Number of entities found and added to the ents array.
+	*/
+	int find_entities(vec2 pos, float radius, ENTITY **ents, int max, int type = -1);
+	
+	/*
+		Function: interserct_character
+			Finds the closest character that intersects the line.
+		Arguments:
+			pos0 - Start position
+			pos2 - End position
+			radius - How for from the line the character is allowed to be.
+			new_pos - Intersection position
+			notthis - Entity to ignore intersecting with
+		Returns:
+			Returns a pointer to the closest hit or NULL of there is no intersection.
+	*/
+	class CHARACTER *intersect_character(vec2 pos0, vec2 pos1, float radius, vec2 &new_pos, class ENTITY *notthis = 0);
+	
+	/*
+		Function: closest_character
+			Finds the closest character to a specific point.
+		Arguments:
+			pos - The center position.
+			radius - How far off the character is allowed to be
+			notthis - Entity to ignore
+		Returns:
+			Returns a pointer to the closest character or NULL if no character is close enough.
+	*/
+	class CHARACTER *closest_character(vec2 pos, float radius, ENTITY *notthis);
+
+	/*
+		Function: insert_entity
+			Adds an entity to the world.
+		Arguments:
+			entity - Entity to add
+	*/
+	void insert_entity(ENTITY *entity);
+
+	/*
+		Function: remove_entity
+			Removes an entity from the world.
+		Arguments:
+			entity - Entity to remove
+	*/
+	void remove_entity(ENTITY *entity);
+
+	/*
+		Function: destroy_entity
+			Destroys an entity in the world.
+		Arguments:
+			entity - Entity to destroy
+	*/
+	void destroy_entity(ENTITY *entity);
+	
+	/*
+		Function: snap
+			Calls snap on all the entities in the world to create
+			the snapshot.
+		Arguments:
+			snapping_client - ID of the client which snapshot
+			is being created.
+	*/
 	void snap(int snapping_client);
+	
+	/*
+		Function: tick
+			Calls tick on all the entities in the world to progress
+			the world to the next tick.
+		
+	*/
 	void tick();
 };
 
-extern GAMEWORLD *world;
-
-// game object
-// TODO: should change name of this one
-class GAMECONTROLLER : public ENTITY
+/*
+	Class: Game Controller
+		Controls the main game logic. Keeping track of team and player score,
+		winning conditions and specific game logic.
+*/
+class GAMECONTROLLER
 {
 protected:
 	void cyclemap();
@@ -145,25 +259,57 @@ public:
 	void endround();
 	
 	bool is_friendly_fire(int cid1, int cid2);
+
+	/*
 	
-	virtual bool on_entity(int index, vec2 pos);
-	
-	virtual void post_reset();
+	*/	
 	virtual void tick();
 	
-	virtual void on_player_spawn(class PLAYER *p) {}
-	virtual int on_player_death(class PLAYER *victim, class PLAYER *killer, int weapon);
+	virtual void snap(int snapping_client);
+	
+	/*
+		Function: on_entity
+			Called when the map is loaded to process an entity
+			in the map.
+		Arguments:
+			index - Entity index.
+			pos - Where the entity is located in the world.
+		Returns:
+			bool?
+	*/
+	virtual bool on_entity(int index, vec2 pos);
 	
+	/*
+		Function: on_character_spawn
+			Called when a character spawns into the game world.
+		Arguments:
+			chr - The character that was spawned.
+	*/
+	virtual void on_character_spawn(class CHARACTER *chr) {}
+	
+	/*
+		Function: on_character_death
+			Called when a character in the world dies.
+		Arguments:
+			victim - The character that died.
+			killer - The player that killed it.
+			weapon - What weapon that killed it. Can be -1 for undefined
+				weapon when switching team or player suicides.
+	*/
+	virtual int on_character_death(class CHARACTER *victim, class PLAYER *killer, int weapon);
+
 	virtual void on_player_info_change(class PLAYER *p);
+
+	/*
 	
-	virtual void snap(int snapping_client);
+	*/	
+	virtual const char *get_team_name(int team);
 	virtual int get_auto_team(int notthisid);
 	virtual bool can_join_team(int team, int notthisid);
 	int clampteam(int team);
-};
-
-extern GAMECONTROLLER *gamecontroller;
 
+	virtual void post_reset();
+};
 
 // TODO: move to seperate file
 class PICKUP : public ENTITY
@@ -221,25 +367,28 @@ class LASER : public ENTITY
 	float energy;
 	int bounces;
 	int eval_tick;
-	PLAYER *owner;
+	CHARACTER *owner;
 	
-	bool hit_player(vec2 from, vec2 to);
+	bool hit_character(vec2 from, vec2 to);
 	void do_bounce();
 	
 public:
 	
-	LASER(vec2 pos, vec2 direction, float start_energy, PLAYER *owner);
+	LASER(vec2 pos, vec2 direction, float start_energy, CHARACTER *owner);
 	
 	virtual void reset();
 	virtual void tick();
 	virtual void snap(int snapping_client);
 };
 
-// player entity
-class PLAYER : public ENTITY
+
+class CHARACTER : public ENTITY
 {
 public:
-	static const int phys_size = 28;
+	// player controlling this character
+	class PLAYER *player;
+	
+	bool alive;
 
 	// weapon info
 	ENTITY *hitobjects[10];
@@ -264,14 +413,13 @@ public:
 	int emote_type;
 	int emote_stop;
 
-	int last_action; // last tick that the player took any action ie some input
-	
-	//
-	int client_id;
+	// TODO: clean this up
 	char skin_name[64];
 	int use_custom_color;
 	int color_body;
 	int color_feet;
+	
+	int last_action; // last tick that the player took any action ie some input
 
 	// these are non-heldback inputs
 	NETOBJ_PLAYER_INPUT latest_previnput;
@@ -298,40 +446,19 @@ public:
 	} ninja;
 
 	//
-	int score;
+	//int score;
 	int team;
 	int player_state; // if the client is chatting, accessing a menu or so
-	
-	bool spawning;
-	bool dead;
-	int die_tick;
-	vec2 die_pos;
-	
-	// latency calculations
-	int latency_accum;
-	int latency_accum_min;
-	int latency_accum_max;
-	int latency_avg;
-	int latency_min;
-	int latency_max;
 
 	// the player core for the physics	
-	PLAYER_CORE core;
-	
-	//
-	int64 last_chat;
+	CHARACTER_CORE core;
 
 	//
-	PLAYER();
-	void init();
+	CHARACTER();
+	
 	virtual void reset();
 	virtual void destroy();
 		
-	void try_respawn();
-	void respawn();
-	
-	void set_team(int team);
-	
 	bool is_grounded();
 	
 	void set_weapon(int w);
@@ -341,17 +468,123 @@ public:
 	
 	int handle_weapons();
 	int handle_ninja();
-	
-	void on_direct_input(NETOBJ_PLAYER_INPUT *input);
+
+	void on_predicted_input(NETOBJ_PLAYER_INPUT *new_input);
+	void on_direct_input(NETOBJ_PLAYER_INPUT *new_input);
 	void fire_weapon();
 
+	void die(int killer, int weapon);
+
+	bool take_damage(vec2 force, int dmg, int from, int weapon);	
+
+	
+	bool spawn(PLAYER *player, vec2 pos, int team);
+	//bool init_tryspawn(int team);
+	bool remove();
+
+	static const int phys_size = 28;
+
 	virtual void tick();
 	virtual void tick_defered();
+	virtual void snap(int snaping_client);
 	
-	void die(int killer, int weapon);
+	bool increase_health(int amount);
+	bool increase_armor(int amount);
+};
+
+// player object
+class PLAYER
+{
+public:
+	PLAYER();
+
+	// TODO: clean this up
+	char skin_name[64];
+	int use_custom_color;
+	int color_body;
+	int color_feet;
 	
-	virtual bool take_damage(vec2 force, int dmg, int from, int weapon);
-	virtual void snap(int snaping_client);
+	//
+	bool spawning;
+	int client_id;
+	int team;
+	int score;
+
+	//
+	int64 last_chat;
+
+	// network latency calculations	
+	struct
+	{
+		int accum;
+		int accum_min;
+		int accum_max;
+		int avg;
+		int min;
+		int max;	
+	} latency;
+	
+	CHARACTER character;
+	
+	// this is used for snapping so we know how we can clip the view for the player
+	vec2 view_pos;
+
+	void init(int client_id);
+	
+	CHARACTER *get_character();
+	
+	void kill_character();
+
+	void try_respawn();
+	void respawn();
+	void set_team(int team);
+	
+	void tick();
+	void snap(int snaping_client);
+
+	void on_direct_input(NETOBJ_PLAYER_INPUT *new_input);
+	void on_predicted_input(NETOBJ_PLAYER_INPUT *new_input);
+	void on_disconnect();
+};
+
+class GAMECONTEXT
+{
+public:
+	GAMECONTEXT();
+	void clear();
+	
+	EVENT_HANDLER events;
+	PLAYER players[MAX_CLIENTS];
+	
+	GAMECONTROLLER *controller;
+	GAMEWORLD world;
+
+	void tick();
+	void snap(int client_id);
+
+	// helper functions
+	void create_damageind(vec2 p, float angle_mod, int amount);
+	void create_explosion(vec2 p, int owner, int weapon, bool bnodamage);
+	void create_smoke(vec2 p);
+	void create_playerspawn(vec2 p);
+	void create_death(vec2 p, int who);
+	void create_sound(vec2 pos, int sound, int mask=-1);
+	void create_sound_global(int sound, int target=-1);	
+
+	// network
+	void send_chat(int cid, int team, const char *text);
+	void send_emoticon(int cid, int emoticon);
+	void send_weapon_pickup(int cid, int weapon);
+	void send_broadcast(const char *text, int cid);
+	void send_info(int who, int to_who);
 };
 
-extern PLAYER *players;
+extern GAMECONTEXT game;
+
+enum
+{
+	CHAT_ALL=-2,
+	CHAT_SPEC=-1,
+	CHAT_RED=0,
+	CHAT_BLUE=1
+};
diff --git a/src/game/server/gs_ent_pickup.cpp b/src/game/server/gs_ent_pickup.cpp
new file mode 100644
index 00000000..304596d0
--- /dev/null
+++ b/src/game/server/gs_ent_pickup.cpp
@@ -0,0 +1,140 @@
+#include <engine/e_server_interface.h>
+#include "gs_common.hpp"
+
+//////////////////////////////////////////////////
+// powerup
+//////////////////////////////////////////////////
+PICKUP::PICKUP(int _type, int _subtype)
+: ENTITY(NETOBJTYPE_PICKUP)
+{
+	type = _type;
+	subtype = _subtype;
+	proximity_radius = phys_size;
+
+	reset();
+
+	// TODO: should this be done here?
+	game.world.insert_entity(this);
+}
+
+void PICKUP::reset()
+{
+	if (data->pickups[type].spawndelay > 0)
+		spawntick = server_tick() + server_tickspeed() * data->pickups[type].spawndelay;
+	else
+		spawntick = -1;
+}
+
+void PICKUP::tick()
+{
+	// wait for respawn
+	if(spawntick > 0)
+	{
+		if(server_tick() > spawntick)
+		{
+			// respawn
+			spawntick = -1;
+
+			if(type == POWERUP_WEAPON)
+				game.create_sound(pos, SOUND_WEAPON_SPAWN);
+		}
+		else
+			return;
+	}
+	// Check if a player intersected us
+	CHARACTER *chr = game.world.closest_character(pos, 20.0f, 0);
+	if(chr)
+	{
+		// player picked us up, is someone was hooking us, let them go
+		int respawntime = -1;
+		switch (type)
+		{
+		case POWERUP_HEALTH:
+			if(chr->increase_health(1))
+			{
+				game.create_sound(pos, SOUND_PICKUP_HEALTH);
+				respawntime = data->pickups[type].respawntime;
+			}
+			break;
+		case POWERUP_ARMOR:
+			if(chr->increase_armor(1))
+			{
+				game.create_sound(pos, SOUND_PICKUP_ARMOR);
+				respawntime = data->pickups[type].respawntime;
+			}
+			break;
+
+		case POWERUP_WEAPON:
+			if(subtype >= 0 && subtype < NUM_WEAPONS)
+			{
+				if(chr->weapons[subtype].ammo < data->weapons.id[subtype].maxammo || !chr->weapons[subtype].got)
+				{
+					chr->weapons[subtype].got = true;
+					chr->weapons[subtype].ammo = min(data->weapons.id[subtype].maxammo, chr->weapons[subtype].ammo + 10);
+					respawntime = data->pickups[type].respawntime;
+
+					// TODO: data compiler should take care of stuff like this
+					if(subtype == WEAPON_GRENADE)
+						game.create_sound(pos, SOUND_PICKUP_GRENADE);
+					else if(subtype == WEAPON_SHOTGUN)
+						game.create_sound(pos, SOUND_PICKUP_SHOTGUN);
+					else if(subtype == WEAPON_RIFLE)
+						game.create_sound(pos, SOUND_PICKUP_SHOTGUN);
+
+					if(chr->player)
+                    	game.send_weapon_pickup(chr->player->client_id, subtype);
+				}
+			}
+			break;
+		case POWERUP_NINJA:
+			{
+				// activate ninja on target player
+				chr->ninja.activationtick = server_tick();
+				chr->weapons[WEAPON_NINJA].got = true;
+				chr->last_weapon = chr->active_weapon;
+				chr->active_weapon = WEAPON_NINJA;
+				respawntime = data->pickups[type].respawntime;
+				game.create_sound(pos, SOUND_PICKUP_NINJA);
+
+				// loop through all players, setting their emotes
+				ENTITY *ents[64];
+				int num = game.world.find_entities(vec2(0, 0), 1000000, ents, 64, NETOBJTYPE_CHARACTER);
+				for (int i = 0; i < num; i++)
+				{
+					CHARACTER *c = (CHARACTER *)ents[i];
+					if (c != chr)
+					{
+						c->emote_type = EMOTE_SURPRISE;
+						c->emote_stop = server_tick() + server_tickspeed();
+					}
+				}
+
+				chr->emote_type = EMOTE_ANGRY;
+				chr->emote_stop = server_tick() + 1200 * server_tickspeed() / 1000;
+				
+				break;
+			}
+		default:
+			break;
+		};
+
+		if(respawntime >= 0)
+		{
+			dbg_msg("game", "pickup player='%d:%s' item=%d/%d",
+				chr->player->client_id, server_clientname(chr->player->client_id), type, subtype);
+			spawntick = server_tick() + server_tickspeed() * respawntime;
+		}
+	}
+}
+
+void PICKUP::snap(int snapping_client)
+{
+	if(spawntick != -1)
+		return;
+
+	NETOBJ_PICKUP *up = (NETOBJ_PICKUP *)snap_new_item(NETOBJTYPE_PICKUP, id, sizeof(NETOBJ_PICKUP));
+	up->x = (int)pos.x;
+	up->y = (int)pos.y;
+	up->type = type; // TODO: two diffrent types? what gives?
+	up->subtype = subtype;
+}
diff --git a/src/game/server/gs_ent_player.cpp b/src/game/server/gs_ent_player.cpp
new file mode 100644
index 00000000..b476a972
--- /dev/null
+++ b/src/game/server/gs_ent_player.cpp
@@ -0,0 +1,1128 @@
+#include <new>
+#include <engine/e_server_interface.h>
+#include <engine/e_config.h>
+#include "gs_common.hpp"
+
+
+struct INPUT_COUNT
+{
+	int presses;
+	int releases;
+};
+
+static INPUT_COUNT count_input(int prev, int cur)
+{
+	INPUT_COUNT c = {0,0};
+	prev &= INPUT_STATE_MASK;
+	cur &= INPUT_STATE_MASK;
+	int i = prev;
+	while(i != cur)
+	{
+		i = (i+1)&INPUT_STATE_MASK;
+		if(i&1)
+			c.presses++;
+		else
+			c.releases++;
+	}
+
+	return c;
+}
+
+// player
+CHARACTER::CHARACTER()
+: ENTITY(NETOBJTYPE_CHARACTER)
+{}
+
+void CHARACTER::reset()
+{
+	
+}
+
+bool CHARACTER::spawn(PLAYER *player, vec2 pos, int team)
+{
+	player_state = PLAYERSTATE_UNKNOWN;
+	emote_stop = -1;
+	last_action = -1;
+	active_weapon = WEAPON_GUN;
+	last_weapon = WEAPON_HAMMER;
+	queued_weapon = -1;
+	
+	//clear();
+	this->player = player;
+	this->pos = pos;
+	this->team = team;
+	
+	core.reset();
+	core.world = &game.world.core;
+	core.pos = pos;
+	game.world.core.characters[player->client_id] = &core;
+	
+	game.world.insert_entity(this);
+	alive = true;
+	return true;
+}
+
+void CHARACTER::destroy()
+{
+	game.world.core.characters[player->client_id] = 0;
+	alive = false;
+}
+
+void CHARACTER::set_weapon(int w)
+{
+	if(w == active_weapon)
+		return;
+		
+	last_weapon = active_weapon;
+	queued_weapon = -1;
+	active_weapon = w;
+	if(active_weapon < 0 || active_weapon >= NUM_WEAPONS)
+		active_weapon = 0;
+	
+	game.create_sound(pos, SOUND_WEAPON_SWITCH);
+}
+
+bool CHARACTER::is_grounded()
+{
+	if(col_check_point((int)(pos.x+phys_size/2), (int)(pos.y+phys_size/2+5)))
+		return true;
+	if(col_check_point((int)(pos.x-phys_size/2), (int)(pos.y+phys_size/2+5)))
+		return true;
+	return false;
+}
+
+
+int CHARACTER::handle_ninja()
+{
+	vec2 direction = normalize(vec2(latest_input.target_x, latest_input.target_y));
+
+	if ((server_tick() - ninja.activationtick) > (data->weapons.ninja.duration * server_tickspeed() / 1000))
+	{
+		// time's up, return
+		weapons[WEAPON_NINJA].got = false;
+		active_weapon = last_weapon;
+		if(active_weapon == WEAPON_NINJA)
+			active_weapon = WEAPON_GUN;
+		set_weapon(active_weapon);
+		return 0;
+	}
+	
+	// force ninja weapon
+	set_weapon(WEAPON_NINJA);
+
+	// Check if it should activate
+	if (count_input(latest_previnput.fire, latest_input.fire).presses && (server_tick() > ninja.currentcooldown))
+	{
+		// ok then, activate ninja
+		attack_tick = server_tick();
+		ninja.activationdir = direction;
+		ninja.currentmovetime = data->weapons.ninja.movetime * server_tickspeed() / 1000;
+		ninja.currentcooldown = data->weapons.ninja.base->firedelay * server_tickspeed() / 1000 + server_tick();
+		
+		// reset hit objects
+		numobjectshit = 0;
+
+		game.create_sound(pos, SOUND_NINJA_FIRE);
+
+		// release all hooks when ninja is activated
+		//release_hooked();
+		//release_hooks();
+	}
+
+	ninja.currentmovetime--;
+
+	if (ninja.currentmovetime == 0)
+	{
+		// reset player velocity
+		core.vel *= 0.2f;
+		//return MODIFIER_RETURNFLAGS_OVERRIDEWEAPON;
+	}
+
+	if (ninja.currentmovetime > 0)
+	{
+		// Set player velocity
+		core.vel = ninja.activationdir * data->weapons.ninja.velocity;
+		vec2 oldpos = pos;
+		move_box(&core.pos, &core.vel, vec2(phys_size, phys_size), 0.0f);
+		// reset velocity so the client doesn't predict stuff
+		core.vel = vec2(0.0f,0.0f);
+		if ((ninja.currentmovetime % 2) == 0)
+		{
+			//create_smoke(pos);
+		}
+
+		// check if we hit anything along the way
+		{
+			CHARACTER *ents[64];
+			vec2 dir = pos - oldpos;
+			float radius = phys_size * 2.0f; //length(dir * 0.5f);
+			vec2 center = oldpos + dir * 0.5f;
+			int num = game.world.find_entities(center, radius, (ENTITY**)ents, 64, NETOBJTYPE_CHARACTER);
+
+			for (int i = 0; i < num; i++)
+			{
+				// Check if entity is a player
+				if (ents[i] == this)
+					continue;
+				// make sure we haven't hit this object before
+				bool balreadyhit = false;
+				for (int j = 0; j < numobjectshit; j++)
+				{
+					if (hitobjects[j] == ents[i])
+						balreadyhit = true;
+				}
+				if (balreadyhit)
+					continue;
+
+				// check so we are sufficiently close
+				if (distance(ents[i]->pos, pos) > (phys_size * 2.0f))
+					continue;
+
+				// hit a player, give him damage and stuffs...
+				game.create_sound(ents[i]->pos, SOUND_NINJA_HIT);
+				// set his velocity to fast upward (for now)
+				if(numobjectshit < 10)
+					hitobjects[numobjectshit++] = ents[i];
+					
+				ents[i]->take_damage(vec2(0,10.0f), data->weapons.ninja.base->damage, player->client_id,WEAPON_NINJA);
+			}
+		}
+		return 0;
+	}
+
+	return 0;
+}
+
+
+void CHARACTER::do_weaponswitch()
+{
+	if(reload_timer != 0) // make sure we have reloaded
+		return;
+		
+	if(queued_weapon == -1) // check for a queued weapon
+		return;
+
+	if(weapons[WEAPON_NINJA].got) // if we have ninja, no weapon selection is possible
+		return;
+
+	// switch weapon
+	set_weapon(queued_weapon);
+}
+
+void CHARACTER::handle_weaponswitch()
+{
+	int wanted_weapon = active_weapon;
+	if(queued_weapon != -1)
+		wanted_weapon = queued_weapon;
+	
+	// select weapon
+	int next = count_input(latest_previnput.next_weapon, latest_input.next_weapon).presses;
+	int prev = count_input(latest_previnput.prev_weapon, latest_input.prev_weapon).presses;
+
+	if(next < 128) // make sure we only try sane stuff
+	{
+		while(next) // next weapon selection
+		{
+			wanted_weapon = (wanted_weapon+1)%NUM_WEAPONS;
+			if(weapons[wanted_weapon].got)
+				next--;
+		}
+	}
+
+	if(prev < 128) // make sure we only try sane stuff
+	{
+		while(prev) // prev weapon selection
+		{
+			wanted_weapon = (wanted_weapon-1)<0?NUM_WEAPONS-1:wanted_weapon-1;
+			if(weapons[wanted_weapon].got)
+				prev--;
+		}
+	}
+
+	// direct weapon selection
+	if(latest_input.wanted_weapon)
+		wanted_weapon = input.wanted_weapon-1;
+
+	// check for insane values
+	if(wanted_weapon >= 0 && wanted_weapon < NUM_WEAPONS && wanted_weapon != active_weapon && weapons[wanted_weapon].got)
+		queued_weapon = wanted_weapon;
+	
+	do_weaponswitch();
+}
+
+void CHARACTER::fire_weapon()
+{
+	if(reload_timer != 0 || active_weapon == WEAPON_NINJA)
+		return;
+		
+	do_weaponswitch();
+	
+	vec2 direction = normalize(vec2(latest_input.target_x, latest_input.target_y));
+	
+	bool fullauto = false;
+	if(active_weapon == WEAPON_GRENADE || active_weapon == WEAPON_SHOTGUN || active_weapon == WEAPON_RIFLE)
+		fullauto = true;
+
+
+	// check if we gonna fire
+	bool will_fire = false;
+	if(count_input(latest_previnput.fire, latest_input.fire).presses) will_fire = true;
+	if(fullauto && (latest_input.fire&1) && weapons[active_weapon].ammo) will_fire = true;
+	if(!will_fire)
+		return;
+		
+	// check for ammo
+	if(!weapons[active_weapon].ammo)
+	{
+		game.create_sound(pos, SOUND_WEAPON_NOAMMO);
+		return;
+	}
+	
+	vec2 projectile_startpos = pos+direction*phys_size*0.75f;
+	
+	switch(active_weapon)
+	{
+		case WEAPON_HAMMER:
+		{
+			// reset objects hit
+			numobjectshit = 0;
+			game.create_sound(pos, SOUND_HAMMER_FIRE);
+			
+			CHARACTER *ents[64];
+			int num = game.world.find_entities(pos+direction*phys_size*0.75f, phys_size*0.5f, (ENTITY**)ents, 64, NETOBJTYPE_CHARACTER);
+
+			for (int i = 0; i < num; i++)
+			{
+				CHARACTER *target = ents[i];
+				if (target == this)
+					continue;
+					
+				// hit a player, give him damage and stuffs...
+				vec2 fdir = normalize(ents[i]->pos - pos);
+
+				// set his velocity to fast upward (for now)
+				game.create_sound(pos, SOUND_HAMMER_HIT);
+				ents[i]->take_damage(vec2(0,-1.0f), data->weapons.hammer.base->damage, player->client_id, active_weapon);
+				vec2 dir;
+				if (length(target->pos - pos) > 0.0f)
+					dir = normalize(target->pos - pos);
+				else
+					dir = vec2(0,-1);
+					
+				target->core.vel += normalize(dir + vec2(0,-1.1f)) * 10.0f;
+			}
+			
+		} break;
+
+		case WEAPON_GUN:
+		{
+			PROJECTILE *proj = new PROJECTILE(WEAPON_GUN,
+				player->client_id,
+				projectile_startpos,
+				direction,
+				(int)(server_tickspeed()*tuning.gun_lifetime),
+				this,
+				1, 0, 0, -1, WEAPON_GUN);
+				
+			// pack the projectile and send it to the client directly
+			NETOBJ_PROJECTILE p;
+			proj->fill_info(&p);
+			
+			msg_pack_start(NETMSGTYPE_SV_EXTRAPROJECTILE, 0);
+			msg_pack_int(1);
+			for(unsigned i = 0; i < sizeof(NETOBJ_PROJECTILE)/sizeof(int); i++)
+				msg_pack_int(((int *)&p)[i]);
+			msg_pack_end();
+			server_send_msg(player->client_id);
+							
+			game.create_sound(pos, SOUND_GUN_FIRE);
+		} break;
+		
+		case WEAPON_SHOTGUN:
+		{
+			int shotspread = 2;
+
+			msg_pack_start(NETMSGTYPE_SV_EXTRAPROJECTILE, 0);
+			msg_pack_int(shotspread*2+1);
+			
+			for(int i = -shotspread; i <= shotspread; i++)
+			{
+				float spreading[] = {-0.185f, -0.070f, 0, 0.070f, 0.185f};
+				float a = get_angle(direction);
+				a += spreading[i+2];
+				float v = 1-(abs(i)/(float)shotspread);
+				float speed = mix((float)tuning.shotgun_speeddiff, 1.0f, v);
+				PROJECTILE *proj = new PROJECTILE(WEAPON_SHOTGUN,
+					player->client_id,
+					projectile_startpos,
+					vec2(cosf(a), sinf(a))*speed,
+					(int)(server_tickspeed()*tuning.shotgun_lifetime),
+					this,
+					1, 0, 0, -1, WEAPON_SHOTGUN);
+					
+				// pack the projectile and send it to the client directly
+				NETOBJ_PROJECTILE p;
+				proj->fill_info(&p);
+				
+				for(unsigned i = 0; i < sizeof(NETOBJ_PROJECTILE)/sizeof(int); i++)
+					msg_pack_int(((int *)&p)[i]);
+			}
+
+			msg_pack_end();
+			server_send_msg(player->client_id);					
+			
+			game.create_sound(pos, SOUND_SHOTGUN_FIRE);
+		} break;
+
+		case WEAPON_GRENADE:
+		{
+			PROJECTILE *proj = new PROJECTILE(WEAPON_GRENADE,
+				player->client_id,
+				projectile_startpos,
+				direction,
+				(int)(server_tickspeed()*tuning.grenade_lifetime),
+				this,
+				1, PROJECTILE::PROJECTILE_FLAGS_EXPLODE, 0, SOUND_GRENADE_EXPLODE, WEAPON_GRENADE);
+
+			// pack the projectile and send it to the client directly
+			NETOBJ_PROJECTILE p;
+			proj->fill_info(&p);
+			
+			msg_pack_start(NETMSGTYPE_SV_EXTRAPROJECTILE, 0);
+			msg_pack_int(1);
+			for(unsigned i = 0; i < sizeof(NETOBJ_PROJECTILE)/sizeof(int); i++)
+				msg_pack_int(((int *)&p)[i]);
+			msg_pack_end();
+			server_send_msg(player->client_id);
+
+			game.create_sound(pos, SOUND_GRENADE_FIRE);
+		} break;
+		
+		case WEAPON_RIFLE:
+		{
+			new LASER(pos, direction, tuning.laser_reach, this);
+			game.create_sound(pos, SOUND_RIFLE_FIRE);
+		} break;
+		
+	}
+
+	if(weapons[active_weapon].ammo > 0) // -1 == unlimited
+		weapons[active_weapon].ammo--;
+	attack_tick = server_tick();
+	reload_timer = data->weapons.id[active_weapon].firedelay * server_tickspeed() / 1000;
+}
+
+int CHARACTER::handle_weapons()
+{
+	vec2 direction = normalize(vec2(latest_input.target_x, latest_input.target_y));
+
+	/*
+	if(config.dbg_stress)
+	{
+		for(int i = 0; i < NUM_WEAPONS; i++)
+		{
+			weapons[i].got = true;
+			weapons[i].ammo = 10;
+		}
+
+		if(reload_timer) // twice as fast reload
+			reload_timer--;
+	} */
+
+	// check reload timer
+	if(reload_timer)
+	{
+		reload_timer--;
+		return 0;
+	}
+	
+	if (active_weapon == WEAPON_NINJA)
+	{
+		// don't update other weapons while ninja is active
+		return handle_ninja();
+	}
+
+	// fire weapon, if wanted
+	fire_weapon();
+
+	// ammo regen
+	int ammoregentime = data->weapons.id[active_weapon].ammoregentime;
+	if(ammoregentime)
+	{
+		// If equipped and not active, regen ammo?
+		if (reload_timer <= 0)
+		{
+			if (weapons[active_weapon].ammoregenstart < 0)
+				weapons[active_weapon].ammoregenstart = server_tick();
+
+			if ((server_tick() - weapons[active_weapon].ammoregenstart) >= ammoregentime * server_tickspeed() / 1000)
+			{
+				// Add some ammo
+				weapons[active_weapon].ammo = min(weapons[active_weapon].ammo + 1, 10);
+				weapons[active_weapon].ammoregenstart = -1;
+			}
+		}
+		else
+		{
+			weapons[active_weapon].ammoregenstart = -1;
+		}
+	}
+	
+	return 0;
+}
+
+void CHARACTER::on_predicted_input(NETOBJ_PLAYER_INPUT *new_input)
+{
+	// check for changes
+	if(mem_comp(&input, new_input, sizeof(NETOBJ_PLAYER_INPUT)) != 0)
+		last_action = server_tick();
+		
+	// copy new input
+	mem_copy(&input, new_input, sizeof(input));
+	num_inputs++;
+	
+	// or are not allowed to aim in the center
+	if(input.target_x == 0 && input.target_y == 0)
+		input.target_y = -1;	
+}
+
+void CHARACTER::on_direct_input(NETOBJ_PLAYER_INPUT *new_input)
+{
+	mem_copy(&latest_previnput, &latest_input, sizeof(latest_input));
+	mem_copy(&latest_input, new_input, sizeof(latest_input));
+	
+	if(num_inputs > 2 && team != -1)
+	{
+		handle_weaponswitch();
+		fire_weapon();
+	}
+}
+
+void CHARACTER::tick()
+{
+	//input = latest_input;
+	
+	// grab latest input
+	/*
+	{
+		int size = 0;
+		int *input = server_latestinput(client_id, &size);
+		if(input)
+		{
+			mem_copy(&latest_previnput, &latest_input, sizeof(latest_input));
+			mem_copy(&latest_input, input, sizeof(latest_input));
+		}
+	}*/
+	
+	// check if we have enough input
+	// this is to prevent initial weird clicks
+	/*
+	if(num_inputs < 2)
+	{
+		latest_previnput = latest_input;
+		previnput = input;
+	}*/
+	
+	//game.world.core.players[player->client_id] = &core;
+
+	// enable / disable physics
+	/*
+	if(team == -1 || dead)
+	{
+		game.world.core.players[client_id] = 0;
+		//game.world.remove_entity(this);
+	}
+	else
+	{
+		game.world.core.players[client_id] = &core;
+		//game.world._entity(this);
+	}
+
+	// spectator
+	if(team == -1)
+		return;
+
+	if(spawning)
+		try_respawn();
+
+	// TODO: rework the input to be more robust
+	if(dead)
+	{
+		if(server_tick()-die_tick >= server_tickspeed()/2 && count_input(latest_previnput.fire, latest_input.fire).presses)
+			die_tick = -1;
+		if(server_tick()-die_tick >= server_tickspeed()*5) // auto respawn after 3 sec
+			respawn();
+		//if((input.fire&1) && server_tick()-die_tick >= server_tickspeed()/2) // auto respawn after 0.5 sec
+			//respawn();
+		return;
+	}
+	* */
+
+	//player_core core;
+	//core.pos = pos;
+	//core.jumped = jumped;
+	core.input = input;
+	core.tick();
+
+	// handle weapons
+	handle_weapons();
+
+	player_state = input.player_state;
+
+	// Previnput
+	previnput = input;
+	return;
+}
+
+void CHARACTER::tick_defered()
+{
+	/*if(!dead)
+	{*/
+		vec2 start_pos = core.pos;
+		vec2 start_vel = core.vel;
+		bool stuck_before = test_box(core.pos, vec2(28.0f, 28.0f));
+		
+		core.move();
+		bool stuck_after_move = test_box(core.pos, vec2(28.0f, 28.0f));
+		core.quantize();
+		bool stuck_after_quant = test_box(core.pos, vec2(28.0f, 28.0f));
+		pos = core.pos;
+		
+		if(!stuck_before && (stuck_after_move || stuck_after_quant))
+		{
+			dbg_msg("player", "STUCK!!! %d %d %d %f %f %f %f %x %x %x %x", 
+				stuck_before,
+				stuck_after_move,
+				stuck_after_quant,
+				start_pos.x, start_pos.y,
+				start_vel.x, start_vel.y,
+				*((unsigned *)&start_pos.x), *((unsigned *)&start_pos.y),
+				*((unsigned *)&start_vel.x), *((unsigned *)&start_vel.y));
+		}
+
+		int events = core.triggered_events;
+		int mask = cmask_all_except_one(player->client_id);
+		
+		if(events&COREEVENT_GROUND_JUMP) game.create_sound(pos, SOUND_PLAYER_JUMP, mask);
+		if(events&COREEVENT_AIR_JUMP)
+		{
+			game.create_sound(pos, SOUND_PLAYER_AIRJUMP, mask);
+			NETEVENT_COMMON *c = (NETEVENT_COMMON *)game.events.create(NETEVENTTYPE_AIRJUMP, sizeof(NETEVENT_COMMON), mask);
+			if(c)
+			{
+				c->x = (int)pos.x;
+				c->y = (int)pos.y;
+			}
+		}
+		
+		//if(events&COREEVENT_HOOK_LAUNCH) snd_play_random(CHN_WORLD, SOUND_HOOK_LOOP, 1.0f, pos);
+		if(events&COREEVENT_HOOK_ATTACH_PLAYER) game.create_sound(pos, SOUND_HOOK_ATTACH_PLAYER, cmask_all());
+		if(events&COREEVENT_HOOK_ATTACH_GROUND) game.create_sound(pos, SOUND_HOOK_ATTACH_GROUND, mask);
+		//if(events&COREEVENT_HOOK_RETRACT) snd_play_random(CHN_WORLD, SOUND_PLAYER_JUMP, 1.0f, pos);
+	//}
+	
+	if(team == -1)
+	{
+		pos.x = input.target_x;
+		pos.y = input.target_y;
+	}
+}
+
+bool CHARACTER::increase_health(int amount)
+{
+	if(health >= 10)
+		return false;
+	health = clamp(health+amount, 0, 10);
+	return true;
+}
+
+bool CHARACTER::increase_armor(int amount)
+{
+	if(armor >= 10)
+		return false;
+	armor = clamp(armor+amount, 0, 10);
+	return true;
+}
+
+void CHARACTER::die(int killer, int weapon)
+{
+	/*if (dead || team == -1)
+		return;*/
+	int mode_special = game.controller->on_character_death(this, &game.players[killer], weapon);
+
+	dbg_msg("game", "kill killer='%d:%s' victim='%d:%s' weapon=%d special=%d",
+		killer, server_clientname(killer),
+		player->client_id, server_clientname(player->client_id), weapon, mode_special);
+
+	// send the kill message
+	NETMSG_SV_KILLMSG msg;
+	msg.killer = killer;
+	msg.victim = player->client_id;
+	msg.weapon = weapon;
+	msg.mode_special = mode_special;
+	msg.pack(MSGFLAG_VITAL);
+	server_send_msg(-1);
+
+	// a nice sound
+	game.create_sound(pos, SOUND_PLAYER_DIE);
+
+	// set dead state
+	// TODO: do stuff here
+	/*
+	die_pos = pos;
+	dead = true;
+	die_tick = server_tick();
+	*/
+	alive = false;
+	game.world.remove_entity(this);
+	game.create_death(pos, player->client_id);
+}
+
+bool CHARACTER::take_damage(vec2 force, int dmg, int from, int weapon)
+{
+	core.vel += force;
+	
+	if(game.controller->is_friendly_fire(player->client_id, from) && !config.sv_teamdamage)
+		return false;
+
+	// player only inflicts half damage on self
+	if(from == player->client_id)
+		dmg = max(1, dmg/2);
+
+	// CTF and TDM (TODO: check for FF)
+	//if (gameobj->gametype != GAMETYPE_DM && from >= 0 && players[from].team == team)
+		//return false;
+
+	damage_taken++;
+
+	// create healthmod indicator
+	if(server_tick() < damage_taken_tick+25)
+	{
+		// make sure that the damage indicators doesn't group together
+		game.create_damageind(pos, damage_taken*0.25f, dmg);
+	}
+	else
+	{
+		damage_taken = 0;
+		game.create_damageind(pos, 0, dmg);
+	}
+
+	if(dmg)
+	{
+		if(armor)
+		{
+			if(dmg > 1)
+			{
+				health--;
+				dmg--;
+			}
+			
+			if(dmg > armor)
+			{
+				dmg -= armor;
+				armor = 0;
+			}
+			else
+			{
+				armor -= dmg;
+				dmg = 0;
+			}
+		}
+		
+		health -= dmg;
+	}
+
+	damage_taken_tick = server_tick();
+
+	// do damage hit sound
+	if(from >= 0 && from != player->client_id)
+		game.create_sound(game.players[from].view_pos, SOUND_HIT, cmask_one(from));
+
+	// check for death
+	if(health <= 0)
+	{
+		die(from, weapon);
+
+		// set attacker's face to happy (taunt!)
+		if (from >= 0 && from != player->client_id)
+		{
+			CHARACTER *chr = &game.players[from].character;
+			chr->emote_type = EMOTE_HAPPY;
+			chr->emote_stop = server_tick() + server_tickspeed();
+		}
+
+		return false;
+	}
+
+	if (dmg > 2)
+		game.create_sound(pos, SOUND_PLAYER_PAIN_LONG);
+	else
+		game.create_sound(pos, SOUND_PLAYER_PAIN_SHORT);
+
+	emote_type = EMOTE_PAIN;
+	emote_stop = server_tick() + 500 * server_tickspeed() / 1000;
+
+	// spawn blood?
+	return true;
+}
+
+void CHARACTER::snap(int snaping_client)
+{
+	if(distance(game.players[snaping_client].view_pos, pos) > 1000.0f)
+		return;
+	
+	NETOBJ_CHARACTER *character = (NETOBJ_CHARACTER *)snap_new_item(NETOBJTYPE_CHARACTER, player->client_id, sizeof(NETOBJ_CHARACTER));
+
+	core.write(character);
+
+	// this is to make sure that players that are just standing still
+	// isn't sent. this is because the physics keep bouncing between
+	// 0-128 when just standing.
+	// TODO: fix the physics so this isn't needed
+	if(snaping_client != player->client_id && abs(character->vy) < 256.0f)
+		character->vy = 0;
+
+	if (emote_stop < server_tick())
+	{
+		emote_type = EMOTE_NORMAL;
+		emote_stop = -1;
+	}
+
+	character->emote = emote_type;
+
+	character->ammocount = 0;
+	character->health = 0;
+	character->armor = 0;
+	
+	character->weapon = active_weapon;
+	character->attacktick = attack_tick;
+
+	character->wanted_direction = input.direction;
+	/*
+	if(input.left && !input.right)
+		character->wanted_direction = -1;
+	else if(!input.left && input.right)
+		character->wanted_direction = 1;*/
+
+
+	if(player->client_id == snaping_client)
+	{
+		character->health = health;
+		character->armor = armor;
+		if(weapons[active_weapon].ammo > 0)
+			character->ammocount = weapons[active_weapon].ammo;
+	}
+
+	if (character->emote == EMOTE_NORMAL)
+	{
+		if(250 - ((server_tick() - last_action)%(250)) < 5)
+			character->emote = EMOTE_BLINK;
+	}
+
+	character->player_state = player_state;
+}
+
+PLAYER::PLAYER()
+{
+}
+
+void PLAYER::init(int client_id)
+{
+	// clear everything
+	mem_zero(this, sizeof(*this));
+	new(this) PLAYER();
+	this->client_id = client_id;
+}
+
+void PLAYER::tick()
+{
+	server_setclientscore(client_id, score);
+
+	// do latency stuff
+	{
+		CLIENT_INFO info;
+		if(server_getclientinfo(client_id, &info))
+		{
+			latency.accum += info.latency;
+			latency.accum_max = max(latency.accum_max, info.latency);
+			latency.accum_min = min(latency.accum_min, info.latency);
+		}
+
+		if(server_tick()%server_tickspeed() == 0)
+		{
+			latency.avg = latency.accum/server_tickspeed();
+			latency.max = latency.accum_max;
+			latency.min = latency.accum_min;
+			latency.accum = 0;
+			latency.accum_min = 1000;
+			latency.accum_max = 0;
+		}
+	}
+	
+	if(spawning && !get_character())
+		try_respawn();
+		
+	if(get_character())
+		view_pos = get_character()->pos;
+}
+
+void PLAYER::snap(int snaping_client)
+{
+	NETOBJ_PLAYER_INFO *info = (NETOBJ_PLAYER_INFO *)snap_new_item(NETOBJTYPE_PLAYER_INFO, client_id, sizeof(NETOBJ_PLAYER_INFO));
+
+	info->latency = latency.min;
+	info->latency_flux = latency.max-latency.min;
+	info->local = 0;
+	info->cid = client_id;
+	info->score = score;
+	info->team = team;
+
+	if(client_id == snaping_client)
+		info->local = 1;	
+}
+
+void PLAYER::on_disconnect()
+{
+	kill_character();
+	
+	//game.controller->on_player_death(&game.players[client_id], 0, -1);
+		
+	char buf[512];
+	str_format(buf, sizeof(buf),  "%s has left the game", server_clientname(client_id));
+	game.send_chat(-1, CHAT_ALL, buf);
+
+	dbg_msg("game", "leave player='%d:%s'", client_id, server_clientname(client_id));
+
+	// clear this whole structure
+	init(-1);
+
+	/*game.world.remove_entity(&game.players[client_id]);
+	game.world.core.players[client_id] = 0x0;
+	game.players[client_id].client_id = -1;	*/
+}
+
+void PLAYER::on_predicted_input(NETOBJ_PLAYER_INPUT *new_input)
+{
+	CHARACTER *chr = get_character();
+	if(chr)
+		chr->on_predicted_input(new_input);
+}
+
+void PLAYER::on_direct_input(NETOBJ_PLAYER_INPUT *new_input)
+{
+	CHARACTER *chr = get_character();
+	if(chr)
+		chr->on_direct_input(new_input);
+
+	if(!chr && team >= 0 && (new_input->fire&1))
+	{
+		spawning = true;
+		dbg_msg("", "I wanna spawn");
+	}
+}
+
+CHARACTER *PLAYER::get_character()
+{
+	if(character.alive)
+		return &character;
+	return 0;
+}
+
+void PLAYER::kill_character()
+{
+	CHARACTER *chr = get_character();
+	if(chr)
+		chr->die(-1, -1);
+}
+
+void PLAYER::respawn()
+{
+	spawning = true;
+}
+
+void PLAYER::set_team(int new_team)
+{
+	// clamp the team
+	new_team = game.controller->clampteam(new_team);
+	if(team == new_team)
+		return;
+		
+	char buf[512];
+	str_format(buf, sizeof(buf), "%s joined the %s", server_clientname(client_id), game.controller->get_team_name(new_team));
+	game.send_chat(-1, CHAT_ALL, buf); 
+	
+	kill_character();
+	team = new_team;
+	score = 0;
+	dbg_msg("game", "team_join player='%d:%s' team=%d", client_id, server_clientname(client_id), team);
+	
+	game.controller->on_player_info_change(&game.players[client_id]);
+
+	// send all info to this client
+	for(int i = 0; i < MAX_CLIENTS; i++)
+	{
+		if(game.players[i].client_id != -1)
+			game.send_info(i, -1);
+	}
+}
+
+vec2 spawn_points[3][64];
+int num_spawn_points[3] = {0};
+
+struct SPAWNEVAL
+{
+	SPAWNEVAL()
+	{
+		got = false;
+		friendly_team = -1;
+//		die_pos = vec2(0,0);
+		pos = vec2(100,100);
+	}
+		
+	vec2 pos;
+	bool got;
+	int friendly_team;
+	float score;
+//	vec2 die_pos;
+};
+
+static float evaluate_spawn(SPAWNEVAL *eval, vec2 pos)
+{
+	float score = 0.0f;
+	CHARACTER *c = (CHARACTER *)game.world.find_first(NETOBJTYPE_CHARACTER);
+	for(; c; c = (CHARACTER *)c->typenext())
+	{
+		// team mates are not as dangerous as enemies
+		float scoremod = 1.0f;
+		if(eval->friendly_team != -1 && c->team == eval->friendly_team)
+			scoremod = 0.5f;
+			
+		float d = distance(pos, c->pos);
+		if(d == 0)
+			score += 1000000000.0f;
+		else
+			score += 1.0f/d;
+	}
+	
+	// weight in the die posititon
+	/*
+	float d = distance(pos, eval->die_pos);
+	if(d == 0)
+		score += 1000000000.0f;
+	else
+		score += 1.0f/d;*/
+	
+	return score;
+}
+
+static void evaluate_spawn_type(SPAWNEVAL *eval, int t)
+{
+	// get spawn point
+	/*
+	int start, num;
+	map_get_type(t, &start, &num);
+	if(!num)
+		return;
+	*/
+	for(int i  = 0; i < num_spawn_points[t]; i++)
+	{
+		//num_spawn_points[t]
+		//mapres_spawnpoint *sp = (mapres_spawnpoint*)map_get_item(start + i, NULL, NULL);
+		vec2 p = spawn_points[t][i];// vec2((float)sp->x, (float)sp->y);
+		float s = evaluate_spawn(eval, p);
+		if(!eval->got || eval->score > s)
+		{
+			eval->got = true;
+			eval->score = s;
+			eval->pos = p;
+		}
+	}
+}
+
+void PLAYER::try_respawn()
+{
+	vec2 spawnpos = vec2(100.0f, -60.0f);
+	
+	// get spawn point
+	SPAWNEVAL eval;
+	//eval.die_pos = die_pos;
+	
+	eval.pos = vec2(100, 100);
+	
+	if(game.controller->gametype == GAMETYPE_CTF)
+	{
+		eval.friendly_team = team;
+		
+		// try first try own team spawn, then normal spawn and then enemy
+		evaluate_spawn_type(&eval, 1+(team&1));
+		if(!eval.got)
+		{
+			evaluate_spawn_type(&eval, 0);
+			if(!eval.got)
+				evaluate_spawn_type(&eval, 1+((team+1)&1));
+		}
+	}
+	else
+	{
+		if(game.controller->gametype == GAMETYPE_TDM)
+			eval.friendly_team = team;
+			
+		evaluate_spawn_type(&eval, 0);
+		evaluate_spawn_type(&eval, 1);
+		evaluate_spawn_type(&eval, 2);
+	}
+	
+	spawnpos = eval.pos;
+
+	// check if the position is occupado
+	ENTITY *ents[2] = {0};
+	int num_ents = game.world.find_entities(spawnpos, 64, ents, 2, NETOBJTYPE_CHARACTER);
+	
+	if(num_ents == 0)
+	{
+		spawning = false;
+		character.spawn(this, spawnpos, team);
+	}
+	
+	/*
+	pos = spawnpos;
+
+	core.pos = pos;
+	core.vel = vec2(0,0);
+	core.hooked_player = -1;
+
+	health = 10;
+	armor = 0;
+	jumped = 0;
+	
+	mem_zero(&ninja, sizeof(ninja));
+	
+	dead = false;
+	player_state = PLAYERSTATE_PLAYING;
+	
+	game.world.insert_entity(this);
+
+	core.hook_state = HOOK_IDLE;
+
+	mem_zero(&input, sizeof(input));
+
+	// init weapons
+	mem_zero(&weapons, sizeof(weapons));
+	weapons[WEAPON_HAMMER].got = true;
+	weapons[WEAPON_HAMMER].ammo = -1;
+	weapons[WEAPON_GUN].got = true;
+	weapons[WEAPON_GUN].ammo = 10;
+
+	active_weapon = WEAPON_GUN;
+	last_weapon = WEAPON_HAMMER;
+	queued_weapon = 0;
+
+	reload_timer = 0;
+
+	// Create sound and spawn effects
+	game.create_sound(pos, SOUND_PLAYER_SPAWN);
+	game.create_playerspawn(pos);
+
+	game.controller->on_player_spawn(player);
+	*/
+}
diff --git a/src/game/server/gs_game.cpp b/src/game/server/gs_game.cpp
index 55ffc288..685d61ad 100644
--- a/src/game/server/gs_game.cpp
+++ b/src/game/server/gs_game.cpp
@@ -6,7 +6,6 @@
 #include "gs_common.hpp"
 
 GAMECONTROLLER::GAMECONTROLLER()
-: ENTITY(NETOBJTYPE_GAME)
 {
 	// select gametype
 	if(strcmp(config.sv_gametype, "ctf") == 0)
@@ -91,14 +90,32 @@ void GAMECONTROLLER::endround()
 	if(warmup) // game can't end when we are running warmup
 		return;
 		
-	world->paused = true;
+	game.world.paused = true;
 	game_over_tick = server_tick();
 	sudden_death = 0;
 }
 
 void GAMECONTROLLER::resetgame()
 {
-	world->reset_requested = true;
+	game.world.reset_requested = true;
+}
+
+const char *GAMECONTROLLER::get_team_name(int team)
+{
+	if(is_teamplay)
+	{
+		if(team == 0)
+			return "red team";
+		else if(team == 1)
+			return "blue team";
+	}
+	else
+	{
+		if(team == 0)
+			return "game";
+	}
+	
+	return "spectators";
 }
 
 static bool is_separator(char c) { return c == ';' || c == ' ' || c == ',' || c == '\t'; }
@@ -110,7 +127,7 @@ void GAMECONTROLLER::startround()
 	round_start_tick = server_tick();
 	sudden_death = 0;
 	game_over_tick = -1;
-	world->paused = false;
+	game.world.paused = false;
 	teamscore[0] = 0;
 	teamscore[1] = 0;
 	round_count++;
@@ -178,8 +195,8 @@ void GAMECONTROLLER::post_reset()
 {
 	for(int i = 0; i < MAX_CLIENTS; i++)
 	{
-		if(players[i].client_id != -1)
-			players[i].respawn();
+		if(game.players[i].client_id != -1)
+			game.players[i].respawn();
 	}
 }
 	
@@ -198,13 +215,13 @@ void GAMECONTROLLER::on_player_info_change(class PLAYER *p)
 }
 
 
-int GAMECONTROLLER::on_player_death(class PLAYER *victim, class PLAYER *killer, int weapon)
+int GAMECONTROLLER::on_character_death(class CHARACTER *victim, class PLAYER *killer, int weapon)
 {
 	// do scoreing
 	if(!killer)
 		return 0;
-	if(killer == victim)
-		victim->score--; // suicide
+	if(killer == victim->player)
+		victim->player->score--; // suicide
 	else
 	{
 		if(is_teamplay && victim->team == killer->team)
@@ -227,7 +244,7 @@ bool GAMECONTROLLER::is_friendly_fire(int cid1, int cid2)
 	
 	if(is_teamplay)
 	{
-		if(players[cid1].team == players[cid2].team)
+		if(game.players[cid1].team == game.players[cid2].team)
 			return true;
 	}
 	
@@ -271,8 +288,8 @@ void GAMECONTROLLER::tick()
 		{
 			for(int i = 0; i < MAX_CLIENTS; i++)
 			{
-				if(players[i].client_id != -1)
-					prog = max(prog, (players[i].score*100)/config.sv_scorelimit);
+				if(game.players[i].client_id != -1)
+					prog = max(prog, (game.players[i].score*100)/config.sv_scorelimit);
 			}
 		}
 	}
@@ -285,20 +302,20 @@ void GAMECONTROLLER::tick()
 
 void GAMECONTROLLER::snap(int snapping_client)
 {
-	NETOBJ_GAME *game = (NETOBJ_GAME *)snap_new_item(NETOBJTYPE_GAME, 0, sizeof(NETOBJ_GAME));
-	game->paused = world->paused;
-	game->game_over = game_over_tick==-1?0:1;
-	game->sudden_death = sudden_death;
+	NETOBJ_GAME *gameobj = (NETOBJ_GAME *)snap_new_item(NETOBJTYPE_GAME, 0, sizeof(NETOBJ_GAME));
+	gameobj->paused = game.world.paused;
+	gameobj->game_over = game_over_tick==-1?0:1;
+	gameobj->sudden_death = sudden_death;
 	
-	game->score_limit = config.sv_scorelimit;
-	game->time_limit = config.sv_timelimit;
-	game->round_start_tick = round_start_tick;
-	game->gametype = gametype;
+	gameobj->score_limit = config.sv_scorelimit;
+	gameobj->time_limit = config.sv_timelimit;
+	gameobj->round_start_tick = round_start_tick;
+	gameobj->gametype = gametype;
 	
-	game->warmup = warmup;
+	gameobj->warmup = warmup;
 	
-	game->teamscore_red = teamscore[0];
-	game->teamscore_blue = teamscore[1];
+	gameobj->teamscore_red = teamscore[0];
+	gameobj->teamscore_blue = teamscore[1];
 }
 
 int GAMECONTROLLER::get_auto_team(int notthisid)
@@ -306,10 +323,10 @@ int GAMECONTROLLER::get_auto_team(int notthisid)
 	int numplayers[2] = {0,0};
 	for(int i = 0; i < MAX_CLIENTS; i++)
 	{
-		if(players[i].client_id != -1 && players[i].client_id != notthisid)
+		if(game.players[i].client_id != -1 && game.players[i].client_id != notthisid)
 		{
-			if(players[i].team == 0 || players[i].team == 1)
-				numplayers[players[i].team]++;
+			if(game.players[i].team == 0 || game.players[i].team == 1)
+				numplayers[game.players[i].team]++;
 		}
 	}
 
@@ -328,10 +345,10 @@ bool GAMECONTROLLER::can_join_team(int team, int notthisid)
 	int numplayers[2] = {0,0};
 	for(int i = 0; i < MAX_CLIENTS; i++)
 	{
-		if(players[i].client_id != -1 && players[i].client_id != notthisid)
+		if(game.players[i].client_id != -1 && game.players[i].client_id != notthisid)
 		{
-			if(players[i].team >= 0 || players[i].team == 1)
-				numplayers[players[i].team]++;
+			if(game.players[i].team >= 0 || game.players[i].team == 1)
+				numplayers[game.players[i].team]++;
 		}
 	}
 	
@@ -347,14 +364,14 @@ void GAMECONTROLLER::do_player_score_wincheck()
 		int topscore_count = 0;
 		for(int i = 0; i < MAX_CLIENTS; i++)
 		{
-			if(players[i].client_id != -1)
+			if(game.players[i].client_id != -1)
 			{
-				if(players[i].score > topscore)
+				if(game.players[i].score > topscore)
 				{
-					topscore = players[i].score;
+					topscore = game.players[i].score;
 					topscore_count = 1;
 				}
-				else if(players[i].score == topscore)
+				else if(game.players[i].score == topscore)
 					topscore_count++;
 			}
 		}
diff --git a/src/game/server/gs_game_ctf.cpp b/src/game/server/gs_game_ctf.cpp
index 031be42a..27da121a 100644
--- a/src/game/server/gs_game_ctf.cpp
+++ b/src/game/server/gs_game_ctf.cpp
@@ -29,26 +29,22 @@ bool GAMECONTROLLER_CTF::on_entity(int index, vec2 pos)
 	return true;
 }
 
-void GAMECONTROLLER_CTF::on_player_spawn(class PLAYER *p)
+int GAMECONTROLLER_CTF::on_character_death(class CHARACTER *victim, class PLAYER *killer, int weaponid)
 {
-}
-
-int GAMECONTROLLER_CTF::on_player_death(class PLAYER *victim, class PLAYER *killer, int weaponid)
-{
-	GAMECONTROLLER::on_player_death(victim, killer, weaponid);
+	GAMECONTROLLER::on_character_death(victim, killer, weaponid);
 	int had_flag = 0;
 	
 	// drop flags
 	for(int fi = 0; fi < 2; fi++)
 	{
 		FLAG *f = flags[fi];
-		if(f && f->carrying_player == killer)
+		if(f && f->carrying_character == &killer->character)
 			had_flag |= 2;
-		if(f && f->carrying_player == victim)
+		if(f && f->carrying_character == victim)
 		{
-			create_sound_global(SOUND_CTF_DROP);
+			game.create_sound_global(SOUND_CTF_DROP);
 			f->drop_tick = server_tick();
-			f->carrying_player = 0;
+			f->carrying_character = 0;
 			f->vel = vec2(0,0);
 			
 			if(killer && killer->team != victim->team)
@@ -75,10 +71,10 @@ void GAMECONTROLLER_CTF::tick()
 			continue;
 		
 		//
-		if(f->carrying_player)
+		if(f->carrying_character)
 		{
 			// update flag position
-			f->pos = f->carrying_player->pos;
+			f->pos = f->carrying_character->pos;
 			
 			if(flags[fi^1] && flags[fi^1]->at_stand)
 			{
@@ -86,35 +82,38 @@ void GAMECONTROLLER_CTF::tick()
 				{
 					// CAPTURE! \o/
 					teamscore[fi^1] += 100;
-					f->carrying_player->score += 5;
+					f->carrying_character->player->score += 5;
 
-					dbg_msg("game", "flag_capture player='%d:%s'", f->carrying_player->client_id, server_clientname(f->carrying_player->client_id));
+					dbg_msg("game", "flag_capture player='%d:%s'",
+						f->carrying_character->player->client_id,
+						server_clientname(f->carrying_character->player->client_id));
 
 					for(int i = 0; i < 2; i++)
 						flags[i]->reset();
 					
-					create_sound_global(SOUND_CTF_CAPTURE);
+					game.create_sound_global(SOUND_CTF_CAPTURE);
 				}
 			}			
 		}
 		else
 		{
-			PLAYER *close_players[MAX_CLIENTS];
-			int types[] = {NETOBJTYPE_PLAYER_CHARACTER};
-			int num = world->find_entities(f->pos, 32.0f, (ENTITY**)close_players, MAX_CLIENTS, types, 1);
+			CHARACTER *close_characters[MAX_CLIENTS];
+			int num = game.world.find_entities(f->pos, 32.0f, (ENTITY**)close_characters, MAX_CLIENTS, NETOBJTYPE_CHARACTER);
 			for(int i = 0; i < num; i++)
 			{
-				if(close_players[i]->team == f->team)
+				if(close_characters[i]->team == f->team)
 				{
 					// return the flag
 					if(!f->at_stand)
 					{
-						PLAYER *p = close_players[i];
-						p->score += 1;
+						CHARACTER *chr = close_characters[i];
+						chr->player->score += 1;
 
-						dbg_msg("game", "flag_return player='%d:%s'", p->client_id, server_clientname(p->client_id));
+						dbg_msg("game", "flag_return player='%d:%s'",
+							chr->player->client_id,
+							server_clientname(chr->player->client_id));
 
-						create_sound_global(SOUND_CTF_RETURN);
+						game.create_sound_global(SOUND_CTF_RETURN);
 						f->reset();
 					}
 				}
@@ -124,35 +123,37 @@ void GAMECONTROLLER_CTF::tick()
 					if(f->at_stand)
 						teamscore[fi^1]++;
 					f->at_stand = 0;
-					f->carrying_player = close_players[i];
-					f->carrying_player->score += 1;
+					f->carrying_character = close_characters[i];
+					f->carrying_character->player->score += 1;
 
-					dbg_msg("game", "flag_grab player='%d:%s'", f->carrying_player->client_id, server_clientname(f->carrying_player->client_id));
+					dbg_msg("game", "flag_grab player='%d:%s'",
+						f->carrying_character->player->client_id,
+						server_clientname(f->carrying_character->player->client_id));
 					
 					for(int c = 0; c < MAX_CLIENTS; c++)
 					{
-						if(players[c].client_id == -1)
+						if(game.players[c].client_id == -1)
 							continue;
 							
-						if(players[c].team == fi)
-							create_sound_global(SOUND_CTF_GRAB_EN, players[c].client_id);
+						if(game.players[c].team == fi)
+							game.create_sound_global(SOUND_CTF_GRAB_EN, game.players[c].client_id);
 						else
-							create_sound_global(SOUND_CTF_GRAB_PL, players[c].client_id);
+							game.create_sound_global(SOUND_CTF_GRAB_PL, game.players[c].client_id);
 					}
 					break;
 				}
 			}
 			
-			if(!f->carrying_player && !f->at_stand)
+			if(!f->carrying_character && !f->at_stand)
 			{
 				if(server_tick() > f->drop_tick + server_tickspeed()*30)
 				{
-					create_sound_global(SOUND_CTF_RETURN);
+					game.create_sound_global(SOUND_CTF_RETURN);
 					f->reset();
 				}
 				else
 				{
-					f->vel.y += world->core.tuning.gravity;
+					f->vel.y += game.world.core.tuning.gravity;
 					move_box(&f->pos, &f->vel, vec2(f->phys_size, f->phys_size), 0.5f);
 				}
 			}
@@ -166,17 +167,17 @@ FLAG::FLAG(int _team)
 {
 	team = _team;
 	proximity_radius = phys_size;
-	carrying_player = 0x0;
+	carrying_character = 0x0;
 	
 	reset();
 	
 	// TODO: should this be done here?
-	world->insert_entity(this);
+	game.world.insert_entity(this);
 }
 
 void FLAG::reset()
 {
-	carrying_player = 0;
+	carrying_character = 0;
 	at_stand = 1;
 	pos = stand_pos;
 	vel = vec2(0,0);
@@ -192,6 +193,6 @@ void FLAG::snap(int snapping_client)
 	
 	if(at_stand)
 		flag->carried_by = -2;
-	else if(carrying_player)
-		flag->carried_by = carrying_player->client_id;
+	else if(carrying_character)
+		flag->carried_by = carrying_character->player->client_id;
 }
diff --git a/src/game/server/gs_game_ctf.hpp b/src/game/server/gs_game_ctf.hpp
index 6d512815..bf6282e7 100644
--- a/src/game/server/gs_game_ctf.hpp
+++ b/src/game/server/gs_game_ctf.hpp
@@ -10,9 +10,7 @@ public:
 	virtual void tick();
 	
 	virtual bool on_entity(int index, vec2 pos);
-	
-	virtual void on_player_spawn(class PLAYER *p);
-	virtual int on_player_death(class PLAYER *victim, class PLAYER *killer, int weapon);
+	virtual int on_character_death(class CHARACTER *victim, class PLAYER *killer, int weapon);
 };
 
 // TODO: move to seperate file
@@ -20,7 +18,7 @@ class FLAG : public ENTITY
 {
 public:
 	static const int phys_size = 14;
-	PLAYER *carrying_player;
+	CHARACTER *carrying_character;
 	vec2 vel;
 	vec2 stand_pos;
 	
diff --git a/src/game/server/gs_game_tdm.cpp b/src/game/server/gs_game_tdm.cpp
index c591b447..d865a65a 100644
--- a/src/game/server/gs_game_tdm.cpp
+++ b/src/game/server/gs_game_tdm.cpp
@@ -8,14 +8,14 @@ GAMECONTROLLER_TDM::GAMECONTROLLER_TDM()
 	is_teamplay = true;
 }
 
-int GAMECONTROLLER_TDM::on_player_death(class PLAYER *victim, class PLAYER *killer, int weapon)
+int GAMECONTROLLER_TDM::on_character_death(class CHARACTER *victim, class PLAYER *killer, int weapon)
 {
-	GAMECONTROLLER::on_player_death(victim, killer, weapon);
+	GAMECONTROLLER::on_character_death(victim, killer, weapon);
 	
 	if(weapon >= 0)
 	{
 		// do team scoring
-		if(killer == victim)
+		if(killer == victim->player)
 			teamscore[killer->team&1]--; // klant arschel
 		else
 			teamscore[killer->team&1]++; // good shit
diff --git a/src/game/server/gs_game_tdm.hpp b/src/game/server/gs_game_tdm.hpp
index 516b581c..bb35260c 100644
--- a/src/game/server/gs_game_tdm.hpp
+++ b/src/game/server/gs_game_tdm.hpp
@@ -5,6 +5,6 @@ class GAMECONTROLLER_TDM : public GAMECONTROLLER
 public:
 	GAMECONTROLLER_TDM();
 	
-	int on_player_death(class PLAYER *victim, class PLAYER *killer, int weapon);
+	int on_character_death(class CHARACTER *victim, class PLAYER *killer, int weapon);
 	virtual void tick();
 };
diff --git a/src/game/server/gs_server.cpp b/src/game/server/gs_server.cpp
index 4e7b4179..5f5662d0 100644
--- a/src/game/server/gs_server.cpp
+++ b/src/game/server/gs_server.cpp
@@ -14,32 +14,12 @@
 #include "gs_game_dm.hpp"
 
 TUNING_PARAMS tuning;
+GAMECONTEXT game;
 
-void create_damageind(vec2 p, float angle_mod, int amount);
-void create_explosion(vec2 p, int owner, int weapon, bool bnodamage);
-void create_smoke(vec2 p);
-void create_playerspawn(vec2 p);
-void create_death(vec2 p, int who);
-void create_sound(vec2 pos, int sound, int mask=-1);
-
-PLAYER *get_player(int index);
-class PLAYER *intersect_player(vec2 pos0, vec2 pos1, float radius, vec2 &new_pos, class ENTITY *notthis = 0);
-class PLAYER *closest_player(vec2 pos, float radius, ENTITY *notthis);
-
-GAMEWORLD *world;
-
-enum
-{
-	CHAT_ALL=-2,
-	CHAT_SPEC=-1,
-	CHAT_RED=0,
-	CHAT_BLUE=1
-};
-
-static void send_chat(int cid, int team, const char *text)
+void GAMECONTEXT::send_chat(int chatter_cid, int team, const char *text)
 {
-	if(cid >= 0 && cid < MAX_CLIENTS)
-		dbg_msg("chat", "%d:%d:%s: %s", cid, team, server_clientname(cid), text);
+	if(chatter_cid >= 0 && chatter_cid < MAX_CLIENTS)
+		dbg_msg("chat", "%d:%d:%s: %s", chatter_cid, team, server_clientname(chatter_cid), text);
 	else
 		dbg_msg("chat", "*** %s", text);
 
@@ -47,7 +27,7 @@ static void send_chat(int cid, int team, const char *text)
 	{
 		NETMSG_SV_CHAT msg;
 		msg.team = 0;
-		msg.cid = cid;
+		msg.cid = chatter_cid;
 		msg.message = text;
 		msg.pack(MSGFLAG_VITAL);
 		server_send_msg(-1);
@@ -56,20 +36,20 @@ static void send_chat(int cid, int team, const char *text)
 	{
 		NETMSG_SV_CHAT msg;
 		msg.team = 1;
-		msg.cid = cid;
+		msg.cid = chatter_cid;
 		msg.message = text;
 		msg.pack(MSGFLAG_VITAL);
 
 		for(int i = 0; i < MAX_CLIENTS; i++)
 		{
-			if(players[i].client_id != -1 && players[i].team == team)
+			if(game.players[i].client_id != -1 && game.players[i].team == team)
 				server_send_msg(i);
 		}
 	}
 }
 
 
-void send_info(int who, int to_who)
+void GAMECONTEXT::send_info(int who, int to_who)
 {
 	NETMSG_SV_SETINFO msg;
 	msg.cid = who;
@@ -77,13 +57,13 @@ void send_info(int who, int to_who)
 	msg.skin = players[who].skin_name;
 	msg.use_custom_color = players[who].use_custom_color;
 	msg.color_body = players[who].color_body;
-	msg.color_feet =players[who].color_feet;
+	msg.color_feet = players[who].color_feet;
 	msg.pack(MSGFLAG_VITAL);
 	
 	server_send_msg(to_who);
 }
 
-void send_emoticon(int cid, int emoticon)
+void GAMECONTEXT::send_emoticon(int cid, int emoticon)
 {
 	NETMSG_SV_EMOTICON msg;
 	msg.cid = cid;
@@ -92,7 +72,7 @@ void send_emoticon(int cid, int emoticon)
 	server_send_msg(-1);
 }
 
-void send_weapon_pickup(int cid, int weapon)
+void GAMECONTEXT::send_weapon_pickup(int cid, int weapon)
 {
 	NETMSG_SV_WEAPONPICKUP msg;
 	msg.weapon = weapon;
@@ -101,7 +81,7 @@ void send_weapon_pickup(int cid, int weapon)
 }
 
 
-void send_broadcast(const char *text, int cid)
+void GAMECONTEXT::send_broadcast(const char *text, int cid)
 {
 	NETMSG_SV_BROADCAST msg;
 	msg.message = text;
@@ -159,7 +139,7 @@ void EVENT_HANDLER::snap(int snapping_client)
 		if(cmask_is_set(client_masks[i], snapping_client))
 		{
 			NETEVENT_COMMON *ev = (NETEVENT_COMMON *)&data[offsets[i]];
-			if(distance(players[snapping_client].pos, vec2(ev->x, ev->y)) < 1500.0f)
+			if(distance(game.players[snapping_client].view_pos, vec2(ev->x, ev->y)) < 1500.0f)
 			{
 				void *d = snap_new_item(types[i], i, sizes[i]);
 				mem_copy(d, &data[offsets[i]], sizes[i]);
@@ -168,8 +148,6 @@ void EVENT_HANDLER::snap(int snapping_client)
 	}
 }
 
-EVENT_HANDLER events;
-
 //////////////////////////////////////////////////
 // Entity
 //////////////////////////////////////////////////
@@ -177,9 +155,9 @@ ENTITY::ENTITY(int objtype)
 {
 	this->objtype = objtype;
 	pos = vec2(0,0);
-	flags = FLAG_PHYSICS;
 	proximity_radius = 0;
 
+	marked_for_destroy = false;	
 	id = snap_new_id();
 
 	next_entity = 0;
@@ -190,7 +168,7 @@ ENTITY::ENTITY(int objtype)
 
 ENTITY::~ENTITY()
 {
-	world->remove_entity(this);
+	game.world.remove_entity(this);
 	snap_free_id(id);
 }
 
@@ -213,14 +191,18 @@ GAMEWORLD::~GAMEWORLD()
 		delete first_entity;
 }
 
-int GAMEWORLD::find_entities(vec2 pos, float radius, ENTITY **ents, int max)
+ENTITY *GAMEWORLD::find_first(int type)
+{
+	return first_entity_types[type];
+}
+
+
+int GAMEWORLD::find_entities(vec2 pos, float radius, ENTITY **ents, int max, int type)
 {
 	int num = 0;
-	for(ENTITY *ent = first_entity; ent; ent = ent->next_entity)
+	for(ENTITY *ent = (type<0) ? first_entity : first_entity_types[type];
+		ent; ent = (type<0) ? ent->next_entity : ent->next_type_entity)
 	{
-		if(!(ent->flags&ENTITY::FLAG_PHYSICS))
-			continue;
-
 		if(distance(ent->pos, pos) < radius+ent->proximity_radius)
 		{
 			ents[num] = ent;
@@ -233,29 +215,6 @@ int GAMEWORLD::find_entities(vec2 pos, float radius, ENTITY **ents, int max)
 	return num;
 }
 
-int GAMEWORLD::find_entities(vec2 pos, float radius, ENTITY **ents, int max, const int* types, int maxtypes)
-{
-	int num = 0;
-	for(int t = 0; t < maxtypes; t++)
-	{
-		for(ENTITY *ent = first_entity_types[types[t]]; ent; ent = ent->next_type_entity)
-		{
-			if(!(ent->flags&ENTITY::FLAG_PHYSICS))
-				continue;
-
-			if(distance(ent->pos, pos) < radius+ent->proximity_radius)
-			{
-				ents[num] = ent;
-				num++;
-				if(num == max)
-					break;
-			}
-		}
-	}
-
-	return num;
-}
-
 void GAMEWORLD::insert_entity(ENTITY *ent)
 {
 	ENTITY *cur = first_entity;
@@ -282,7 +241,7 @@ void GAMEWORLD::insert_entity(ENTITY *ent)
 
 void GAMEWORLD::destroy_entity(ENTITY *ent)
 {
-	ent->set_flag(ENTITY::FLAG_DESTROY);
+	ent->marked_for_destroy = true;
 }
 
 void GAMEWORLD::remove_entity(ENTITY *ent)
@@ -326,8 +285,7 @@ void GAMEWORLD::reset()
 		ent->reset();
 	remove_entities();
 
-	for(ENTITY *ent = first_entity; ent; ent = ent->next_entity)
-		ent->post_reset();
+	game.controller->post_reset();
 	remove_entities();
 
 	reset_requested = false;
@@ -340,7 +298,7 @@ void GAMEWORLD::remove_entities()
 	while(ent)
 	{
 		ENTITY *next = ent->next_entity;
-		if(ent->flags&ENTITY::FLAG_DESTROY)
+		if(ent->marked_for_destroy)
 		{
 			remove_entity(ent);
 			ent->destroy();
@@ -356,86 +314,17 @@ void GAMEWORLD::tick()
 
 	if(!paused)
 	{
-		/*
-		static PERFORMACE_INFO scopes[OBJTYPE_FLAG+1] =
-		{
-			{"null", 0},
-			{"game", 0},
-			{"player_info", 0},
-			{"player_character", 0},
-			{"projectile", 0},
-			{"powerup", 0},
-			{"flag", 0}
-		};
-
-		static PERFORMACE_INFO scopes_def[OBJTYPE_FLAG+1] =
-		{
-			{"null", 0},
-			{"game", 0},
-			{"player_info", 0},
-			{"player_character", 0},
-			{"projectile", 0},
-			{"powerup", 0},
-			{"flag", 0}
-		};
-				
-		static PERFORMACE_INFO tick_scope = {"tick", 0};
-		perf_start(&tick_scope);*/
-		
 		// update all objects
 		for(ENTITY *ent = first_entity; ent; ent = ent->next_entity)
-		{
-			/*if(ent->objtype >= 0 && ent->objtype < OBJTYPE_FLAG)
-				perf_start(&scopes[ent->objtype]);*/
 			ent->tick();
-			/*if(ent->objtype >= 0 && ent->objtype < OBJTYPE_FLAG)
-				perf_end();*/
-		}
 		
-		/*
-		perf_end();
-
-		static PERFORMACE_INFO deftick_scope = {"tick_defered", 0};
-		perf_start(&deftick_scope);*/
 		for(ENTITY *ent = first_entity; ent; ent = ent->next_entity)
-		{
-			/*if(ent->objtype >= 0 && ent->objtype < OBJTYPE_FLAG)
-				perf_start(&scopes_def[ent->objtype]);*/
 			ent->tick_defered();
-			/*if(ent->objtype >= 0 && ent->objtype < OBJTYPE_FLAG)
-				perf_end();*/
-		}
-		/*perf_end();*/
 	}
 
 	remove_entities();
 }
 
-struct INPUT_COUNT
-{
-	int presses;
-	int releases;
-};
-
-static INPUT_COUNT count_input(int prev, int cur)
-{
-	INPUT_COUNT c = {0,0};
-	prev &= INPUT_STATE_MASK;
-	cur &= INPUT_STATE_MASK;
-	int i = prev;
-	while(i != cur)
-	{
-		i = (i+1)&INPUT_STATE_MASK;
-		if(i&1)
-			c.presses++;
-		else
-			c.releases++;
-	}
-
-	return c;
-}
-
-
 //////////////////////////////////////////////////
 // projectile
 //////////////////////////////////////////////////
@@ -456,12 +345,12 @@ PROJECTILE::PROJECTILE(int type, int owner, vec2 pos, vec2 dir, int span, ENTITY
 	this->weapon = weapon;
 	this->bounce = 0;
 	this->start_tick = server_tick();
-	world->insert_entity(this);
+	game.world.insert_entity(this);
 }
 
 void PROJECTILE::reset()
 {
-	world->destroy_entity(this);
+	game.world.destroy_entity(this);
 }
 
 vec2 PROJECTILE::get_pos(float time)
@@ -501,20 +390,20 @@ void PROJECTILE::tick()
 	int collide = col_intersect_line(prevpos, curpos, &curpos);
 	//int collide = col_check_point((int)curpos.x, (int)curpos.y);
 	
-	ENTITY *targetplayer = (ENTITY*)intersect_player(prevpos, curpos, 6.0f, curpos, powner);
-	if(targetplayer || collide || lifespan < 0)
+	CHARACTER *targetchr = game.world.intersect_character(prevpos, curpos, 6.0f, curpos, powner);
+	if(targetchr || collide || lifespan < 0)
 	{
-		if (lifespan >= 0 || weapon == WEAPON_GRENADE)
-			create_sound(curpos, sound_impact);
+		if(lifespan >= 0 || weapon == WEAPON_GRENADE)
+			game.create_sound(curpos, sound_impact);
 
-		if (flags & PROJECTILE_FLAGS_EXPLODE)
-			create_explosion(curpos, owner, weapon, false);
-		else if (targetplayer)
+		if(flags & PROJECTILE_FLAGS_EXPLODE)
+			game.create_explosion(curpos, owner, weapon, false);
+		else if(targetchr)
 		{
-			targetplayer->take_damage(direction * max(0.001f, force), damage, owner, weapon);
+			targetchr->take_damage(direction * max(0.001f, force), damage, owner, weapon);
 		}
 
-		world->destroy_entity(this);
+		game.world.destroy_entity(this);
 	}
 }
 
@@ -532,7 +421,7 @@ void PROJECTILE::snap(int snapping_client)
 {
 	float ct = (server_tick()-start_tick)/(float)server_tickspeed();
 	
-	if(distance(players[snapping_client].pos, get_pos(ct)) > 1000.0f)
+	if(distance(game.players[snapping_client].view_pos, get_pos(ct)) > 1000.0f)
 		return;
 
 	NETOBJ_PROJECTILE *proj = (NETOBJ_PROJECTILE *)snap_new_item(NETOBJTYPE_PROJECTILE, id, sizeof(NETOBJ_PROJECTILE));
@@ -543,7 +432,7 @@ void PROJECTILE::snap(int snapping_client)
 //////////////////////////////////////////////////
 // laser
 //////////////////////////////////////////////////
-LASER::LASER(vec2 pos, vec2 direction, float start_energy, PLAYER *owner)
+LASER::LASER(vec2 pos, vec2 direction, float start_energy, CHARACTER *owner)
 : ENTITY(NETOBJTYPE_LASER)
 {
 	this->pos = pos;
@@ -553,21 +442,21 @@ LASER::LASER(vec2 pos, vec2 direction, float start_energy, PLAYER *owner)
 	bounces = 0;
 	do_bounce();
 	
-	world->insert_entity(this);
+	game.world.insert_entity(this);
 }
 
 
-bool LASER::hit_player(vec2 from, vec2 to)
+bool LASER::hit_character(vec2 from, vec2 to)
 {
 	vec2 at;
-	PLAYER *hit = intersect_player(pos, to, 0.0f, at, owner);
+	CHARACTER *hit = game.world.intersect_character(pos, to, 0.0f, at, owner);
 	if(!hit)
 		return false;
 
 	this->from = from;
 	pos = at;
 	energy = -1;		
-	hit->take_damage(vec2(0,0), tuning.laser_damage, owner->client_id, WEAPON_RIFLE);
+	hit->take_damage(vec2(0,0), tuning.laser_damage, owner->player->client_id, WEAPON_RIFLE);
 	return true;
 }
 
@@ -578,7 +467,7 @@ void LASER::do_bounce()
 	if(energy < 0)
 	{
 		//dbg_msg("laser", "%d removed", server_tick());
-		world->destroy_entity(this);
+		game.world.destroy_entity(this);
 		return;
 	}
 	
@@ -586,7 +475,7 @@ void LASER::do_bounce()
 	
 	if(col_intersect_line(pos, to, &to))
 	{
-		if(!hit_player(pos, to))
+		if(!hit_character(pos, to))
 		{
 			// intersected
 			from = pos;
@@ -604,12 +493,12 @@ void LASER::do_bounce()
 			if(bounces > tuning.laser_bounce_num)
 				energy = -1;
 				
-			create_sound(pos, SOUND_RIFLE_BOUNCE);
+			game.create_sound(pos, SOUND_RIFLE_BOUNCE);
 		}
 	}
 	else
 	{
-		if(!hit_player(pos, to))
+		if(!hit_character(pos, to))
 		{
 			from = pos;
 			pos = to;
@@ -622,7 +511,7 @@ void LASER::do_bounce()
 	
 void LASER::reset()
 {
-	world->destroy_entity(this);
+	game.world.destroy_entity(this);
 }
 
 void LASER::tick()
@@ -636,7 +525,7 @@ void LASER::tick()
 
 void LASER::snap(int snapping_client)
 {
-	if(distance(players[snapping_client].pos, pos) > 1000.0f)
+	if(distance(game.players[snapping_client].view_pos, pos) > 1000.0f)
 		return;
 
 	NETOBJ_LASER *obj = (NETOBJ_LASER *)snap_new_item(NETOBJTYPE_LASER, id, sizeof(NETOBJ_LASER));
@@ -647,1193 +536,20 @@ void LASER::snap(int snapping_client)
 	obj->start_tick = eval_tick;
 }
 
-
-//////////////////////////////////////////////////
-// player
-//////////////////////////////////////////////////
-// TODO: move to separate file
-PLAYER::PLAYER()
-: ENTITY(NETOBJTYPE_PLAYER_CHARACTER)
-{
-	init();
-}
-
-void PLAYER::init()
-{
-	proximity_radius = phys_size;
-	client_id = -1;
-	team = -1; // -1 == spectator
-
-	latency_accum = 0;
-	latency_accum_min = 0;
-	latency_accum_max = 0;
-	latency_avg = 0;
-	latency_min = 0;
-	latency_max = 0;
-
-	reset();
-}
-
-void PLAYER::reset()
-{
-	pos = vec2(0.0f, 0.0f);
-	core.reset();
-	
-	emote_type = 0;
-	emote_stop = -1;
-	
-	//direction = vec2(0.0f, 1.0f);
-	score = 0;
-	dead = true;
-	clear_flag(ENTITY::FLAG_PHYSICS);
-	spawning = false;
-	die_tick = 0;
-	die_pos = vec2(0,0);
-	damage_taken = 0;
-	last_chat = 0;
-	player_state = PLAYERSTATE_UNKNOWN;
-
-	mem_zero(&input, sizeof(input));
-	mem_zero(&previnput, sizeof(previnput));
-	num_inputs = 0;
-
-	last_action = -1;
-
-	emote_stop = 0;
-	damage_taken_tick = 0;
-	attack_tick = 0;
-
-	mem_zero(&ninja, sizeof(ninja));
-	
-	active_weapon = WEAPON_GUN;
-	last_weapon = WEAPON_HAMMER;
-	queued_weapon = -1;
-}
-
-void PLAYER::destroy() {  }
-
-void PLAYER::set_weapon(int w)
-{
-	if(w == active_weapon)
-		return;
-		
-	last_weapon = active_weapon;
-	queued_weapon = -1;
-	active_weapon = w;
-	if(active_weapon < 0 || active_weapon >= NUM_WEAPONS)
-		active_weapon = 0;
-		
-	create_sound(pos, SOUND_WEAPON_SWITCH);
-}
-
-void PLAYER::respawn()
-{
-	spawning = true;
-}
-
-const char *get_team_name(int team)
+GAMECONTEXT::GAMECONTEXT()
 {
-	if(gamecontroller->gametype == GAMETYPE_DM)
-	{
-		if(team == 0)
-			return "game";
-	}
-	else
-	{
-		if(team == 0)
-			return "red team";
-		else if(team == 1)
-			return "blue team";
-	}
-	
-	return "spectators";
+	clear();
 }
 
-void PLAYER::set_team(int new_team)
+void GAMECONTEXT::clear()
 {
-	// clamp the team
-	new_team = gamecontroller->clampteam(new_team);
-	if(team == new_team)
-		return;
-		
-	char buf[512];
-	str_format(buf, sizeof(buf), "%s joined the %s", server_clientname(client_id), get_team_name(new_team));
-	send_chat(-1, CHAT_ALL, buf); 
-	
-	die(client_id, -1);
-	team = new_team;
-	score = 0;
-	dbg_msg("game", "team_join player='%d:%s' team=%d", client_id, server_clientname(client_id), team);
-	
-	gamecontroller->on_player_info_change(&players[client_id]);
-
-	// send all info to this client
+	// reset all players
 	for(int i = 0; i < MAX_CLIENTS; i++)
-	{
-		if(players[i].client_id != -1)
-			send_info(i, -1);
-	}
-}
-
-vec2 spawn_points[3][64];
-int num_spawn_points[3] = {0};
-
-struct SPAWNEVAL
-{
-	SPAWNEVAL()
-	{
-		got = false;
-		friendly_team = -1;
-		die_pos = vec2(0,0);
-		pos = vec2(100,100);
-	}
-		
-	vec2 pos;
-	bool got;
-	int friendly_team;
-	float score;
-	vec2 die_pos;
-};
-
-static float evaluate_spawn(SPAWNEVAL *eval, vec2 pos)
-{
-	float score = 0.0f;
-	
-	for(int c = 0; c < MAX_CLIENTS; c++)
-	{
-		if(players[c].client_id == -1)
-			continue;
-			
-		// don't count dead people
-		if(!(players[c].flags&ENTITY::FLAG_PHYSICS))
-			continue;
-		
-		// team mates are not as dangerous as enemies
-		float scoremod = 1.0f;
-		if(eval->friendly_team != -1 && players[c].team == eval->friendly_team)
-			scoremod = 0.5f;
-			
-		float d = distance(pos, players[c].pos);
-		if(d == 0)
-			score += 1000000000.0f;
-		else
-			score += 1.0f/d;
-	}
-	
-	// weight in the die posititon
-	float d = distance(pos, eval->die_pos);
-	if(d == 0)
-		score += 1000000000.0f;
-	else
-		score += 1.0f/d;
-	
-	return score;
-}
-
-static void evaluate_spawn_type(SPAWNEVAL *eval, int t)
-{
-	// get spawn point
-	/*
-	int start, num;
-	map_get_type(t, &start, &num);
-	if(!num)
-		return;
-	*/
-	for(int i  = 0; i < num_spawn_points[t]; i++)
-	{
-		//num_spawn_points[t]
-		//mapres_spawnpoint *sp = (mapres_spawnpoint*)map_get_item(start + i, NULL, NULL);
-		vec2 p = spawn_points[t][i];// vec2((float)sp->x, (float)sp->y);
-		float s = evaluate_spawn(eval, p);
-		if(!eval->got || eval->score > s)
-		{
-			eval->got = true;
-			eval->score = s;
-			eval->pos = p;
-		}
-	}
-}
-
-void PLAYER::try_respawn()
-{
-	vec2 spawnpos = vec2(100.0f, -60.0f);
-	
-	// get spawn point
-	SPAWNEVAL eval;
-	eval.die_pos = die_pos;
-	
-	eval.pos = vec2(100, 100);
-	
-	if(gamecontroller->gametype == GAMETYPE_CTF)
-	{
-		eval.friendly_team = team;
-		
-		// try first try own team spawn, then normal spawn and then enemy
-		evaluate_spawn_type(&eval, 1+(team&1));
-		if(!eval.got)
-		{
-			evaluate_spawn_type(&eval, 0);
-			if(!eval.got)
-				evaluate_spawn_type(&eval, 1+((team+1)&1));
-		}
-	}
-	else
-	{
-		if(gamecontroller->gametype == GAMETYPE_TDM)
-			eval.friendly_team = team;
-			
-		evaluate_spawn_type(&eval, 0);
-		evaluate_spawn_type(&eval, 1);
-		evaluate_spawn_type(&eval, 2);
-	}
-	
-	spawnpos = eval.pos;
-
-	// check if the position is occupado
-	ENTITY *ents[2] = {0};
-	int types[] = {NETOBJTYPE_PLAYER_CHARACTER};
-	int num_ents = world->find_entities(spawnpos, 64, ents, 2, types, 1);
-	for(int i = 0; i < num_ents; i++)
-	{
-		if(ents[i] != this)
-			return;
-	}
-	
-	spawning = false;
-	pos = spawnpos;
-
-	core.pos = pos;
-	core.vel = vec2(0,0);
-	core.hooked_player = -1;
-
-	health = 10;
-	armor = 0;
-	jumped = 0;
-	
-	mem_zero(&ninja, sizeof(ninja));
-	
-	dead = false;
-	set_flag(ENTITY::FLAG_PHYSICS);
-	player_state = PLAYERSTATE_PLAYING;
-
-	core.hook_state = HOOK_IDLE;
-
-	mem_zero(&input, sizeof(input));
-
-	// init weapons
-	mem_zero(&weapons, sizeof(weapons));
-	weapons[WEAPON_HAMMER].got = true;
-	weapons[WEAPON_HAMMER].ammo = -1;
-	weapons[WEAPON_GUN].got = true;
-	weapons[WEAPON_GUN].ammo = 10;
-
-	/*weapons[WEAPON_RIFLE].got = true;
-	weapons[WEAPON_RIFLE].ammo = -1;*/
-	
-	active_weapon = WEAPON_GUN;
-	last_weapon = WEAPON_HAMMER;
-	queued_weapon = 0;
-
-	reload_timer = 0;
-
-	// Create sound and spawn effects
-	create_sound(pos, SOUND_PLAYER_SPAWN);
-	create_playerspawn(pos);
-
-	gamecontroller->on_player_spawn(this);
-}
-
-bool PLAYER::is_grounded()
-{
-	if(col_check_point((int)(pos.x+phys_size/2), (int)(pos.y+phys_size/2+5)))
-		return true;
-	if(col_check_point((int)(pos.x-phys_size/2), (int)(pos.y+phys_size/2+5)))
-		return true;
-	return false;
-}
-
-
-int PLAYER::handle_ninja()
-{
-	vec2 direction = normalize(vec2(latest_input.target_x, latest_input.target_y));
-
-	if ((server_tick() - ninja.activationtick) > (data->weapons.ninja.duration * server_tickspeed() / 1000))
-	{
-		// time's up, return
-		weapons[WEAPON_NINJA].got = false;
-		active_weapon = last_weapon;
-		if(active_weapon == WEAPON_NINJA)
-			active_weapon = WEAPON_GUN;
-		set_weapon(active_weapon);
-		return 0;
-	}
-	
-	// force ninja weapon
-	set_weapon(WEAPON_NINJA);
-
-	// Check if it should activate
-	if (count_input(latest_previnput.fire, latest_input.fire).presses && (server_tick() > ninja.currentcooldown))
-	{
-		// ok then, activate ninja
-		attack_tick = server_tick();
-		ninja.activationdir = direction;
-		ninja.currentmovetime = data->weapons.ninja.movetime * server_tickspeed() / 1000;
-		ninja.currentcooldown = data->weapons.ninja.base->firedelay * server_tickspeed() / 1000 + server_tick();
-		
-		// reset hit objects
-		numobjectshit = 0;
-
-		create_sound(pos, SOUND_NINJA_FIRE);
-
-		// release all hooks when ninja is activated
-		//release_hooked();
-		//release_hooks();
-	}
-
-	ninja.currentmovetime--;
-
-	if (ninja.currentmovetime == 0)
-	{
-		// reset player velocity
-		core.vel *= 0.2f;
-		//return MODIFIER_RETURNFLAGS_OVERRIDEWEAPON;
-	}
-
-	if (ninja.currentmovetime > 0)
-	{
-		// Set player velocity
-		core.vel = ninja.activationdir * data->weapons.ninja.velocity;
-		vec2 oldpos = pos;
-		move_box(&core.pos, &core.vel, vec2(phys_size, phys_size), 0.0f);
-		// reset velocity so the client doesn't predict stuff
-		core.vel = vec2(0.0f,0.0f);
-		if ((ninja.currentmovetime % 2) == 0)
-		{
-			//create_smoke(pos);
-		}
-
-		// check if we hit anything along the way
-		{
-			int type = NETOBJTYPE_PLAYER_CHARACTER;
-			ENTITY *ents[64];
-			vec2 dir = pos - oldpos;
-			float radius = phys_size * 2.0f; //length(dir * 0.5f);
-			vec2 center = oldpos + dir * 0.5f;
-			int num = world->find_entities(center, radius, ents, 64, &type, 1);
-
-			for (int i = 0; i < num; i++)
-			{
-				// Check if entity is a player
-				if (ents[i] == this)
-					continue;
-				// make sure we haven't hit this object before
-				bool balreadyhit = false;
-				for (int j = 0; j < numobjectshit; j++)
-				{
-					if (hitobjects[j] == ents[i])
-						balreadyhit = true;
-				}
-				if (balreadyhit)
-					continue;
-
-				// check so we are sufficiently close
-				if (distance(ents[i]->pos, pos) > (phys_size * 2.0f))
-					continue;
-
-				// hit a player, give him damage and stuffs...
-				create_sound(ents[i]->pos, SOUND_NINJA_HIT);
-				// set his velocity to fast upward (for now)
-				if(numobjectshit < 10)
-					hitobjects[numobjectshit++] = ents[i];
-				ents[i]->take_damage(vec2(0,10.0f), data->weapons.ninja.base->damage, client_id,WEAPON_NINJA);
-			}
-		}
-		return 0;
-	}
-
-	return 0;
-}
-
-
-void PLAYER::do_weaponswitch()
-{
-	if(reload_timer != 0) // make sure we have reloaded
-		return;
-		
-	if(queued_weapon == -1) // check for a queued weapon
-		return;
-
-	if(weapons[WEAPON_NINJA].got) // if we have ninja, no weapon selection is possible
-		return;
-
-	// switch weapon
-	set_weapon(queued_weapon);
-}
-
-void PLAYER::handle_weaponswitch()
-{
-	int wanted_weapon = active_weapon;
-	if(queued_weapon != -1)
-		wanted_weapon = queued_weapon;
-	
-	// select weapon
-	int next = count_input(latest_previnput.next_weapon, latest_input.next_weapon).presses;
-	int prev = count_input(latest_previnput.prev_weapon, latest_input.prev_weapon).presses;
-
-	if(next < 128) // make sure we only try sane stuff
-	{
-		while(next) // next weapon selection
-		{
-			wanted_weapon = (wanted_weapon+1)%NUM_WEAPONS;
-			if(weapons[wanted_weapon].got)
-				next--;
-		}
-	}
-
-	if(prev < 128) // make sure we only try sane stuff
-	{
-		while(prev) // prev weapon selection
-		{
-			wanted_weapon = (wanted_weapon-1)<0?NUM_WEAPONS-1:wanted_weapon-1;
-			if(weapons[wanted_weapon].got)
-				prev--;
-		}
-	}
-
-	// direct weapon selection
-	if(latest_input.wanted_weapon)
-		wanted_weapon = input.wanted_weapon-1;
-
-	// check for insane values
-	if(wanted_weapon >= 0 && wanted_weapon < NUM_WEAPONS && wanted_weapon != active_weapon && weapons[wanted_weapon].got)
-		queued_weapon = wanted_weapon;
-	
-	do_weaponswitch();
-}
-
-void PLAYER::fire_weapon()
-{
-	if(reload_timer != 0 || active_weapon == WEAPON_NINJA)
-		return;
-		
-	do_weaponswitch();
-	
-	vec2 direction = normalize(vec2(latest_input.target_x, latest_input.target_y));
-	
-	bool fullauto = false;
-	if(active_weapon == WEAPON_GRENADE || active_weapon == WEAPON_SHOTGUN || active_weapon == WEAPON_RIFLE)
-		fullauto = true;
-
-
-	// check if we gonna fire
-	bool will_fire = false;
-	if(count_input(latest_previnput.fire, latest_input.fire).presses) will_fire = true;
-	if(fullauto && (latest_input.fire&1) && weapons[active_weapon].ammo) will_fire = true;
-	if(!will_fire)
-		return;
-		
-	// check for ammo
-	if(!weapons[active_weapon].ammo)
-	{
-		create_sound(pos, SOUND_WEAPON_NOAMMO);
-		return;
-	}
-	
-	vec2 projectile_startpos = pos+direction*phys_size*0.75f;
-	
-	switch(active_weapon)
-	{
-		case WEAPON_HAMMER:
-		{
-			// reset objects hit
-			numobjectshit = 0;
-			create_sound(pos, SOUND_HAMMER_FIRE);
-			
-			int type = NETOBJTYPE_PLAYER_CHARACTER;
-			ENTITY *ents[64];
-			int num = world->find_entities(pos+direction*phys_size*0.75f, phys_size*0.5f, ents, 64, &type, 1);			
-
-			for (int i = 0; i < num; i++)
-			{
-				PLAYER *target = (PLAYER*)ents[i];
-				if (target == this)
-					continue;
-					
-				// hit a player, give him damage and stuffs...
-				vec2 fdir = normalize(ents[i]->pos - pos);
-
-				// set his velocity to fast upward (for now)
-				create_sound(pos, SOUND_HAMMER_HIT);
-				ents[i]->take_damage(vec2(0,-1.0f), data->weapons.hammer.base->damage, client_id, active_weapon);
-				vec2 dir;
-				if (length(target->pos - pos) > 0.0f)
-					dir = normalize(target->pos - pos);
-				else
-					dir = vec2(0,-1);
-					
-				target->core.vel += normalize(dir + vec2(0,-1.1f)) * 10.0f;
-			}
-			
-		} break;
-
-		case WEAPON_GUN:
-		{
-			PROJECTILE *proj = new PROJECTILE(WEAPON_GUN,
-				client_id,
-				projectile_startpos,
-				direction,
-				(int)(server_tickspeed()*tuning.gun_lifetime),
-				this,
-				1, 0, 0, -1, WEAPON_GUN);
-				
-			// pack the projectile and send it to the client directly
-			NETOBJ_PROJECTILE p;
-			proj->fill_info(&p);
-			
-			msg_pack_start(NETMSGTYPE_SV_EXTRAPROJECTILE, 0);
-			msg_pack_int(1);
-			for(unsigned i = 0; i < sizeof(NETOBJ_PROJECTILE)/sizeof(int); i++)
-				msg_pack_int(((int *)&p)[i]);
-			msg_pack_end();
-			server_send_msg(client_id);
-							
-			create_sound(pos, SOUND_GUN_FIRE);
-		} break;
-		
-		case WEAPON_SHOTGUN:
-		{
-			int shotspread = 2;
-
-			msg_pack_start(NETMSGTYPE_SV_EXTRAPROJECTILE, 0);
-			msg_pack_int(shotspread*2+1);
-			
-			for(int i = -shotspread; i <= shotspread; i++)
-			{
-				float spreading[] = {-0.185f, -0.070f, 0, 0.070f, 0.185f};
-				float a = get_angle(direction);
-				a += spreading[i+2];
-				float v = 1-(abs(i)/(float)shotspread);
-				float speed = mix((float)tuning.shotgun_speeddiff, 1.0f, v);
-				PROJECTILE *proj = new PROJECTILE(WEAPON_SHOTGUN,
-					client_id,
-					projectile_startpos,
-					vec2(cosf(a), sinf(a))*speed,
-					(int)(server_tickspeed()*tuning.shotgun_lifetime),
-					this,
-					1, 0, 0, -1, WEAPON_SHOTGUN);
-					
-				// pack the projectile and send it to the client directly
-				NETOBJ_PROJECTILE p;
-				proj->fill_info(&p);
-				
-				for(unsigned i = 0; i < sizeof(NETOBJ_PROJECTILE)/sizeof(int); i++)
-					msg_pack_int(((int *)&p)[i]);
-			}
-
-			msg_pack_end();
-			server_send_msg(client_id);					
-			
-			create_sound(pos, SOUND_SHOTGUN_FIRE);
-		} break;
-
-		case WEAPON_GRENADE:
-		{
-			PROJECTILE *proj = new PROJECTILE(WEAPON_GRENADE,
-				client_id,
-				projectile_startpos,
-				direction,
-				(int)(server_tickspeed()*tuning.grenade_lifetime),
-				this,
-				1, PROJECTILE::PROJECTILE_FLAGS_EXPLODE, 0, SOUND_GRENADE_EXPLODE, WEAPON_GRENADE);
-
-			// pack the projectile and send it to the client directly
-			NETOBJ_PROJECTILE p;
-			proj->fill_info(&p);
-			
-			msg_pack_start(NETMSGTYPE_SV_EXTRAPROJECTILE, 0);
-			msg_pack_int(1);
-			for(unsigned i = 0; i < sizeof(NETOBJ_PROJECTILE)/sizeof(int); i++)
-				msg_pack_int(((int *)&p)[i]);
-			msg_pack_end();
-			server_send_msg(client_id);
-
-			create_sound(pos, SOUND_GRENADE_FIRE);
-		} break;
-		
-		case WEAPON_RIFLE:
-		{
-			new LASER(pos, direction, tuning.laser_reach, this);
-			create_sound(pos, SOUND_RIFLE_FIRE);
-		} break;
-		
-	}
-
-	if(weapons[active_weapon].ammo > 0) // -1 == unlimited
-		weapons[active_weapon].ammo--;
-	attack_tick = server_tick();
-	reload_timer = data->weapons.id[active_weapon].firedelay * server_tickspeed() / 1000;
-}
-
-int PLAYER::handle_weapons()
-{
-	vec2 direction = normalize(vec2(latest_input.target_x, latest_input.target_y));
-
-	if(config.dbg_stress)
-	{
-		for(int i = 0; i < NUM_WEAPONS; i++)
-		{
-			weapons[i].got = true;
-			weapons[i].ammo = 10;
-		}
-
-		if(reload_timer) // twice as fast reload
-			reload_timer--;
-	}
-
-	// check reload timer
-	if(reload_timer)
-	{
-		reload_timer--;
-		return 0;
-	}
-	
-	if (active_weapon == WEAPON_NINJA)
-	{
-		// don't update other weapons while ninja is active
-		return handle_ninja();
-	}
-
-	// fire weapon, if wanted
-	fire_weapon();
-
-	// ammo regen
-	int ammoregentime = data->weapons.id[active_weapon].ammoregentime;
-	if(ammoregentime)
-	{
-		// If equipped and not active, regen ammo?
-		if (reload_timer <= 0)
-		{
-			if (weapons[active_weapon].ammoregenstart < 0)
-				weapons[active_weapon].ammoregenstart = server_tick();
-
-			if ((server_tick() - weapons[active_weapon].ammoregenstart) >= ammoregentime * server_tickspeed() / 1000)
-			{
-				// Add some ammo
-				weapons[active_weapon].ammo = min(weapons[active_weapon].ammo + 1, 10);
-				weapons[active_weapon].ammoregenstart = -1;
-			}
-		}
-		else
-		{
-			weapons[active_weapon].ammoregenstart = -1;
-		}
-	}
-	
-	return 0;
-}
-
-void PLAYER::on_direct_input(NETOBJ_PLAYER_INPUT *new_input)
-{
-	mem_copy(&latest_previnput, &latest_input, sizeof(latest_input));
-	mem_copy(&latest_input, new_input, sizeof(latest_input));
-	if(num_inputs > 2 && team != -1 && !dead)
-	{
-		handle_weaponswitch();
-		fire_weapon();
-	}
-}
-
-void PLAYER::tick()
-{
-	server_setclientscore(client_id, score);
-
-	// grab latest input
-	/*
-	{
-		int size = 0;
-		int *input = server_latestinput(client_id, &size);
-		if(input)
-		{
-			mem_copy(&latest_previnput, &latest_input, sizeof(latest_input));
-			mem_copy(&latest_input, input, sizeof(latest_input));
-		}
-	}*/
-	
-	// check if we have enough input
-	// this is to prevent initial weird clicks
-	if(num_inputs < 2)
-	{
-		latest_previnput = latest_input;
-		previnput = input;
-	}
-	
-	// do latency stuff
-	{
-		CLIENT_INFO info;
-		if(server_getclientinfo(client_id, &info))
-		{
-			latency_accum += info.latency;
-			latency_accum_max = max(latency_accum_max, info.latency);
-			latency_accum_min = min(latency_accum_min, info.latency);
-		}
-
-		if(server_tick()%server_tickspeed() == 0)
-		{
-			latency_avg = latency_accum/server_tickspeed();
-			latency_max = latency_accum_max;
-			latency_min = latency_accum_min;
-			latency_accum = 0;
-			latency_accum_min = 1000;
-			latency_accum_max = 0;
-		}
-	}
-
-	// enable / disable physics
-	if(team == -1 || dead)
-	{
-		world->core.players[client_id] = 0;
-		clear_flag(FLAG_PHYSICS);
-	}
-	else
-	{
-		world->core.players[client_id] = &core;
-		set_flag(FLAG_PHYSICS);
-	}
-
-	// spectator
-	if(team == -1)
-		return;
-
-	if(spawning)
-		try_respawn();
-
-	// TODO: rework the input to be more robust
-	if(dead)
-	{
-		if(server_tick()-die_tick >= server_tickspeed()/2 && count_input(latest_previnput.fire, latest_input.fire).presses)
-			die_tick = -1;
-		if(server_tick()-die_tick >= server_tickspeed()*5) // auto respawn after 3 sec
-			respawn();
-		//if((input.fire&1) && server_tick()-die_tick >= server_tickspeed()/2) // auto respawn after 0.5 sec
-			//respawn();
-		return;
-	}
-
-	//player_core core;
-	//core.pos = pos;
-	//core.jumped = jumped;
-	core.input = input;
-	core.tick();
-
-	// handle weapons
-	handle_weapons();
-
-	player_state = input.player_state;
-
-	// Previnput
-	previnput = input;
-	return;
-}
-
-void PLAYER::tick_defered()
-{
-	if(!dead)
-	{
-		vec2 start_pos = core.pos;
-		vec2 start_vel = core.vel;
-		bool stuck_before = test_box(core.pos, vec2(28.0f, 28.0f));
-		
-		core.move();
-		bool stuck_after_move = test_box(core.pos, vec2(28.0f, 28.0f));
-		core.quantize();
-		bool stuck_after_quant = test_box(core.pos, vec2(28.0f, 28.0f));
-		pos = core.pos;
-		
-		if(!stuck_before && (stuck_after_move || stuck_after_quant))
-		{
-			dbg_msg("player", "STUCK!!! %d %d %d %f %f %f %f %x %x %x %x", 
-				stuck_before,
-				stuck_after_move,
-				stuck_after_quant,
-				start_pos.x, start_pos.y,
-				start_vel.x, start_vel.y,
-				*((unsigned *)&start_pos.x), *((unsigned *)&start_pos.y),
-				*((unsigned *)&start_vel.x), *((unsigned *)&start_vel.y));
-		}
-
-		int events = core.triggered_events;
-		int mask = cmask_all_except_one(client_id);
-		
-		if(events&COREEVENT_GROUND_JUMP) create_sound(pos, SOUND_PLAYER_JUMP, mask);
-		if(events&COREEVENT_AIR_JUMP)
-		{
-			create_sound(pos, SOUND_PLAYER_AIRJUMP, mask);
-			NETEVENT_COMMON *c = (NETEVENT_COMMON *)::events.create(NETEVENTTYPE_AIRJUMP, sizeof(NETEVENT_COMMON), mask);
-			if(c)
-			{
-				c->x = (int)pos.x;
-				c->y = (int)pos.y;
-			}
-		}
-		
-		//if(events&COREEVENT_HOOK_LAUNCH) snd_play_random(CHN_WORLD, SOUND_HOOK_LOOP, 1.0f, pos);
-		if(events&COREEVENT_HOOK_ATTACH_PLAYER) create_sound(pos, SOUND_HOOK_ATTACH_PLAYER, cmask_all());
-		if(events&COREEVENT_HOOK_ATTACH_GROUND) create_sound(pos, SOUND_HOOK_ATTACH_GROUND, mask);
-		//if(events&COREEVENT_HOOK_RETRACT) snd_play_random(CHN_WORLD, SOUND_PLAYER_JUMP, 1.0f, pos);
-		
-	}
-	
-	if(team == -1)
-	{
-		pos.x = input.target_x;
-		pos.y = input.target_y;
-	}
-}
-
-void PLAYER::die(int killer, int weapon)
-{
-	if (dead || team == -1)
-		return;
-
-	int mode_special = gamecontroller->on_player_death(this, get_player(killer), weapon);
-
-	dbg_msg("game", "kill killer='%d:%s' victim='%d:%s' weapon=%d special=%d",
-		killer, server_clientname(killer),
-		client_id, server_clientname(client_id), weapon, mode_special);
-
-	// send the kill message
-	NETMSG_SV_KILLMSG msg;
-	msg.killer = killer;
-	msg.victim = client_id;
-	msg.weapon = weapon;
-	msg.mode_special = mode_special;
-	msg.pack(MSGFLAG_VITAL);
-	server_send_msg(-1);
-
-	// a nice sound
-	create_sound(pos, SOUND_PLAYER_DIE);
-
-	// set dead state
-	die_pos = pos;
-	dead = true;
-	die_tick = server_tick();
-	clear_flag(FLAG_PHYSICS);
-	create_death(pos, client_id);
-}
-
-bool PLAYER::take_damage(vec2 force, int dmg, int from, int weapon)
-{
-	core.vel += force;
-	
-	if(gamecontroller->is_friendly_fire(client_id, from) && !config.sv_teamdamage)
-		return false;
-
-	// player only inflicts half damage on self
-	if(from == client_id)
-		dmg = max(1, dmg/2);
-
-	// CTF and TDM (TODO: check for FF)
-	//if (gameobj->gametype != GAMETYPE_DM && from >= 0 && players[from].team == team)
-		//return false;
-
-	damage_taken++;
-
-	// create healthmod indicator
-	if(server_tick() < damage_taken_tick+25)
-	{
-		// make sure that the damage indicators doesn't group together
-		create_damageind(pos, damage_taken*0.25f, dmg);
-	}
-	else
-	{
-		damage_taken = 0;
-		create_damageind(pos, 0, dmg);
-	}
-
-	if(dmg)
-	{
-		if(armor)
-		{
-			if(dmg > 1)
-			{
-				health--;
-				dmg--;
-			}
-			
-			if(dmg > armor)
-			{
-				dmg -= armor;
-				armor = 0;
-			}
-			else
-			{
-				armor -= dmg;
-				dmg = 0;
-			}
-		}
-		
-		health -= dmg;
-	}
-
-	damage_taken_tick = server_tick();
-
-	// do damage hit sound
-	if(from >= 0 && from != client_id)
-		create_sound(get_player(from)->pos, SOUND_HIT, cmask_one(from));
-
-	// check for death
-	if(health <= 0)
-	{
-		die(from, weapon);
-
-		// set attacker's face to happy (taunt!)
-		if (from >= 0 && from != client_id)
-		{
-			PLAYER *p = get_player(from);
-
-			p->emote_type = EMOTE_HAPPY;
-			p->emote_stop = server_tick() + server_tickspeed();
-		}
-
-		return false;
-	}
-
-	if (dmg > 2)
-		create_sound(pos, SOUND_PLAYER_PAIN_LONG);
-	else
-		create_sound(pos, SOUND_PLAYER_PAIN_SHORT);
-
-	emote_type = EMOTE_PAIN;
-	emote_stop = server_tick() + 500 * server_tickspeed() / 1000;
-
-	// spawn blood?
-	return true;
-}
-
-void PLAYER::snap(int snaping_client)
-{
-	if(1)
-	{
-		NETOBJ_PLAYER_INFO *info = (NETOBJ_PLAYER_INFO *)snap_new_item(NETOBJTYPE_PLAYER_INFO, client_id, sizeof(NETOBJ_PLAYER_INFO));
-
-		info->latency = latency_min;
-		info->latency_flux = latency_max-latency_min;
-		info->local = 0;
-		info->cid = client_id;
-		info->score = score;
-		info->team = team;
-
-		if(client_id == snaping_client)
-			info->local = 1;
-	}
-
-	if(!dead && health > 0 && team >= 0 && distance(players[snaping_client].pos, pos) < 1000.0f)
-	{
-		NETOBJ_PLAYER_CHARACTER *character = (NETOBJ_PLAYER_CHARACTER *)snap_new_item(NETOBJTYPE_PLAYER_CHARACTER, client_id, sizeof(NETOBJ_PLAYER_CHARACTER));
-
-		core.write(character);
-
-		// this is to make sure that players that are just standing still
-		// isn't sent. this is because the physics keep bouncing between
-		// 0-128 when just standing.
-		// TODO: fix the physics so this isn't needed
-		if(snaping_client != client_id && abs(character->vy) < 256.0f)
-			character->vy = 0;
-
-		if (emote_stop < server_tick())
-		{
-			emote_type = EMOTE_NORMAL;
-			emote_stop = -1;
-		}
-
-		character->emote = emote_type;
-
-		character->ammocount = 0;
-		character->health = 0;
-		character->armor = 0;
-		
-		character->weapon = active_weapon;
-		character->attacktick = attack_tick;
-
-		character->wanted_direction = input.direction;
-		/*
-		if(input.left && !input.right)
-			character->wanted_direction = -1;
-		else if(!input.left && input.right)
-			character->wanted_direction = 1;*/
-
-
-		if(client_id == snaping_client)
-		{
-			character->health = health;
-			character->armor = armor;
-			if(weapons[active_weapon].ammo > 0)
-				character->ammocount = weapons[active_weapon].ammo;
-		}
-
-		if (character->emote == EMOTE_NORMAL)
-		{
-			if(250 - ((server_tick() - last_action)%(250)) < 5)
-				character->emote = EMOTE_BLINK;
-		}
-
-		character->player_state = player_state;
-	}
-}
-
-PLAYER *players;
-
-//////////////////////////////////////////////////
-// powerup
-//////////////////////////////////////////////////
-PICKUP::PICKUP(int _type, int _subtype)
-: ENTITY(NETOBJTYPE_PICKUP)
-{
-	type = _type;
-	subtype = _subtype;
-	proximity_radius = phys_size;
-
-	reset();
-
-	// TODO: should this be done here?
-	world->insert_entity(this);
-}
-
-void PICKUP::reset()
-{
-	if (data->pickups[type].spawndelay > 0)
-		spawntick = server_tick() + server_tickspeed() * data->pickups[type].spawndelay;
-	else
-		spawntick = -1;
-}
-
-
-void send_weapon_pickup(int cid, int weapon);
-
-void PICKUP::tick()
-{
-	// wait for respawn
-	if(spawntick > 0)
-	{
-		if(server_tick() > spawntick)
-		{
-			// respawn
-			spawntick = -1;
-
-			if(type == POWERUP_WEAPON)
-				create_sound(pos, SOUND_WEAPON_SPAWN);
-		}
-		else
-			return;
-	}
-	// Check if a player intersected us
-	PLAYER* pplayer = closest_player(pos, 20.0f, 0);
-	if (pplayer)
-	{
-		// player picked us up, is someone was hooking us, let them go
-		int respawntime = -1;
-		switch (type)
-		{
-		case POWERUP_HEALTH:
-			if(pplayer->health < 10)
-			{
-				create_sound(pos, SOUND_PICKUP_HEALTH);
-				pplayer->health = min(10, pplayer->health + 1);
-				respawntime = data->pickups[type].respawntime;
-			}
-			break;
-		case POWERUP_ARMOR:
-			if(pplayer->armor < 10)
-			{
-				create_sound(pos, SOUND_PICKUP_ARMOR);
-				pplayer->armor = min(10, pplayer->armor + 1);
-				respawntime = data->pickups[type].respawntime;
-			}
-			break;
-
-		case POWERUP_WEAPON:
-			if(subtype >= 0 && subtype < NUM_WEAPONS)
-			{
-				if(pplayer->weapons[subtype].ammo < data->weapons.id[subtype].maxammo || !pplayer->weapons[subtype].got)
-				{
-					pplayer->weapons[subtype].got = true;
-					pplayer->weapons[subtype].ammo = min(data->weapons.id[subtype].maxammo, pplayer->weapons[subtype].ammo + 10);
-					respawntime = data->pickups[type].respawntime;
-
-					// TODO: data compiler should take care of stuff like this
-					if(subtype == WEAPON_GRENADE)
-						create_sound(pos, SOUND_PICKUP_GRENADE);
-					else if(subtype == WEAPON_SHOTGUN)
-						create_sound(pos, SOUND_PICKUP_SHOTGUN);
-					else if(subtype == WEAPON_RIFLE)
-						create_sound(pos, SOUND_PICKUP_SHOTGUN);
-
-                    send_weapon_pickup(pplayer->client_id, subtype);
-				}
-			}
-			break;
-		case POWERUP_NINJA:
-			{
-				// activate ninja on target player
-				pplayer->ninja.activationtick = server_tick();
-				pplayer->weapons[WEAPON_NINJA].got = true;
-				pplayer->last_weapon = pplayer->active_weapon;
-				pplayer->active_weapon = WEAPON_NINJA;
-				respawntime = data->pickups[type].respawntime;
-				create_sound(pos, SOUND_PICKUP_NINJA);
-
-				// loop through all players, setting their emotes
-				ENTITY *ents[64];
-				const int types[] = {NETOBJTYPE_PLAYER_CHARACTER};
-				int num = world->find_entities(vec2(0, 0), 1000000, ents, 64, types, 1);
-				for (int i = 0; i < num; i++)
-				{
-					PLAYER *p = (PLAYER *)ents[i];
-					if (p != pplayer)
-					{
-						p->emote_type = EMOTE_SURPRISE;
-						p->emote_stop = server_tick() + server_tickspeed();
-					}
-				}
-
-				pplayer->emote_type = EMOTE_ANGRY;
-				pplayer->emote_stop = server_tick() + 1200 * server_tickspeed() / 1000;
-				
-				break;
-			}
-		default:
-			break;
-		};
-
-		if(respawntime >= 0)
-		{
-			dbg_msg("game", "pickup player='%d:%s' item=%d/%d",
-				pplayer->client_id, server_clientname(pplayer->client_id), type, subtype);
-			spawntick = server_tick() + server_tickspeed() * respawntime;
-		}
-	}
-}
-
-void PICKUP::snap(int snapping_client)
-{
-	if(spawntick != -1)
-		return;
-
-	NETOBJ_PICKUP *up = (NETOBJ_PICKUP *)snap_new_item(NETOBJTYPE_PICKUP, id, sizeof(NETOBJ_PICKUP));
-	up->x = (int)pos.x;
-	up->y = (int)pos.y;
-	up->type = type; // TODO: two diffrent types? what gives?
-	up->subtype = subtype;
+		players[i].init(-1);
 }
 
-// POWERUP END ///////////////////////
 
-PLAYER *get_player(int index)
-{
-	return &players[index];
-}
-
-void create_damageind(vec2 p, float angle, int amount)
+void GAMECONTEXT::create_damageind(vec2 p, float angle, int amount)
 {
 	float a = 3 * 3.14159f / 2 + angle;
 	//float a = get_angle(dir);
@@ -1852,7 +568,7 @@ void create_damageind(vec2 p, float angle, int amount)
 	}
 }
 
-void create_explosion(vec2 p, int owner, int weapon, bool bnodamage)
+void GAMECONTEXT::create_explosion(vec2 p, int owner, int weapon, bool bnodamage)
 {
 	// create the event
 	NETEVENT_EXPLOSION *ev = (NETEVENT_EXPLOSION *)events.create(NETEVENTTYPE_EXPLOSION, sizeof(NETEVENT_EXPLOSION));
@@ -1865,11 +581,10 @@ void create_explosion(vec2 p, int owner, int weapon, bool bnodamage)
 	if (!bnodamage)
 	{
 		// deal damage
-		ENTITY *ents[64];
+		CHARACTER *ents[64];
 		float radius = 128.0f;
 		float innerradius = 42.0f;
-
-		int num = world->find_entities(p, radius, ents, 64);
+		int num = game.world.find_entities(p, radius, (ENTITY**)ents, 64, NETOBJTYPE_CHARACTER);
 		for(int i = 0; i < num; i++)
 		{
 			vec2 diff = ents[i]->pos - p;
@@ -1897,7 +612,7 @@ void create_smoke(vec2 p)
 	}
 }*/
 
-void create_playerspawn(vec2 p)
+void GAMECONTEXT::create_playerspawn(vec2 p)
 {
 	// create the event
 	NETEVENT_SPAWN *ev = (NETEVENT_SPAWN *)events.create(NETEVENTTYPE_SPAWN, sizeof(NETEVENT_SPAWN));
@@ -1908,7 +623,7 @@ void create_playerspawn(vec2 p)
 	}
 }
 
-void create_death(vec2 p, int cid)
+void GAMECONTEXT::create_death(vec2 p, int cid)
 {
 	// create the event
 	NETEVENT_DEATH *ev = (NETEVENT_DEATH *)events.create(NETEVENTTYPE_DEATH, sizeof(NETEVENT_DEATH));
@@ -1920,7 +635,7 @@ void create_death(vec2 p, int cid)
 	}
 }
 
-void create_sound(vec2 pos, int sound, int mask)
+void GAMECONTEXT::create_sound(vec2 pos, int sound, int mask)
 {
 	if (sound < 0)
 		return;
@@ -1935,7 +650,7 @@ void create_sound(vec2 pos, int sound, int mask)
 	}
 }
 
-void create_sound_global(int sound, int target)
+void GAMECONTEXT::create_sound_global(int sound, int target)
 {
 	if (sound < 0)
 		return;
@@ -1947,30 +662,28 @@ void create_sound_global(int sound, int target)
 }
 
 // TODO: should be more general
-PLAYER *intersect_player(vec2 pos0, vec2 pos1, float radius, vec2& new_pos, ENTITY *notthis)
+CHARACTER *GAMEWORLD::intersect_character(vec2 pos0, vec2 pos1, float radius, vec2& new_pos, ENTITY *notthis)
 {
 	// Find other players
 	float closest_len = distance(pos0, pos1) * 100.0f;
 	vec2 line_dir = normalize(pos1-pos0);
-	PLAYER *closest = 0;
-		
-	for(int i = 0; i < MAX_CLIENTS; i++)
-	{
-		if(players[i].client_id < 0 || (ENTITY *)&players[i] == notthis)
+	CHARACTER *closest = 0;
+
+	CHARACTER *p = (CHARACTER *)game.world.find_first(NETOBJTYPE_CHARACTER);
+	for(; p; p = (CHARACTER *)p->typenext())
+ 	{
+		if(p == notthis)
 			continue;
 			
-		if(!(players[i].flags&ENTITY::FLAG_PHYSICS))
-			continue;
-
-		vec2 intersect_pos = closest_point_on_line(pos0, pos1, players[i].pos);
-		float len = distance(players[i].pos, intersect_pos);
-		if(len < PLAYER::phys_size+radius)
+		vec2 intersect_pos = closest_point_on_line(pos0, pos1, p->pos);
+		float len = distance(p->pos, intersect_pos);
+		if(len < CHARACTER::phys_size+radius)
 		{
 			if(len < closest_len)
 			{
 				new_pos = intersect_pos;
 				closest_len = len;
-				closest = &players[i];
+				closest = p;
 			}
 		}
 	}
@@ -1979,27 +692,25 @@ PLAYER *intersect_player(vec2 pos0, vec2 pos1, float radius, vec2& new_pos, ENTI
 }
 
 
-PLAYER *closest_player(vec2 pos, float radius, ENTITY *notthis)
+CHARACTER *GAMEWORLD::closest_character(vec2 pos, float radius, ENTITY *notthis)
 {
 	// Find other players
 	float closest_range = radius*2;
-	PLAYER *closest = 0;
+	CHARACTER *closest = 0;
 		
-	for(int i = 0; i < MAX_CLIENTS; i++)
-	{
-		if(players[i].client_id < 0 || (ENTITY *)&players[i] == notthis)
+	CHARACTER *p = (CHARACTER *)game.world.find_first(NETOBJTYPE_CHARACTER);
+	for(; p; p = (CHARACTER *)p->typenext())
+ 	{
+		if(p == notthis)
 			continue;
 			
-		if(!(players[i].flags&ENTITY::FLAG_PHYSICS))
-			continue;
-
-		float len = distance(pos, players[i].pos);
-		if(len < PLAYER::phys_size+radius)
+		float len = distance(pos, p->pos);
+		if(len < CHARACTER::phys_size+radius)
 		{
 			if(len < closest_range)
 			{
 				closest_range = len;
-				closest = &players[i];
+				closest = p;
 			}
 		}
 	}
@@ -2007,26 +718,40 @@ PLAYER *closest_player(vec2 pos, float radius, ENTITY *notthis)
 	return closest;
 }
 
-// Server hooks
-void mods_tick()
+
+void GAMECONTEXT::tick()
 {
-	world->core.tuning = tuning;
-	world->tick();
+	world.core.tuning = tuning;
+	world.tick();
 
-	if(world->paused) // make sure that the game object always updates
-		gamecontroller->tick();
+	if(world.paused) // make sure that the game object always updates
+		controller->tick();
+		
+	for(int i = 0; i < MAX_CLIENTS; i++)
+	{
+		if(players[i].client_id != -1)
+			players[i].tick();
+	}
 }
 
-void mods_snap(int client_id)
+void GAMECONTEXT::snap(int client_id)
 {
-	world->snap(client_id);
+	world.snap(client_id);
 	events.snap(client_id);
+	controller->snap(client_id);
+	
+	for(int i = 0; i < MAX_CLIENTS; i++)
+	{
+		if(players[i].client_id != -1)
+			players[i].snap(client_id);
+	}
 }
 
+// Server hooks
 void mods_client_direct_input(int client_id, void *input)
 {
-	if(!world->paused)
-		players[client_id].on_direct_input((NETOBJ_PLAYER_INPUT *)input);
+	if(!game.world.paused)
+		game.players[client_id].on_direct_input((NETOBJ_PLAYER_INPUT *)input);
 	
 	/*
 	if(i->fire)
@@ -2039,45 +764,60 @@ void mods_client_direct_input(int client_id, void *input)
 
 void mods_client_predicted_input(int client_id, void *input)
 {
-	if(!world->paused)
+	if(!game.world.paused)
+		game.players[client_id].on_predicted_input((NETOBJ_PLAYER_INPUT *)input);
+	
+	/*
 	{
-		if (memcmp(&players[client_id].input, input, sizeof(NETOBJ_PLAYER_INPUT)) != 0)
-			players[client_id].last_action = server_tick();
+		
+		on_predicted_input()
+		if (memcmp(&game.players[client_id].input, input, sizeof(NETOBJ_PLAYER_INPUT)) != 0)
+			game.players[client_id].last_action = server_tick();
 
-		//players[client_id].previnput = players[client_id].input;
-		players[client_id].input = *(NETOBJ_PLAYER_INPUT*)input;
-		players[client_id].num_inputs++;
+		//game.players[client_id].previnput = game.players[client_id].input;
+		game.players[client_id].input = *(NETOBJ_PLAYER_INPUT*)input;
+		game.players[client_id].num_inputs++;
 		
-		if(players[client_id].input.target_x == 0 && players[client_id].input.target_y == 0)
-			players[client_id].input.target_y = -1;
-	}
+		if(game.players[client_id].input.target_x == 0 && game.players[client_id].input.target_y == 0)
+			game.players[client_id].input.target_y = -1;
+	}*/
 }
 
+// Server hooks
+void mods_tick()
+{
+	game.tick();
+}
+
+void mods_snap(int client_id)
+{
+	game.snap(client_id);
+}
 
 void mods_client_enter(int client_id)
 {
-	world->insert_entity(&players[client_id]);
-	players[client_id].respawn();
+	//game.world.insert_entity(&game.players[client_id]);
+	game.players[client_id].respawn();
 	dbg_msg("game", "join player='%d:%s'", client_id, server_clientname(client_id));
 
 
 	char buf[512];
-	str_format(buf, sizeof(buf), "%s entered and joined the %s", server_clientname(client_id), get_team_name(players[client_id].team));
-	send_chat(-1, CHAT_ALL, buf); 
+	str_format(buf, sizeof(buf), "%s entered and joined the %s", server_clientname(client_id), game.controller->get_team_name(game.players[client_id].team));
+	game.send_chat(-1, CHAT_ALL, buf); 
 
-	dbg_msg("game", "team_join player='%d:%s' team=%d", client_id, server_clientname(client_id), players[client_id].team);
+	dbg_msg("game", "team_join player='%d:%s' team=%d", client_id, server_clientname(client_id), game.players[client_id].team);
 }
 
 void mods_connected(int client_id)
 {
-	players[client_id].init();
-	players[client_id].client_id = client_id;
+	game.players[client_id].init(client_id);
+	//game.players[client_id].client_id = client_id;
 	
 	// Check which team the player should be on
 	if(config.sv_tournament_mode)
-		players[client_id].team = -1;
+		game.players[client_id].team = -1;
 	else
-		players[client_id].team = gamecontroller->get_auto_team(client_id);
+		game.players[client_id].team = game.controller->get_auto_team(client_id);
 
 	// send motd
 	NETMSG_SV_MOTD msg;
@@ -2088,16 +828,8 @@ void mods_connected(int client_id)
 
 void mods_client_drop(int client_id)
 {
-	char buf[512];
-	str_format(buf, sizeof(buf),  "%s has left the game", server_clientname(client_id));
-	send_chat(-1, CHAT_ALL, buf);
-
-	dbg_msg("game", "leave player='%d:%s'", client_id, server_clientname(client_id));
+	game.players[client_id].on_disconnect();
 
-	gamecontroller->on_player_death(&players[client_id], 0, -1);
-	world->remove_entity(&players[client_id]);
-	world->core.players[client_id] = 0x0;
-	players[client_id].client_id = -1;
 }
 
 void mods_message(int msgtype, int client_id)
@@ -2114,18 +846,18 @@ void mods_message(int msgtype, int client_id)
 		NETMSG_CL_SAY *msg = (NETMSG_CL_SAY *)rawmsg;
 		int team = msg->team;
 		if(team)
-			team = players[client_id].team;
+			team = game.players[client_id].team;
 		else
 			team = CHAT_ALL;
 		
-		if(config.sv_spamprotection && players[client_id].last_chat+time_freq() > time_get())
+		if(config.sv_spamprotection && game.players[client_id].last_chat+time_freq() > time_get())
 		{
 			// consider this as spam
 		}
 		else
 		{
-			players[client_id].last_chat = time_get();
-			send_chat(client_id, team, msg->message);
+			game.players[client_id].last_chat = time_get();
+			game.send_chat(client_id, team, msg->message);
 		}
 	}
 	else if (msgtype == NETMSGTYPE_CL_SETTEAM)
@@ -2133,21 +865,21 @@ void mods_message(int msgtype, int client_id)
 		NETMSG_CL_SETTEAM *msg = (NETMSG_CL_SETTEAM *)rawmsg;
 
 		// Switch team on given client and kill/respawn him
-		if(gamecontroller->can_join_team(msg->team, client_id))
-			players[client_id].set_team(msg->team);
+		if(game.controller->can_join_team(msg->team, client_id))
+			game.players[client_id].set_team(msg->team);
 		else
 		{
 			char buf[128];
 			str_format(buf, sizeof(buf), "Only %d active players are allowed", config.sv_max_clients-config.sv_spectator_slots);
-			send_broadcast(buf, client_id);
+			game.send_broadcast(buf, client_id);
 		}
 	}
 	else if (msgtype == NETMSGTYPE_CL_CHANGEINFO || msgtype == NETMSGTYPE_CL_STARTINFO)
 	{
 		NETMSG_CL_CHANGEINFO *msg = (NETMSG_CL_CHANGEINFO *)rawmsg;
-		players[client_id].use_custom_color = msg->use_custom_color;
-		players[client_id].color_body = msg->color_body;
-		players[client_id].color_feet = msg->color_feet;
+		game.players[client_id].use_custom_color = msg->use_custom_color;
+		game.players[client_id].color_body = msg->color_body;
+		game.players[client_id].color_feet = msg->color_feet;
 
 		// check for invalid chars
 		/*
@@ -2168,13 +900,13 @@ void mods_message(int msgtype, int client_id)
 		{
 			char chattext[256];
 			str_format(chattext, sizeof(chattext), "%s changed name to %s", oldname, server_clientname(client_id));
-			send_chat(-1, CHAT_ALL, chattext);
+			game.send_chat(-1, CHAT_ALL, chattext);
 		}
 		
 		// set skin
-		str_copy(players[client_id].skin_name, msg->skin, sizeof(players[client_id].skin_name));
+		str_copy(game.players[client_id].skin_name, msg->skin, sizeof(game.players[client_id].skin_name));
 		
-		gamecontroller->on_player_info_change(&players[client_id]);
+		game.controller->on_player_info_change(&game.players[client_id]);
 		
 		if(msgtype == NETMSGTYPE_CL_STARTINFO)
 		{
@@ -2183,8 +915,8 @@ void mods_message(int msgtype, int client_id)
 			// send all info to this client
 			for(int i = 0; i < MAX_CLIENTS; i++)
 			{
-				if(players[i].client_id != -1)
-					send_info(i, client_id);
+				if(game.players[i].client_id != -1)
+					game.send_info(i, client_id);
 			}
 
 			// send tuning parameters to client
@@ -2196,23 +928,20 @@ void mods_message(int msgtype, int client_id)
 			server_send_msg(client_id);			
 		}
 		
-		send_info(client_id, -1);
+		game.send_info(client_id, -1);
 	}
 	else if (msgtype == NETMSGTYPE_CL_EMOTICON)
 	{
 		NETMSG_CL_EMOTICON *msg = (NETMSG_CL_EMOTICON *)rawmsg;
-		send_emoticon(client_id, msg->emoticon);
+		game.send_emoticon(client_id, msg->emoticon);
 	}
 	else if (msgtype == NETMSGTYPE_CL_KILL)
 	{
-		PLAYER *pplayer = get_player(client_id);
-		pplayer->die(client_id, -1);
+		//PLAYER *pplayer = get_player(client_id);
+		game.players[client_id].kill_character(); //(client_id, -1);
 	}
 }
 
-extern unsigned char internal_data[];
-
-
 static void con_tune_param(void *result, void *user_data)
 {
 	const char *param_name = console_arg_string(result, 0);
@@ -2249,19 +978,19 @@ static void con_tune_dump(void *result, void *user_data)
 static void con_restart(void *result, void *user_data)
 {
 	if(console_arg_num(result))
-		gamecontroller->do_warmup(console_arg_int(result, 0));
+		game.controller->do_warmup(console_arg_int(result, 0));
 	else
-		gamecontroller->startround();
+		game.controller->startround();
 }
 
 static void con_broadcast(void *result, void *user_data)
 {
-	send_broadcast(console_arg_string(result, 0), -1);
+	game.send_broadcast(console_arg_string(result, 0), -1);
 }
 
 static void con_say(void *result, void *user_data)
 {
-	send_chat(-1, CHAT_ALL, console_arg_string(result, 0));
+	game.send_chat(-1, CHAT_ALL, console_arg_string(result, 0));
 }
 
 static void con_set_team(void *result, void *user_data)
@@ -2271,10 +1000,10 @@ static void con_set_team(void *result, void *user_data)
 	
 	dbg_msg("", "%d %d", client_id, team);
 	
-	if(players[client_id].client_id != client_id)
+	if(game.players[client_id].client_id != client_id)
 		return;
 	
-	players[client_id].set_team(team);
+	game.players[client_id].set_team(team);
 }
 
 void mods_console_init()
@@ -2293,32 +1022,38 @@ void mods_init()
 {
 	//if(!data) /* only load once */
 		//data = load_data_from_memory(internal_data);
+		
+	for(int i = 0; i < NUM_NETOBJTYPES; i++)
+		snap_set_staticsize(i, netobj_get_size(i));
 
 	layers_init();
 	col_init();
 
-	world = new GAMEWORLD;
-	players = new PLAYER[MAX_CLIENTS];
+	// reset everything here
+	//world = new GAMEWORLD;
+	//players = new PLAYER[MAX_CLIENTS];
 
 	// select gametype
 	if(strcmp(config.sv_gametype, "ctf") == 0)
-		gamecontroller = new GAMECONTROLLER_CTF;
+		game.controller = new GAMECONTROLLER_CTF;
 	else if(strcmp(config.sv_gametype, "tdm") == 0)
-		gamecontroller = new GAMECONTROLLER_TDM;
+		game.controller = new GAMECONTROLLER_TDM;
 	else
-		gamecontroller = new GAMECONTROLLER_DM;
+		game.controller = new GAMECONTROLLER_DM;
 
 	// setup core world
-	for(int i = 0; i < MAX_CLIENTS; i++)
-		players[i].core.world = &world->core;
+	//for(int i = 0; i < MAX_CLIENTS; i++)
+	//	game.players[i].core.world = &game.world.core;
 
 	// create all entities from the game layer
 	MAPITEM_LAYER_TILEMAP *tmap = layers_game_layer();
 	TILE *tiles = (TILE *)map_get_data(tmap->data);
 	
+	/*
 	num_spawn_points[0] = 0;
 	num_spawn_points[1] = 0;
 	num_spawn_points[2] = 0;
+	*/
 	
 	for(int y = 0; y < tmap->height; y++)
 	{
@@ -2326,11 +1061,11 @@ void mods_init()
 		{
 			int index = tiles[y*tmap->width+x].index - ENTITY_OFFSET;
 			vec2 pos(x*32.0f+16.0f, y*32.0f+16.0f);
-			gamecontroller->on_entity(index, pos);
+			game.controller->on_entity(index, pos);
 		}
 	}
 
-	world->insert_entity(gamecontroller);
+	//game.world.insert_entity(game.controller);
 
 	if(config.dbg_dummies)
 	{
@@ -2338,26 +1073,22 @@ void mods_init()
 		{
 			mods_connected(MAX_CLIENTS-i-1);
 			mods_client_enter(MAX_CLIENTS-i-1);
-			if(gamecontroller->gametype != GAMETYPE_DM)
-				players[MAX_CLIENTS-i-1].team = i&1;
+			if(game.controller->gametype != GAMETYPE_DM)
+				game.players[MAX_CLIENTS-i-1].team = i&1;
 		}
 	}
 }
 
 void mods_shutdown()
 {
-	delete [] players;
-	delete gamecontroller;
-	delete world;
-	gamecontroller = 0;
-	players = 0;
-	world = 0;
+	delete game.controller;
+	game.controller = 0;
 }
 
 void mods_presnap() {}
 void mods_postsnap()
 {
-	events.clear();
+	game.events.clear();
 }
 
 extern "C" const char *mods_net_version() { return GAME_NETVERSION; }
diff --git a/src/mastersrv/mastersrv.cpp b/src/mastersrv/mastersrv.cpp
index c9022328..c27bb03c 100644
--- a/src/mastersrv/mastersrv.cpp
+++ b/src/mastersrv/mastersrv.cpp
@@ -13,22 +13,22 @@ enum {
 	EXPIRE_TIME = 90
 };
 
-static struct check_server
+static struct CHECK_SERVER
 {
-	NETADDR4 address;
-	NETADDR4 alt_address;
+	NETADDR address;
+	NETADDR alt_address;
 	int try_count;
 	int64 try_time;
 } check_servers[MAX_SERVERS];
 static int num_checkservers = 0;
 
-static struct packet_data
+static struct PACKET_DATA
 {
 	unsigned char header[sizeof(SERVERBROWSE_LIST)];
-	NETADDR4 servers[MAX_SERVERS];
+	NETADDR servers[MAX_SERVERS];
 } data;
 
-static struct count_packet_data
+static struct COUNT_PACKET_DATA
 {
 	unsigned char header[sizeof(SERVERBROWSE_COUNT)];
 	unsigned char high;
@@ -41,7 +41,7 @@ static int num_servers = 0;
 static net_client net_checker; // NAT/FW checker
 static net_client net_op; // main
 
-void send_ok(NETADDR4 *addr)
+void send_ok(NETADDR *addr)
 {
 	NETCHUNK p;
 	p.client_id = -1;
@@ -55,7 +55,7 @@ void send_ok(NETADDR4 *addr)
 	net_op.send(&p);
 }
 
-void send_error(NETADDR4 *addr)
+void send_error(NETADDR *addr)
 {
 	NETCHUNK p;
 	p.client_id = -1;
@@ -66,7 +66,7 @@ void send_error(NETADDR4 *addr)
 	net_op.send(&p);
 }
 
-void send_check(NETADDR4 *addr)
+void send_check(NETADDR *addr)
 {
 	NETCHUNK p;
 	p.client_id = -1;
@@ -77,7 +77,7 @@ void send_check(NETADDR4 *addr)
 	net_checker.send(&p);
 }
 
-void add_checkserver(NETADDR4 *info, NETADDR4 *alt)
+void add_checkserver(NETADDR *info, NETADDR *alt)
 {
 	// add server
 	if(num_checkservers == MAX_SERVERS)
@@ -96,13 +96,13 @@ void add_checkserver(NETADDR4 *info, NETADDR4 *alt)
 	num_checkservers++;
 }
 
-void add_server(NETADDR4 *info)
+void add_server(NETADDR *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)
+		if(net_addr_comp(&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);
@@ -183,7 +183,7 @@ void purge_servers()
 
 int main(int argc, char **argv)
 {
-	NETADDR4 bindaddr;
+	NETADDR bindaddr;
 	mem_zero(&bindaddr, sizeof(bindaddr));
 	bindaddr.port = MASTERSERVER_PORT;
 	
@@ -210,7 +210,7 @@ int main(int argc, char **argv)
 			if(packet.data_size == sizeof(SERVERBROWSE_HEARTBEAT)+2 &&
 				memcmp(packet.data, SERVERBROWSE_HEARTBEAT, sizeof(SERVERBROWSE_HEARTBEAT)) == 0)
 			{
-				NETADDR4 alt;
+				NETADDR alt;
 				unsigned char *d = (unsigned char *)packet.data;
 				alt = packet.address;
 				alt.port =
@@ -244,7 +244,7 @@ int main(int argc, char **argv)
 				p.client_id = -1;
 				p.address = packet.address;
 				p.flags = NETSENDFLAG_CONNLESS;
-				p.data_size = num_servers*sizeof(NETADDR4)+sizeof(SERVERBROWSE_LIST);
+				p.data_size = num_servers*sizeof(NETADDR)+sizeof(SERVERBROWSE_LIST);
 				p.data = &data;
 				net_op.send(&p);
 			}
@@ -259,8 +259,8 @@ int main(int argc, char **argv)
 				// remove it from checking
 				for(int i = 0; i < num_checkservers; i++)
 				{
-					if(net_addr4_cmp(&check_servers[i].address, &packet.address) == 0 ||
-						net_addr4_cmp(&check_servers[i].alt_address, &packet.address) == 0)
+					if(net_addr_comp(&check_servers[i].address, &packet.address) == 0 ||
+						net_addr_comp(&check_servers[i].alt_address, &packet.address) == 0)
 					{
 						num_checkservers--;
 						check_servers[i] = check_servers[num_checkservers];
diff --git a/src/tools/crapnet.cpp b/src/tools/crapnet.cpp
index ba8a30d8..f807bb61 100644
--- a/src/tools/crapnet.cpp
+++ b/src/tools/crapnet.cpp
@@ -8,7 +8,7 @@ struct packet
 	packet *prev;
 	packet *next;
 	
-	NETADDR4 send_to;
+	NETADDR send_to;
 	int64 timestamp;
 	int id;
 	int data_size;
@@ -20,10 +20,10 @@ static packet *last = (packet *)0;
 static int current_latency = 0;
 static int debug = 0;
 
-int run(int port, NETADDR4 dest)
+int run(int port, NETADDR dest)
 {
-	NETADDR4 src = {{0,0,0,0},port};
-	NETSOCKET socket = net_udp4_create(src);
+	NETADDR src = {NETTYPE_IPV4, {0,0,0,0},port};
+	NETSOCKET socket = net_udp_create(src);
 	
 	char buffer[1024*2];
 	int id = 0;
@@ -35,8 +35,8 @@ int run(int port, NETADDR4 dest)
 		{
 			// fetch data
 			int data_trash = 0;
-			NETADDR4 from;
-			int bytes = net_udp4_recv(socket, &from, buffer, 1024*2);
+			NETADDR from;
+			int bytes = net_udp_recv(socket, &from, buffer, 1024*2);
 			if(bytes <= 0)
 				break;
 				
@@ -46,7 +46,7 @@ int run(int port, NETADDR4 dest)
 			// create new packet				
 			packet *p = (packet *)mem_alloc(sizeof(packet)+bytes, 1);
 
-			if(net_addr4_cmp(&from, &dest) == 0)
+			if(net_addr_comp(&from, &dest) == 0)
 			{
 				p->send_to = src; // from the server
 			}
@@ -112,7 +112,7 @@ int run(int port, NETADDR4 dest)
 				
 				// send and remove packet
 				//if((rand()%20) != 0) // heavy packetloss
-				net_udp4_send(socket, &p->send_to, p->data, p->data_size);
+				net_udp_send(socket, &p->send_to, p->data, p->data_size);
 				
 				// update lag
 				double flux = rand()/(double)RAND_MAX;
@@ -136,7 +136,7 @@ int run(int port, NETADDR4 dest)
 
 int main(int argc, char **argv)
 {
-	NETADDR4 a = {{127,0,0,1},8303};
+	NETADDR a = {NETTYPE_IPV4, {127,0,0,1},8303};
 	run(8302, a);
 	return 0;
 }
diff --git a/src/tools/fake_server.c b/src/tools/fake_server.c
index 9f588e37..763caa73 100644
--- a/src/tools/fake_server.c
+++ b/src/tools/fake_server.c
@@ -37,7 +37,7 @@ int flags = 0;
 const char *version = "0.3.0 2d82e361de24cb25";
 const char *map = "somemap";
 const char *server_name = "unnamed server";
-NETADDR4 master_servers[16] = {{{0},0}};
+NETADDR master_servers[16] = {{0,{0},0}};
 int num_masters = 0;
 
 const char *player_names[16] = {0};
@@ -109,7 +109,7 @@ static void build_infomessage()
 	}
 }
 
-static void send_serverinfo(NETADDR4 *addr)
+static void send_serverinfo(NETADDR *addr)
 {
 	NETCHUNK p;
 	p.client_id = -1;
@@ -120,7 +120,7 @@ static void send_serverinfo(NETADDR4 *addr)
 	netserver_send(net, &p);
 }
 
-static void send_fwcheckresponse(NETADDR4 *addr)
+static void send_fwcheckresponse(NETADDR *addr)
 {
 	NETCHUNK p;
 	p.client_id = -1;
@@ -134,7 +134,7 @@ static void send_fwcheckresponse(NETADDR4 *addr)
 static int run()
 {
 	int64 next_heartbeat = 0;
-	NETADDR4 bindaddr = {{0},0};
+	NETADDR bindaddr = {NETTYPE_IPV4, {0},0};
 	net = netserver_open(bindaddr, 0, 0);
 	if(!net)
 		return -1;
diff --git a/src/tools/packetgen.c b/src/tools/packetgen.c
index dcfe0885..9e46c44d 100644
--- a/src/tools/packetgen.c
+++ b/src/tools/packetgen.c
@@ -3,15 +3,15 @@
 
 enum { NUM_SOCKETS = 64 };
 
-int run(NETADDR4 dest)
+int run(NETADDR dest)
 {
 	NETSOCKET sockets[NUM_SOCKETS];
 	int i;
 	
 	for(i = 0; i < NUM_SOCKETS; i++)
 	{
-		NETADDR4 bindaddr = {{0,0,0,0}, 0};
-	 	sockets[i] = net_udp4_create(bindaddr);
+		NETADDR bindaddr = {NETTYPE_IPV4, {0}, 0};
+	 	sockets[i] = net_udp_create(bindaddr);
 	}
 	
 	while(1)
@@ -24,13 +24,13 @@ int run(NETADDR4 dest)
 		size %= 256;
 		socket_to_use %= NUM_SOCKETS;
 		io_read(io_stdin(), data, size);
-		net_udp4_send(sockets[socket_to_use], &dest, data, size);
+		net_udp_send(sockets[socket_to_use], &dest, data, size);
 	}
 }
 
 int main(int argc, char **argv)
 {
-	NETADDR4 dest = {{127,0,0,1},8303};
+	NETADDR dest = {NETTYPE_IPV4, {127,0,0,1},8303};
 	run(dest);
 	return 0;
 }