about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/base/system.c45
-rw-r--r--src/base/system.h10
-rw-r--r--src/engine/client/ec_client.c6
-rw-r--r--src/engine/e_network.c1044
-rw-r--r--src/engine/e_network.h24
-rw-r--r--src/engine/e_network_client.c148
-rw-r--r--src/engine/e_network_conn.c364
-rw-r--r--src/engine/e_network_internal.h149
-rw-r--r--src/engine/e_network_server.c387
-rw-r--r--src/engine/e_ringbuffer.c61
-rw-r--r--src/engine/e_ringbuffer.h20
-rw-r--r--src/engine/server/es_server.c2
-rw-r--r--src/game/client/components/console.cpp4
13 files changed, 1224 insertions, 1040 deletions
diff --git a/src/base/system.c b/src/base/system.c
index e2c37bbd..5fb48e12 100644
--- a/src/base/system.c
+++ b/src/base/system.c
@@ -58,6 +58,7 @@ static DBG_LOGGER loggers[16];
 static int num_loggers = 0;
 
 static NETSTATS network_stats = {0};
+static MEMSTATS memory_stats = {0};
 
 void dbg_logger(DBG_LOGGER logger)
 {
@@ -140,41 +141,39 @@ void dbg_logger_file(const char *filename)
 
 int memory_alloced = 0;
 
-struct memheader
+typedef struct MEMHEADER
 {
 	const char *filename;
 	int line;
 	int size;
-	struct memheader *prev;
-	struct memheader *next;
-};
+	struct MEMHEADER *prev;
+	struct MEMHEADER *next;
+} MEMHEADER;
 
-struct memtail
+typedef struct MEMTAIL
 {
 	int guard;
-};
+} MEMTAIL;
 
