frontlib: Started work on the server connection code
authorMedo <smaxein@googlemail.com>
Tue, 19 Jun 2012 21:17:05 +0200
changeset 7234 613998625a3c
parent 7230 240620f46dd7
child 7267 710f3ced8934
frontlib: Started work on the server connection code
project_files/frontlib/hwconsts.h
project_files/frontlib/ipc/gameconn.c
project_files/frontlib/ipc/gameconn.h
project_files/frontlib/ipc/ipcbase.c
project_files/frontlib/ipc/ipcbase.h
project_files/frontlib/ipc/ipcconn.c
project_files/frontlib/ipc/ipcconn.h
project_files/frontlib/ipc/mapconn.c
project_files/frontlib/net/netbase.c
project_files/frontlib/net/netbase.h
project_files/frontlib/net/netconn.c
project_files/frontlib/net/netconn.h
project_files/frontlib/net/netprotocol.h
project_files/frontlib/socket.c
project_files/frontlib/socket.h
project_files/frontlib/test.c
project_files/frontlib/util/buffer.c
project_files/frontlib/util/buffer.h
--- a/project_files/frontlib/hwconsts.h	Fri Jun 15 19:57:25 2012 +0200
+++ b/project_files/frontlib/hwconsts.h	Tue Jun 19 21:17:05 2012 +0200
@@ -8,6 +8,8 @@
 
 #define HEDGEHOGS_PER_TEAM 8
 #define NETGAME_DEFAULT_PORT 46631
+#define PROTOCOL_VERSION 42
+#define MIN_SERVER_VERSION 1
 
 #define GAMEMOD_PERHOGAMMO_MASKBIT 22
 #define GAMEMOD_SHAREDAMMO_MASKBIT 16
--- a/project_files/frontlib/ipc/gameconn.c	Fri Jun 15 19:57:25 2012 +0200
+++ b/project_files/frontlib/ipc/gameconn.c	Tue Jun 19 21:17:05 2012 +0200
@@ -1,5 +1,5 @@
 #include "gameconn.h"
-#include "ipcconn.h"
+#include "ipcbase.h"
 #include "ipcprotocol.h"
 #include "../util/logging.h"
 #include "../util/util.h"
@@ -15,7 +15,7 @@
 } gameconn_state;
 
 struct _flib_gameconn {
-	flib_ipcconn *connection;
+	flib_ipcbase *ipcBase;
 	flib_vector *configBuffer;
 	flib_vector *demoBuffer;
 	char *playerName;
@@ -38,8 +38,8 @@
 	void (*onGameRecordedCb)(void *context, const uint8_t *record, int size, bool isSavegame);
 	void *onGameRecordedCtx;
 
-	void (*onNetMessageCb)(void *context, const uint8_t *em, int size);
-	void *onNetMessageCtx;
+	void (*onEngineMessageCb)(void *context, const uint8_t *em, int size);
+	void *onEngineMessageCtx;
 
 	bool running;
 	bool destroyRequested;
@@ -52,7 +52,7 @@
 }
 static void defaultCallback_onChat(void* context, const char *msg, bool teamchat) {}
 static void defaultCallback_onGameRecorded(void *context, const uint8_t *record, int size, bool isSavegame) {}