-static struct memheader *first = 0;
-
-int mem_allocated()
-{
-	return memory_alloced;
-}
+static struct MEMHEADER *first = 0;
 
 void *mem_alloc_debug(const char *filename, int line, unsigned size, unsigned alignment)
 {
 	/* TODO: fix alignment */
 	/* TODO: add debugging */
-	struct memheader *header = (struct memheader *)malloc(size+sizeof(struct memheader)+sizeof(struct memtail));
-	struct memtail *tail = (struct memtail *)(((char*)(header+1))+size);
+	MEMHEADER *header = (struct MEMHEADER *)malloc(size+sizeof(MEMHEADER)+sizeof(MEMTAIL));
+	MEMTAIL *tail = (struct MEMTAIL *)(((char*)(header+1))+size);
 	header->size = size;
 	header->filename = filename;
 	header->line = line;
-	memory_alloced += header->size;
+
+	memory_stats.allocated += header->size;
+	memory_stats.total_allocations++;
+	memory_stats.active_allocations++;
 	
 	tail->guard = 0xbaadc0de;
 
-	header->prev = (struct memheader *)0;
+	header->prev = (MEMHEADER *)0;
 	header->next = first;
 	if(first)
 		first->prev = header;
@@ -188,13 +187,14 @@ void mem_free(void *p)
 {
 	if(p)
 	{
-		struct memheader *header = (struct memheader *)p - 1;
-		struct memtail *tail = (struct memtail *)(((char*)(header+1))+header->size);
+		MEMHEADER *header = (MEMHEADER *)p - 1;
+		MEMTAIL *tail = (MEMTAIL *)(((char*)(header+1))+header->size);
 		
 		if(tail->guard != 0xbaadc0de)
 			dbg_msg("mem", "!! %p", p);
 		/* dbg_msg("mem", "-- %p", p); */
-		memory_alloced -= header->size;
+		memory_stats.allocated -= header->size;
+		memory_stats.active_allocations--;
 		
 		if(header->prev)
 			header->prev->next = header->next;
@@ -210,7 +210,7 @@ void mem_free(void *p)
 void mem_debug_dump()
 {
 	char buf[1024];
-	struct memheader *header = first;
+	MEMHEADER *header = first;
 	IOHANDLE f = io_open("memory.txt", IOFLAG_WRITE);
 	
 	while(header)
@@ -1109,6 +1109,11 @@ int mem_comp(const void *a, const void *b, int size)
 	return memcmp(a,b,size);
 }
 
+const MEMSTATS *mem_stats()
+{
+	return &memory_stats;
+}
+
 void net_stats(NETSTATS *stats_inout)
 {
 	*stats_inout = network_stats;
diff --git a/src/base/system.h b/src/base/system.h
index fdbfe72d..e7c3586f 100644
--- a/src/base/system.h
+++ b/src/base/system.h
@@ -885,7 +885,6 @@ int net_would_block();
 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);
 
@@ -897,6 +896,15 @@ void dbg_logger_file(const char *filename);
 
 typedef struct
 {
+	int allocated;
+	int active_allocations;
+	int total_allocations;
+} MEMSTATS;
+
+const MEMSTATS *mem_stats();
+
+typedef struct
+{
 	int sent_packets;
 	int sent_bytes;
 	int recv_packets;
diff --git a/src/engine/client/ec_client.c b/src/engine/client/ec_client.c
index 1a6a24f1..8d8bdad7 100644
--- a/src/engine/client/ec_client.c
+++ b/src/engine/client/ec_client.c
@@ -586,12 +586,12 @@ static void client_debug_render()
 		udp = 8
 		total = 42
 	*/
-	
 	frametime_avg = frametime_avg*0.9f + frametime*0.1f;
-	str_format(buffer, sizeof(buffer), "ticks: %8d %8d snaploss: %d  mem %dk   gfxmem: %dk  fps: %3d",
+	str_format(buffer, sizeof(buffer), "ticks: %8d %8d snaploss: %d  mem %dk %d  gfxmem: %dk  fps: %3d",
 		current_tick, current_predtick,
 		snaploss,
-		mem_allocated()/1024,
+		mem_stats()->allocated/1024,
+		mem_stats()->total_allocations,
 		gfx_memory_usage()/1024,
 		(int)(1.0f/frametime_avg));
 	gfx_quads_text(2, 2, 16, buffer);
diff --git a/src/engine/e_network.c b/src/engine/e_network.c
index 85a9117c..2ed45707 100644
--- a/src/engine/e_network.c
+++ b/src/engine/e_network.c
@@ -5,213 +5,77 @@
 
 #include "e_config.h"
 #include "e_network.h"
+#include "e_network_internal.h"
 #include "e_huffman.h"
 
-/*
-
-CURRENT:
-	packet header: 3 bytes
-		unsigned char flags_ack; // 4bit flags, 4bit ack
-		unsigned char ack; // 8 bit ack
-		unsigned char num_chunks; // 8 bit chunks
-		
-		(unsigned char padding[3])	// 24 bit extra incase it's a connection less packet
-									// this is to make sure that it's compatible with the
-									// old protocol
-
-	chunk header: 2-3 bytes
-		unsigned char flags_size; // 2bit flags, 6 bit size
-		unsigned char size_seq; // 4bit size, 4bit seq
-		(unsigned char seq;) // 8bit seq, if vital flag is set
-*/
-
-enum
+void recvinfo_clear(NETRECVINFO *info)
 {
-	NET_VERSION = 2,
-
-	NET_MAX_CHUNKSIZE = 1024,
-	NET_MAX_PAYLOAD = NET_MAX_CHUNKSIZE+16,
-	NET_MAX_PACKETSIZE = NET_MAX_PAYLOAD+16,
-	NET_MAX_CHUNKHEADERSIZE = 5,
-	NET_PACKETHEADERSIZE = 3,
-	NET_MAX_CLIENTS = 128,
-	NET_MAX_SEQUENCE = 1<<10,
-	NET_SEQUENCE_MASK = NET_MAX_SEQUENCE-1,
-
-	NET_CONNSTATE_OFFLINE=0,
-	NET_CONNSTATE_CONNECT=1,
-	NET_CONNSTATE_CONNECTACCEPTED=2,
-	NET_CONNSTATE_ONLINE=3,
-	NET_CONNSTATE_ERROR=4,
-
-	NET_PACKETFLAG_CONTROL=1,
-	NET_PACKETFLAG_CONNLESS=2,
-	NET_PACKETFLAG_RESEND=4,
-	NET_PACKETFLAG_COMPRESSION=8,
-
-	NET_CHUNKFLAG_VITAL=1,
-	NET_CHUNKFLAG_RESEND=2,
-	
-	NET_CTRLMSG_KEEPALIVE=0,
-	NET_CTRLMSG_CONNECT=1,
-	NET_CTRLMSG_CONNECTACCEPT=2,
-	NET_CTRLMSG_ACCEPT=3,
-	NET_CTRLMSG_CLOSE=4,
-	
-	NET_ENUM_TERMINATOR
-};
-
-typedef struct
-{
-	int flags;
-	int ack;
-	int num_chunks;
-	int data_size;
-	unsigned char chunk_data[NET_MAX_PAYLOAD];
-} NETPACKETCONSTRUCT;
-
-typedef struct
-{
-	int flags;
-	int size;
-	int sequence;
-} NETCHUNKHEADER;
-
-typedef struct
-{
-	int flags;
-	int data_size;
-	unsigned char *data;
-
-	int sequence;
-	int64 first_send_time;
-} NETCHUNKDATA;
-
-typedef struct RINGBUFFER_ITEM
-{
-	struct RINGBUFFER_ITEM *next;
-	struct RINGBUFFER_ITEM *prev;
-	int size;
-} RINGBUFFER_ITEM;
-
-typedef struct
-{
-	RINGBUFFER_ITEM *first;
-	RINGBUFFER_ITEM *last;
-	unsigned buffer_size;
-} RINGBUFFER;
-
-static void rb_init(RINGBUFFER *rb)
-{
-	rb->first = 0;
-	rb->last = 0;
-	rb->buffer_size = 0;
+	info->valid = 0;
 }
 
-static void *rb_item_data(RINGBUFFER_ITEM *item)
+void recvinfo_start(NETRECVINFO *info, NETADDR *addr, NETCONNECTION *conn, int cid)
 {
-	return (void*)(item+1);
+	info->addr = *addr;
+	info->conn = conn;
+	info->client_id = cid;
+	info->current_chunk = 0;
+	info->valid = 1;
 }
 
-static void *rb_alloc(RINGBUFFER *rb, int size)
+/* TODO: rename this function */
+int recvinfo_fetch_chunk(NETRECVINFO *info, NETCHUNK *chunk)
 {
-	RINGBUFFER_ITEM *item = (RINGBUFFER_ITEM*)mem_alloc(sizeof(RINGBUFFER_ITEM)+size, 1);
-	item->size = size;
-	
-	item->prev = rb->last;
-	item->next = 0;
-	if(rb->last)
-		rb->last->next = item;
-	else
-		rb->first = item;
-	rb->last = item;
+	NETCHUNKHEADER header;
+	unsigned char *data = info->data.chunk_data;
+	int i;
 	
-	rb->buffer_size += size;
-	return rb_item_data(item);
-}
-
-static void rb_pop_first(RINGBUFFER *rb)
-{
-	if(rb->first)
+	while(1)
 	{
-		RINGBUFFER_ITEM *next = rb->first->next;
-		rb->buffer_size -= rb->first->size;
-		mem_free(rb->first);
-		rb->first = next;
-		if(rb->first)
-			rb->first->prev = (void*)0;
-		else
-			rb->last = (void*)0;
+		/* check for old data to unpack */
+		if(!info->valid || info->current_chunk >= info->data.num_chunks)
+		{
+			recvinfo_clear(info);
+			return 0;
+		}
+		
+		/* TODO: add checking here so we don't read too far */
+		for(i = 0; i < info->current_chunk; i++)
+		{
+			data = unpack_chunk_header(data, &header);
+			data += header.size;
+		}
+		
+		/* unpack the header */	
+		data = unpack_chunk_header(data, &header);
+		info->current_chunk++;
+		
+		/* handle sequence stuff */
+		if(info->conn && (header.flags&NET_CHUNKFLAG_VITAL))
+		{
+			if(header.sequence == (info->conn->ack+1)%NET_MAX_SEQUENCE)
+			{
+				/* in sequence */
+				info->conn->ack = (info->conn->ack+1)%NET_MAX_SEQUENCE;
+			}
+			else
+			{
+				/* out of sequence, request resend */
+				dbg_msg("conn", "asking for resend %d %d", header.sequence, (info->conn->ack+1)%NET_MAX_SEQUENCE);
+				conn_want_resend(info->conn);
+				continue; /* take the next chunk in the packet */
+			}
+		}
+		
+		/* fill in the info */
+		chunk->client_id = info->client_id;
+		chunk->address = info->addr;
+		chunk->flags = 0;
+		chunk->data_size = header.size;
+		chunk->data = data;
+		return 1;
 	}
 }
 
-static void rb_clear(RINGBUFFER *rb)
-{
-	while(rb->first)
-		rb_pop_first(rb);
-}
-
-
-typedef struct
-{
-	unsigned short seq;
-	unsigned short ack;
-	unsigned state;
-	
-	int token;
-	int remote_closed;
-	
-	RINGBUFFER buffer;
-	
-	int64 last_update_time;
-	int64 last_recv_time;
-	int64 last_send_time;
-	
-	char error_string[256];
-	
-	NETPACKETCONSTRUCT construct;
-	
-	NETADDR peeraddr;
-	NETSOCKET socket;
-	NETSTATS stats;
-} NETCONNECTION;
-
-typedef struct
-{
-	NETCONNECTION conn;
-} NETSLOT;
-
-typedef struct
-{
-	NETADDR addr;
-	NETCONNECTION *conn;
-	int current_chunk;
-	int client_id;
-	int valid;
-	NETPACKETCONSTRUCT data;
-	unsigned char buffer[NET_MAX_PACKETSIZE];
-} NETRECVINFO;
-
-struct NETSERVER
-{
-	NETSOCKET socket;
-	NETSLOT slots[NET_MAX_CLIENTS];
-	int max_clients;
-	NETFUNC_NEWCLIENT new_client;
-	NETFUNC_NEWCLIENT del_client;
-	void *user_ptr;
-	
-	NETRECVINFO recv;
-};
-
-struct NETCLIENT
-{
-	NETADDR server_addr;
-	NETSOCKET socket;
-	
-	NETRECVINFO recv;
-	NETCONNECTION conn;
-};
 
 static IOHANDLE datalog = 0;
 static HUFFMAN_STATE huffmanstate;
@@ -219,7 +83,7 @@ static HUFFMAN_STATE huffmanstate;
 #define COMPRESSION 1
 
 /* packs the data tight and sends it */
-static void send_packet_connless(NETSOCKET socket, NETADDR *addr, const void *data, int data_size)
+void send_packet_connless(NETSOCKET socket, NETADDR *addr, const void *data, int data_size)
 {
 	unsigned char buffer[NET_MAX_PACKETSIZE];
 	buffer[0] = 0xff;
@@ -232,7 +96,7 @@ static void send_packet_connless(NETSOCKET socket, NETADDR *addr, const void *da
 	net_udp_send(socket, addr, buffer, 6+data_size);
 }
 
-static void send_packet(NETSOCKET socket, NETADDR *addr, NETPACKETCONSTRUCT *packet)
+void send_packet(NETSOCKET socket, NETADDR *addr, NETPACKETCONSTRUCT *packet)
 {
 	unsigned char buffer[NET_MAX_PACKETSIZE];
 	int compressed_size = -1;
@@ -274,7 +138,7 @@ static void send_packet(NETSOCKET socket, NETADDR *addr, NETPACKETCONSTRUCT *pac
 }
 
 /* TODO: rename this function */
-static int unpack_packet(unsigned char *buffer, int size, NETPACKETCONSTRUCT *packet)
+int unpack_packet(unsigned char *buffer, int size, NETPACKETCONSTRUCT *packet)
 {
 	/* check the size */
 	if(size < NET_PACKETHEADERSIZE || size > NET_MAX_PACKETSIZE)
@@ -311,7 +175,7 @@ static int unpack_packet(unsigned char *buffer, int size, NETPACKETCONSTRUCT *pa
 
 
 /* TODO: change the arguments of this function */
-static unsigned char *pack_chunk_header(unsigned char *data, int flags, int size, int sequence)
+unsigned char *pack_chunk_header(unsigned char *data, int flags, int size, int sequence)
 {
 	data[0] = ((flags&3)<<6)|((size>>4)&0x3f);
 	data[1] = (size&0xf);
@@ -324,7 +188,7 @@ static unsigned char *pack_chunk_header(unsigned char *data, int flags, int size
 	return data + 2;
 }
 
-static unsigned char *unpack_chunk_header(unsigned char *data, NETCHUNKHEADER *header)
+unsigned char *unpack_chunk_header(unsigned char *data, NETCHUNKHEADER *header)
 {
 	header->flags = (data[0]>>6)&3;
 	header->size = ((data[0]&0x3f)<<4) | (data[1]&0xf);
@@ -338,790 +202,6 @@ static unsigned char *unpack_chunk_header(unsigned char *data, NETCHUNKHEADER *h
 }
 
 
-static void conn_reset_stats(NETCONNECTION *conn)
-{
-	mem_zero(&conn->stats, sizeof(conn->stats));
-}
-
-static void conn_reset(NETCONNECTION *conn)
-{
-	conn->seq = 0;
-	conn->ack = 0;
-	conn->remote_closed = 0;
-	
-	conn->state = NET_CONNSTATE_OFFLINE;
-	conn->state = NET_CONNSTATE_OFFLINE;
-	conn->last_send_time = 0;
-	conn->last_recv_time = 0;
-	conn->last_update_time = 0;
-	conn->token = -1;
-	mem_zero(&conn->peeraddr, sizeof(conn->peeraddr));
-	
-	rb_clear(&conn->buffer);
-	
-	mem_zero(&conn->construct, sizeof(conn->construct));
-}
-
-
-static const char *conn_error(NETCONNECTION *conn)
-{
-	return conn->error_string;
-}
-
-static void conn_set_error(NETCONNECTION *conn, const char *str)
-{
-	str_copy(conn->error_string, str, sizeof(conn->error_string));
-}
-
-static void conn_init(NETCONNECTION *conn, NETSOCKET socket)
-{
-	conn_reset(conn);
-	conn_reset_stats(conn);
-	conn->socket = socket;
-	rb_init(&conn->buffer);
-	mem_zero(conn->error_string, sizeof(conn->error_string));
-}
-
-
-static void conn_ack(NETCONNECTION *conn, int ack)
-{
-	while(1)
-	{
-		RINGBUFFER_ITEM *item = conn->buffer.first;
-		NETCHUNKDATA *resend;
-		if(!item)
-			break;
-			
-		resend = (NETCHUNKDATA *)rb_item_data(item);
-		if(resend->sequence <= ack || (ack < NET_MAX_SEQUENCE/3 && resend->sequence > NET_MAX_SEQUENCE/2))
-			rb_pop_first(&conn->buffer);
-		else
-			break;
-	}
-}
-
-static void conn_want_resend(NETCONNECTION *conn)
-{
-	conn->construct.flags |= NET_PACKETFLAG_RESEND;
-}
-
-static int conn_flush(NETCONNECTION *conn)
-{
-	int num_chunks = conn->construct.num_chunks;
-	if(!num_chunks && !conn->construct.flags)
-		return 0;
-	
-	conn->construct.ack = conn->ack;
-	send_packet(conn->socket, &conn->peeraddr, &conn->construct);
-	conn->last_send_time = time_get();
-	
-	/* clear construct so we can start building a new package */
-	mem_zero(&conn->construct, sizeof(conn->construct));
-	return num_chunks;
-}
-
-/*NETCHUNKDATA *data*/
-
-static void conn_queue_chunk(NETCONNECTION *conn, int flags, int data_size, const void *data)
-{
-	unsigned char *chunk_data;
-	/* check if we have space for it, if not, flush the connection */
-	if(conn->construct.data_size + data_size + NET_MAX_CHUNKHEADERSIZE > sizeof(conn->construct.chunk_data))
-		conn_flush(conn);
-
-	if(flags&NET_CHUNKFLAG_VITAL && !(flags&NET_CHUNKFLAG_RESEND))
-		conn->seq = (conn->seq+1)%NET_MAX_SEQUENCE;
-
-	/* pack all the data */
-	chunk_data = &conn->construct.chunk_data[conn->construct.data_size];
-	chunk_data = pack_chunk_header(chunk_data, flags, data_size, conn->seq);
-	mem_copy(chunk_data, data, data_size);
-	chunk_data += data_size;
-
-	/* */
-	conn->construct.num_chunks++;
-	conn->construct.data_size = (int)(chunk_data-conn->construct.chunk_data);
-	
-	/* set packet flags aswell */
-	
-	if(flags&NET_CHUNKFLAG_VITAL && !(flags&NET_CHUNKFLAG_RESEND))
-	{
-		/* save packet if we need to resend */
-		NETCHUNKDATA *resend = (NETCHUNKDATA *)rb_alloc(&conn->buffer, sizeof(NETCHUNKDATA)+data_size);
-		resend->sequence = conn->seq;
-		resend->flags = flags;
-		resend->data_size = data_size;
-		resend->data = (unsigned char *)(resend+1);
-		resend->first_send_time = time_get();
-		mem_copy(resend->data, data, data_size);
-	}
-}
-
-static void conn_send_control(NETCONNECTION *conn, int controlmsg, const void *extra, int extra_size)
-{
-	NETPACKETCONSTRUCT construct;
-	construct.flags = NET_PACKETFLAG_CONTROL;
-	construct.ack = conn->ack;
-	construct.num_chunks = 0;
-	construct.data_size = 1+extra_size;
-	construct.chunk_data[0] = controlmsg;
-	mem_copy(&construct.chunk_data[1], extra, extra_size);
-
-	/* send the control message */
-	send_packet(conn->socket, &conn->peeraddr, &construct);
-	conn->last_send_time = time_get();
-}
-
-static void conn_resend(NETCONNECTION *conn)
-{
-	int resend_count = 0;
-	int max = 10;
-	RINGBUFFER_ITEM *item = conn->buffer.first;
-	while(item)
-	{
-		NETCHUNKDATA *resend = (NETCHUNKDATA *)rb_item_data(item);
-		conn_queue_chunk(conn, resend->flags|NET_CHUNKFLAG_RESEND, resend->data_size, resend->data);
-		item = item->next;
-		max--;
-		resend_count++;
-		if(!max)
-			break;
-	}
-	
-	dbg_msg("conn", "resent %d packets", resend_count);
-}
-
-static int conn_connect(NETCONNECTION *conn, NETADDR *addr)
-{
-	if(conn->state != NET_CONNSTATE_OFFLINE)
-		return -1;
-	
-	/* init connection */
-	conn_reset(conn);
-	conn->peeraddr = *addr;
-	mem_zero(conn->error_string, sizeof(conn->error_string));
-	conn->state = NET_CONNSTATE_CONNECT;
-	conn_send_control(conn, NET_CTRLMSG_CONNECT, 0, 0);
-	return 0;
-}
-
-static void conn_disconnect(NETCONNECTION *conn, const char *reason)
-{
-	if(conn->state == NET_CONNSTATE_OFFLINE)
-		return;
-
-	if(conn->remote_closed == 0)
-	{
-		if(reason)
-			conn_send_control(conn, NET_CTRLMSG_CLOSE, reason, strlen(reason)+1);
-		else
-			conn_send_control(conn, NET_CTRLMSG_CLOSE, 0, 0);
-
-		conn->error_string[0] = 0;
-		if(reason)
-			str_copy(conn->error_string, reason, sizeof(conn->error_string));
-	}
-	
-	conn_reset(conn);
-}
-
-static int conn_feed(NETCONNECTION *conn, NETPACKETCONSTRUCT *packet, NETADDR *addr)
-{
-	int64 now = time_get();
-	conn->last_recv_time = now;
-	
-	/* check if resend is requested */
-	if(packet->flags&NET_PACKETFLAG_RESEND)
-		conn_resend(conn);
-
-	/* */									
-	if(packet->flags&NET_PACKETFLAG_CONTROL)
-	{
-		int ctrlmsg = packet->chunk_data[0];
-		
-		if(ctrlmsg == NET_CTRLMSG_CLOSE)
-		{
-			conn->state = NET_CONNSTATE_ERROR;
-			conn->remote_closed = 1;
-			
-			if(packet->data_size)
-			{
-				/* make sure to sanitize the error string form the other party*/
-				char str[128];
-				if(packet->data_size < 128)
-					str_copy(str, (char *)packet->chunk_data, packet->data_size);
-				else
-					str_copy(str, (char *)packet->chunk_data, 128);
-				str_sanitize_strong(str);
-				
-				/* set the error string */
-				conn_set_error(conn, str);
-			}
-			else
-				conn_set_error(conn, "no reason given");
-				
-			if(config.debug)
-				dbg_msg("conn", "closed reason='%s'", conn_error(conn));
-			return 0;			
-		}
-		else
-		{
-			if(conn->state == NET_CONNSTATE_OFFLINE)
-			{
-				if(ctrlmsg == NET_CTRLMSG_CONNECT)
-				{
-					/* send response and init connection */
-					conn_reset(conn);
-					conn->state = NET_CONNSTATE_ONLINE;
-					conn->peeraddr = *addr;
-					conn->last_send_time = now;
-					conn->last_recv_time = now;
-					conn->last_update_time = now;
-					conn_send_control(conn, NET_CTRLMSG_CONNECTACCEPT, 0, 0);
-					if(config.debug)
-						dbg_msg("connection", "got connection, sending connect+accept");			
-				}
-			}
-			else if(conn->state == NET_CONNSTATE_CONNECT)
-			{
-				/* connection made */
-				if(ctrlmsg == NET_CTRLMSG_CONNECTACCEPT)
-				{
-					conn_send_control(conn, NET_CTRLMSG_ACCEPT, 0, 0);
-					conn->state = NET_CONNSTATE_ONLINE;
-					if(config.debug)
-						dbg_msg("connection", "got connect+accept, sending accept. connection online");
-				}
-			}
-			else if(conn->state == NET_CONNSTATE_ONLINE)
-			{
-				/* connection made */
-				/*
-				if(ctrlmsg == NET_CTRLMSG_CONNECTACCEPT)
-				{
-					
-				}*/
-			}
-		}
-	}
-	
-	if(conn->state == NET_CONNSTATE_ONLINE)
-	{
-		conn_ack(conn, packet->ack);
-	}
-	
-	return 1;
-}
-
-static int conn_update(NETCONNECTION *conn)
-{
-	int64 now = time_get();
-
-	if(conn->state == NET_CONNSTATE_OFFLINE || conn->state == NET_CONNSTATE_ERROR)
-		return 0;
-
-	/* watch out for major hitches */
-	{
-		/* TODO: fix this */
-		/*
-		int64 delta = now-conn->last_update_time;
-		if(conn->last_update_time && delta > time_freq()/2)
-		{
-			RINGBUFFER_ITEM *item = conn->buffer.first;
-	
-			dbg_msg("conn", "hitch %d", (int)((delta*1000)/time_freq()));
-			conn->last_recv_time += delta;
-	
-			while(item)
-			{
-				NETPACKETDATA *resend = (NETPACKETDATA *)rb_item_data(item);
-				resend->first_send_time += delta;
-				item = item->next;
-			}
-		}
-
-		conn->last_update_time = now;
-		*/
-	}
-		
-	
-	/* check for timeout */
-	if(conn->state != NET_CONNSTATE_OFFLINE &&
-		conn->state != NET_CONNSTATE_CONNECT &&
-		(now-conn->last_recv_time) > time_freq()*10)
-	{
-		conn->state = NET_CONNSTATE_ERROR;
-		conn_set_error(conn, "timeout");
-	}
-	
-	/* check for large buffer errors */
-	if(conn->buffer.buffer_size > 1024*64)
-	{
-		conn->state = NET_CONNSTATE_ERROR;
-		conn_set_error(conn, "too weak connection (out of buffer)");
-	}
-	
-	if(conn->buffer.first)
-	{
-		/* TODO: fix this */
-		NETCHUNKDATA *resend = (NETCHUNKDATA *)(conn->buffer.first+1);
-		if(now-resend->first_send_time > time_freq()*10)
-		{
-			conn->state = NET_CONNSTATE_ERROR;
-			conn_set_error(conn, "too weak connection (not acked for 10 seconds)");
-		}
-	}
-	
-	/* send keep alives if nothing has happend for 1000ms */
-	if(conn->state == NET_CONNSTATE_ONLINE)
-	{
-		if(time_get()-conn->last_send_time > time_freq()/2) /* flush connection after 250ms if needed */
-		{
-			int num_flushed_chunks = conn_flush(conn);
-			if(num_flushed_chunks && config.debug)
-				dbg_msg("connection", "flushed connection due to timeout. %d chunks.", num_flushed_chunks);
-		}
-			
-		if(time_get()-conn->last_send_time > time_freq())
-			conn_send_control(conn, NET_CTRLMSG_KEEPALIVE, 0, 0);
-	}
-	else if(conn->state == NET_CONNSTATE_CONNECT)
-	{
-		if(time_get()-conn->last_send_time > time_freq()/2) /* send a new connect every 500ms */
-			conn_send_control(conn, NET_CTRLMSG_CONNECT, 0, 0);
-			/*conn_send(conn, NETWORK_PACKETFLAG_CONNECT, 0, 0);*/
-	}
-	else if(conn->state == NET_CONNSTATE_CONNECTACCEPTED)
-	{
-
-		if(time_get()-conn->last_send_time > time_freq()/2) /* send a new connect/accept every 500ms */
-			conn_send_control(conn, NET_CTRLMSG_CONNECTACCEPT, 0, 0);
-			/*conn_send(conn, NETWORK_PACKETFLAG_CONNECT|NETWORK_PACKETFLAG_ACCEPT, 0, 0);*/
-	}
-	
-	return 0;
-}
-
-NETSERVER *netserver_open(NETADDR bindaddr, int max_clients, int flags)
-{
-	int i;
-	NETSERVER *server;
-	NETSOCKET socket = net_udp_create(bindaddr);
-	if(socket == NETSOCKET_INVALID)
-		return 0;
-	
-	server = (NETSERVER *)mem_alloc(sizeof(NETSERVER), 1);
-	mem_zero(server, sizeof(NETSERVER));
-	server->socket = socket;
-	server->max_clients = max_clients;
-	if(server->max_clients > NET_MAX_CLIENTS)
-		server->max_clients = NET_MAX_CLIENTS;
-	if(server->max_clients < 1)
-		server->max_clients = 1;
-	
-	for(i = 0; i < NET_MAX_CLIENTS; i++)
-		conn_init(&server->slots[i].conn, server->socket);
-
-	return server;
-}
-
-int netserver_set_callbacks(NETSERVER *s, NETFUNC_NEWCLIENT new_client, NETFUNC_DELCLIENT del_client, void *user)
-{
-	s->new_client = new_client;
-	s->del_client = del_client;
-	s->user_ptr = user;
-	return 0;
-}
-
-int netserver_max_clients(NETSERVER *s)
-{
-	return s->max_clients;
-}
-
-int netserver_close(NETSERVER *s)
-{
-	/* TODO: implement me */
-	return 0;
-}
-
-int netserver_drop(NETSERVER *s, int client_id, const char *reason)
-{
-	/* TODO: insert lots of checks here */
-	dbg_msg("net_server", "client dropped. cid=%d reason=\"%s\"", client_id, reason);
-	conn_disconnect(&s->slots[client_id].conn, reason);
-
-	if(s->del_client)
-		s->del_client(client_id, s->user_ptr);
-		
-	return 0;
-}
-
-int netserver_update(NETSERVER *s)
-{
-	int i;
-	for(i = 0; i < s->max_clients; i++)
-	{
-		conn_update(&s->slots[i].conn);
-		if(s->slots[i].conn.state == NET_CONNSTATE_ERROR)
-			netserver_drop(s, i, conn_error(&s->slots[i].conn));
-	}
-	return 0;
-}
-
-static void recvinfo_clear(NETRECVINFO *info)
-{
-	info->valid = 0;
-}
-
-static void recvinfo_start(NETRECVINFO *info, NETADDR *addr, NETCONNECTION *conn, int cid)
-{
-	info->addr = *addr;
-	info->conn = conn;
-	info->client_id = cid;
-	info->current_chunk = 0;
-	info->valid = 1;
-}
-
-/* TODO: rename this function */
-static int recvinfo_fetch_chunk(NETRECVINFO *info, NETCHUNK *chunk)
-{
-	NETCHUNKHEADER header;
-	unsigned char *data = info->data.chunk_data;
-	int i;
-	
-	while(1)
-	{
-		/* check for old data to unpack */
-		if(!info->valid || info->current_chunk >= info->data.num_chunks)
-		{
-			recvinfo_clear(info);
-			return 0;
-		}
-		
-		/* TODO: add checking here so we don't read too far */
-		for(i = 0; i < info->current_chunk; i++)
-		{
-			data = unpack_chunk_header(data, &header);
-			data += header.size;
-		}
-		
-		/* unpack the header */	
-		data = unpack_chunk_header(data, &header);
-		info->current_chunk++;
-		
-		/* handle sequence stuff */
-		if(info->conn && (header.flags&NET_CHUNKFLAG_VITAL))
-		{
-			if(header.sequence == (info->conn->ack+1)%NET_MAX_SEQUENCE)
-			{
-				/* in sequence */
-				info->conn->ack = (info->conn->ack+1)%NET_MAX_SEQUENCE;
-			}
-			else
-			{
-				/* out of sequence, request resend */
-				dbg_msg("conn", "asking for resend %d %d", header.sequence, (info->conn->ack+1)%NET_MAX_SEQUENCE);
-				conn_want_resend(info->conn);
-				continue; /* take the next chunk in the packet */
-			}
-		}
-		
-		/* fill in the info */
-		chunk->client_id = info->client_id;
-		chunk->address = info->addr;
-		chunk->flags = 0;
-		chunk->data_size = header.size;
-		chunk->data = data;
-		return 1;
-	}
-}
-
-int netserver_recv(NETSERVER *s, NETCHUNK *chunk)
-{
-	while(1)
-	{
-		NETADDR addr;
-		int i, bytes, found;
-			
-		/* check for a chunk */
-		if(recvinfo_fetch_chunk(&s->recv, chunk))
-			return 1;
-		
-		/* TODO: empty the recvinfo */
-		bytes = net_udp_recv(s->socket, &addr, s->recv.buffer, NET_MAX_PACKETSIZE);
-
-		/* no more packets for now */
-		if(bytes <= 0)
-			break;
-		
-		if(unpack_packet(s->recv.buffer, bytes, &s->recv.data) == 0)
-		{
-			if(s->recv.data.flags&NET_PACKETFLAG_CONNLESS)
-			{
-				chunk->flags = NETSENDFLAG_CONNLESS;
-				chunk->client_id = -1;
-				chunk->address = addr;
-				chunk->data_size = s->recv.data.data_size;
-				chunk->data = s->recv.data.chunk_data;
-				return 1;
-			}
-			else
-			{			
-				/* TODO: check size here */
-				if(s->recv.data.flags&NET_PACKETFLAG_CONTROL && s->recv.data.chunk_data[0] == NET_CTRLMSG_CONNECT)
-				{
-					found = 0;
-					
-					/* check if we already got this client */
-					for(i = 0; i < s->max_clients; i++)
-					{
-						if(s->slots[i].conn.state != NET_CONNSTATE_OFFLINE &&
-							net_addr_comp(&s->slots[i].conn.peeraddr, &addr) == 0)
-						{
-							found = 1; /* silent ignore.. we got this client already */
-							break;
-						}
-					}
-					
-					/* client that wants to connect */
-					if(!found)
-					{
-						found = 0;
-						
-						for(i = 0; i < s->max_clients; i++)
-						{
-							if(s->slots[i].conn.state == NET_CONNSTATE_OFFLINE)
-							{
-								found = 1;
-								conn_feed(&s->slots[i].conn, &s->recv.data, &addr);
-								if(s->new_client)
-									s->new_client(i, s->user_ptr);
-								break;
-							}
-						}
-						
-						if(!found)
-						{
-							/* TODO: send server full emssage */
-						}
-					}
-				}
-				else
-				{
-					/* normal packet, find matching slot */
-					for(i = 0; i < s->max_clients; i++)
-					{
-						if(net_addr_comp(&s->slots[i].conn.peeraddr, &addr) == 0)
-						{
-							if(conn_feed(&s->slots[i].conn, &s->recv.data, &addr))
-							{
-								if(s->recv.data.data_size)
-									recvinfo_start(&s->recv, &addr, &s->slots[i].conn, i);
-							}
-						}
-					}
-				}
-			}
-		}
-	}
-	return 0;
-}
-
-int netserver_send(NETSERVER *s, NETCHUNK *chunk)
-{
-	if(chunk->data_size >= NET_MAX_PAYLOAD)
-	{
-		dbg_msg("netserver", "packet payload too big. %d. dropping packet", chunk->data_size);
-		return -1;
-	}
-	
-	if(chunk->flags&NETSENDFLAG_CONNLESS)
-	{
-		/* send connectionless packet */
-		send_packet_connless(s->socket, &chunk->address, chunk->data, chunk->data_size);
-	}
-	else
-	{
-		int f = 0;
-		dbg_assert(chunk->client_id >= 0, "errornous client id");
-		dbg_assert(chunk->client_id < s->max_clients, "errornous client id");
-		
-		if(chunk->flags&NETSENDFLAG_VITAL)
-			f = NET_CHUNKFLAG_VITAL;
-		
-		conn_queue_chunk(&s->slots[chunk->client_id].conn, f, chunk->data_size, chunk->data);
-
-		if(chunk->flags&NETSENDFLAG_FLUSH)
-			conn_flush(&s->slots[chunk->client_id].conn);
-	}
-	return 0;
-}
-
-void netserver_stats(NETSERVER *s, NETSTATS *stats)
-{
-	int num_stats = sizeof(NETSTATS)/sizeof(int);
-	int *istats = (int *)stats;
-	int c, i;
-
-	mem_zero(stats, sizeof(NETSTATS));
-	
-	for(c = 0; c < s->max_clients; c++)
-	{
-		if(s->slots[c].conn.state != NET_CONNSTATE_OFFLINE)
-		{
-			int *sstats = (int *)(&(s->slots[c].conn.stats));
-			for(i = 0; i < num_stats; i++)
-				istats[i] += sstats[i];
-		}
-	}
-}
-
-NETSOCKET netserver_socket(NETSERVER *s)
-{
-	return s->socket;
-}
-
-int netserver_client_addr(NETSERVER *s, int client_id, NETADDR *addr)
-{
-	*addr = s->slots[client_id].conn.peeraddr;
-	return 1;
-}
-
-NETCLIENT *netclient_open(NETADDR bindaddr, int flags)
-{
-	NETCLIENT *client = (NETCLIENT *)mem_alloc(sizeof(NETCLIENT), 1);
-	mem_zero(client, sizeof(NETCLIENT));
-	client->socket = net_udp_create(bindaddr);
-	conn_init(&client->conn, client->socket);
-	return client;
-}
-
-int netclient_close(NETCLIENT *c)
-{
-	/* TODO: implement me */
-	return 0;
-}
-
-int netclient_update(NETCLIENT *c)
-{
-	conn_update(&c->conn);
-	if(c->conn.state == NET_CONNSTATE_ERROR)
-		netclient_disconnect(c, conn_error(&c->conn));
-	return 0;
-}
-
-int netclient_disconnect(NETCLIENT *c, const char *reason)
-{
-	dbg_msg("netclient", "disconnected. reason=\"%s\"", reason);
-	conn_disconnect(&c->conn, reason);
-	return 0;
-}
-
-int netclient_connect(NETCLIENT *c, NETADDR *addr)
-{
-	conn_connect(&c->conn, addr);
-	return 0;
-}
-
-int netclient_recv(NETCLIENT *c, NETCHUNK *chunk)
-{
-	while(1)
-	{
-		NETADDR addr;
-		int bytes;
-			
-		/* check for a chunk */
-		if(recvinfo_fetch_chunk(&c->recv, chunk))
-			return 1;
-		
-		/* TODO: empty the recvinfo */
-		bytes = net_udp_recv(c->socket, &addr, c->recv.buffer, NET_MAX_PACKETSIZE);
-
-		/* no more packets for now */
-		if(bytes <= 0)
-			break;
-
-		if(unpack_packet(c->recv.buffer, bytes, &c->recv.data) == 0)
-		{
-			if(c->recv.data.flags&NET_PACKETFLAG_CONNLESS)
-			{
-				chunk->flags = NETSENDFLAG_CONNLESS;
-				chunk->client_id = -1;
-				chunk->address = addr;
-				chunk->data_size = c->recv.data.data_size;
-				chunk->data = c->recv.data.chunk_data;
-				return 1;
-			}
-			else
-			{
-				if(conn_feed(&c->conn, &c->recv.data, &addr))
-					recvinfo_start(&c->recv, &addr, &c->conn, 0);
-			}
-		}
-	}
-	return 0;
-}
-
-int netclient_send(NETCLIENT *c, NETCHUNK *chunk)
-{
-	if(chunk->data_size >= NET_MAX_PAYLOAD)
-	{
-		dbg_msg("netclient", "chunk payload too big. %d. dropping chunk", chunk->data_size);
-		return -1;
-	}
-	
-	if(chunk->flags&NETSENDFLAG_CONNLESS)
-	{
-		/* send connectionless packet */
-		send_packet_connless(c->socket, &chunk->address, chunk->data, chunk->data_size);
-	}
-	else
-	{
-		int f = 0;
-		dbg_assert(chunk->client_id == 0, "errornous client id");
-		
-		if(chunk->flags&NETSENDFLAG_VITAL)
-			f = NET_CHUNKFLAG_VITAL;
-		
-		conn_queue_chunk(&c->conn, f, chunk->data_size, chunk->data);
-
-		if(chunk->flags&NETSENDFLAG_FLUSH)
-			conn_flush(&c->conn);
-	}
-	return 0;
-}
-
-int netclient_state(NETCLIENT *c)
-{
-	if(c->conn.state == NET_CONNSTATE_ONLINE)
-		return NETSTATE_ONLINE;
-	if(c->conn.state == NET_CONNSTATE_OFFLINE)
-		return NETSTATE_OFFLINE;
-	return NETSTATE_CONNECTING;
-}
-
-int netclient_flush(NETCLIENT *c)
-{
-	return conn_flush(&c->conn);
-}
-
-int netclient_gotproblems(NETCLIENT *c)
-{
-	if(time_get() - c->conn.last_recv_time > time_freq())
-		return 1;
-	return 0;
-}
-
-void netclient_stats(NETCLIENT *c, NETSTATS *stats)
-{
-	*stats = c->conn.stats;
-}
-
-const char *netclient_error_string(NETCLIENT *c)
-{
-	return conn_error(&c->conn);
-}
-
 void netcommon_openlog(const char *filename)
 {
 	datalog = io_open(filename, IOFLAG_WRITE);
diff --git a/src/engine/e_network.h b/src/engine/e_network.h
index efacc9fa..1ac02708 100644
--- a/src/engine/e_network.h
+++ b/src/engine/e_network.h
@@ -1,5 +1,8 @@
+#ifndef ENGINE_NETWORK_H
+#define ENGINE_NETWORK_H
 /* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
 
+
 typedef struct
 {
 	/* -1 means that it's a stateless packet */
@@ -11,6 +14,14 @@ typedef struct
 	const void *data;
 } NETCHUNK;
 
+
+typedef struct
+{
+	NETADDR addr;
+	int type;
+	int expires;
+} NETBANINFO;
+
 /*typedef struct
 {
 	int send_bytes;
@@ -34,7 +45,10 @@ enum
 	
 	NETSTATE_OFFLINE=0,
 	NETSTATE_CONNECTING,
-	NETSTATE_ONLINE
+	NETSTATE_ONLINE,
+	
+	NETBANTYPE_SOFT=1,
+	NETBANTYPE_DROP=2
 };
 
 typedef int (*NETFUNC_DELCLIENT)(int cid, void *user);
@@ -55,6 +69,12 @@ 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, NETADDR *addr);
 int netserver_max_clients(NETSERVER *s);
+
+int netserver_ban_add(NETSERVER *s, NETADDR addr, int type, int seconds);
+int netserver_ban_remove(NETSERVER *s, NETADDR addr);
+int netserver_ban_num(NETSERVER *s); /* caution, slow */
+int netserver_ban_get(NETSERVER *s, int index, NETBANINFO *info); /* caution, slow */
+
 /*void netserver_stats(NETSERVER *s, NETSTATS *stats);*/
 
 /* client side */
@@ -120,3 +140,5 @@ public:
 };
 #endif
 
+
+#endif
diff --git a/src/engine/e_network_client.c b/src/engine/e_network_client.c
new file mode 100644
index 00000000..df7e4c26
--- /dev/null
+++ b/src/engine/e_network_client.c
@@ -0,0 +1,148 @@
+#include <base/system.h>
+#include "e_network.h"
+#include "e_network_internal.h"
+
+struct NETCLIENT
+{
+	NETADDR server_addr;
+	NETSOCKET socket;
+	
+	NETRECVINFO recv;
+	NETCONNECTION conn;
+};
+
+NETCLIENT *netclient_open(NETADDR bindaddr, int flags)
+{
+	NETCLIENT *client = (NETCLIENT *)mem_alloc(sizeof(NETCLIENT), 1);
+	mem_zero(client, sizeof(NETCLIENT));
+	client->socket = net_udp_create(bindaddr);
+	conn_init(&client->conn, client->socket);
+	return client;
+}
+
+int netclient_close(NETCLIENT *c)
+{
+	/* TODO: implement me */
+	return 0;
+}
+
+
+int netclient_disconnect(NETCLIENT *c, const char *reason)
+{
+	dbg_msg("netclient", "disconnected. reason=\"%s\"", reason);
+	conn_disconnect(&c->conn, reason);
+	return 0;
+}
+
+int netclient_update(NETCLIENT *c)
+{
+	conn_update(&c->conn);
+	if(c->conn.state == NET_CONNSTATE_ERROR)
+		netclient_disconnect(c, conn_error(&c->conn));
+	return 0;
+}
+
+int netclient_connect(NETCLIENT *c, NETADDR *addr)
+{
+	conn_connect(&c->conn, addr);
+	return 0;
+}
+
+int netclient_recv(NETCLIENT *c, NETCHUNK *chunk)
+{
+	while(1)
+	{
+		NETADDR addr;
+		int bytes;
+			
+		/* check for a chunk */
+		if(recvinfo_fetch_chunk(&c->recv, chunk))
+			return 1;
+		
+		/* TODO: empty the recvinfo */
+		bytes = net_udp_recv(c->socket, &addr, c->recv.buffer, NET_MAX_PACKETSIZE);
+
+		/* no more packets for now */
+		if(bytes <= 0)
+			break;
+
+		if(unpack_packet(c->recv.buffer, bytes, &c->recv.data) == 0)
+		{
+			if(c->recv.data.flags&NET_PACKETFLAG_CONNLESS)
+			{
+				chunk->flags = NETSENDFLAG_CONNLESS;
+				chunk->client_id = -1;
+				chunk->address = addr;
+				chunk->data_size = c->recv.data.data_size;
+				chunk->data = c->recv.data.chunk_data;
+				return 1;
+			}
+			else
+			{
+				if(conn_feed(&c->conn, &c->recv.data, &addr))
+					recvinfo_start(&c->recv, &addr, &c->conn, 0);
+			}
+		}
+	}
+	return 0;
+}
+
+int netclient_send(NETCLIENT *c, NETCHUNK *chunk)
+{
+	if(chunk->data_size >= NET_MAX_PAYLOAD)
+	{
+		dbg_msg("netclient", "chunk payload too big. %d. dropping chunk", chunk->data_size);
+		return -1;
+	}
+	
+	if(chunk->flags&NETSENDFLAG_CONNLESS)
+	{
+		/* send connectionless packet */
+		send_packet_connless(c->socket, &chunk->address, chunk->data, chunk->data_size);
+	}
+	else
+	{
+		int f = 0;
+		dbg_assert(chunk->client_id == 0, "errornous client id");
+		
+		if(chunk->flags&NETSENDFLAG_VITAL)
+			f = NET_CHUNKFLAG_VITAL;
+		
+		conn_queue_chunk(&c->conn, f, chunk->data_size, chunk->data);
+
+		if(chunk->flags&NETSENDFLAG_FLUSH)
+			conn_flush(&c->conn);
+	}
+	return 0;
+}
+
+int netclient_state(NETCLIENT *c)
+{
+	if(c->conn.state == NET_CONNSTATE_ONLINE)
+		return NETSTATE_ONLINE;
+	if(c->conn.state == NET_CONNSTATE_OFFLINE)
+		return NETSTATE_OFFLINE;
+	return NETSTATE_CONNECTING;
+}
+
+int netclient_flush(NETCLIENT *c)
+{
+	return conn_flush(&c->conn);
+}
+
+int netclient_gotproblems(NETCLIENT *c)
+{
+	if(time_get() - c->conn.last_recv_time > time_freq())
+		return 1;
+	return 0;
+}
+
+void netclient_stats(NETCLIENT *c, NETSTATS *stats)
+{
+	*stats = c->conn.stats;
+}
+
+const char *netclient_error_string(NETCLIENT *c)
+{
+	return conn_error(&c->conn);
+}
diff --git a/src/engine/e_network_conn.c b/src/engine/e_network_conn.c
new file mode 100644
index 00000000..05ced197
--- /dev/null
+++ b/src/engine/e_network_conn.c
@@ -0,0 +1,364 @@
+#include <base/system.h>
+#include <string.h>
+#include "e_config.h"
+#include "e_network_internal.h"
+
+static void conn_reset_stats(NETCONNECTION *conn)
+{
+	mem_zero(&conn->stats, sizeof(conn->stats));
+}
+
+static void conn_reset(NETCONNECTION *conn)
+{
+	conn->seq = 0;
+	conn->ack = 0;
+	conn->remote_closed = 0;
+	
+	conn->state = NET_CONNSTATE_OFFLINE;
+	conn->state = NET_CONNSTATE_OFFLINE;
+	conn->last_send_time = 0;
+	conn->last_recv_time = 0;
+	conn->last_update_time = 0;
+	conn->token = -1;
+	mem_zero(&conn->peeraddr, sizeof(conn->peeraddr));
+	
+	conn->buffer = ringbuf_init(conn->buffer_memory, sizeof(conn->buffer_memory), 0);
+	
+	mem_zero(&conn->construct, sizeof(conn->construct));
+}
+
+
+const char *conn_error(NETCONNECTION *conn)
+{
+	return conn->error_string;
+}
+
+static void conn_set_error(NETCONNECTION *conn, const char *str)
+{
+	str_copy(conn->error_string, str, sizeof(conn->error_string));
+}
+
+void conn_init(NETCONNECTION *conn, NETSOCKET socket)
+{
+	conn_reset(conn);
+	conn_reset_stats(conn);
+	conn->socket = socket;
+	mem_zero(conn->error_string, sizeof(conn->error_string));
+}
+
+
+static void conn_ack(NETCONNECTION *conn, int ack)
+{
+	while(1)
+	{
+		NETCHUNKDATA *resend = (NETCHUNKDATA *)ringbuf_first(conn->buffer);
+		if(!resend)
+			break;
+			
+		if(resend->sequence <= ack || (ack < NET_MAX_SEQUENCE/3 && resend->sequence > NET_MAX_SEQUENCE/2))
+			ringbuf_popfirst(conn->buffer);
+		else
+			break;
+	}
+}
+
+void conn_want_resend(NETCONNECTION *conn)
+{
+	conn->construct.flags |= NET_PACKETFLAG_RESEND;
+}
+
+int conn_flush(NETCONNECTION *conn)
+{
+	int num_chunks = conn->construct.num_chunks;
+	if(!num_chunks && !conn->construct.flags)
+		return 0;
+	
+	conn->construct.ack = conn->ack;
+	send_packet(conn->socket, &conn->peeraddr, &conn->construct);
+	conn->last_send_time = time_get();
+	
+	/* clear construct so we can start building a new package */
+	mem_zero(&conn->construct, sizeof(conn->construct));
+	return num_chunks;
+}
+
+void conn_queue_chunk(NETCONNECTION *conn, int flags, int data_size, const void *data)
+{
+	unsigned char *chunk_data;
+	/* check if we have space for it, if not, flush the connection */
+	if(conn->construct.data_size + data_size + NET_MAX_CHUNKHEADERSIZE > sizeof(conn->construct.chunk_data))
+		conn_flush(conn);
+
+	if(flags&NET_CHUNKFLAG_VITAL && !(flags&NET_CHUNKFLAG_RESEND))
+		conn->seq = (conn->seq+1)%NET_MAX_SEQUENCE;
+
+	/* pack all the data */
+	chunk_data = &conn->construct.chunk_data[conn->construct.data_size];
+	chunk_data = pack_chunk_header(chunk_data, flags, data_size, conn->seq);
+	mem_copy(chunk_data, data, data_size);
+	chunk_data += data_size;
+
+	/* */
+	conn->construct.num_chunks++;
+	conn->construct.data_size = (int)(chunk_data-conn->construct.chunk_data);
+	
+	/* set packet flags aswell */
+	
+	if(flags&NET_CHUNKFLAG_VITAL && !(flags&NET_CHUNKFLAG_RESEND))
+	{
+		/* save packet if we need to resend */
+		NETCHUNKDATA *resend = (NETCHUNKDATA *)ringbuf_allocate(conn->buffer, sizeof(NETCHUNKDATA)+data_size);
+		if(resend)
+		{
+			resend->sequence = conn->seq;
+			resend->flags = flags;
+			resend->data_size = data_size;
+			resend->data = (unsigned char *)(resend+1);
+			resend->first_send_time = time_get();
+			mem_copy(resend->data, data, data_size);
+		}
+		else
+		{
+			/* out of buffer */
+			conn_disconnect(conn, "too weak connection (out of buffer)");
+		}
+	}
+}
+
+static void conn_send_control(NETCONNECTION *conn, int controlmsg, const void *extra, int extra_size)
+{
+	NETPACKETCONSTRUCT construct;
+	construct.flags = NET_PACKETFLAG_CONTROL;
+	construct.ack = conn->ack;
+	construct.num_chunks = 0;
+	construct.data_size = 1+extra_size;
+	construct.chunk_data[0] = controlmsg;
+	mem_copy(&construct.chunk_data[1], extra, extra_size);
+
+	/* send the control message */
+	send_packet(conn->socket, &conn->peeraddr, &construct);
+	conn->last_send_time = time_get();
+}
+
+static void conn_resend(NETCONNECTION *conn)
+{
+	int resend_count = 0;
+	int max = 10;
+	void *item = ringbuf_first(conn->buffer);
+	while(item)
+	{
+		NETCHUNKDATA *resend = item;
+		conn_queue_chunk(conn, resend->flags|NET_CHUNKFLAG_RESEND, resend->data_size, resend->data);
+		item = ringbuf_next(conn->buffer, item);
+		max--;
+		resend_count++;
+		if(!max)
+			break;
+	}
+	
+	dbg_msg("conn", "resent %d packets", resend_count);
+}
+
+int conn_connect(NETCONNECTION *conn, NETADDR *addr)
+{
+	if(conn->state != NET_CONNSTATE_OFFLINE)
+		return -1;
+	
+	/* init connection */
+	conn_reset(conn);
+	conn->peeraddr = *addr;
+	mem_zero(conn->error_string, sizeof(conn->error_string));
+	conn->state = NET_CONNSTATE_CONNECT;
+	conn_send_control(conn, NET_CTRLMSG_CONNECT, 0, 0);
+	return 0;
+}
+
+void conn_disconnect(NETCONNECTION *conn, const char *reason)
+{
+	if(conn->state == NET_CONNSTATE_OFFLINE)
+		return;
+
+	if(conn->remote_closed == 0)
+	{
+		if(reason)
+			conn_send_control(conn, NET_CTRLMSG_CLOSE, reason, strlen(reason)+1);
+		else
+			conn_send_control(conn, NET_CTRLMSG_CLOSE, 0, 0);
+
+		conn->error_string[0] = 0;
+		if(reason)
+			str_copy(conn->error_string, reason, sizeof(conn->error_string));
+	}
+	
+	conn_reset(conn);
+}
+
+int conn_feed(NETCONNECTION *conn, NETPACKETCONSTRUCT *packet, NETADDR *addr)
+{
+	int64 now = time_get();
+	conn->last_recv_time = now;
+	
+	/* check if resend is requested */
+	if(packet->flags&NET_PACKETFLAG_RESEND)
+		conn_resend(conn);
+
+	/* */									
+	if(packet->flags&NET_PACKETFLAG_CONTROL)
+	{
+		int ctrlmsg = packet->chunk_data[0];
+		
+		if(ctrlmsg == NET_CTRLMSG_CLOSE)
+		{
+			conn->state = NET_CONNSTATE_ERROR;
+			conn->remote_closed = 1;
+			
+			if(packet->data_size)
+			{
+				/* make sure to sanitize the error string form the other party*/
+				char str[128];
+				if(packet->data_size < 128)
+					str_copy(str, (char *)packet->chunk_data, packet->data_size);
+				else
+					str_copy(str, (char *)packet->chunk_data, 128);
+				str_sanitize_strong(str);
+				
+				/* set the error string */
+				conn_set_error(conn, str);
+			}
+			else
+				conn_set_error(conn, "no reason given");
+				
+			if(config.debug)
+				dbg_msg("conn", "closed reason='%s'", conn_error(conn));
+			return 0;			
+		}
+		else
+		{
+			if(conn->state == NET_CONNSTATE_OFFLINE)
+			{
+				if(ctrlmsg == NET_CTRLMSG_CONNECT)
+				{
+					/* send response and init connection */
+					conn_reset(conn);
+					conn->state = NET_CONNSTATE_ONLINE;
+					conn->peeraddr = *addr;
+					conn->last_send_time = now;
+					conn->last_recv_time = now;
+					conn->last_update_time = now;
+					conn_send_control(conn, NET_CTRLMSG_CONNECTACCEPT, 0, 0);
+					if(config.debug)
+						dbg_msg("connection", "got connection, sending connect+accept");			
+				}
+			}
+			else if(conn->state == NET_CONNSTATE_CONNECT)
+			{
+				/* connection made */
+				if(ctrlmsg == NET_CTRLMSG_CONNECTACCEPT)
+				{
+					conn_send_control(conn, NET_CTRLMSG_ACCEPT, 0, 0);
+					conn->state = NET_CONNSTATE_ONLINE;
+					if(config.debug)
+						dbg_msg("connection", "got connect+accept, sending accept. connection online");
+				}
+			}
+			else if(conn->state == NET_CONNSTATE_ONLINE)
+			{
+				/* connection made */
+				/*
+				if(ctrlmsg == NET_CTRLMSG_CONNECTACCEPT)
+				{
+					
+				}*/
+			}
+		}
+	}
+	
+	if(conn->state == NET_CONNSTATE_ONLINE)
+	{
+		conn_ack(conn, packet->ack);
+	}
+	
+	return 1;
+}
+
+int conn_update(NETCONNECTION *conn)
+{
+	int64 now = time_get();
+
+	if(conn->state == NET_CONNSTATE_OFFLINE || conn->state == NET_CONNSTATE_ERROR)
+		return 0;
+
+	/* watch out for major hitches */
+	{
+		/* TODO: fix this */
+		/*
+		int64 delta = now-conn->last_update_time;
+		if(conn->last_update_time && delta > time_freq()/2)
+		{
+			RINGBUFFER_ITEM *item = conn->buffer.first;
+	
+			dbg_msg("conn", "hitch %d", (int)((delta*1000)/time_freq()));
+			conn->last_recv_time += delta;
+	
+			while(item)
+			{
+				NETPACKETDATA *resend = (NETPACKETDATA *)rb_item_data(item);
+				resend->first_send_time += delta;
+				item = item->next;
+			}
+		}
+
+		conn->last_update_time = now;
+		*/
+	}
+		
+	
+	/* check for timeout */
+	if(conn->state != NET_CONNSTATE_OFFLINE &&
+		conn->state != NET_CONNSTATE_CONNECT &&
+		(now-conn->last_recv_time) > time_freq()*10)
+	{
+		conn->state = NET_CONNSTATE_ERROR;
+		conn_set_error(conn, "timeout");
+	}
+
+	if(ringbuf_first(conn->buffer))
+	{
+		/* TODO: fix this */
+		NETCHUNKDATA *resend = (NETCHUNKDATA *)ringbuf_first(conn->buffer);
+		if(now-resend->first_send_time > time_freq()*10)
+		{
+			conn->state = NET_CONNSTATE_ERROR;
+			conn_set_error(conn, "too weak connection (not acked for 10 seconds)");
+		}
+	}
+	
+	/* send keep alives if nothing has happend for 1000ms */
+	if(conn->state == NET_CONNSTATE_ONLINE)
+	{
+		if(time_get()-conn->last_send_time > time_freq()/2) /* flush connection after 250ms if needed */
+		{
+			int num_flushed_chunks = conn_flush(conn);
+			if(num_flushed_chunks && config.debug)
+				dbg_msg("connection", "flushed connection due to timeout. %d chunks.", num_flushed_chunks);
+		}
+			
+		if(time_get()-conn->last_send_time > time_freq())
+			conn_send_control(conn, NET_CTRLMSG_KEEPALIVE, 0, 0);
+	}
+	else if(conn->state == NET_CONNSTATE_CONNECT)
+	{
+		if(time_get()-conn->last_send_time > time_freq()/2) /* send a new connect every 500ms */
+			conn_send_control(conn, NET_CTRLMSG_CONNECT, 0, 0);
+			/*conn_send(conn, NETWORK_PACKETFLAG_CONNECT, 0, 0);*/
+	}
+	else if(conn->state == NET_CONNSTATE_CONNECTACCEPTED)
+	{
+
+		if(time_get()-conn->last_send_time > time_freq()/2) /* send a new connect/accept every 500ms */
+			conn_send_control(conn, NET_CTRLMSG_CONNECTACCEPT, 0, 0);
+			/*conn_send(conn, NETWORK_PACKETFLAG_CONNECT|NETWORK_PACKETFLAG_ACCEPT, 0, 0);*/
+	}
+	
+	return 0;
+}
diff --git a/src/engine/e_network_internal.h b/src/engine/e_network_internal.h
new file mode 100644
index 00000000..b4b7e794
--- /dev/null
+++ b/src/engine/e_network_internal.h
@@ -0,0 +1,149 @@
+#include <base/system.h>
+#include "e_network.h"
+#include "e_ringbuffer.h"
+
+/*
+
+CURRENT:
+	packet header: 3 bytes
+		unsigned char flags_ack; // 4bit flags, 4bit ack
+		unsigned char ack; // 8 bit ack
+		unsigned char num_chunks; // 8 bit chunks
+		
+		(unsigned char padding[3])	// 24 bit extra incase it's a connection less packet
+									// this is to make sure that it's compatible with the
+									// old protocol
+
+	chunk header: 2-3 bytes
+		unsigned char flags_size; // 2bit flags, 6 bit size
+		unsigned char size_seq; // 4bit size, 4bit seq
+		(unsigned char seq;) // 8bit seq, if vital flag is set
+*/
+
+enum
+{
+	NET_VERSION = 2,
+
+	NET_MAX_CHUNKSIZE = 1024,
+	NET_MAX_PAYLOAD = NET_MAX_CHUNKSIZE+16,
+	NET_MAX_PACKETSIZE = NET_MAX_PAYLOAD+16,
+	NET_MAX_CHUNKHEADERSIZE = 5,
+	NET_PACKETHEADERSIZE = 3,
+	NET_MAX_CLIENTS = 16,
+	NET_MAX_SEQUENCE = 1<<10,
+	NET_SEQUENCE_MASK = NET_MAX_SEQUENCE-1,
+
+	NET_CONNSTATE_OFFLINE=0,
+	NET_CONNSTATE_CONNECT=1,
+	NET_CONNSTATE_CONNECTACCEPTED=2,
+	NET_CONNSTATE_ONLINE=3,
+	NET_CONNSTATE_ERROR=4,
+
+	NET_PACKETFLAG_CONTROL=1,
+	NET_PACKETFLAG_CONNLESS=2,
+	NET_PACKETFLAG_RESEND=4,
+	NET_PACKETFLAG_COMPRESSION=8,
+
+	NET_CHUNKFLAG_VITAL=1,
+	NET_CHUNKFLAG_RESEND=2,
+	
+	NET_CTRLMSG_KEEPALIVE=0,
+	NET_CTRLMSG_CONNECT=1,
+	NET_CTRLMSG_CONNECTACCEPT=2,
+	NET_CTRLMSG_ACCEPT=3,
+	NET_CTRLMSG_CLOSE=4,
+	
+	NET_SERVER_MAXBANS=1024,
+	
+	NET_CONN_BUFFERSIZE=1024*16,
+	
+	NET_ENUM_TERMINATOR
+};
+
+
+typedef struct NETPACKETCONSTRUCT
+{
+	int flags;
+	int ack;
+	int num_chunks;
+	int data_size;
+	unsigned char chunk_data[NET_MAX_PAYLOAD];
+} NETPACKETCONSTRUCT;
+
+typedef struct NETCHUNKHEADER
+{
+	int flags;
+	int size;
+	int sequence;
+} NETCHUNKHEADER;
+
+typedef struct
+{
+	int flags;
+	int data_size;
+	unsigned char *data;
+
+	int sequence;
+	int64 first_send_time;
+} NETCHUNKDATA;
+
+typedef struct
+{
+	unsigned short seq;
+	unsigned short ack;
+	unsigned state;
+	
+	int token;
+	int remote_closed;
+	
+	RINGBUFFER *buffer;
+	
+	int64 last_update_time;
+	int64 last_recv_time;
+	int64 last_send_time;
+	
+	char error_string[256];
+	
+	NETPACKETCONSTRUCT construct;
+	
+	NETADDR peeraddr;
+	NETSOCKET socket;
+	NETSTATS stats;
+
+	char buffer_memory[NET_CONN_BUFFERSIZE];
+} NETCONNECTION;
+
+typedef struct NETRECVINFO
+{
+	NETADDR addr;
+	NETCONNECTION *conn;
+	int current_chunk;
+	int client_id;
+	int valid;
+	NETPACKETCONSTRUCT data;
+	unsigned char buffer[NET_MAX_PACKETSIZE];
+} NETRECVINFO;
+
+
+/* connection functions */
+void conn_init(NETCONNECTION *conn, NETSOCKET socket);
+int conn_connect(NETCONNECTION *conn, NETADDR *addr);
+void conn_disconnect(NETCONNECTION *conn, const char *reason);
+int conn_update(NETCONNECTION *conn);
+int conn_feed(NETCONNECTION *conn, NETPACKETCONSTRUCT *packet, NETADDR *addr);
+void conn_queue_chunk(NETCONNECTION *conn, int flags, int data_size, const void *data);
+const char *conn_error(NETCONNECTION *conn);
+void conn_want_resend(NETCONNECTION *conn);
+int conn_flush(NETCONNECTION *conn);
+
+/* recvinfo functions */
+void recvinfo_clear(NETRECVINFO *info);
+void recvinfo_start(NETRECVINFO *info, NETADDR *addr, NETCONNECTION *conn, int cid);
+int recvinfo_fetch_chunk(NETRECVINFO *info, NETCHUNK *chunk);
+
+/* misc helper functions */
+void send_packet_connless(NETSOCKET socket, NETADDR *addr, const void *data, int data_size);
+void send_packet(NETSOCKET socket, NETADDR *addr, NETPACKETCONSTRUCT *packet);
+int unpack_packet(unsigned char *buffer, int size, NETPACKETCONSTRUCT *packet);
+unsigned char *pack_chunk_header(unsigned char *data, int flags, int size, int sequence);
+unsigned char *unpack_chunk_header(unsigned char *data, NETCHUNKHEADER *header);
diff --git a/src/engine/e_network_server.c b/src/engine/e_network_server.c
new file mode 100644
index 00000000..98d162a5
--- /dev/null
+++ b/src/engine/e_network_server.c
@@ -0,0 +1,387 @@
+#include <base/system.h>
+#include "e_network.h"
+#include "e_network_internal.h"
+
+typedef struct
+{
+	NETCONNECTION conn;
+} NETSLOT;
+
+typedef struct NETBAN
+{
+	NETBANINFO info;
+	
+	struct NETBAN *hashnext;
+	struct NETBAN *hashprev;
+	
+	struct NETBAN *next;
+	struct NETBAN *prev;
+} NETBAN;
+
+struct NETSERVER
+{
+	NETSOCKET socket;
+	NETSLOT slots[NET_MAX_CLIENTS];
+	int max_clients;
+
+	NETBAN *bans[256];
+	NETBAN banpool[NET_SERVER_MAXBANS];
+	NETBAN *banpool_firstfree;
+	NETBAN *banpool_firstused;
+
+	NETFUNC_NEWCLIENT new_client;
+	NETFUNC_NEWCLIENT del_client;
+	void *user_ptr;
+	
+	NETRECVINFO recv;
+};
+
+NETSERVER *netserver_open(NETADDR bindaddr, int max_clients, int flags)
+{
+	int i;
+	NETSERVER *server;
+	NETSOCKET socket = net_udp_create(bindaddr);
+	if(socket == NETSOCKET_INVALID)
+		return 0;
+	
+	server = (NETSERVER *)mem_alloc(sizeof(NETSERVER), 1);
+	mem_zero(server, sizeof(NETSERVER));
+	server->socket = socket;
+	server->max_clients = max_clients;
+	if(server->max_clients > NET_MAX_CLIENTS)
+		server->max_clients = NET_MAX_CLIENTS;
+	if(server->max_clients < 1)
+		server->max_clients = 1;
+	
+	for(i = 0; i < NET_MAX_CLIENTS; i++)
+		conn_init(&server->slots[i].conn, server->socket);
+	
+	/* setup all pointers for bans */
+	for(i = 1; i < NET_SERVER_MAXBANS-1; i++)
+	{
+		server->banpool[i].next = &server->banpool[i+1];
+		server->banpool[i].prev = &server->banpool[i-1];
+	}
+	
+	server->banpool[0].next = &server->banpool[1];
+	server->banpool[NET_SERVER_MAXBANS-1].prev = &server->banpool[NET_SERVER_MAXBANS-2];
+	server->banpool_firstfree = &server->banpool[0];
+
+	return server;
+}
+
+int netserver_set_callbacks(NETSERVER *s, NETFUNC_NEWCLIENT new_client, NETFUNC_DELCLIENT del_client, void *user)
+{
+	s->new_client = new_client;
+	s->del_client = del_client;
+	s->user_ptr = user;
+	return 0;
+}
+
+int netserver_max_clients(NETSERVER *s)
+{
+	return s->max_clients;
+}
+
+int netserver_close(NETSERVER *s)
+{
+	/* TODO: implement me */
+	return 0;
+}
+
+int netserver_drop(NETSERVER *s, int client_id, const char *reason)
+{
+	/* TODO: insert lots of checks here */
+	dbg_msg("net_server", "client dropped. cid=%d reason=\"%s\"", client_id, reason);
+	conn_disconnect(&s->slots[client_id].conn, reason);
+
+	if(s->del_client)
+		s->del_client(client_id, s->user_ptr);
+		
+	return 0;
+}
+
+int netserver_ban_get(NETSERVER *s, int index, NETBANINFO *info)
+{
+	NETBAN *ban;
+	for(ban = s->banpool_firstused; ban && index; ban = ban->next, index--)
+		{}
+		
+	if(!ban)
+		return 0;
+	*info = ban->info;
+	return 1;
+}
+
+int netserver_ban_num(NETSERVER *s)
+{
+	int count = 0;
+	NETBAN *ban;
+	for(ban = s->banpool_firstused; ban; ban = ban->next)
+		count++;
+	return count;
+}
+
+int netserver_ban_remove(NETSERVER *s, NETADDR addr)
+{
+	return 0;
+}
+
+int netserver_ban_add(NETSERVER *s, NETADDR addr, int type, int seconds)
+{
+	int iphash = (addr.ip[0]+addr.ip[1]+addr.ip[2]+addr.ip[3])&0xff;
+	unsigned stamp = time_timestamp() + seconds;
+	NETBAN *ban;
+	NETBAN *insert_after;
+	
+	/* search to see if it already exists */
+	for(ban = s->bans[iphash]; ban; ban = ban->hashnext)
+	{
+		if(net_addr_comp(&ban->info.addr, &addr) == 0)
+		{
+			if(ban->info.expires < stamp)
+			{
+				/* decide what to do here */
+			}
+			return -1;
+		}
+	}
+	
+	if(!s->banpool_firstfree)
+		return -1;
+
+	/* fetch and clear the new ban */	
+	ban = s->banpool_firstfree;
+	s->banpool_firstfree->prev = 0;
+	ban->next = 0;
+	ban->prev = 0;
+	ban->info.expires = stamp;
+	ban->info.type = type;
+	
+	/* add it to the ban hash */
+	if(s->bans[iphash])
+		s->bans[iphash]->hashprev = ban;
+	ban->hashnext = s->bans[iphash];
+	ban->hashprev = 0;
+	s->bans[iphash] = ban;
+	
+	/* insert it into the used list */
+	insert_after = s->banpool_firstused;
+	while(1)
+	{
+		if(!insert_after->next)
+			break;
+		if(insert_after->next->info.expires < stamp)
+			break;
+		insert_after = insert_after->next;
+	}
+	
+	if(!insert_after || insert_after->info.expires > stamp)
+	{
+		/* insert first */
+		insert_after->prev = ban;
+		s->banpool_firstused = ban;
+		ban->next = insert_after;
+		ban->prev = 0;
+	}
+	else
+	{
+		/* insert after */
+		ban->next = insert_after->next;
+		ban->prev = insert_after;
+		if(ban->next)
+			ban->next->prev = ban;
+		insert_after->next = ban;
+	}
+	
+	return 0;
+}
+
+int netserver_update(NETSERVER *s)
+{
+	int i;
+	for(i = 0; i < s->max_clients; i++)
+	{
+		conn_update(&s->slots[i].conn);
+		if(s->slots[i].conn.state == NET_CONNSTATE_ERROR)
+			netserver_drop(s, i, conn_error(&s->slots[i].conn));
+	}
+	return 0;
+}
+
+/*
+	TODO: chopp up this function into smaller working parts
+*/
+int netserver_recv(NETSERVER *s, NETCHUNK *chunk)
+{
+	unsigned now = time_timestamp();
+	
+	while(1)
+	{
+		NETADDR addr;
+		int i, bytes, found;
+			
+		/* check for a chunk */
+		if(recvinfo_fetch_chunk(&s->recv, chunk))
+			return 1;
+		
+		/* TODO: empty the recvinfo */
+		bytes = net_udp_recv(s->socket, &addr, s->recv.buffer, NET_MAX_PACKETSIZE);
+
+		/* no more packets for now */
+		if(bytes <= 0)
+			break;
+		
+		if(unpack_packet(s->recv.buffer, bytes, &s->recv.data) == 0)
+		{
+			NETBAN *ban = 0;
+			int iphash = (addr.ip[0]+addr.ip[1]+addr.ip[2]+addr.ip[3])&0xff;
+			found = 0;
+			
+			/* search a ban */
+			for(ban = s->bans[iphash]; ban; ban = ban->hashnext)
+			{
+				if(net_addr_comp(&ban->info.addr, &addr) == 0)
+					break;
+			}
+			
+			/* check if we just should drop the packet */
+			if(ban && ban->info.type == NETBANTYPE_DROP && ban->info.expires > now)
+				continue;
+			
+			if(s->recv.data.flags&NET_PACKETFLAG_CONNLESS)
+			{
+				chunk->flags = NETSENDFLAG_CONNLESS;
+				chunk->client_id = -1;
+				chunk->address = addr;
+				chunk->data_size = s->recv.data.data_size;
+				chunk->data = s->recv.data.chunk_data;
+				return 1;
+			}
+			else
+			{			
+				/* TODO: check size here */
+				if(s->recv.data.flags&NET_PACKETFLAG_CONTROL && s->recv.data.chunk_data[0] == NET_CTRLMSG_CONNECT)
+				{
+					found = 0;
+					
+					if(ban && ban->info.expires > now)
+					{
+						/* TODO: soft ban, reply with a message */
+					}
+					else
+					{
+						/* check if we already got this client */
+						for(i = 0; i < s->max_clients; i++)
+						{
+							if(s->slots[i].conn.state != NET_CONNSTATE_OFFLINE &&
+								net_addr_comp(&s->slots[i].conn.peeraddr, &addr) == 0)
+							{
+								found = 1; /* silent ignore.. we got this client already */
+								break;
+							}
+						}
+						
+						/* client that wants to connect */
+						if(!found)
+						{
+							for(i = 0; i < s->max_clients; i++)
+							{
+								if(s->slots[i].conn.state == NET_CONNSTATE_OFFLINE)
+								{
+									found = 1;
+									conn_feed(&s->slots[i].conn, &s->recv.data, &addr);
+									if(s->new_client)
+										s->new_client(i, s->user_ptr);
+									break;
+								}
+							}
+							
+							if(!found)
+							{
+								/* TODO: send server full message */
+							}
+						}
+					}
+				}
+				else
+				{
+					/* normal packet, find matching slot */
+					for(i = 0; i < s->max_clients; i++)
+					{
+						if(net_addr_comp(&s->slots[i].conn.peeraddr, &addr) == 0)
+						{
+							if(conn_feed(&s->slots[i].conn, &s->recv.data, &addr))
+							{
+								if(s->recv.data.data_size)
+									recvinfo_start(&s->recv, &addr, &s->slots[i].conn, i);
+							}
+						}
+					}
+				}
+			}
+		}
+	}
+	return 0;
+}
+
+int netserver_send(NETSERVER *s, NETCHUNK *chunk)
+{
+	if(chunk->data_size >= NET_MAX_PAYLOAD)
+	{
+		dbg_msg("netserver", "packet payload too big. %d. dropping packet", chunk->data_size);
+		return -1;
+	}
+	
+	if(chunk->flags&NETSENDFLAG_CONNLESS)
+	{
+		/* send connectionless packet */
+		send_packet_connless(s->socket, &chunk->address, chunk->data, chunk->data_size);
+	}
+	else
+	{
+		int f = 0;
+		dbg_assert(chunk->client_id >= 0, "errornous client id");
+		dbg_assert(chunk->client_id < s->max_clients, "errornous client id");
+		
+		if(chunk->flags&NETSENDFLAG_VITAL)
+			f = NET_CHUNKFLAG_VITAL;
+		
+		conn_queue_chunk(&s->slots[chunk->client_id].conn, f, chunk->data_size, chunk->data);
+
+		if(chunk->flags&NETSENDFLAG_FLUSH)
+			conn_flush(&s->slots[chunk->client_id].conn);
+	}
+	return 0;
+}
+
+void netserver_stats(NETSERVER *s, NETSTATS *stats)
+{
+	int num_stats = sizeof(NETSTATS)/sizeof(int);
+	int *istats = (int *)stats;
+	int c, i;
+
+	mem_zero(stats, sizeof(NETSTATS));
+	
+	for(c = 0; c < s->max_clients; c++)
+	{
+		if(s->slots[c].conn.state != NET_CONNSTATE_OFFLINE)
+		{
+			int *sstats = (int *)(&(s->slots[c].conn.stats));
+			for(i = 0; i < num_stats; i++)
+				istats[i] += sstats[i];
+		}
+	}
+}
+
+NETSOCKET netserver_socket(NETSERVER *s)
+{
+	return s->socket;
+}
+
+
+int netserver_client_addr(NETSERVER *s, int client_id, NETADDR *addr)
+{
+	*addr = s->slots[client_id].conn.peeraddr;
+	return 1;
+}
diff --git a/src/engine/e_ringbuffer.c b/src/engine/e_ringbuffer.c
index 281c3751..ed3326d4 100644
--- a/src/engine/e_ringbuffer.c
+++ b/src/engine/e_ringbuffer.c
@@ -1,10 +1,26 @@
 #include <base/system.h>
 
+#include "e_ringbuffer.h"
+
 enum
 {
 	RBFLAG_FREE=1
 };
 
+/*
+
+*/
+struct RINGBUFFER
+{
+	struct RBITEM_t *next_alloc;
+	struct RBITEM_t *last_alloc;
+	struct RBITEM_t *first;
+	struct RBITEM_t *last;
+	void *memory;
+	int size;
+	int flags;
+};
+
 typedef struct RBITEM_t
 {
     struct RBITEM_t *prev;
@@ -12,19 +28,8 @@ typedef struct RBITEM_t
     int flags;
     int size;
 } RBITEM;
-
-typedef struct
-{
-    /* what you need */
-    RBITEM *next_alloc;
-    RBITEM *last_alloc;
-    RBITEM *first;
-    RBITEM *last;
-    void *memory;
-    int size;
-} RINGBUFFER; 
  
-RINGBUFFER *ringbuf_init(void *memory, int size)
+RINGBUFFER *ringbuf_init(void *memory, int size, int flags)
 {
 	RINGBUFFER *rb = (RINGBUFFER *)memory;
 	mem_zero(memory, size);
@@ -37,6 +42,8 @@ RINGBUFFER *ringbuf_init(void *memory, int size)
 	rb->last = rb->first;
 	rb->next_alloc = rb->first;
 	
+	rb->flags = flags;
+	
 	return rb;
 }
 
@@ -45,6 +52,10 @@ static RBITEM *ringbuf_free(RINGBUFFER *rb, RBITEM *item)
 	dbg_assert(!(item->flags&RBFLAG_FREE), "trying to  free element that is already freed");
 	item->flags |= RBFLAG_FREE;
 
+	/* TODO: this should be handled better */	
+	if(item == rb->last_alloc)
+		rb->last_alloc = 0;
+	
 	/* merge with all free items backwards */
 	while(item->prev && (item->prev->flags&RBFLAG_FREE))
 	{
@@ -66,7 +77,7 @@ static RBITEM *ringbuf_free(RINGBUFFER *rb, RBITEM *item)
 	
 	if(!item->next)
 		rb->last = item;
-	
+		
 	return item;
 }
 
@@ -126,6 +137,17 @@ static RBITEM *ringbuf_try_allocate(RINGBUFFER *rb, int wanted_size)
 	return item;
 }
 
+void ringbuf_popfirst(RINGBUFFER *rb)
+{
+	if(rb->next_alloc->next)
+		rb->next_alloc = ringbuf_free(rb, rb->next_alloc->next);
+	else
+	{
+		rb->next_alloc = rb->first;
+		rb->next_alloc = ringbuf_free(rb, rb->next_alloc);
+	}
+}
+
 void *ringbuf_allocate(RINGBUFFER *rb, int size)
 {
 	int wanted_size = (size+sizeof(RBITEM)+sizeof(RBITEM)-1)/sizeof(RBITEM)*sizeof(RBITEM);
@@ -143,17 +165,16 @@ void *ringbuf_allocate(RINGBUFFER *rb, int size)
 	block = ringbuf_try_allocate(rb, wanted_size);
 	if(block)
 		return block+1;
+
+	/* check if we just should return null */
+	if(!(rb->flags&RINGBUF_FLAG_RECYCLE))
+		return 0;
 	
 	/* ok, we need to wipe some blocks in order to get space */
 	while(1)
 	{
-		if(rb->next_alloc->next)
-			rb->next_alloc = ringbuf_free(rb, rb->next_alloc->next);
-		else
-		{
-			rb->next_alloc = rb->first;
-			rb->next_alloc = ringbuf_free(rb, rb->next_alloc);
-		}
+		/* pop one */
+		ringbuf_popfirst(rb);
 
 		/* try allocate again */
 		block = ringbuf_try_allocate(rb, wanted_size);
diff --git a/src/engine/e_ringbuffer.h b/src/engine/e_ringbuffer.h
index 40043492..71b5831b 100644
--- a/src/engine/e_ringbuffer.h
+++ b/src/engine/e_ringbuffer.h
@@ -1,18 +1,16 @@
 #ifndef _RINGBUFFER_H
 #define _RINGBUFFER_H
 
-typedef struct RINGBUFFER
+typedef struct RINGBUFFER RINGBUFFER;
+
+enum
 {
-	/* what you need */
-	struct RBITEM_t *next_alloc;
-	struct RBITEM_t *last_alloc;
-	struct RBITEM_t *first;
-	struct RBITEM_t *last;
-	void *memory;
-	int size;
-} RINGBUFFER;
+	/* Will start to destroy items to try to fit the next one */
+	RINGBUF_FLAG_RECYCLE=1
+};
  
-RINGBUFFER *ringbuf_init(void *memory, int size);
+RINGBUFFER *ringbuf_init(void *memory, int size, int flags);
+void ringbuf_clear(RINGBUFFER *rb);
 void *ringbuf_allocate(RINGBUFFER *rb, int size);
 void ringbuf_validate(RINGBUFFER *rb);
 
@@ -23,4 +21,6 @@ void *ringbuf_next(RINGBUFFER *rb, void *current);
 void *ringbuf_first(RINGBUFFER *rb);
 void *ringbuf_last(RINGBUFFER *rb);
 
+void ringbuf_popfirst(RINGBUFFER *rb);
+
 #endif
diff --git a/src/engine/server/es_server.c b/src/engine/server/es_server.c
index 58866e09..dd4fe81f 100644
--- a/src/engine/server/es_server.c
+++ b/src/engine/server/es_server.c
@@ -963,7 +963,7 @@ static int server_run()
 		game_start_time = time_get();
 	
 		if(config.debug)
-			dbg_msg("server", "baseline memory usage %dk", mem_allocated()/1024);
+			dbg_msg("server", "baseline memory usage %dk", mem_stats()->allocated/1024);
 
 		while(run_server)
 		{
diff --git a/src/game/client/components/console.cpp b/src/game/client/components/console.cpp
index a5098ce1..c8fe018e 100644
--- a/src/game/client/components/console.cpp
+++ b/src/game/client/components/console.cpp
@@ -34,8 +34,8 @@ enum
 CONSOLE::INSTANCE::INSTANCE(int t)
 {
 	// init ringbuffers
-	history = ringbuf_init(history_data, sizeof(history_data));
-	backlog = ringbuf_init(backlog_data, sizeof(backlog_data));
+	history = ringbuf_init(history_data, sizeof(history_data), RINGBUF_FLAG_RECYCLE);
+	backlog = ringbuf_init(backlog_data, sizeof(backlog_data), RINGBUF_FLAG_RECYCLE);
 	
 	history_entry = 0x0;