-static void defaultCallback_onNetMessage(void *context, const uint8_t *em, int size) {}
+static void defaultCallback_onEngineMessage(void *context, const uint8_t *em, int size) {}
 
 static void clearCallbacks(flib_gameconn *conn) {
 	conn->onConnectCb = &defaultCallback_onConnect;
@@ -60,17 +60,17 @@
 	conn->onErrorMessageCb = &defaultCallback_onErrorMessage;
 	conn->onChatCb = &defaultCallback_onChat;
 	conn->onGameRecordedCb = &defaultCallback_onGameRecorded;
-	conn->onNetMessageCb = &defaultCallback_onNetMessage;
+	conn->onEngineMessageCb = &defaultCallback_onEngineMessage;
 }
 
 static flib_gameconn *flib_gameconn_create_partial(bool record, const char *playerName, bool netGame) {
 	flib_gameconn *result = NULL;
 	flib_gameconn *tempConn = flib_calloc(1, sizeof(flib_gameconn));
 	if(tempConn) {
-		tempConn->connection = flib_ipcconn_create();
+		tempConn->ipcBase = flib_ipcbase_create();
 		tempConn->configBuffer = flib_vector_create();
 		tempConn->playerName = flib_strdupnull(playerName);
-		if(tempConn->connection && tempConn->configBuffer && tempConn->playerName) {
+		if(tempConn->ipcBase && tempConn->configBuffer && tempConn->playerName) {
 			if(record) {
 				tempConn->demoBuffer = flib_vector_create();
 			}
@@ -135,7 +135,7 @@
 			clearCallbacks(conn);
 			conn->destroyRequested = true;
 		} else {
-			flib_ipcconn_destroy(conn->connection);
+			flib_ipcbase_destroy(conn->ipcBase);
 			flib_vector_destroy(conn->configBuffer);
 			flib_vector_destroy(conn->demoBuffer);
 			free(conn->playerName);
@@ -149,7 +149,7 @@
 		flib_log_e("null parameter in flib_gameconn_getport");
 		return 0;
 	} else {
-		return flib_ipcconn_port(conn->connection);
+		return flib_ipcbase_port(conn->ipcBase);
 	}
 }
 
@@ -201,7 +201,7 @@
 	int result = -1;
 	if(!conn || (!data && len>0)) {
 		flib_log_e("null parameter in flib_gameconn_send_enginemsg");
-	} else if(!flib_ipcconn_send_raw(conn->connection, data, len)) {
+	} else if(!flib_ipcbase_send_raw(conn->ipcBase, data, len)) {
 		demo_append(conn, data, len);
 		result = 0;
 	}
@@ -217,7 +217,7 @@
 		int size = snprintf((char*)converted+1, 256, "s%c%s", (char)msgtype, msg);
 		if(size>0) {
 			converted[0] = size>255 ? 255 : size;
-			if(!flib_ipcconn_send_raw(conn->connection, converted, converted[0]+1)) {
+			if(!flib_ipcbase_send_raw(conn->ipcBase, converted, converted[0]+1)) {
 				demo_append(conn, converted, converted[0]+1);
 				result = 0;
 			}
@@ -233,7 +233,7 @@
 		flib_log_e("null parameter in flib_gameconn_send_chatmsg");
 	} else if(format_chatmessage(converted, playername, msg)) {
 		flib_log_e("Error formatting message in flib_gameconn_send_chatmsg");
-	} else if(!flib_ipcconn_send_raw(conn->connection, converted, converted[0]+1)) {
+	} else if(!flib_ipcbase_send_raw(conn->ipcBase, converted, converted[0]+1)) {
 		demo_append(conn, converted, converted[0]+1);
 		result = 0;
 	}
@@ -285,23 +285,23 @@
 	}
 }
 
-void flib_gameconn_onNetMessage(flib_gameconn *conn, void (*callback)(void *context, const uint8_t *em, int size), void* context) {
+void flib_gameconn_onEngineMessage(flib_gameconn *conn, void (*callback)(void *context, const uint8_t *em, int size), void* context) {
 	if(!conn) {
-		flib_log_e("null parameter in flib_gameconn_onNetMessage");
+		flib_log_e("null parameter in flib_gameconn_onEngineMessage");
 	} else {
-		conn->onNetMessageCb = callback ? callback : &defaultCallback_onNetMessage;
-		conn->onNetMessageCtx = context;
+		conn->onEngineMessageCb = callback ? callback : &defaultCallback_onEngineMessage;
+		conn->onEngineMessageCtx = context;
 	}
 }
 
 static void flib_gameconn_wrappedtick(flib_gameconn *conn) {
 	if(conn->state == AWAIT_CONNECTION) {
-		flib_ipcconn_accept(conn->connection);
-		switch(flib_ipcconn_state(conn->connection)) {
+		flib_ipcbase_accept(conn->ipcBase);
+		switch(flib_ipcbase_state(conn->ipcBase)) {
 		case IPC_CONNECTED:
 			{
 				flib_constbuffer configBuffer = flib_vector_as_constbuffer(conn->configBuffer);
-				if(flib_ipcconn_send_raw(conn->connection, configBuffer.data, configBuffer.size)) {
+				if(flib_ipcbase_send_raw(conn->ipcBase, configBuffer.data, configBuffer.size)) {
 					conn->state = FINISHED;
 					conn->onDisconnectCb(conn->onDisconnectCtx, GAME_END_ERROR);
 					return;
@@ -327,7 +327,7 @@
 	if(conn->state == CONNECTED) {
 		uint8_t msgbuffer[257];
 		int len;
-		while(!conn->destroyRequested && (len = flib_ipcconn_recv_message(conn->connection, msgbuffer))>=0) {
+		while(!conn->destroyRequested && (len = flib_ipcbase_recv_message(conn->ipcBase, msgbuffer))>=0) {
 			if(len<2) {
 				flib_log_w("Received short message from IPC (<2 bytes)");
 				continue;
@@ -383,13 +383,13 @@
 			default:	// Engine message
 				demo_append(conn, msgbuffer, len);
 
-				conn->onNetMessageCb(conn->onNetMessageCtx, msgbuffer, len);
+				conn->onEngineMessageCb(conn->onEngineMessageCtx, msgbuffer, len);
 				break;
 			}
 		}
 	}
 
-	if(flib_ipcconn_state(conn->connection) == IPC_NOT_CONNECTED) {
+	if(flib_ipcbase_state(conn->ipcBase) == IPC_NOT_CONNECTED) {
 		conn->state = FINISHED;
 		conn->onDisconnectCb(conn->onDisconnectCtx, GAME_END_ERROR);
 	}
--- a/project_files/frontlib/ipc/gameconn.h	Fri Jun 15 19:57:25 2012 +0200
+++ b/project_files/frontlib/ipc/gameconn.h	Tue Jun 19 21:17:05 2012 +0200
@@ -1,7 +1,6 @@
 #ifndef GAMECONN_H_
 #define GAMECONN_H_
 
-#include "../util/buffer.h"
 #include "../model/gamesetup.h"
 
 #include <stddef.h>
@@ -73,7 +72,7 @@
  * ...needs to be passed on to the server in a net game
  * handleEngineMessage(void *context, const uint8_t *em, int size)
  */
-void flib_gameconn_onNetMessage(flib_gameconn *conn, void (*callback)(void *context, const uint8_t *em, int size), void* context);
+void flib_gameconn_onEngineMessage(flib_gameconn *conn, void (*callback)(void *context, const uint8_t *em, int size), void* context);
 
 // TODO efinish
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/ipc/ipcbase.c	Tue Jun 19 21:17:05 2012 +0200
@@ -0,0 +1,200 @@
+#include "ipcbase.h"
+#include "../util/logging.h"
+#include "../util/util.h"
+#include "../socket.h"
+
+#include <string.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+/*
+ * The receive buffer has to be able to hold any message that might be received. Normally
+ * the messages are at most 256 bytes, but the map preview contains 4097 bytes (4096 for a
+ * bitmap, 1 for the number of hogs which fit on the map).
+ *
+ * We don't need to worry about wasting a few kb though, and I like powers of two...
+ */
+struct _flib_ipcbase {
+	uint8_t readBuffer[8192];
+	int readBufferSize;
+
+	flib_acceptor *acceptor;
+	uint16_t port;
+
+	flib_tcpsocket *sock;
+};
+
+flib_ipcbase *flib_ipcbase_create() {
+	flib_ipcbase *result = flib_calloc(1, sizeof(flib_ipcbase));
+	flib_acceptor *acceptor = flib_acceptor_create(0);
+
+	if(!result || !acceptor) {
+		free(result);
+		flib_acceptor_close(acceptor);
+		return NULL;
+	}
+
+	result->acceptor = acceptor;
+	result->sock = NULL;
+	result->readBufferSize = 0;
+	result->port = flib_acceptor_listenport(acceptor);
+
+	flib_log_i("Started listening for IPC connections on port %u", (unsigned)result->port);
+	return result;
+}
+
+uint16_t flib_ipcbase_port(flib_ipcbase *ipc) {
+	if(!ipc) {
+		flib_log_e("null parameter in flib_ipcbase_port");
+		return 0;
+	}
+	return ipc->port;
+}
+
+void flib_ipcbase_destroy(flib_ipcbase *ipc) {
+	if(ipc) {
+		flib_acceptor_close(ipc->acceptor);
+		flib_socket_close(ipc->sock);
+		free(ipc);
+	}
+}
+
+IpcState flib_ipcbase_state(flib_ipcbase *ipc) {
+	if(!ipc) {
+		flib_log_e("null parameter in flib_ipcbase_state");
+		return IPC_NOT_CONNECTED;
+	} else if(ipc->sock) {
+		return IPC_CONNECTED;
+	} else if(ipc->acceptor) {
+		return IPC_LISTENING;
+	} else {
+		return IPC_NOT_CONNECTED;
+	}
+}
+
+static bool isMessageReady(flib_ipcbase *ipc) {
+	return ipc->readBufferSize >= ipc->readBuffer[0]+1;
+}
+
+static void receiveToBuffer(flib_ipcbase *ipc) {
+	if(ipc->sock) {
+		int size = flib_socket_nbrecv(ipc->sock, ipc->readBuffer+ipc->readBufferSize, sizeof(ipc->readBuffer)-ipc->readBufferSize);
+		if(size>=0) {
+			ipc->readBufferSize += size;
+		} else {
+			flib_socket_close(ipc->sock);
+			ipc->sock = NULL;
+		}
+	}
+}
+
+int flib_ipcbase_recv_message(flib_ipcbase *ipc, void *data) {
+	if(!ipc || !data) {
+		flib_log_e("null parameter in flib_ipcbase_recv_message");
+		return -1;
+	}
+
+	if(!isMessageReady(ipc)) {
+		receiveToBuffer(ipc);
+	}
+
+	if(isMessageReady(ipc)) {
+		int msgsize = ipc->readBuffer[0]+1;
+		memcpy(data, ipc->readBuffer, msgsize);
+		memmove(ipc->readBuffer, ipc->readBuffer+msgsize, ipc->readBufferSize-msgsize);
+		ipc->readBufferSize -= msgsize;
+		return msgsize;
+	} else if(!ipc->sock && ipc->readBufferSize>0) {
+		flib_log_w("Last message from engine data stream is incomplete (received %u of %u bytes)", (unsigned)ipc->readBufferSize, (unsigned)(ipc->readBuffer[0])+1);
+		ipc->readBufferSize = 0;
+		return -1;
+	} else {
+		return -1;
+	}
+}
+
+int flib_ipcbase_recv_map(flib_ipcbase *ipc, void *data) {
+	if(!ipc || !data) {
+		flib_log_e("null parameter in flib_ipcbase_recv_map");
+		return -1;
+	}
+
+	receiveToBuffer(ipc);
+
+	if(ipc->readBufferSize >= IPCBASE_MAPMSG_BYTES) {
+		memcpy(data, ipc->readBuffer, IPCBASE_MAPMSG_BYTES);
+		memmove(ipc->readBuffer, ipc->readBuffer+IPCBASE_MAPMSG_BYTES, ipc->readBufferSize-IPCBASE_MAPMSG_BYTES);
+		return IPCBASE_MAPMSG_BYTES;
+	} else {
+		return -1;
+	}
+}
+
+static void logSentMsg(const uint8_t *data, size_t len) {
+	if(flib_log_isActive(FLIB_LOGLEVEL_DEBUG)) {
+		size_t msgStart = 0;
+		while(msgStart < len) {
+			uint8_t msglen = data[msgStart];
+			if(msgStart+msglen < len) {
+				flib_log_d("[IPC OUT][%03u]%*.*s",(unsigned)msglen, (unsigned)msglen, (unsigned)msglen, data+msgStart+1);
+			} else {
+				uint8_t msglen2 = len-msgStart-1;
+				flib_log_d("[IPC OUT][%03u/%03u]%*.*s",(unsigned)msglen2, (unsigned)msglen, (unsigned)msglen2, (unsigned)msglen2, data+msgStart+1);
+			}
+			msgStart += (uint8_t)data[msgStart]+1;
+		}
+	}
+}
+
+int flib_ipcbase_send_raw(flib_ipcbase *ipc, const void *data, size_t len) {
+	if(!ipc || (!data && len>0)) {
+		flib_log_e("null parameter in flib_ipcbase_send_raw");
+		return -1;
+	}
+	if(!ipc->sock) {
+		flib_log_w("flib_ipcbase_send_raw: Not connected.");
+		return -1;
+	}
+
+	if(flib_socket_send(ipc->sock, data, len) == len) {
+		logSentMsg(data, len);
+		return 0;
+	} else {
+		flib_log_w("Failed or incomplete ICP write: engine connection lost.");
+		flib_socket_close(ipc->sock);
+		ipc->sock = NULL;
+		return -1;
+	}
+}
+
+int flib_ipcbase_send_message(flib_ipcbase *ipc, void *data, size_t len) {
+	if(!ipc || (!data && len>0)) {
+		flib_log_e("null parameter in flib_ipcbase_send_message");
+		return -1;
+	} else if(len>255) {
+		flib_log_e("Overlong message (%zu bytes) in flib_ipcbase_send_message", len);
+		return -1;
+	}
+
+	uint8_t sendbuf[256];
+	sendbuf[0] = len;
+	memcpy(sendbuf+1, data, len);
+	return flib_ipcbase_send_raw(ipc, sendbuf, len+1);
+}
+
+int flib_ipcbase_send_messagestr(flib_ipcbase *ipc, char *data) {
+	return flib_ipcbase_send_message(ipc, data, strlen(data));
+}
+
+void flib_ipcbase_accept(flib_ipcbase *ipc) {
+	if(!ipc) {
+		flib_log_e("null parameter in flib_ipcbase_accept");
+	} else if(!ipc->sock && ipc->acceptor) {
+		ipc->sock = flib_socket_accept(ipc->acceptor, true);
+		if(ipc->sock) {
+			flib_acceptor_close(ipc->acceptor);
+			ipc->acceptor = NULL;
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/ipc/ipcbase.h	Tue Jun 19 21:17:05 2012 +0200
@@ -0,0 +1,86 @@
+/*
+ * Low-level protocol support for the IPC connection to the engine.
+ */
+
+#ifndef IPCBASE_H_
+#define IPCBASE_H_
+
+#include <stddef.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+#define IPCBASE_MAPMSG_BYTES 4097
+
+typedef enum {IPC_NOT_CONNECTED, IPC_LISTENING, IPC_CONNECTED} IpcState;
+
+struct _flib_ipcbase;
+typedef struct _flib_ipcbase flib_ipcbase;
+
+/**
+ * Start an engine connection by listening on a random port. The selected port can
+ * be queried with flib_ipcbase_port and has to be passed to the engine.
+ *
+ * Returns NULL on error. Destroy the created object with flib_ipcbase_destroy.
+ *
+ * We stop accepting new connections once a connection has been established, so you
+ * need to create a new ipcbase in order to start a new connection.
+ */
+flib_ipcbase *flib_ipcbase_create();
+
+uint16_t flib_ipcbase_port(flib_ipcbase *ipc);
+
+/**
+ * Free resources and close sockets.
+ */
+void flib_ipcbase_destroy(flib_ipcbase *ipc);
+
+/**
+ * Determine the current connection state
+ */
+IpcState flib_ipcbase_state(flib_ipcbase *ipc);
+
+/**
+ * Receive a single message (up to 256 bytes) and copy it into the data buffer.
+ * Returns the length of the received message, a negative value if no message could
+ * be read.
+ *
+ * The first byte of a message is its content length, which is one less than the returned
+ * value.
+ *
+ * Note: When a connection is closed, you probably want to call this function until
+ * no further message is returned, to ensure you see all messages that were sent
+ * before the connection closed.
+ */
+int flib_ipcbase_recv_message(flib_ipcbase *ipc, void *data);
+
+/**
+ * Try to receive 4097 bytes. This is the size of the reply the engine sends
+ * when successfully queried for map data. The first 4096 bytes are a bit-packed
+ * twocolor image of the map (256x128), the last byte is the number of hogs that
+ * fit on the map.
+ */
+int flib_ipcbase_recv_map(flib_ipcbase *ipc, void *data);
+
+int flib_ipcbase_send_raw(flib_ipcbase *ipc, const void *data, size_t len);
+
+/**
+ * Write a single message (up to 255 bytes) to the engine. This call blocks until the
+ * message is completely written or the connection is closed or an error occurs.
+ *
+ * Calling this function in a state other than IPC_CONNECTED will fail immediately.
+ * Returns a negative value on failure.
+ */
+int flib_ipcbase_send_message(flib_ipcbase *ipc, void *data, size_t len);
+
+/**
+ * Convenience function for sending a 0-delimited string.
+ */
+int flib_ipcbase_send_messagestr(flib_ipcbase *ipc, char *data);
+
+/**
+ * Try to accept a connection. Only has an effect in state IPC_LISTENING.
+ */
+void flib_ipcbase_accept(flib_ipcbase *ipc);
+
+#endif /* IPCBASE_H_ */
+
--- a/project_files/frontlib/ipc/ipcconn.c	Fri Jun 15 19:57:25 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,200 +0,0 @@
-#include "ipcconn.h"
-#include "../util/logging.h"
-#include "../util/util.h"
-#include "../socket.h"
-
-#include <string.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <stdio.h>
-
-/*
- * The receive buffer has to be able to hold any message that might be received. Normally
- * the messages are at most 256 bytes, but the map preview contains 4097 bytes (4096 for a
- * bitmap, 1 for the number of hogs which fit on the map).
- *
- * We don't need to worry about wasting a few kb though, and I like powers of two...
- */
-struct _flib_ipcconn {
-	uint8_t readBuffer[8192];
-	int readBufferSize;
-
-	flib_acceptor *acceptor;
-	uint16_t port;
-
-	flib_tcpsocket *sock;
-};
-
-flib_ipcconn *flib_ipcconn_create() {
-	flib_ipcconn *result = flib_malloc(sizeof(flib_ipcconn));
-	flib_acceptor *acceptor = flib_acceptor_create(0);
-
-	if(!result || !acceptor) {
-		free(result);
-		flib_acceptor_close(acceptor);
-		return NULL;
-	}
-
-	result->acceptor = acceptor;
-	result->sock = NULL;
-	result->readBufferSize = 0;
-	result->port = flib_acceptor_listenport(acceptor);
-
-	flib_log_i("Started listening for IPC connections on port %u", (unsigned)result->port);
-	return result;
-}
-
-uint16_t flib_ipcconn_port(flib_ipcconn *ipc) {
-	if(!ipc) {
-		flib_log_e("null parameter in flib_ipcconn_port");
-		return 0;
-	}
-	return ipc->port;
-}
-
-void flib_ipcconn_destroy(flib_ipcconn *ipc) {
-	if(ipc) {
-		flib_acceptor_close(ipc->acceptor);
-		flib_socket_close(ipc->sock);
-		free(ipc);
-	}
-}
-
-IpcConnState flib_ipcconn_state(flib_ipcconn *ipc) {
-	if(!ipc) {
-		flib_log_e("null parameter in flib_ipcconn_state");
-		return IPC_NOT_CONNECTED;
-	} else if(ipc->sock) {
-		return IPC_CONNECTED;
-	} else if(ipc->acceptor) {
-		return IPC_LISTENING;
-	} else {
-		return IPC_NOT_CONNECTED;
-	}
-}
-
-static bool isMessageReady(flib_ipcconn *ipc) {
-	return ipc->readBufferSize >= ipc->readBuffer[0]+1;
-}
-
-static void receiveToBuffer(flib_ipcconn *ipc) {
-	if(ipc->sock) {
-		int size = flib_socket_nbrecv(ipc->sock, ipc->readBuffer+ipc->readBufferSize, sizeof(ipc->readBuffer)-ipc->readBufferSize);
-		if(size>=0) {
-			ipc->readBufferSize += size;
-		} else {
-			flib_socket_close(ipc->sock);
-			ipc->sock = NULL;
-		}
-	}
-}
-
-int flib_ipcconn_recv_message(flib_ipcconn *ipc, void *data) {
-	if(!ipc || !data) {
-		flib_log_e("null parameter in flib_ipcconn_recv_message");
-		return -1;
-	}
-
-	if(!isMessageReady(ipc)) {
-		receiveToBuffer(ipc);
-	}
-
-	if(isMessageReady(ipc)) {
-		int msgsize = ipc->readBuffer[0]+1;
-		memcpy(data, ipc->readBuffer, msgsize);
-		memmove(ipc->readBuffer, ipc->readBuffer+msgsize, ipc->readBufferSize-msgsize);
-		ipc->readBufferSize -= msgsize;
-		return msgsize;
-	} else if(!ipc->sock && ipc->readBufferSize>0) {
-		flib_log_w("Last message from engine data stream is incomplete (received %u of %u bytes)", (unsigned)ipc->readBufferSize, (unsigned)(ipc->readBuffer[0])+1);
-		ipc->readBufferSize = 0;
-		return -1;
-	} else {
-		return -1;
-	}
-}
-
-int flib_ipcconn_recv_map(flib_ipcconn *ipc, void *data) {
-	if(!ipc || !data) {
-		flib_log_e("null parameter in flib_ipcconn_recv_map");
-		return -1;
-	}
-
-	receiveToBuffer(ipc);
-
-	if(ipc->readBufferSize >= IPCCONN_MAPMSG_BYTES) {
-		memcpy(data, ipc->readBuffer, IPCCONN_MAPMSG_BYTES);
-		memmove(ipc->readBuffer, ipc->readBuffer+IPCCONN_MAPMSG_BYTES, ipc->readBufferSize-IPCCONN_MAPMSG_BYTES);
-		return IPCCONN_MAPMSG_BYTES;
-	} else {
-		return -1;
-	}
-}
-
-static void logSentMsg(const uint8_t *data, size_t len) {
-	if(flib_log_isActive(FLIB_LOGLEVEL_DEBUG)) {
-		size_t msgStart = 0;
-		while(msgStart < len) {
-			uint8_t msglen = data[msgStart];
-			if(msgStart+msglen < len) {
-				flib_log_d("[IPC OUT][%03u]%*.*s",(unsigned)msglen, (unsigned)msglen, (unsigned)msglen, data+msgStart+1);
-			} else {
-				uint8_t msglen2 = len-msgStart-1;
-				flib_log_d("[IPC OUT][%03u/%03u]%*.*s",(unsigned)msglen2, (unsigned)msglen, (unsigned)msglen2, (unsigned)msglen2, data+msgStart+1);
-			}
-			msgStart += (uint8_t)data[msgStart]+1;
-		}
-	}
-}
-
-int flib_ipcconn_send_raw(flib_ipcconn *ipc, const void *data, size_t len) {
-	if(!ipc || (!data && len>0)) {
-		flib_log_e("null parameter in flib_ipcconn_send_raw");
-		return -1;
-	}
-	if(!ipc->sock) {
-		flib_log_w("flib_ipcconn_send_raw: Not connected.");
-		return -1;
-	}
-
-	if(flib_socket_send(ipc->sock, data, len) == len) {
-		logSentMsg(data, len);
-		return 0;
-	} else {
-		flib_log_w("Failed or incomplete ICP write: engine connection lost.");
-		flib_socket_close(ipc->sock);
-		ipc->sock = NULL;
-		return -1;
-	}
-}
-
-int flib_ipcconn_send_message(flib_ipcconn *ipc, void *data, size_t len) {
-	if(!ipc || (!data && len>0)) {
-		flib_log_e("null parameter in flib_ipcconn_send_message");
-		return -1;
-	} else if(len>255) {
-		flib_log_e("Overlong message (%zu bytes) in flib_ipcconn_send_message", len);
-		return -1;
-	}
-
-	uint8_t sendbuf[256];
-	sendbuf[0] = len;
-	memcpy(sendbuf+1, data, len);
-	return flib_ipcconn_send_raw(ipc, sendbuf, len+1);
-}
-
-int flib_ipcconn_send_messagestr(flib_ipcconn *ipc, char *data) {
-	return flib_ipcconn_send_message(ipc, data, strlen(data));
-}
-
-void flib_ipcconn_accept(flib_ipcconn *ipc) {
-	if(!ipc) {
-		flib_log_e("null parameter in flib_ipcconn_accept");
-	} else if(!ipc->sock && ipc->acceptor) {
-		ipc->sock = flib_socket_accept(ipc->acceptor, true);
-		if(ipc->sock) {
-			flib_acceptor_close(ipc->acceptor);
-			ipc->acceptor = NULL;
-		}
-	}
-}
--- a/project_files/frontlib/ipc/ipcconn.h	Fri Jun 15 19:57:25 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,91 +0,0 @@
-/*
- * Low-level protocol support for the IPC connection to the engine.
- */
-
-#ifndef IPCCONN_H_
-#define IPCCONN_H_
-
-#include "../util/buffer.h"
-
-#include <stddef.h>
-#include <stdbool.h>
-
-#define IPCCONN_MAPMSG_BYTES 4097
-
-typedef enum {IPC_NOT_CONNECTED, IPC_LISTENING, IPC_CONNECTED} IpcConnState;
-
-struct _flib_ipcconn;
-typedef struct _flib_ipcconn flib_ipcconn;
-
-/**
- * Start an engine connection by listening on a random port. The selected port can
- * be queried with flib_ipcconn_port and has to be passed to the engine.
- *
- * The parameter "recordDemo" can be used to control whether demo recording should
- * be enabled for this connection. The localPlayerName is needed for demo
- * recording purposes.
- *
- * Returns NULL on error. Destroy the created object with flib_ipcconn_destroy.
- *
- * We stop accepting new connections once a connection has been established, so you
- * need to create a new ipcconn in order to start a new connection.
- */
-flib_ipcconn *flib_ipcconn_create();
-
-uint16_t flib_ipcconn_port(flib_ipcconn *ipc);
-
-/**
- * Free resources and close sockets.
- */
-void flib_ipcconn_destroy(flib_ipcconn *ipc);
-
-/**
- * Determine the current connection state
- */
-IpcConnState flib_ipcconn_state(flib_ipcconn *ipc);
-
-/**
- * Receive a single message (up to 256 bytes) and copy it into the data buffer.
- * Returns the length of the received message, a negative value if no message could
- * be read.
- *
- * The first byte of a message is its content length, which is one less than the returned
- * value.
- *
- * Note: When a connection is closed, you probably want to call this function until
- * no further message is returned, to ensure you see all messages that were sent
- * before the connection closed.
- */
-int flib_ipcconn_recv_message(flib_ipcconn *ipc, void *data);
-
-/**
- * Try to receive 4097 bytes. This is the size of the reply the engine sends
- * when successfully queried for map data. The first 4096 bytes are a bit-packed
- * twocolor image of the map (256x128), the last byte is the number of hogs that
- * fit on the map.
- */
-int flib_ipcconn_recv_map(flib_ipcconn *ipc, void *data);
-
-int flib_ipcconn_send_raw(flib_ipcconn *ipc, const void *data, size_t len);
-
-/**
- * Write a single message (up to 255 bytes) to the engine. This call blocks until the
- * message is completely written or the connection is closed or an error occurs.
- *
- * Calling this function in a state other than IPC_CONNECTED will fail immediately.
- * Returns a negative value on failure.
- */
-int flib_ipcconn_send_message(flib_ipcconn *ipc, void *data, size_t len);
-
-/**
- * Convenience function for sending a 0-delimited string.
- */
-int flib_ipcconn_send_messagestr(flib_ipcconn *ipc, char *data);
-
-/**
- * Call regularly to allow background work to proceed
- */
-void flib_ipcconn_accept(flib_ipcconn *ipc);
-
-#endif /* IPCCONN_H_ */
-
--- a/project_files/frontlib/ipc/mapconn.c	Fri Jun 15 19:57:25 2012 +0200
+++ b/project_files/frontlib/ipc/mapconn.c	Tue Jun 19 21:17:05 2012 +0200
@@ -1,5 +1,5 @@
 #include "mapconn.h"
-#include "ipcconn.h"
+#include "ipcbase.h"
 #include "ipcprotocol.h"
 
 #include "../util/logging.h"
@@ -15,8 +15,8 @@
 } mapconn_state;
 
 struct _flib_mapconn {
-	uint8_t mapBuffer[IPCCONN_MAPMSG_BYTES];
-	flib_ipcconn *connection;
+	uint8_t mapBuffer[IPCBASE_MAPMSG_BYTES];
+	flib_ipcbase *ipcBase;
 	flib_vector *configBuffer;
 
 	mapconn_state progress;
@@ -60,9 +60,9 @@
 	flib_mapconn *result = NULL;
 	flib_mapconn *tempConn = flib_calloc(1, sizeof(flib_mapconn));
 	if(tempConn) {
-		tempConn->connection = flib_ipcconn_create();
+		tempConn->ipcBase = flib_ipcbase_create();
 		tempConn->configBuffer = createConfigBuffer(seed, mapdesc);
-		if(tempConn->connection && tempConn->configBuffer) {
+		if(tempConn->ipcBase && tempConn->configBuffer) {
 			tempConn->progress = AWAIT_CONNECTION;
 			clearCallbacks(tempConn);
 			result = tempConn;
@@ -84,7 +84,7 @@
 			clearCallbacks(conn);
 			conn->destroyRequested = true;
 		} else {
-			flib_ipcconn_destroy(conn->connection);
+			flib_ipcbase_destroy(conn->ipcBase);
 			flib_vector_destroy(conn->configBuffer);
 			free(conn);
 		}
@@ -96,7 +96,7 @@
 		flib_log_e("null parameter in flib_mapconn_getport");
 		return 0;
 	} else {
-		return flib_ipcconn_port(conn->connection);
+		return flib_ipcbase_port(conn->ipcBase);
 	}
 }
 
@@ -120,12 +120,12 @@
 
 static void flib_mapconn_wrappedtick(flib_mapconn *conn) {
 	if(conn->progress == AWAIT_CONNECTION) {
-		flib_ipcconn_accept(conn->connection);
-		switch(flib_ipcconn_state(conn->connection)) {
+		flib_ipcbase_accept(conn->ipcBase);
+		switch(flib_ipcbase_state(conn->ipcBase)) {
 		case IPC_CONNECTED:
 			{
 				flib_constbuffer configBuffer = flib_vector_as_constbuffer(conn->configBuffer);
-				if(flib_ipcconn_send_raw(conn->connection, configBuffer.data, configBuffer.size)) {
+				if(flib_ipcbase_send_raw(conn->ipcBase, configBuffer.data, configBuffer.size)) {
 					conn->progress = FINISHED;
 					conn->onFailureCb(conn->onFailureCtx, "Error sending map information to the engine.");
 					return;
@@ -144,11 +144,11 @@
 	}
 
 	if(conn->progress == AWAIT_REPLY) {
-		if(flib_ipcconn_recv_map(conn->connection, conn->mapBuffer) >= 0) {
+		if(flib_ipcbase_recv_map(conn->ipcBase, conn->mapBuffer) >= 0) {
 			conn->progress = FINISHED;
-			conn->onSuccessCb(conn->onSuccessCtx, conn->mapBuffer, conn->mapBuffer[IPCCONN_MAPMSG_BYTES-1]);
+			conn->onSuccessCb(conn->onSuccessCtx, conn->mapBuffer, conn->mapBuffer[IPCBASE_MAPMSG_BYTES-1]);
 			return;
-		} else if(flib_ipcconn_state(conn->connection) != IPC_CONNECTED) {
+		} else if(flib_ipcbase_state(conn->ipcBase) != IPC_CONNECTED) {
 			conn->progress = FINISHED;
 			conn->onFailureCb(conn->onSuccessCtx, "Engine connection closed unexpectedly.");
 			return;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/net/netbase.c	Tue Jun 19 21:17:05 2012 +0200
@@ -0,0 +1,248 @@
+#include "netbase.h"
+#include "../util/buffer.h"
+#include "../util/logging.h"
+#include "../util/util.h"
+#include "../socket.h"
+
+#include <string.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#define NET_READBUFFER_LIMIT (1024*1024)
+
+struct _flib_netbase {
+	flib_vector *readBuffer;
+	flib_tcpsocket *sock;
+};
+
+flib_netbase *flib_netbase_create(const char *server, uint16_t port) {
+	flib_netbase *result = NULL;
+	flib_netbase *newNet =  flib_calloc(1, sizeof(flib_netbase));
+
+	if(newNet) {
+		newNet->readBuffer = flib_vector_create();
+		newNet->sock = flib_socket_connect(server, port);
+		if(newNet->readBuffer && newNet->sock) {
+			flib_log_i("Connected to server %s:%u", server, (unsigned)port);
+			result = newNet;
+			newNet = NULL;
+		}
+	}
+	flib_netbase_destroy(newNet);
+
+	return result;
+}
+
+void flib_netbase_destroy(flib_netbase *net) {
+	if(net) {
+		flib_socket_close(net->sock);
+		flib_vector_destroy(net->readBuffer);
+		free(net);
+	}
+}
+
+bool flib_netbase_connected(flib_netbase *net) {
+	if(!net) {
+		flib_log_e("null parameter in flib_netbase_connected");
+		return false;
+	} else if(net->sock) {
+		return true;
+	} else {
+		return false;
+	}
+}
+
+/**
+ * Parses and returns a message, and removes it from the vector.
+ */
+static flib_netmsg *parseMessage(flib_vector *vec) {
+	const uint8_t *partStart = flib_vector_data(vec);
+	const uint8_t *end = partStart+flib_vector_size(vec);
+	flib_netmsg *result = flib_netmsg_create();
+	if(!result) {
+		return NULL;
+	}
+
+	while(1) {
+		const uint8_t *partEnd = memchr(partStart, '\n', end-partStart);
+		if(!partEnd) {
+			// message incomplete
+			flib_netmsg_destroy(result);
+			return NULL;
+		} else if(partEnd-partStart == 0) {
+			// Zero-length part, message end marker. Remove the message from the vector.
+			uint8_t *vectorStart = flib_vector_data(vec);
+			size_t msgLen = partEnd+1-vectorStart;
+			memmove(vectorStart, partEnd+1, flib_vector_size(vec)-msgLen);
+			flib_vector_resize(vec, flib_vector_size(vec)-msgLen);
+			return result;
+		} else {
+			if(flib_netmsg_append_part(result, partStart, partEnd-partStart)) {
+				flib_netmsg_destroy(result);
+				return NULL;
+			}
+			partStart = partEnd+1; // Skip the '\n'
+		}
+	}
+	return NULL; // Never reached
+}
+
+/**
+ * Receive some bytes and add them to the buffer.
+ * Returns the number of bytes received.
+ * Automatically closes the socket if an error occurs
+ * and sets sock=NULL.
+ */
+static int receiveToBuffer(flib_netbase *net) {
+	uint8_t buffer[256];
+	if(!net->sock) {
+		return 0;
+	} else if(flib_vector_size(net->readBuffer) > NET_READBUFFER_LIMIT) {
+		flib_log_e("Net connection closed: Net message too big");
+		flib_socket_close(net->sock);
+		net->sock = NULL;
+		return 0;
+	} else {
+		int size = flib_socket_nbrecv(net->sock, buffer, sizeof(buffer));
+		if(size>=0) {
+			flib_vector_append(net->readBuffer, buffer, size);
+			return size;
+		} else {
+			flib_socket_close(net->sock);
+			net->sock = NULL;
+			return 0;
+		}
+	}
+}
+
+flib_netmsg *flib_netbase_recv_message(flib_netbase *net) {
+	if(!net) {
+		flib_log_e("null parameter in flib_netbase_recv_message");
+		return NULL;
+	}
+
+	flib_netmsg *msg;
+	while(!(msg=parseMessage(net->readBuffer))
+			&& receiveToBuffer(net)) {}
+
+	if(msg) {
+		return msg;
+	} else if(!net->sock && flib_vector_size(net->readBuffer)>0) {
+		// Connection is down and we didn't get a complete message, just flush the rest.
+		flib_vector_resize(net->readBuffer, 0);
+	}
+	return NULL;
+}
+
+static void logSentMsg(const uint8_t *data, size_t len) {
+	if(flib_log_isActive(FLIB_LOGLEVEL_DEBUG)) {
+		flib_log_d("[NET OUT][%03u]%*.*s",(unsigned)len, (unsigned)len, (unsigned)len, data);
+	}
+}
+
+int flib_netbase_send_raw(flib_netbase *net, const void *data, size_t len) {
+	if(!net || (!data && len>0)) {
+		flib_log_e("null parameter in flib_netbase_send_raw");
+		return -1;
+	}
+	if(!net->sock) {
+		flib_log_w("flib_netbase_send_raw: Not connected.");
+		return -1;
+	}
+
+	if(flib_socket_send(net->sock, data, len) == len) {
+		logSentMsg(data, len);
+		return 0;
+	} else {
+		flib_log_w("Failed or incomplete write: net connection lost.");
+		flib_socket_close(net->sock);
+		net->sock = NULL;
+		return -1;
+	}
+}
+
+int flib_netbase_send_message(flib_netbase *net, flib_netmsg *msg) {
+	if(!net || !msg) {
+		flib_log_e("null parameter in flib_netbase_send_message");
+		return -1;
+	}
+
+	size_t totalSize = 0;
+	for(int i=0; i<msg->partCount; i++) {
+		totalSize += strlen(msg->parts[i]) + 1;
+	}
+	totalSize++; // Last part ends in two '\n' instead of one
+
+	uint8_t *buffer = flib_malloc(totalSize);
+	if(!buffer) {
+		return -1;
+	}
+	size_t pos = 0;
+	for(int i=0; i<msg->partCount; i++) {
+		size_t partsize = strlen(msg->parts[i]);
+		memcpy(buffer+pos, msg->parts[i], partsize);
+		pos += partsize;
+		buffer[pos++] = '\n';
+	}
+	buffer[pos++] = '\n';
+	return flib_netbase_send_raw(net, buffer, pos);
+}
+
+int flib_netbase_sendf(flib_netbase *net, const char *format, ...) {
+	int result = -1;
+	if(!net || !format) {
+		flib_log_e("null parameter in flib_netbase_sendf");
+	} else {
+		va_list argp;
+		va_start(argp, format);
+		char *buffer = flib_vasprintf(format, argp);
+		if(buffer) {
+			result = flib_netbase_send_raw(net, buffer, strlen(buffer));
+		}
+		free(buffer);
+		va_end(argp);
+	}
+	return result;
+}
+
+flib_netmsg *flib_netmsg_create() {
+	flib_netmsg *result = flib_calloc(1, sizeof(flib_netmsg));
+	if(result) {
+		result->partCount = 0;
+		result->parts = NULL;
+		return result;
+	} else {
+		return NULL;
+	}
+}
+
+void flib_netmsg_destroy(flib_netmsg *msg) {
+	if(msg) {
+		for(int i=0; i<msg->partCount; i++) {
+			free(msg->parts[i]);
+		}
+		free(msg->parts);
+		free(msg);
+	}
+}
+
+int flib_netmsg_append_part(flib_netmsg *msg, const void *part, size_t partlen) {
+	int result = -1;
+	if(!msg) {
+		flib_log_e("null parameter in flib_netmsg_append_part");
+	} else {
+		char **newParts = realloc(msg->parts, (msg->partCount+1)*sizeof(*msg->parts));
+		if(newParts) {
+			msg->parts = newParts;
+			msg->parts[msg->partCount] = flib_malloc(partlen+1);
+			if(msg->parts[msg->partCount]) {
+				memcpy(msg->parts[msg->partCount], part, partlen);
+				msg->parts[msg->partCount][partlen] = 0;
+				msg->partCount++;
+				result = 0;
+			}
+		}
+	}
+	return result;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/net/netbase.h	Tue Jun 19 21:17:05 2012 +0200
@@ -0,0 +1,72 @@
+/*
+ * Low-level protocol support for the network connection
+ */
+
+#ifndef NETBASE_H_
+#define NETBASE_H_
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+struct _flib_netbase;
+typedef struct _flib_netbase flib_netbase;
+
+typedef struct {
+	int partCount;
+	char **parts;
+} flib_netmsg;
+
+/**
+ * Start a connection to the specified Hedgewars server.
+ *
+ * Returns NULL on error. Destroy the created object with flib_netconn_destroy.
+ */
+flib_netbase *flib_netbase_create(const char *server, uint16_t port);
+
+/**
+ * Free resources and close sockets.
+ */
+void flib_netbase_destroy(flib_netbase *net);
+
+/**
+ * Determine the current connection state. Starts out true, and turns to
+ * false when we are disconnected from the server.
+ */
+bool flib_netbase_connected(flib_netbase *net);
+
+/**
+ * Receive a new message and return it as a flib_netmsg. The netmsg has to be
+ * destroyed with flib_netmsg_destroy after use.
+ * Returns NULL if no message is available.
+ *
+ * Note: When a connection is closed, you probably want to call this function until
+ * no further message is returned, to ensure you see all messages that were sent
+ * before the connection closed.
+ */
+flib_netmsg *flib_netbase_recv_message(flib_netbase *net);
+
+int flib_netbase_send_raw(flib_netbase *net, const void *data, size_t len);
+
+/**
+ * Write a single message to the server. This call blocks until the
+ * message is completely written or the connection is closed or an error occurs.
+ *
+ * Returns a negative value on failure.
+ */
+int flib_netbase_send_message(flib_netbase *net, flib_netmsg *msg);
+
+/**
+ * Send a message printf-style.
+ *
+ * flib_netbase_sendf(net, "%s\n\n", "TOGGLE_READY");
+ * flib_netbase_sendf(net, "%s\n%s\n%i\n\n", "CFG", "MAPGEN", MAPGEN_MAZE);
+ */
+int flib_netbase_sendf(flib_netbase *net, const char *format, ...);
+
+flib_netmsg *flib_netmsg_create();
+void flib_netmsg_destroy(flib_netmsg *msg);
+int flib_netmsg_append_part(flib_netmsg *msg, const void *param, size_t len);
+
+#endif /* NETBASE_H_ */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/net/netconn.c	Tue Jun 19 21:17:05 2012 +0200
@@ -0,0 +1,469 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (c) 2006-2008 Igor Ulyanov <iulyanov@gmail.com>
+ * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
+ * Copyright (c) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include "netconn.h"
+#include "netbase.h"
+#include "netprotocol.h"
+#include "../util/logging.h"
+#include "../util/util.h"
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+struct _flib_netconn {
+	flib_netbase *netBase;
+	char *playerName;
+
+	int netconnState;	// One of the NETCONN_STATE constants
+
+	void (*onErrorCb)(void* context, int errorCode, const char *errorMsg);
+	void *onErrorCtx;
+
+	void (*onConnectedCb)(void *context, const char *serverMessage);
+	void *onConnectedCtx;
+
+	bool running;
+	bool destroyRequested;
+};
+
+static void defaultCallback_onError(void* context, int errorCode, const char *errormsg) {}
+static void defaultCallback_onConnected(void *context, const char *serverMessage) {}
+
+static void clearCallbacks(flib_netconn *conn) {
+	conn->onErrorCb = &defaultCallback_onError;
+	conn->onConnectedCb = &defaultCallback_onConnected;
+}
+
+
+flib_netconn *flib_netconn_create(const char *playerName, const char *host, uint16_t port) {
+	flib_netconn *result = NULL;
+	flib_netconn *newConn = flib_calloc(1, sizeof(flib_netconn));
+	if(newConn) {
+		newConn->netconnState = NETCONN_STATE_AWAIT_CONNECTED;
+		newConn->running = false;
+		newConn->destroyRequested = false;
+		clearCallbacks(newConn);
+		newConn->netBase = flib_netbase_create(host, port);
+		newConn->playerName = flib_strdupnull(playerName);
+		if(newConn->netBase && newConn->playerName) {
+			result = newConn;
+			newConn = NULL;
+		}
+	}
+	flib_netconn_destroy(newConn);
+	return result;
+}
+
+void flib_netconn_destroy(flib_netconn *conn) {
+	if(conn) {
+		if(conn->running) {
+			/*
+			 * The function was called from a callback, so the tick function is still running
+			 * and we delay the actual destruction. We ensure no further callbacks will be
+			 * sent to prevent surprises.
+			 */
+			clearCallbacks(conn);
+			conn->destroyRequested = true;
+		} else {
+			flib_netbase_destroy(conn->netBase);
+			free(conn->playerName);
+			free(conn);
+		}
+	}
+}
+
+void flib_netconn_onError(flib_netconn *conn, void (*callback)(void *context, int errorCode, const char *errorMsg), void* context) {
+	if(!conn) {
+		flib_log_e("null parameter in flib_netconn_onError");
+	} else {
+		conn->onErrorCb = callback ? callback : &defaultCallback_onError;
+		conn->onErrorCtx = context;
+	}
+}
+
+void flib_netconn_onConnected(flib_netconn *conn, void (*callback)(void *context, const char *serverMessage), void* context) {
+	if(!conn) {
+		flib_log_e("null parameter in flib_netconn_onConnected");
+	} else {
+		conn->onConnectedCb = callback ? callback : &defaultCallback_onConnected;
+		conn->onConnectedCtx = context;
+	}
+}
+
+static void flib_netconn_wrappedtick(flib_netconn *conn) {
+	flib_netmsg *netmsg;
+	flib_netbase *net = conn->netBase;
+	bool exit = false;
+
+	while(!exit && !conn->destroyRequested && (netmsg=flib_netbase_recv_message(conn->netBase))) {
+		if(netmsg->partCount==0) {
+			flib_log_w("Empty server message");
+			continue;
+		}
+
+		const char *cmd = netmsg->parts[0];
+
+	    if (!strcmp(cmd, "NICK") && netmsg->partCount>=2) {
+	    	free(conn->playerName);
+	    	conn->playerName = flib_strdupnull(netmsg->parts[1]);
+	    	if(!conn->playerName) {
+	    		// TODO handle error
+	    	}
+	    	// TODO callback?
+	    } else if (!strcmp(cmd, "PROTO")) {
+	        // The server just echoes this back apparently
+		} else if (!strcmp(cmd, "ERROR")) {
+			// TODO: onErrorMessage?
+			if (netmsg->partCount == 2) {
+				conn->onErrorCb(conn->onErrorCtx, NETCONN_ERROR_FROM_SERVER, netmsg->parts[1]);
+			} else {
+				conn->onErrorCb(conn->onErrorCtx, NETCONN_ERROR_FROM_SERVER, "Unknown Error");
+			}
+		} else if(!strcmp(cmd, "WARNING")) {
+			// TODO: onWarnMessage?
+			if (netmsg->partCount == 2) {
+				conn->onErrorCb(conn->onErrorCtx, NETCONN_ERROR_FROM_SERVER, netmsg->parts[1]);
+			} else {
+				conn->onErrorCb(conn->onErrorCtx, NETCONN_ERROR_FROM_SERVER, "Unknown Warning");
+			}
+	    } else if(!strcmp(cmd, "CONNECTED")) {
+			if(netmsg->partCount<3 || atol(netmsg->parts[2])<MIN_SERVER_VERSION) {
+				flib_log_w("Server too old");
+				flib_netbase_sendf(net, "%s\n%s\n\n", "QUIT", "Server too old");
+				// TODO actually disconnect?
+				conn->netconnState = NETCONN_STATE_DISCONNECTED;
+				conn->onErrorCb(conn->onErrorCtx, NETCONN_ERROR_SERVER_TOO_OLD, "Server too old");
+				exit = true;
+			} else {
+				flib_netbase_sendf(net, "%s\n%s\n\n", "NICK", conn->playerName);
+				flib_netbase_sendf(net, "%s\n%i\n\n", "PROTO", (int)PROTOCOL_VERSION);
+				conn->netconnState = NETCONN_STATE_LOBBY;
+			}
+		} else if(!strcmp(cmd, "PING")) {
+	        if (netmsg->partCount > 1) {
+	        	flib_netbase_sendf(net, "%s\n%s\n\n", "PONG", netmsg->parts[1]);
+	        } else {
+	        	flib_netbase_sendf(net, "%s\n\n", "PONG");
+	        }
+	    } else if(!strcmp(cmd, "ROOMS")) {
+	        if(netmsg->partCount % 8 != 1) {
+	        	flib_log_w("Net: Malformed ROOMS message");
+	        } else {
+	        	// TODO
+				//QStringList tmp = lst;
+				//tmp.removeFirst();
+				//m_roomsListModel->setRoomsList(tmp);
+	        }
+	    } else if (!strcmp(cmd, "SERVER_MESSAGE")) {
+	        if(netmsg->partCount < 2) {
+	        	flib_log_w("Net: Empty SERVERMESSAGE message");
+	        } else {
+	        	// TODO
+	        	// emit serverMessage(lst[1]);
+	        }
+	    } else if (!strcmp(cmd, "CHAT")) {
+	        if(netmsg->partCount < 3) {
+	        	flib_log_w("Net: Empty CHAT message");
+	        } else {
+	        	// TODO
+				// if (netClientState == InLobby)
+				// 	emit chatStringLobby(lst[1], HWProto::formatChatMsgForFrontend(lst[2]));
+				// else
+				//	emit chatStringFromNet(HWProto::formatChatMsg(lst[1], lst[2]));
+	        }
+	    } else if (!strcmp(cmd, "INFO")) {
+	        if(netmsg->partCount < 5) {
+	        	flib_log_w("Net: Malformed INFO message");
+	        } else {
+	        	// TODO
+//				QStringList tmp = lst;
+//				tmp.removeFirst();
+//				if (netClientState == InLobby)
+//					emit chatStringLobby(tmp.join("\n").prepend('\x01'));
+//				else
+//					emit chatStringFromNet(tmp.join("\n").prepend('\x01'));
+	        }
+	    } else if(!strcmp(cmd, "SERVER_VARS")) {
+	    	// TODO
+//	        QStringList tmp = lst;
+//	        tmp.removeFirst();
+//	        while (tmp.size() >= 2)
+//	        {
+//	            if(tmp[0] == "MOTD_NEW") emit serverMessageNew(tmp[1]);
+//	            else if(tmp[0] == "MOTD_OLD") emit serverMessageOld(tmp[1]);
+//	            else if(tmp[0] == "LATEST_PROTO") emit latestProtocolVar(tmp[1].toInt());
+//
+//	            tmp.removeFirst();
+//	            tmp.removeFirst();
+//	        }
+	    } else if (!strcmp(cmd, "CLIENT_FLAGS")) {
+	        if(netmsg->partCount < 3 || strlen(netmsg->parts[1]) < 2) {
+	        	flib_log_w("Net: Malformed CLIENT_FLAGS message");
+	        } else {
+				const char *flags = netmsg->parts[1];
+				bool setFlag = flags[0] == '+';
+
+				for(int i=1; flags[i]; i++) {
+					switch(flags[i]) {
+					case 'r':
+						for(int j = 2; j < netmsg->partCount; ++j) {
+							if (!strcmp(conn->playerName, netmsg->parts[i])) {
+								// TODO
+								// if (isChief && !setFlag) ToggleReady();
+								// else emit setMyReadyStatus(setFlag);
+							}
+							// TODO
+							// emit setReadyStatus(lst[i], setFlag);
+						}
+						break;
+					default:
+						flib_log_w("Net: Unknown flag %c in CLIENT_FLAGS message", flags[i]);
+						break;
+					}
+				}
+	        }
+	    } else if (!strcmp(cmd, "ADD_TEAM")) {
+	        if(netmsg->partCount != 24) {
+	            flib_log_w("Net: Bad ADD_TEAM message");
+	        } else {
+	        	// TODO
+//				QStringList tmp = lst;
+//				tmp.removeFirst();
+//				emit AddNetTeam(tmp);
+	        }
+	    } else if (!strcmp(cmd, "REMOVE_TEAM")) {
+	        if(netmsg->partCount != 2) {
+	            flib_log_w("Net: Bad REMOVETEAM message");
+	        } else {
+	        	// TODO
+	        	// emit RemoveNetTeam(HWTeam(lst[1]));
+	        }
+	    } else if(!strcmp(cmd, "ROOMABANDONED")) {
+	        conn->netconnState = NETCONN_STATE_LOBBY;
+//	        TODO
+//	        askRoomsList();
+//	        emit LeftRoom(tr("Room destroyed"));
+	    } else if(!strcmp(cmd, "KICKED")) {
+	    	conn->netconnState = NETCONN_STATE_LOBBY;
+//	    	TODO
+//	        askRoomsList();
+//	        emit LeftRoom(tr("You got kicked"));
+	    } else if(!strcmp(cmd, "JOINED")) {
+	        if(netmsg->partCount < 2) {
+	            flib_log_w("Net: Bad JOINED message");
+	        } else {
+				for(int i = 1; i < netmsg->partCount; ++i)
+				{
+					bool isMe = !strcmp(conn->playerName, netmsg->parts[i]);
+					if (isMe) {
+						conn->netconnState = NETCONN_STATE_ROOM;
+//						TODO
+//						emit EnteredGame();
+//						emit roomMaster(isChief);
+//						if (isChief)
+//							emit configAsked();
+					}
+
+//					TODO
+//					emit nickAdded(lst[i], isChief && !isMe));
+//					emit chatStringFromNet(tr("%1 *** %2 has joined the room").arg('\x03').arg(lst[i]));
+				}
+	        }
+	    } else if(!strcmp(cmd, "LOBBY:JOINED")) {
+	        if(netmsg->partCount < 2) {
+	            flib_log_w("Net: Bad JOINED message");
+	        } else {
+				for(int i = 1; i < netmsg->partCount; ++i)
+				{
+					bool isMe = !strcmp(conn->playerName, netmsg->parts[i]);
+					if (isMe) {
+						conn->netconnState = NETCONN_STATE_LOBBY;
+						// TODO
+//						RawSendNet(QString("LIST"));
+//						emit connected();
+					}
+					// TODO
+//					emit nickAddedLobby(lst[i], false);
+//					emit chatStringLobby(lst[i], tr("%1 *** %2 has joined").arg('\x03').arg("|nick|"));
+				}
+	        }
+	    } else if(!strcmp(cmd, "LEFT")) {
+	        if(netmsg->partCount < 2) {
+	            flib_log_w("Net: Bad LEFT message");
+	        } else {
+	        	// TODO
+//				emit nickRemoved(lst[1]);
+//				if (netmsg->partCount < 3)
+//					emit chatStringFromNet(tr("%1 *** %2 has left").arg('\x03').arg(lst[1]));
+//				else
+//					emit chatStringFromNet(tr("%1 *** %2 has left (%3)").arg('\x03').arg(lst[1], lst[2]));
+	        }
+	    } else if(!strcmp(cmd, "ROOM") && netmsg->partCount >= 2) {
+	    	const char *subcmd = netmsg->parts[1];
+	    	if(!strcmp(subcmd, "ADD") && netmsg->partCount == 10) {
+	    		// TODO
+//				QStringList tmp = lst;
+//				tmp.removeFirst();
+//				tmp.removeFirst();
+//
+//				m_roomsListModel->addRoom(tmp);
+			} else if(!strcmp(subcmd, "UPD") && netmsg->partCount == 11) {
+				// TODO
+//				QStringList tmp = lst;
+//				tmp.removeFirst();
+//				tmp.removeFirst();
+//
+//				QString roomName = tmp.takeFirst();
+//				m_roomsListModel->updateRoom(roomName, tmp);
+			} else if(!strcmp(subcmd, "DEL") && netmsg->partCount == 3) {
+				// TODO
+				// m_roomsListModel->removeRoom(lst[2]);
+			} else {
+				flib_log_w("Net: Unknown or malformed ROOM subcommand: %s", subcmd);
+			}
+	    } else if(!strcmp(cmd, "LOBBY:LEFT")) {
+	        if(netmsg->partCount < 2) {
+	            flib_log_w("Net: Bad LOBBY:LEFT message");
+	        } else {
+	        	// TODO
+//				emit nickRemovedLobby(lst[1]);
+//				if (netmsg->partCount < 3)
+//					emit chatStringLobby(tr("%1 *** %2 has left").arg('\x03').arg(lst[1]));
+//				else
+//					emit chatStringLobby(lst[1], tr("%1 *** %2 has left (%3)").arg('\x03').arg("|nick|", lst[2]));
+	        }
+	    } else if (!strcmp(cmd, "RUN_GAME")) {
+	        conn->netconnState = NETCONN_STATE_INGAME;
+	        // TODO
+	        // emit AskForRunGame();
+	    } else if (!strcmp(cmd, "ASKPASSWORD")) {
+	    	// TODO
+	        // emit AskForPassword(mynick);
+	    } else if (!strcmp(cmd, "NOTICE")) {
+	        if(netmsg->partCount < 2) {
+	            flib_log_w("Net: Bad NOTICE message");
+	        } else {
+				errno = 0;
+				long n = strtol(netmsg->parts[1], NULL, 10);
+				if(errno) {
+					flib_log_w("Net: Bad NOTICE message");
+				} else {
+					// TODO
+					// handleNotice(n);
+				}
+	        }
+	    } else if (!strcmp(cmd, "TEAM_ACCEPTED")) {
+	        if (netmsg->partCount != 2) {
+	            flib_log_w("Net: Bad TEAM_ACCEPTED message");
+	        } else {
+	        	// TODO
+	        	// emit TeamAccepted(lst[1]);
+	        }
+	    } else if (!strcmp(cmd, "CFG")) {
+	        if(netmsg->partCount < 3) {
+	            flib_log_w("Net: Bad CFG message");
+	        } else {
+	        	// TODO
+//				QStringList tmp = lst;
+//				tmp.removeFirst();
+//				tmp.removeFirst();
+//				if (lst[1] == "SCHEME")
+//					emit netSchemeConfig(tmp);
+//				else
+//					emit paramChanged(lst[1], tmp);
+	        }
+	    } else if (!strcmp(cmd, "HH_NUM")) {
+	        if (netmsg->partCount != 3) {
+	            flib_log_w("Net: Bad TEAM_ACCEPTED message");
+	        } else {
+	        	// TODO
+//				HWTeam tmptm(lst[1]);
+//				tmptm.setNumHedgehogs(lst[2].toUInt());
+//				emit hhnumChanged(tmptm);
+	        }
+	    } else if (!strcmp(cmd, "TEAM_COLOR")) {
+	        if (netmsg->partCount != 3) {
+	            flib_log_w("Net: Bad TEAM_COLOR message");
+	        } else {
+	        	// TODO
+//				HWTeam tmptm(lst[1]);
+//				tmptm.setColor(lst[2].toInt());
+//				emit teamColorChanged(tmptm);
+	        }
+	    } else if (!strcmp(cmd, "EM")) {
+	        if(netmsg->partCount < 2) {
+	            flib_log_w("Net: Bad EM message");
+	        } else {
+	        	// TODO
+//				for(int i = 1; i < netmsg->partCount; ++i) {
+//					QByteArray em = QByteArray::fromBase64(lst[i].toAscii());
+//					emit FromNet(em);
+//				}
+	        }
+	    } else if (!strcmp(cmd, "BYE")) {
+	        if (netmsg->partCount < 2) {
+	            flib_log_w("Net: Bad BYE message");
+	        } else {
+				if (!strcmp(netmsg->parts[1], "Authentication failed")) {
+					// TODO
+					//emit AuthFailed();
+				}
+				// TODO
+//				m_game_connected = false;
+//				Disconnect();
+//				emit disconnected(lst[1]);
+	        }
+	    } else if (!strcmp(cmd, "ADMIN_ACCESS")) {
+	    	// TODO
+	        // emit adminAccess(true);
+	    } else if (!strcmp(cmd, "ROOM_CONTROL_ACCESS")) {
+	        if (netmsg->partCount < 2) {
+	            flib_log_w("Net: Bad ROOM_CONTROL_ACCESS message");
+	        } else {
+	        	// TODO
+//				isChief = (lst[1] != "0");
+//				emit roomMaster(isChief);
+	        }
+	    } else {
+	    	flib_log_w("Unknown server command: %s", cmd);
+	    }
+		flib_netmsg_destroy(netmsg);
+	}
+}
+
+void flib_netconn_tick(flib_netconn *conn) {
+	if(!conn) {
+		flib_log_e("null parameter in flib_netconn_tick");
+	} else if(conn->running) {
+		flib_log_w("Call to flib_netconn_tick from a callback");
+	} else if(conn->netconnState == NETCONN_STATE_DISCONNECTED) {
+		flib_log_w("Call to flib_netconn_tick, but we are already done.");
+	} else {
+		conn->running = true;
+		flib_netconn_wrappedtick(conn);
+		conn->running = false;
+
+		if(conn->destroyRequested) {
+			flib_netconn_destroy(conn);
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/net/netconn.h	Tue Jun 19 21:17:05 2012 +0200
@@ -0,0 +1,45 @@
+#ifndef NETCONN_H_
+#define NETCONN_H_
+
+#include "../model/gamesetup.h"
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#define NETCONN_STATE_AWAIT_CONNECTED 0
+#define NETCONN_STATE_LOBBY 1
+#define NETCONN_STATE_ROOM 2
+#define NETCONN_STATE_INGAME 3
+#define NETCONN_STATE_DISCONNECTED 10
+
+#define NETCONN_ERROR_SERVER_TOO_OLD 1
+#define NETCONN_ERROR_FROM_SERVER 2
+
+struct _flib_netconn;
+typedef struct _flib_netconn flib_netconn;
+
+flib_netconn *flib_netconn_create(const char *playerName, const char *host, uint16_t port);
+void flib_netconn_destroy(flib_netconn *conn);
+
+/**
+ * This is called when we can't stay connected due to a problem, e.g. because the
+ * server version is too old, or we are unexpectedly disconnected.
+ *
+ * Once this callback has been called, you should destroy the flib_netconn.
+ */
+void flib_netconn_onError(flib_netconn *conn, void (*callback)(void *context, int errorCode, const char *errormsg), void* context);
+
+/**
+ * This is called when we receive a CONNECTED message from the server, which should be the first
+ * message arriving from the server.
+ */
+void flib_netconn_onConnected(flib_netconn *conn, void (*callback)(void *context, const char *serverMessage), void* context);
+
+/**
+ * Perform I/O operations and call callbacks if something interesting happens.
+ * Should be called regularly.
+ */
+void flib_netconn_tick(flib_netconn *conn);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/net/netprotocol.h	Tue Jun 19 21:17:05 2012 +0200
@@ -0,0 +1,7 @@
+#ifndef NETPROTOCOL_H_
+#define NETPROTOCOL_H_
+
+
+
+
+#endif /* NETPROTOCOL_H_ */
--- a/project_files/frontlib/socket.c	Fri Jun 15 19:57:25 2012 +0200
+++ b/project_files/frontlib/socket.c	Tue Jun 19 21:17:05 2012 +0200
@@ -121,6 +121,30 @@
 	return result;
 }
 
+flib_tcpsocket *flib_socket_connect(const char *host, uint16_t port) {
+	flib_tcpsocket *result = NULL;
+	if(!host || port==0) {
+		flib_log_e("Invalid parameter in flib_socket_connect");
+	} else {
+		IPaddress ip;
+		if(SDLNet_ResolveHost(&ip,host,port)==-1) {
+		   flib_log_e("SDLNet_ResolveHost: %s\n", SDLNet_GetError());
+		} else {
+			TCPsocket sock=SDLNet_TCP_Open(&ip);
+			if(!sock) {
+				flib_log_e("SDLNet_TCP_Open: %s\n", SDLNet_GetError());
+			} else {
+				result = flib_socket_create(sock);
+				if(result) {
+					sock = NULL;
+				}
+			}
+			SDLNet_TCP_Close(sock);
+		}
+	}
+	return result;
+}
+
 void flib_socket_close(flib_tcpsocket *sock) {
 	if(sock) {
 		SDLNet_DelSocket(sock->sockset, (SDLNet_GenericSocket)sock->sock);
--- a/project_files/frontlib/socket.h	Fri Jun 15 19:57:25 2012 +0200
+++ b/project_files/frontlib/socket.h	Tue Jun 19 21:17:05 2012 +0200
@@ -49,6 +49,11 @@
 flib_tcpsocket *flib_socket_accept(flib_acceptor *acceptor, bool localOnly);
 
 /**
+ * Try to connect to the server at the given address.
+ */
+flib_tcpsocket *flib_socket_connect(const char *host, uint16_t port);
+
+/**
  * Close the socket and free its memory.
  * If the socket is already NULL, nothing happens.
  */
--- a/project_files/frontlib/test.c	Fri Jun 15 19:57:25 2012 +0200
+++ b/project_files/frontlib/test.c	Tue Jun 19 21:17:05 2012 +0200
@@ -1,14 +1,17 @@
 #include "frontlib.h"
 #include "util/logging.h"
+#include "util/buffer.h"
 #include "model/map.h"
 #include "model/weapon.h"
 #include "model/schemelist.h"
 #include "ipc/mapconn.h"
 #include "ipc/gameconn.h"
+#include "net/netbase.h"
 
 #include <stdlib.h>
 #include <stdbool.h>
 #include <assert.h>
+#include <string.h>
 
 // Callback function that will be called when the map is rendered
 static void handleMapSuccess(void *context, const uint8_t *bitmap, int numHedgehogs) {
@@ -204,18 +207,37 @@
 	//testSave();
 	//testGame();
 
-	flib_cfg_meta *meta = flib_cfg_meta_from_ini("metasettings.ini");
-	assert(meta);
-	flib_schemelist *schemelist = flib_schemelist_from_ini(meta, "schemes.ini");
-	assert(schemelist);
+	flib_netbase *conn = flib_netbase_create("140.247.62.101", 46631);
 
-	flib_schemelist_to_ini("Copy of Schemelist.ini", schemelist);
-	flib_schemelist_release(schemelist);
-	flib_cfg_meta_release(meta);
-
-	flib_weaponsetlist *weaponsets = flib_weaponsetlist_from_ini("weapons.ini");
-	assert(!flib_weaponsetlist_to_ini("copy of weapons.ini", weaponsets));
-	flib_weaponsetlist_release(weaponsets);
+	while(flib_netbase_connected(conn)) {
+		flib_netmsg *msg = flib_netbase_recv_message(conn);
+		if(msg && msg->partCount>0) {
+			flib_log_i("[NET IN] %s", msg->parts[0]);
+			for(int i=1; i<msg->partCount; i++) {
+				flib_log_i("[NET IN][-] %s", msg->parts[i]);
+			}
+			if(!strcmp(msg->parts[0], "CONNECTED")) {
+				flib_netmsg *nickmsg = flib_netmsg_create();
+				flib_netmsg_append_part(nickmsg, "NICK", 4);
+				flib_netmsg_append_part(nickmsg, "Medo42_frontlib", 15);
+				flib_netmsg *protomsg = flib_netmsg_create();
+				flib_netmsg_append_part(protomsg, "PROTO", 5);
+				flib_netmsg_append_part(protomsg, "42", 2);
+				flib_netbase_send_message(conn, nickmsg);
+				flib_netbase_send_message(conn, protomsg);
+				flib_netmsg_destroy(nickmsg);
+				flib_netmsg_destroy(protomsg);
+			}
+			if(!strcmp(msg->parts[0], "SERVER_MESSAGE")) {
+				flib_netmsg *quitmsg = flib_netmsg_create();
+				flib_netmsg_append_part(quitmsg, "QUIT", 4);
+				flib_netmsg_append_part(quitmsg, "Just testing", 12);
+				flib_netbase_send_message(conn, quitmsg);
+				flib_netmsg_destroy(quitmsg);
+			}
+		}
+		flib_netmsg_destroy(msg);
+	}
 
 	flib_quit();
 	return 0;
--- a/project_files/frontlib/util/buffer.c	Fri Jun 15 19:57:25 2012 +0200
+++ b/project_files/frontlib/util/buffer.c	Tue Jun 19 21:17:05 2012 +0200
@@ -37,16 +37,61 @@
 	}
 }
 
-static void try_realloc(flib_vector *vec, size_t newCapacity) {
+static int setCapacity(flib_vector *vec, size_t newCapacity) {
+	if(newCapacity == vec->capacity) {
+		return 0;
+	}
 	void *newData = realloc(vec->data, newCapacity);
 	if(newData) {
 		vec->data = newData;
 		vec->capacity = newCapacity;
+		return 0;
+	} else {
+		return -1;
+	}
+}
+
+static int allocateExtraCapacity(flib_vector *vec, size_t extraCapacity) {
+	if(extraCapacity <= SIZE_MAX - vec->capacity) {
+		return setCapacity(vec, vec->capacity + extraCapacity);
+	} else {
+		return -1;
 	}
 }
 
-static size_t getFreeCapacity(flib_vector *vec) {
-	return vec->capacity - vec->size;
+int flib_vector_resize(flib_vector *vec, size_t newSize) {
+	if(!vec) {
+		flib_log_e("null parameter in flib_vector_resize");
+		return -1;
+	}
+
+	if(vec->capacity < newSize) {
+		// Resize exponentially for constant amortized time,
+		// But at least by as much as we need of course,
+		// and be extra careful with integer overflows...
+		size_t extraCapacity = (vec->capacity)/2;
+		size_t minExtraCapacity = newSize - vec->capacity;
+		if(extraCapacity < minExtraCapacity) {
+			extraCapacity = minExtraCapacity;
+		}
+
+		if(allocateExtraCapacity(vec, extraCapacity)) {
+			allocateExtraCapacity(vec, minExtraCapacity);
+		}
+	} else if(vec->capacity/2 > newSize) {
+		size_t newCapacity = newSize+newSize/4;
+		if(newCapacity < MIN_VECTOR_CAPACITY) {
+			newCapacity = MIN_VECTOR_CAPACITY;
+		}
+		setCapacity(vec, newCapacity);
+	}
+
+	if(vec->capacity >= newSize) {
+		vec->size = newSize;
+		return 0;
+	} else {
+		return -1;
+	}
 }
 
 int flib_vector_append(flib_vector *vec, const void *data, size_t len) {
@@ -55,35 +100,16 @@
 		return 0;
 	}
 
-	if(getFreeCapacity(vec) < len) {
-		// Resize exponentially for constant amortized time,
-		// But at least by as much as we need of course,
-		// and be extra careful with integer overflows...
-		size_t extraCapacity = (vec->capacity)/2;
-
-		size_t minExtraCapacity = len - getFreeCapacity(vec);
-		if(extraCapacity < minExtraCapacity) {
-			extraCapacity = minExtraCapacity;
-		}
-
-		if(extraCapacity <= SIZE_MAX - vec->capacity) {
-			try_realloc(vec, vec->capacity+extraCapacity);
-		}
-
-		// Check if we were able to resize.
-		// If not, try to allocate at least what we need.
-		if(getFreeCapacity(vec) < len) {
-			try_realloc(vec, vec->capacity+minExtraCapacity);
-
-			// Still not working? Then we fail.
-			if(getFreeCapacity(vec) < len) {
-				return 0;
-			}
-		}
+	if(len > SIZE_MAX-vec->size) {
+		return 0;
 	}
 
-	memmove(((uint8_t*)vec->data) + vec->size, data, len);
-	vec->size += len;
+	size_t oldSize = vec->size;
+	if(flib_vector_resize(vec, vec->size+len)) {
+		return 0;
+	}
+
+	memmove(((uint8_t*)vec->data) + oldSize, data, len);
 	return len;
 }
 
--- a/project_files/frontlib/util/buffer.h	Fri Jun 15 19:57:25 2012 +0200
+++ b/project_files/frontlib/util/buffer.h	Tue Jun 19 21:17:05 2012 +0200
@@ -41,6 +41,13 @@
 void flib_vector_destroy(flib_vector *vec);
 
 /**
+ * Resize the vector. This changes the size, and ensures the capacity is large enough to
+ * for the new size. Can also free memory if the new size is smaller. There is no guarantee
+ * about the contents of extra memory.
+ */
+int flib_vector_resize(flib_vector *vec, size_t newSize);
+
+/**
  * Append the provided data to the end of the vector, enlarging it as required.
  * Returns the ammount of data appended, which is either len (success) or 0 (out of memory).
  * The vector remains unchanged if appending fails.