frontlib: Getting there :) Added commandline client for testing
authorMedo <smaxein@googlemail.com>
Wed, 27 Jun 2012 18:02:45 +0200
changeset 7275 15f722e0b96f
parent 7273 8eed495fd8da
child 7312 d1db8aaa8edc
frontlib: Getting there :) Added commandline client for testing
project_files/frontlib/cmdlineClient.c
project_files/frontlib/hwconsts.c
project_files/frontlib/hwconsts.h
project_files/frontlib/ipc/gameconn.c
project_files/frontlib/ipc/gameconn.h
project_files/frontlib/ipc/ipcprotocol.c
project_files/frontlib/ipc/mapconn.c
project_files/frontlib/ipc/mapconn.h
project_files/frontlib/model/cfg.c
project_files/frontlib/model/cfg.h
project_files/frontlib/model/gamesetup.c
project_files/frontlib/model/gamesetup.h
project_files/frontlib/model/map.h
project_files/frontlib/model/mapcfg.c
project_files/frontlib/model/mapcfg.h
project_files/frontlib/model/roomlist.c
project_files/frontlib/model/roomlist.h
project_files/frontlib/model/schemelist.c
project_files/frontlib/model/team.c
project_files/frontlib/model/team.h
project_files/frontlib/model/teamlist.c
project_files/frontlib/model/teamlist.h
project_files/frontlib/model/weapon.c
project_files/frontlib/model/weapon.h
project_files/frontlib/net/netbase.c
project_files/frontlib/net/netconn.c
project_files/frontlib/net/netconn.h
project_files/frontlib/net/netconn_callbacks.c
project_files/frontlib/net/netconn_internal.h
project_files/frontlib/net/netconn_send.c
project_files/frontlib/net/netprotocol.c
project_files/frontlib/net/netprotocol.h
project_files/frontlib/test.c
project_files/frontlib/test.c.nocompile
project_files/frontlib/util/buffer.c
project_files/frontlib/util/buffer.h
project_files/frontlib/util/list.c
project_files/frontlib/util/list.h
project_files/frontlib/util/logging.c
project_files/frontlib/util/logging.h
project_files/frontlib/util/util.c
project_files/frontlib/util/util.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/cmdlineClient.c	Wed Jun 27 18:02:45 2012 +0200
@@ -0,0 +1,474 @@
+#include "frontlib.h"
+#include "util/logging.h"
+#include "util/buffer.h"
+#include "util/util.h"
+#include "util/list.h"
+#include "model/map.h"
+#include "model/weapon.h"
+#include "model/schemelist.h"
+#include "ipc/mapconn.h"
+#include "ipc/gameconn.h"
+#include "net/netconn.h"
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <assert.h>
+#include <string.h>
+#include <conio.h>
+#include <windows.h>
+
+#define ENGINE_DIR ".\\"
+#define CONFIG_DIR "..\\share\\hedgewars"
+#define DATA_DIR CONFIG_DIR"\\Data"
+
+static flib_netconn *netconn;
+static flib_gameconn *gameconn;
+static flib_mapconn *mapconn;
+static char nickname[128];
+static flib_cfg_meta *metacfg;
+static bool netConnected = false;
+
+// Callback function that will be called when the map is rendered
+static void handleMapGenerated(void *context, const uint8_t *bitmap, int numHedgehogs) {
+	printf("Drawing map for %i brave little hogs...", numHedgehogs);
+
+	// Draw the map as ASCII art
+	for(int y=0; y<MAPIMAGE_HEIGHT; y+=8) {
+		for(int x=0; x<MAPIMAGE_WIDTH; x+=6) {
+			int pixelnum = x + y*MAPIMAGE_WIDTH;
+			bool pixel = bitmap[pixelnum>>3] & (1<<(7-(pixelnum&7)));
+			printf(pixel ? "#" : " ");
+		}
+		printf("\n");
+	}
+
+	// Destroy the connection object (this will end the "tick" loop below)
+	flib_mapconn_destroy(mapconn);
+	mapconn = NULL;
+}
+
+static void onGameDisconnect(void *context, int reason) {
+	flib_log_i("Connection closed. Reason: %i", reason);
+	flib_gameconn_destroy(gameconn);
+	gameconn = NULL;
+}
+
+// Callback function that will be called on error
+static void handleMapFailure(void *context, const char *errormessage) {
+	flib_log_e("Map rendering failed: %s", errormessage);
+
+	// Destroy the connection object (this will end the "tick" loop below)
+	flib_mapconn_destroy(mapconn);
+	mapconn = NULL;
+}
+
+static void startEngineMap(int port) {
+	char cmdbuffer[255];
+	char argbuffer[255];
+	snprintf(cmdbuffer, 255, "%shwengine.exe", ENGINE_DIR);
+	snprintf(argbuffer, 255, "%s %i landpreview", CONFIG_DIR, port);
+	ShellExecute(NULL, NULL, cmdbuffer, argbuffer, NULL, SW_HIDE);
+}
+
+static void startEngineGame(int port) {
+	char cmdbuffer[255];
+	char argbuffer[255];
+	snprintf(cmdbuffer, 255, "%shwengine.exe", ENGINE_DIR);
+	snprintf(argbuffer, 255, "%s 1024 768 32 %i 0 0 0 10 10 %s 0 0 TWVkbzQy 0 0 en.txt", CONFIG_DIR, port, DATA_DIR);
+	ShellExecute(NULL, NULL, cmdbuffer, argbuffer, NULL, SW_HIDE);
+}
+
+void testMapPreview() {
+	// Create a map description and check that there was no error
+	flib_map *map = flib_map_create_maze("This is the seed value", "Jungle", MAZE_SIZE_SMALL_TUNNELS);
+	assert(map);
+
+	// Create a new connection to the engine and check that there was no error
+	flib_mapconn *mapConnection = flib_mapconn_create(map);
+	assert(mapConnection);
+
+	// We don't need the map description anymore
+	flib_map_release(map);
+	map = NULL;
+
+	// Register the callback functions
+	flib_mapconn_onFailure(mapConnection, &handleMapFailure, &mapConnection);
+	flib_mapconn_onSuccess(mapConnection, &handleMapGenerated, &mapConnection);
+
+	// Start the engine process and tell it which port the frontlib is listening on
+	startEngineMap(flib_mapconn_getport(mapConnection));
+
+	// Usually, flib_mapconn_tick will be called in an event loop that runs several
+	// times per second. It handles I/O operations and progress, and calls
+	// callbacks when something interesting happens.
+	while(mapConnection) {
+		flib_mapconn_tick(mapConnection);
+	}
+}
+
+void handleNetDisconnect(void *context, int reason, const char *message) {
+	printf("Disconnected: %s", message);
+	flib_netconn_destroy(netconn);
+	netconn = NULL;
+}
+
+void printRoomList() {
+	const flib_roomlist *roomlist = flib_netconn_get_roomlist(netconn);
+	if(roomlist) {
+		for(int i=0; i<roomlist->roomCount; i++) {
+			if(i>0) {
+				printf(", ");
+			}
+			flib_room *room = roomlist->rooms[i];
+			printf("%s", room->name);
+		}
+		puts("\n");
+	} else {
+		puts("Sorry, due to an error the room list is not available.");
+	}
+}
+
+void printTeamList() {
+	flib_gamesetup *setup = flib_netconn_create_gameSetup(netconn);
+	if(setup) {
+		puts("The following teams are in this room:");
+		for(int i=0; i<setup->teamlist->teamCount; i++) {
+			if(i>0) {
+				printf(", ");
+			}
+			printf("%s", setup->teamlist->teams[i]->name);
+		}
+		puts("\n");
+	} else {
+		puts("Sorry, due to an error the team list is not available.");
+	}
+	flib_gamesetup_destroy(setup);
+}
+
+void handleNetConnected(void *context) {
+	printf("You enter a strange house inhabited by dozens of hedgehogs. There are many rooms in here:\n");
+	printRoomList();
+	printf("\n\nNow, you can chat by just entering text, or join a room with /join <roomname>.");
+	printf(" You can also /quit or let me /describe <roomname>. Once in a room, you can /add <teamname> and set yourself /ready. You can also /list the available rooms (in the lobby) or the teams (in a room).\n");
+	netConnected = true;
+}
+
+void handleChat(void *context, const char *nick, const char *msg) {
+	printf("%s: %s\n", nick, msg);
+}
+
+void handleEnterRoom(void *context, bool isChief) {
+	puts("You have entered the room.");
+}
+
+void handleRoomJoin(void *context, const char *nick) {
+	if(strcmp(nick, nickname)) {
+		printf("%s is here.\n", nick);
+	}
+}
+
+void handleRoomLeave(void *context, const char *nick, const char *partmsg) {
+	if(strcmp(nick, nickname)) {
+		printf("%s leaves.\n", nick);
+	}
+}
+
+void handleReady(void *context, const char *nick, bool ready) {
+	if(strcmp(nick, nickname)) {
+		if(ready) {
+			printf("%s is ready to go.\n", nick);
+		} else {
+			printf("%s is not ready.\n", nick);
+		}
+	} else {
+		if(ready) {
+			printf("You are ready to go.\n");
+		} else {
+			printf("You are not ready.\n");
+		}
+	}
+}
+
+void handleEmFromNet(void *context, const uint8_t *em, size_t size) {
+	if(gameconn) {
+		flib_gameconn_send_enginemsg(gameconn, (const uint8_t*)em, size);
+	}
+}
+
+void handleEmFromEngine(void *context, const uint8_t *em, size_t size) {
+	if(netconn) {
+		flib_netconn_send_engineMessage(netconn, em, size);
+	}
+}
+
+void handleChatFromGame(void *context, const char *message, bool teamchat) {
+	if(netconn) {
+		if(teamchat) {
+			flib_netconn_send_teamchat(netconn, message);
+		} else {
+			flib_netconn_send_chat(netconn, message);
+		}
+	}
+}
+
+void handleRunGame(void *context) {
+	flib_gamesetup *gamesetup = flib_netconn_create_gameSetup(netconn);
+	if(gamesetup) {
+		gameconn = flib_gameconn_create(nickname, gamesetup, true);
+		flib_gameconn_onEngineMessage(gameconn, handleEmFromEngine, NULL);
+		flib_gameconn_onDisconnect(gameconn, onGameDisconnect, NULL);
+		flib_gameconn_onChat(gameconn, handleChatFromGame, NULL);
+		startEngineGame(flib_gameconn_getport(gameconn));
+	}
+	flib_gamesetup_destroy(gamesetup);
+}
+
+void handleNickTaken(void *context, const char *nick) {
+	printf("The nickname %s is already in use, please choose a different one:\n", nick);
+	flib_gets(nickname, sizeof(nickname));
+	flib_netconn_send_nick(netconn, nickname);
+}
+
+void handlePwRequest(void *context, const char *nick) {
+	printf("A password is required to log in as %s, please enter (warning: shown in cleartext):\n", nick);
+	char password[256];
+	flib_gets(password, sizeof(password));
+	flib_netconn_send_password(netconn, password);
+}
+
+void handleMessage(void *context, int type, const char *msg) {
+	printf("*** %s\n", msg);
+}
+
+void handleTeamAccepted(void *context, const char *teamname) {
+	printf("The team %s has been accepted.\n", teamname);
+}
+
+void handleMapChanged(void *context, const flib_map *map, int changetype) {
+	if(map->mapgen != MAPGEN_NAMED && changetype != NETCONN_MAPCHANGE_THEME) {
+		if(mapconn) {
+			flib_mapconn_destroy(mapconn);
+			mapconn = NULL;
+		}
+		mapconn = flib_mapconn_create(map);
+		if(mapconn) {
+			flib_mapconn_onSuccess(mapconn, handleMapGenerated, NULL);
+			flib_mapconn_onFailure(mapconn, handleMapFailure, NULL);
+			startEngineMap(flib_mapconn_getport(mapconn));
+		}
+	}
+}
+
+void handleLeaveRoom(void *context, int reason, const char *msg) {
+	if(reason == NETCONN_ROOMLEAVE_ABANDONED) {
+		printf("The chief has abandoned the room.");
+	} else if(reason == NETCONN_ROOMLEAVE_KICKED) {
+		printf("You have been kicked from the room.");
+	}
+	if(msg) {
+		printf(" (%s)", msg);
+	}
+	puts(" You are back in the lobby.");
+}
+
+void handleSchemeChanged(void *context, flib_cfg *scheme) {
+	printf("Game scheme: %s.\n", scheme->name);
+}
+
+void handleWeaponsetChanged(void *context, flib_weaponset *weaponset) {
+	printf("Weaponset: %s.\n", weaponset->name);
+}
+
+void handleHogcountChanged(void *context, const char *team, int count) {
+	printf("Team %s will send %i hogs into the fight.\n", team, count);
+}
+
+void handleRoomAdd(void *context, const flib_room *room) {
+	printf("%s created a new room called %s.\n", room->owner, room->name);
+}
+
+void handleRoomDelete(void *context, const char *roomName) {
+	printf("The room %s has collapsed.\n", roomName);
+}
+
+void handleScriptChanged(void *context, const char *script) {
+	printf("Game Type: %s\n", script);
+}
+
+void handleTeamAdd(void *context, flib_team *team) {
+	printf("%s puts the team %s to the planning board.\n", team->ownerName, team->name);
+}
+
+void handleTeamDelete(void *context, const char *teamName) {
+	printf("The team %s decided not to fight this battle after all.\n", teamName);
+}
+
+void handleTeamColorChanged(void *context, const char *name, int colorIndex) {
+	static const char* colorNames[] = {"red", "blue", "teal", "purple", "pink", "green", "orange", "brown", "yellow"};
+	const char *colorName = "strange";
+	if(colorIndex>=0 && colorIndex < 9) {
+		colorName = colorNames[colorIndex];
+	}
+	printf("The team %s will wear %s uniforms today.\n", name, colorName);
+}
+
+void tick() {
+	if(gameconn) {
+		flib_gameconn_tick(gameconn);
+	}
+	if(netconn) {
+		flib_netconn_tick(netconn);
+	}
+	if(mapconn) {
+		flib_mapconn_tick(mapconn);
+	}
+}
+
+static HANDLE hStdin;
+
+static int init() {
+	hStdin = GetStdHandle(STD_INPUT_HANDLE);
+	if(hStdin == INVALID_HANDLE_VALUE) {
+		flib_log_e("Unable to get stdin handle");
+		return 1;
+	}
+	if(!flib_init(0)) {
+		flib_log_setLevel(FLIB_LOGLEVEL_WARNING);
+		freopen( "CON", "w", stdout );
+		freopen( "CON", "w", stderr );
+		metacfg = flib_cfg_meta_from_ini("metasettings.ini");
+		if(!metacfg) {
+			flib_quit();
+			return -1;
+		} else {
+			return 0;
+		}
+	}
+	return -1;
+}
+
+int main(int argc, char *argv[]) {
+	if(init()) {
+		return -1;
+	}
+
+	puts("Please enter a nickname:");
+	flib_gets(nickname, sizeof(nickname));
+
+	netconn = flib_netconn_create(nickname, metacfg, DATA_DIR"\\", "140.247.62.101", 46631);
+	if(!netconn) {
+		flib_quit();
+		return -1;
+	}
+
+	flib_netconn_onConnected(netconn, handleNetConnected, NULL);
+	flib_netconn_onDisconnected(netconn, handleNetDisconnect, NULL);
+	flib_netconn_onChat(netconn, handleChat, NULL);
+	flib_netconn_onEnterRoom(netconn, handleEnterRoom, NULL);
+	flib_netconn_onRunGame(netconn, handleRunGame, NULL);
+	flib_netconn_onEngineMessage(netconn, handleEmFromNet, NULL);
+	flib_netconn_onRoomJoin(netconn, handleRoomJoin, NULL);
+	flib_netconn_onRoomLeave(netconn, handleRoomLeave, NULL);
+	flib_netconn_onReadyState(netconn, handleReady, NULL);
+	flib_netconn_onNickTaken(netconn, handleNickTaken, NULL);
+	flib_netconn_onPasswordRequest(netconn, handlePwRequest, NULL);
+	flib_netconn_onMessage(netconn, handleMessage, NULL);
+	flib_netconn_onTeamAccepted(netconn, handleTeamAccepted, NULL);
+	flib_netconn_onMapChanged(netconn, handleMapChanged, NULL);
+	flib_netconn_onLeaveRoom(netconn, handleLeaveRoom, NULL);
+	flib_netconn_onCfgScheme(netconn, handleSchemeChanged, NULL);
+	flib_netconn_onWeaponsetChanged(netconn, handleWeaponsetChanged, NULL);
+	flib_netconn_onHogCountChanged(netconn, handleHogcountChanged, NULL);
+	flib_netconn_onRoomAdd(netconn, handleRoomAdd, NULL);
+	flib_netconn_onRoomDelete(netconn, handleRoomDelete, NULL);
+	flib_netconn_onScriptChanged(netconn, handleScriptChanged, NULL);
+	flib_netconn_onTeamAdd(netconn, handleTeamAdd, NULL);
+	flib_netconn_onTeamDelete(netconn, handleTeamDelete, NULL);
+	flib_netconn_onTeamColorChanged(netconn, handleTeamColorChanged, NULL);
+
+	INPUT_RECORD inputRecord;
+	DWORD eventCount = 0;
+
+	while(netconn || gameconn) {
+		tick();
+		if(netconn && netConnected) {
+			while(PeekConsoleInput(hStdin, &inputRecord, 1, &eventCount) && eventCount>0) {
+				if(inputRecord.EventType != KEY_EVENT) {
+					ReadConsoleInput(hStdin, &inputRecord, 1, &eventCount);
+				} else {
+					printf("%s: ", nickname);
+					char input[256];
+					if(!flib_gets(input, sizeof(input))) {
+						if(!memcmp("/quit", input, strlen("/quit"))) {
+							flib_netconn_send_quit(netconn, "Player quit.");
+						} else if(!memcmp("/describe ", input, strlen("/describe "))) {
+							const char *roomname = input+strlen("/describe ");
+							const flib_roomlist *roomlist = flib_netconn_get_roomlist(netconn);
+							flib_room *room = flib_roomlist_find(roomlist, roomname);
+							if(!room) {
+								puts("Unknown room.");
+							} else {
+								char *text = flib_asprintf(
+										"%s is a room created by %s, where %i players (%i teams) are %s on %s%s, using the %s scheme and %s weaponset.",
+										room->name,
+										room->owner,
+										room->playerCount,
+										room->teamCount,
+										room->inProgress ? "fighting" : "preparing to fight",
+										room->map[0]=='+' ? "" : "the map ",
+										!strcmp("+rnd+", room->map) ? "a random map" :
+												!strcmp("+maze+", room->map) ? "a random maze" :
+												!strcmp("+drawn+", room->map) ? "a hand-drawn map" :
+												room->map,
+										room->scheme,
+										room->weapons);
+								if(text) {
+									puts(text);
+								}
+								free(text);
+							}
+						} else if(!memcmp("/join ", input, strlen("/join "))) {
+							const char *roomname = input+strlen("/join ");
+							flib_netconn_send_joinRoom(netconn, roomname);
+						} else if(!memcmp("/ready", input, strlen("/ready"))) {
+							flib_netconn_send_toggleReady(netconn);
+						} else if(!memcmp("/loglevel ", input, strlen("/loglevel "))) {
+							int loglevel = atoi(input+strlen("/loglevel "));
+							flib_log_setLevel(loglevel);
+						} else if(!memcmp("/list", input, strlen("/list"))) {
+							if(flib_netconn_is_in_room_context(netconn)) {
+								printTeamList();
+							} else {
+								puts("From this big and expansive lobby, hallways branch off to these rooms:");
+								printRoomList();
+							}
+						} else if(!memcmp("/addteam ", input, strlen("/addteam "))) {
+							const char *teamname = input+strlen("/addteam ");
+							if(!flib_contains_dir_separator(teamname)) {
+								char *teamfilename = flib_asprintf("%s.hwt", teamname);
+								if(teamfilename) {
+									flib_team *team = flib_team_from_ini(teamfilename);
+									if(team) {
+										flib_netconn_send_addTeam(netconn, team);
+									} else {
+										printf("Teamfile %s not found.\n", teamfilename);
+									}
+									flib_team_release(team);
+								}
+								free(teamfilename);
+							}
+						} else if(strlen(input)>0) {
+							flib_netconn_send_chat(netconn, input);
+						}
+					}
+				}
+			}
+		}
+		fflush(stdout);
+		Sleep(10);
+	}
+
+
+	flib_cfg_meta_release(metacfg);
+	return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/hwconsts.c	Wed Jun 27 18:02:45 2012 +0200
@@ -0,0 +1,4 @@
+#include "hwconsts.h"
+
+const uint32_t flib_teamcolor_defaults[] = HW_TEAMCOLOR_ARRAY;
+const size_t flib_teamcolor_defaults_len = sizeof(flib_teamcolor_defaults)/sizeof(uint32_t)-1;
--- a/project_files/frontlib/hwconsts.h	Mon Jun 25 15:21:18 2012 +0200
+++ b/project_files/frontlib/hwconsts.h	Wed Jun 27 18:02:45 2012 +0200
@@ -1,3 +1,22 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * 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
+ */
+
 /**
  * This file contains important constants which might need to be changed to adapt to
  * changes in the engine or protocols.
@@ -6,14 +25,34 @@
 #ifndef HWCONSTS_H_
 #define HWCONSTS_H_
 
+#include <inttypes.h>
+#include <stddef.h>
+
 #define HEDGEHOGS_PER_TEAM 8
 #define NETGAME_DEFAULT_PORT 46631
-#define PROTOCOL_VERSION 41
+#define PROTOCOL_VERSION 42
 #define MIN_SERVER_VERSION 1
 
-#define GAMEMOD_PERHOGAMMO_MASKBIT 22
-#define GAMEMOD_SHAREDAMMO_MASKBIT 16
+// Used for sending scripts to the engine
+#define MULTIPLAYER_SCRIPT_PATH "Scripts/Multiplayer/"
 
 #define WEAPONS_COUNT 55
 
+/* A merge of mikade/bugq colours w/ a bit of channel feedback */
+#define HW_TEAMCOLOR_ARRAY  { UINT32_C(0xffff0204), /* red    */ \
+                              UINT32_C(0xff4980c1), /* blue   */ \
+                              UINT32_C(0xff1de6ba), /* teal   */ \
+                              UINT32_C(0xffb541ef), /* purple */ \
+                              UINT32_C(0xffe55bb0), /* pink   */ \
+                              UINT32_C(0xff20bf00), /* green  */ \
+                              UINT32_C(0xfffe8b0e), /* orange */ \
+                              UINT32_C(0xff5f3605), /* brown  */ \
+                              UINT32_C(0xffffff01), /* yellow */ \
+                              /* add new colors here */ \
+                              0 } /* Keep this 0 at the end or the length will be calculated wrong */
+
+// TODO allow setting alternative color lists?
+extern const size_t flib_teamcolor_defaults_len;
+extern const uint32_t flib_teamcolor_defaults[];
+
 #endif
--- a/project_files/frontlib/ipc/gameconn.c	Mon Jun 25 15:21:18 2012 +0200
+++ b/project_files/frontlib/ipc/gameconn.c	Wed Jun 27 18:02:45 2012 +0200
@@ -38,7 +38,7 @@
 	void (*onGameRecordedCb)(void *context, const uint8_t *record, int size, bool isSavegame);
 	void *onGameRecordedCtx;
 
-	void (*onEngineMessageCb)(void *context, const uint8_t *em, int size);
+	void (*onEngineMessageCb)(void *context, const uint8_t *em, size_t size);
 	void *onEngineMessageCtx;
 
 	bool running;
@@ -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_onEngineMessage(void *context, const uint8_t *em, int size) {}
+static void defaultCallback_onEngineMessage(void *context, const uint8_t *em, size_t size) {}
 
 static void clearCallbacks(flib_gameconn *conn) {
 	conn->onConnectCb = &defaultCallback_onConnect;
@@ -65,23 +65,25 @@
 
 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->ipcBase = flib_ipcbase_create();
-		tempConn->configBuffer = flib_vector_create();
-		tempConn->playerName = flib_strdupnull(playerName);
-		if(tempConn->ipcBase && tempConn->configBuffer && tempConn->playerName) {
-			if(record) {
-				tempConn->demoBuffer = flib_vector_create();
+	if(!log_badparams_if(!playerName)) {
+		flib_gameconn *tempConn = flib_calloc(1, sizeof(flib_gameconn));
+		if(tempConn) {
+			tempConn->ipcBase = flib_ipcbase_create();
+			tempConn->configBuffer = flib_vector_create();
+			tempConn->playerName = flib_strdupnull(playerName);
+			if(tempConn->ipcBase && tempConn->configBuffer && tempConn->playerName) {
+				if(record) {
+					tempConn->demoBuffer = flib_vector_create();
+				}
+				tempConn->state = AWAIT_CONNECTION;
+				tempConn->netgame = netGame;
+				clearCallbacks(tempConn);
+				result = tempConn;
+				tempConn = NULL;
 			}
-			tempConn->state = AWAIT_CONNECTION;
-			tempConn->netgame = netGame;
-			clearCallbacks(tempConn);
-			result = tempConn;
-			tempConn = NULL;
 		}
+		flib_gameconn_destroy(tempConn);
 	}
-	flib_gameconn_destroy(tempConn);
 	return result;
 }
 
@@ -89,7 +91,9 @@
 	flib_gameconn *result = NULL;
 	flib_gameconn *tempConn = flib_gameconn_create_partial(true, playerName, netgame);
 	if(tempConn) {
-		if(!flib_ipc_append_fullconfig(tempConn->configBuffer, setup, netgame)) {
+		if(flib_ipc_append_fullconfig(tempConn->configBuffer, setup, netgame)) {
+			flib_log_e("Error generating full game configuration for the engine.");
+		} else {
 			result = tempConn;
 			tempConn = NULL;
 		}
@@ -102,7 +106,7 @@
 	flib_gameconn *result = NULL;
 	flib_gameconn *tempConn = flib_gameconn_create_partial(false, "Player", false);
 	if(tempConn) {
-		if(flib_vector_append(tempConn->configBuffer, demo, size) == size) {
+		if(!flib_vector_append(tempConn->configBuffer, demo, size)) {
 			result = tempConn;
 			tempConn = NULL;
 		}
@@ -115,7 +119,7 @@
 	flib_gameconn *result = NULL;
 	flib_gameconn *tempConn = flib_gameconn_create_partial(true, playerName, false);
 	if(tempConn) {
-		if(flib_vector_append(tempConn->configBuffer, save, size) == size) {
+		if(!flib_vector_append(tempConn->configBuffer, save, size)) {
 			result = tempConn;
 			tempConn = NULL;
 		}
@@ -161,17 +165,15 @@
 }
 
 int flib_gameconn_getport(flib_gameconn *conn) {
-	if(!conn) {
-		flib_log_e("null parameter in flib_gameconn_getport");
-		return 0;
-	} else {
+	if(!log_badparams_if(!conn)) {
 		return flib_ipcbase_port(conn->ipcBase);
 	}
+	return 0;
 }
 
 static void demo_append(flib_gameconn *conn, const void *data, size_t len) {
 	if(conn->demoBuffer) {
-		if(flib_vector_append(conn->demoBuffer, data, len) < len) {
+		if(flib_vector_append(conn->demoBuffer, data, len)) {
 			flib_log_e("Error recording demo: Out of memory.");
 			flib_vector_destroy(conn->demoBuffer);
 			conn->demoBuffer = NULL;
@@ -213,11 +215,10 @@
 	}
 }
 
-int flib_gameconn_send_enginemsg(flib_gameconn *conn, uint8_t *data, int len) {
+int flib_gameconn_send_enginemsg(flib_gameconn *conn, const uint8_t *data, size_t len) {
 	int result = -1;
-	if(!conn || (!data && len>0)) {
-		flib_log_e("null parameter in flib_gameconn_send_enginemsg");
-	} else if(!flib_ipcbase_send_raw(conn->ipcBase, data, len)) {
+	if(!log_badparams_if(!conn || (!data && len>0))
+			&& !flib_ipcbase_send_raw(conn->ipcBase, data, len)) {
 		demo_append(conn, data, len);
 		result = 0;
 	}
@@ -301,7 +302,7 @@
 	}
 }
 
-void flib_gameconn_onEngineMessage(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, size_t size), void* context) {
 	if(!conn) {
 		flib_log_e("null parameter in flib_gameconn_onEngineMessage");
 	} else {
--- a/project_files/frontlib/ipc/gameconn.h	Mon Jun 25 15:21:18 2012 +0200
+++ b/project_files/frontlib/ipc/gameconn.h	Wed Jun 27 18:02:45 2012 +0200
@@ -34,7 +34,7 @@
  */
 void flib_gameconn_tick(flib_gameconn *conn);
 
-int flib_gameconn_send_enginemsg(flib_gameconn *conn, uint8_t *data, int len);
+int flib_gameconn_send_enginemsg(flib_gameconn *conn, const uint8_t *data, size_t len);
 int flib_gameconn_send_textmsg(flib_gameconn *conn, int msgtype, const char *msg);
 int flib_gameconn_send_chatmsg(flib_gameconn *conn, const char *playername, const char *msg);
 
@@ -72,9 +72,9 @@
 
 /**
  * ...needs to be passed on to the server in a net game
- * handleEngineMessage(void *context, const uint8_t *em, int size)
+ * handleEngineMessage(void *context, const uint8_t *em, size_t size)
  */
-void flib_gameconn_onEngineMessage(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, size_t size), void* context);
 
 // TODO efinish
 
--- a/project_files/frontlib/ipc/ipcprotocol.c	Mon Jun 25 15:21:18 2012 +0200
+++ b/project_files/frontlib/ipc/ipcprotocol.c	Wed Jun 27 18:02:45 2012 +0200
@@ -10,9 +10,7 @@
 
 int flib_ipc_append_message(flib_vector *vec, const char *fmt, ...) {
 	int result = -1;
-	if(!vec || !fmt) {
-		flib_log_e("null parameter in flib_ipc_appendmessage");
-	} else {
+	if(!log_badparams_if(!vec || !fmt)) {
 		// 1 byte size prefix, 255 bytes max message length, 1 0-byte for vsnprintf
 		char msgbuffer[257];
 
@@ -22,18 +20,13 @@
 		int msgSize = vsnprintf(msgbuffer+1, 256, fmt, argp);
 		va_end(argp);
 
-		if(msgSize > 255) {
-			flib_log_e("Message too long (%u bytes) in flib_ipc_appendmessage", (unsigned)msgSize);
-		} else if(msgSize<0) {
-			flib_log_e("printf error in flib_ipc_appendmessage");
-		} else {
+		if(!log_e_if(msgSize > 255, "Message too long (%u bytes)", (unsigned)msgSize)
+				&& !log_e_if(msgSize < 0, "printf error")) {
 			// Add the length prefix
 			((uint8_t*)msgbuffer)[0] = msgSize;
 
 			// Append it to the vector
-			if(flib_vector_append(vec, msgbuffer, msgSize+1) == msgSize+1) {
-				result = 0;
-			}
+			result = flib_vector_append(vec, msgbuffer, msgSize+1);
 		}
 	}
 	return result;
@@ -42,26 +35,16 @@
 int flib_ipc_append_mapconf(flib_vector *vec, const flib_map *map, bool mappreview) {
 	int result = -1;
 	flib_vector *tempvector = flib_vector_create();
-	if(!vec || !map) {
-		flib_log_e("null parameter in flib_ipc_append_mapconf");
-	} else if(tempvector) {
+	if(!log_badparams_if(!vec || !map)) {
 		bool error = false;
 
 		if(map->mapgen == MAPGEN_NAMED) {
-			if(map->name) {
-				error |= flib_ipc_append_message(tempvector, "emap %s", map->name);
-			} else {
-				flib_log_e("Missing map name");
-				error = true;
-			}
+			error |= log_e_if(!map->name, "Missing map name")
+					|| flib_ipc_append_message(tempvector, "emap %s", map->name);
 		}
-		if(map->theme && !mappreview) {
-			if(map->theme) {
-				error |= flib_ipc_append_message(tempvector, "etheme %s", map->theme);
-			} else {
-				flib_log_e("Missing map theme");
-				error = true;
-			}
+		if(!mappreview) {
+			error |= log_e_if(!map->theme, "Missing map theme")
+					|| flib_ipc_append_message(tempvector, "etheme %s", map->theme);
 		}
 		error |= flib_ipc_append_seed(tempvector, map->seed);
 		error |= flib_ipc_append_message(tempvector, "e$template_filter %i", map->templateFilter);
@@ -81,16 +64,16 @@
 				int bytesRemaining = map->drawDataSize-offset;
 				int fragmentsize = bytesRemaining < 200 ? bytesRemaining : 200;
 				uint8_t messagesize = edrawlen + fragmentsize;
-				error |= (flib_vector_append(tempvector, &messagesize, 1) != 1);
-				error |= (flib_vector_append(tempvector, edraw, edrawlen) != edrawlen);
-				error |= (flib_vector_append(tempvector, map->drawData+offset, fragmentsize) != fragmentsize);
+				error |= flib_vector_append(tempvector, &messagesize, 1);
+				error |= flib_vector_append(tempvector, edraw, edrawlen);
+				error |= flib_vector_append(tempvector, map->drawData+offset, fragmentsize);
 			}
 		}
 
-		if(!error) {
+		if(!log_e_if(error, "Error generating map config")) {
 			// Message created, now we can copy everything.
 			flib_constbuffer constbuf = flib_vector_as_constbuffer(tempvector);
-			if(flib_vector_append(vec, constbuf.data, constbuf.size) == constbuf.size) {
+			if(!flib_vector_append(vec, constbuf.data, constbuf.size)) {
 				result = 0;
 			}
 		}
@@ -100,53 +83,69 @@
 }
 
 int flib_ipc_append_seed(flib_vector *vec, const char *seed) {
-	if(!vec || !seed) {
-		flib_log_e("null parameter in flib_ipc_append_seed");
-		return -1;
-	} else {
+	if(!log_badparams_if(!vec || !seed)) {
 		return flib_ipc_append_message(vec, "eseed %s", seed);
 	}
+	return -1;
 }
 
 int flib_ipc_append_script(flib_vector *vec, const char *script) {
-	if(!vec || !script) {
-		flib_log_e("null parameter in flib_ipc_append_script");
-		return -1;
-	} else {
-		return flib_ipc_append_message(vec, "escript %s", script);
+	int result = -1;
+	char *copy = flib_strdupnull(script);
+	if(!log_badparams_if(!vec) && copy) {
+		if(!strcmp("Normal", copy)) {
+			// "Normal" means no gametype script
+			result = 0;
+		} else {
+			size_t len = strlen(copy);
+			for(size_t i=0; i<len; i++) {
+				if(copy[i] == ' ') {
+					copy[i] = '_';
+				}
+			}
+
+			result = flib_ipc_append_message(vec, "escript %s%s.lua", MULTIPLAYER_SCRIPT_PATH, copy);
+		}
 	}
+	free(copy);
+	return result;
+}
+
+uint32_t buildModFlags(const flib_cfg *scheme) {
+	uint32_t result = 0;
+	for(int i=0; i<scheme->meta->modCount; i++) {
+		if(scheme->mods[i]) {
+			int bitmaskIndex = scheme->meta->mods[i].bitmaskIndex;
+			result |= (UINT32_C(1) << bitmaskIndex);
+		}
+	}
+	return result;
 }
 
 int flib_ipc_append_gamescheme(flib_vector *vec, const flib_cfg *scheme) {
 	int result = -1;
 	flib_vector *tempvector = flib_vector_create();
-	if(!vec || !scheme) {
-		flib_log_e("null parameter in flib_ipc_append_gamescheme");
-	} else if(tempvector) {
+	if(!log_badparams_if(!vec || !scheme) && tempvector) {
 		const flib_cfg_meta *meta = scheme->meta;
 		bool error = false;
-		uint32_t gamemods = 0;
-		for(int i=0; i<meta->modCount; i++) {
-			if(scheme->mods[i]) {
-				gamemods |= (1<<meta->mods[i].bitmaskIndex);
-			}
-		}
-		error |= flib_ipc_append_message(tempvector, "e$gmflags %"PRIu32, gamemods);
+		error |= flib_ipc_append_message(tempvector, "e$gmflags %"PRIu32, buildModFlags(scheme));
 		for(int i=0; i<meta->settingCount; i++) {
-			int value = scheme->settings[i];
-			if(meta->settings[i].maxMeansInfinity) {
-				value = value>=meta->settings[i].max ? 9999 : value;
+			if(meta->settings[i].engineCommand) {
+				int value = scheme->settings[i];
+				if(meta->settings[i].maxMeansInfinity) {
+					value = value>=meta->settings[i].max ? 9999 : value;
+				}
+				if(meta->settings[i].times1000) {
+					value *= 1000;
+				}
+				error |= flib_ipc_append_message(tempvector, "%s %i", meta->settings[i].engineCommand, value);
 			}
-			if(meta->settings[i].times1000) {
-				value *= 1000;
-			}
-			error |= flib_ipc_append_message(tempvector, "%s %i", meta->settings[i].engineCommand, value);
 		}
 
 		if(!error) {
 			// Message created, now we can copy everything.
 			flib_constbuffer constbuf = flib_vector_as_constbuffer(tempvector);
-			if(flib_vector_append(vec, constbuf.data, constbuf.size) == constbuf.size) {
+			if(!flib_vector_append(vec, constbuf.data, constbuf.size)) {
 				result = 0;
 			}
 		}
@@ -165,19 +164,23 @@
 int flib_ipc_append_addteam(flib_vector *vec, const flib_team *team, bool perHogAmmo, bool noAmmoStore) {
 	int result = -1;
 	flib_vector *tempvector = flib_vector_create();
-	if(!vec || !team) {
-		flib_log_e("invalid parameter in flib_ipc_append_addteam");
-	} else if(tempvector) {
+	if(!log_badparams_if(!vec || !team) && tempvector) {
 		bool error = false;
 
 		if(!perHogAmmo && !noAmmoStore) {
-			error |= appendWeaponSet(tempvector, team->hogs[0].weaponset);
-			error |= flib_ipc_append_message(tempvector, "eammstore");
+			error = error
+					|| appendWeaponSet(tempvector, team->hogs[0].weaponset)
+					|| flib_ipc_append_message(tempvector, "eammstore");
 		}
 
 		// TODO
 		char *hash = team->ownerName ? team->ownerName : "00000000000000000000000000000000";
-		error |= flib_ipc_append_message(tempvector, "eaddteam %s %"PRIu32" %s", hash, team->color, team->name);
+		if(team->colorIndex<0 || team->colorIndex>=flib_teamcolor_defaults_len) {
+			flib_log_e("Color index out of bounds for team %s: %i", team->name, team->colorIndex);
+			error = true;
+		} else {
+			error |= flib_ipc_append_message(tempvector, "eaddteam %s %"PRIu32" %s", hash, flib_teamcolor_defaults[team->colorIndex], team->name);
+		}
 
 		if(team->remoteDriven) {
 			error |= flib_ipc_append_message(tempvector, "erdriven");
@@ -203,7 +206,7 @@
 		if(!error) {
 			// Message created, now we can copy everything.
 			flib_constbuffer constbuf = flib_vector_as_constbuffer(tempvector);
-			if(flib_vector_append(vec, constbuf.data, constbuf.size) == constbuf.size) {
+			if(!flib_vector_append(vec, constbuf.data, constbuf.size)) {
 				result = 0;
 			}
 		}
@@ -212,22 +215,20 @@
 	return result;
 }
 
-static bool getGameMod(const flib_cfg *conf, int maskbit) {
+static bool getGameMod(const flib_cfg *conf, const char *name) {
 	for(int i=0; i<conf->meta->modCount; i++) {
-		if(conf->meta->mods[i].bitmaskIndex == maskbit) {
+		if(!strcmp(conf->meta->mods[i].name, name)) {
 			return conf->mods[i];
 		}
 	}
-	flib_log_e("Unable to find game mod with mask bit %i", maskbit);
+	flib_log_e("Unable to find game mod %s", name);
 	return false;
 }
 
 int flib_ipc_append_fullconfig(flib_vector *vec, const flib_gamesetup *setup, bool netgame) {
 	int result = -1;
 	flib_vector *tempvector = flib_vector_create();
-	if(!vec || !setup) {
-		flib_log_e("null parameter in flib_ipc_append_fullconfig");
-	} else if(tempvector) {
+	if(!log_badparams_if(!vec || !setup) && tempvector) {
 		bool error = false;
 		bool perHogAmmo = false;
 		bool sharedAmmo = false;
@@ -237,40 +238,37 @@
 			error |= flib_ipc_append_mapconf(tempvector, setup->map, false);
 		}
 		if(setup->script) {
-			error |= flib_ipc_append_message(tempvector, "escript %s", setup->script);
+			error |= flib_ipc_append_script(tempvector, setup->script);
 		}
 		if(setup->gamescheme) {
 			error |= flib_ipc_append_gamescheme(tempvector, setup->gamescheme);
-			sharedAmmo = getGameMod(setup->gamescheme, GAMEMOD_SHAREDAMMO_MASKBIT);
+			sharedAmmo = getGameMod(setup->gamescheme, "sharedammo");
 			// Shared ammo has priority over per-hog ammo
-			perHogAmmo = !sharedAmmo && getGameMod(setup->gamescheme, GAMEMOD_PERHOGAMMO_MASKBIT);
+			perHogAmmo = !sharedAmmo && getGameMod(setup->gamescheme, "perhogammo");
 		}
-		if(setup->teams && setup->teamCount>0) {
-			uint32_t *clanColors = flib_calloc(setup->teamCount, sizeof(uint32_t));
+		if(setup->teamlist->teams && setup->teamlist->teamCount>0) {
+			int *clanColors = flib_calloc(setup->teamlist->teamCount, sizeof(int));
 			if(!clanColors) {
 				error = true;
 			} else {
 				int clanCount = 0;
-				for(int i=0; i<setup->teamCount; i++) {
-					flib_team *team = setup->teams[i];
-					bool newClan = false;
-
+				for(int i=0; !error && i<setup->teamlist->teamCount; i++) {
+					flib_team *team = setup->teamlist->teams[i];
 					// Find the clan index of this team (clans are identified by color).
-					// The upper 8 bits (alpha) are ignored in the engine as well.
-					uint32_t color = team->color&UINT32_C(0x00ffffff);
+					bool newClan = false;
 					int clan = 0;
-					while(clan<clanCount && clanColors[clan] != color) {
+					while(clan<clanCount && clanColors[clan] != team->colorIndex) {
 						clan++;
 					}
 					if(clan==clanCount) {
 						newClan = true;
 						clanCount++;
-						clanColors[clan] = color;
+						clanColors[clan] = team->colorIndex;
 					}
 
 					// If shared ammo is active, only add an ammo store for the first team in each clan.
 					bool noAmmoStore = sharedAmmo&&!newClan;
-					error |= flib_ipc_append_addteam(tempvector, setup->teams[i], perHogAmmo, noAmmoStore);
+					error |= flib_ipc_append_addteam(tempvector, setup->teamlist->teams[i], perHogAmmo, noAmmoStore);
 				}
 			}
 			free(clanColors);
@@ -280,7 +278,7 @@
 		if(!error) {
 			// Message created, now we can copy everything.
 			flib_constbuffer constbuf = flib_vector_as_constbuffer(tempvector);
-			if(flib_vector_append(vec, constbuf.data, constbuf.size) == constbuf.size) {
+			if(!flib_vector_append(vec, constbuf.data, constbuf.size)) {
 				result = 0;
 			}
 		}
--- a/project_files/frontlib/ipc/mapconn.c	Mon Jun 25 15:21:18 2012 +0200
+++ b/project_files/frontlib/ipc/mapconn.c	Wed Jun 27 18:02:45 2012 +0200
@@ -39,7 +39,7 @@
 	conn->onFailureCb = &noop_handleFailure;
 }
 
-static flib_vector *createConfigBuffer(flib_map *mapdesc) {
+static flib_vector *createConfigBuffer(const flib_map *mapdesc) {
 	flib_vector *result = NULL;
 	flib_vector *tempbuffer = flib_vector_create();
 	if(tempbuffer) {
@@ -55,7 +55,7 @@
 	return result;
 }
 
-flib_mapconn *flib_mapconn_create(flib_map *mapdesc) {
+flib_mapconn *flib_mapconn_create(const flib_map *mapdesc) {
 	flib_mapconn *result = NULL;
 	flib_mapconn *tempConn = flib_calloc(1, sizeof(flib_mapconn));
 	if(tempConn) {
--- a/project_files/frontlib/ipc/mapconn.h	Mon Jun 25 15:21:18 2012 +0200
+++ b/project_files/frontlib/ipc/mapconn.h	Wed Jun 27 18:02:45 2012 +0200
@@ -17,10 +17,14 @@
  * engine process. Once this connection is established, the required information
  * will be sent to the engine, and the reply is read.
  *
+ * The map must be a regular, maze or drawn map - for a preview of a named map,
+ * use the preview images in the map's directory, and for the hog count read the
+ * map information (flib_mapcfg_read).
+ *
  * No NULL parameters allowed, returns NULL on failure.
  * Use flib_mapconn_destroy to free the returned object.
  */
-flib_mapconn *flib_mapconn_create(flib_map *mapdesc);
+flib_mapconn *flib_mapconn_create(const flib_map *mapdesc);
 
 /**
  * Destroy the mapconn object. Passing NULL is allowed and does nothing.
--- a/project_files/frontlib/model/cfg.c	Mon Jun 25 15:21:18 2012 +0200
+++ b/project_files/frontlib/model/cfg.c	Wed Jun 27 18:02:45 2012 +0200
@@ -170,7 +170,7 @@
 	return result;
 }
 
-flib_cfg *flib_cfg_copy(flib_cfg *cfg) {
+flib_cfg *flib_cfg_copy(const flib_cfg *cfg) {
 	flib_cfg *result = NULL;
 	if(cfg) {
 		result = flib_cfg_create(cfg->meta, cfg->name);
--- a/project_files/frontlib/model/cfg.h	Mon Jun 25 15:21:18 2012 +0200
+++ b/project_files/frontlib/model/cfg.h	Wed Jun 27 18:02:45 2012 +0200
@@ -73,7 +73,7 @@
 /**
  * Create a copy of the scheme. Returns NULL on error or if NULL was passed.
  */
-flib_cfg *flib_cfg_copy(flib_cfg *cfg);
+flib_cfg *flib_cfg_copy(const flib_cfg *cfg);
 
 /**
  * Increase the reference count of the object. Call this if you store a pointer to it somewhere.
--- a/project_files/frontlib/model/gamesetup.c	Mon Jun 25 15:21:18 2012 +0200
+++ b/project_files/frontlib/model/gamesetup.c	Wed Jun 27 18:02:45 2012 +0200
@@ -1,2 +1,13 @@
 #include "gamesetup.h"
 
+#include <stdlib.h>
+
+void flib_gamesetup_destroy(flib_gamesetup *gamesetup) {
+	if(gamesetup) {
+		free(gamesetup->script);
+		flib_cfg_release(gamesetup->gamescheme);
+		flib_map_release(gamesetup->map);
+		flib_teamlist_destroy(gamesetup->teamlist);
+		free(gamesetup);
+	}
+}
--- a/project_files/frontlib/model/gamesetup.h	Mon Jun 25 15:21:18 2012 +0200
+++ b/project_files/frontlib/model/gamesetup.h	Wed Jun 27 18:02:45 2012 +0200
@@ -9,14 +9,15 @@
 #include "cfg.h"
 #include "weapon.h"
 #include "map.h"
-#include "team.h"
+#include "teamlist.h"
 
 typedef struct {
     char *script;
     flib_cfg *gamescheme;
     flib_map *map;
-	int teamCount;
-	flib_team **teams;
+	flib_teamlist *teamlist;
 } flib_gamesetup;
 
+void flib_gamesetup_destroy(flib_gamesetup *gamesetup);
+
 #endif
--- a/project_files/frontlib/model/map.h	Mon Jun 25 15:21:18 2012 +0200
+++ b/project_files/frontlib/model/map.h	Wed Jun 27 18:02:45 2012 +0200
@@ -1,12 +1,3 @@
-/**
- * Data structure for defining a map. This contains the whole recipe to
- * exactly recreate a particular map. For named maps, you also need the
- * corresponding files.
- *
- * The required fields depend on the map generator, see the comments
- * at the struct for details.
- */
-
 #ifndef MODEL_MAP_H_
 #define MODEL_MAP_H_
 
@@ -32,12 +23,20 @@
 #define MAZE_SIZE_MEDIUM_ISLANDS 4
 #define MAZE_SIZE_LARGE_ISLANDS 5
 
+/**
+ * Data structure for defining a map. This contains the whole recipe to
+ * exactly recreate a particular map. For named maps, you also need the
+ * corresponding files.
+ *
+ * The required fields depend on the map generator, see the comments
+ * at the struct for details.
+ */
 typedef struct {
 	int _referenceCount;
 	int mapgen;				// Always one of the MAPGEN_ constants
 	char *name;				// The name of the map for MAPGEN_NAMED, otherwise one of "+rnd+", "+maze+" or "+drawn+".
 	char *seed;				// Used for all maps
-	char *theme;			// Used for all except MAPGEN_NAMED
+	char *theme;			// Used for all maps
 	uint8_t *drawData;		// Used for MAPGEN_DRAWN
 	int drawDataSize;		// Used for MAPGEN_DRAWN
 	int templateFilter;		// Used for MAPGEN_REGULAR
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/mapcfg.c	Wed Jun 27 18:02:45 2012 +0200
@@ -0,0 +1,44 @@
+#include "mapcfg.h"
+
+#include "../util/util.h"
+#include "../util/logging.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+void removeNewline(char *str) {
+	for(;*str;str++) {
+		if(*str=='\n' || *str=='\r') {
+			*str = 0;
+			return;
+		}
+	}
+}
+
+int flib_mapcfg_read(const char *dataDirPath, const char *mapname, flib_mapcfg *out) {
+	int result = -1;
+	if(!log_badparams_if(!dataDirPath || !mapname || !out)
+			&& !log_e_if(flib_contains_dir_separator(mapname), "Illegal character in mapname %s", mapname)) {
+		char *path = flib_asprintf("%sMaps/%s/map.cfg", dataDirPath, mapname);
+		if(path) {
+			FILE *file = fopen(path, "rb");
+			if(!log_e_if(!file, "Unable to open map config file %s", path)) {
+				if(!log_e_if(!fgets(out->theme, sizeof(out->theme), file), "Error reading theme from %s", path)) {
+					removeNewline(out->theme);
+					char buf[64];
+					if(!log_e_if(!fgets(buf, sizeof(buf), file), "Error reading hoglimit from %s", path)) {
+						removeNewline(buf);
+						errno = 0;
+						out->hogLimit = strtol(buf, NULL, 10);
+						result = !log_e_if(errno, "Invalid hoglimit in %s: %i", path, buf);
+					}
+				}
+				fclose(file);
+			}
+		}
+		free(path);
+	}
+	return result;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/mapcfg.h	Wed Jun 27 18:02:45 2012 +0200
@@ -0,0 +1,19 @@
+/*
+ * Data structure and functions for accessing the map.cfg of named maps.
+ */
+
+#ifndef MAPCFG_H_
+#define MAPCFG_H_
+
+typedef struct {
+	char theme[256];
+	int hogLimit;
+} flib_mapcfg;
+
+/**
+ * Read the map configuration for the map with this name.
+ * The dataDirPath must end in a path separator.
+ */
+int flib_mapcfg_read(const char *dataDirPath, const char *mapname, flib_mapcfg *out);
+
+#endif /* MAPCFG_H_ */
--- a/project_files/frontlib/model/roomlist.c	Mon Jun 25 15:21:18 2012 +0200
+++ b/project_files/frontlib/model/roomlist.c	Wed Jun 27 18:02:45 2012 +0200
@@ -11,7 +11,7 @@
 	return flib_calloc(1, sizeof(flib_roomlist));
 }
 
-static void flib_roomlist_room_destroy(flib_roomlist_room *room) {
+static void flib_roomlist_room_destroy(flib_room *room) {
 	if(room) {
 		free(room->map);
 		free(room->name);
@@ -27,13 +27,14 @@
 		for(int i=0; i<list->roomCount; i++) {
 			flib_roomlist_room_destroy(list->rooms[i]);
 		}
+		free(list->rooms);
 		free(list);
 	}
 }
 
-static flib_roomlist_room *fillRoomFromParams(char **params) {
-	flib_roomlist_room *result = NULL;
-	flib_roomlist_room *tmpRoom = flib_calloc(1, sizeof(flib_roomlist_room));
+static flib_room *fillRoomFromParams(char **params) {
+	flib_room *result = NULL;
+	flib_room *tmpRoom = flib_calloc(1, sizeof(flib_room));
 	if(tmpRoom) {
 		tmpRoom->inProgress = !strcmp(params[0], "True");
 		tmpRoom->name = flib_strdupnull(params[1]);
@@ -52,16 +53,26 @@
 	return result;
 }
 
+GENERATE_STATIC_LIST_INSERT(insertRoom, flib_room*)
+GENERATE_STATIC_LIST_DELETE(deleteRoom, flib_room*)
+
+static int findRoom(const flib_roomlist *list, const char *name) {
+	for(int i=0; i<list->roomCount; i++) {
+		if(!strcmp(name, list->rooms[i]->name)) {
+			return i;
+		}
+	}
+	return -1;
+}
+
 int flib_roomlist_add(flib_roomlist *list, char **params) {
 	int result = -1;
 	if(!list || !params) {
 		flib_log_e("null parameter in flib_roomlist_add");
 	} else {
-		flib_roomlist_room *tmpRoom = fillRoomFromParams(params);
+		flib_room *tmpRoom = fillRoomFromParams(params);
 		if(tmpRoom) {
-			flib_roomlist_room **rooms = flib_list_insert(list->rooms, &list->roomCount, sizeof(*list->rooms), &tmpRoom, 0);
-			if(rooms) {
-				list->rooms = rooms;
+			if(!insertRoom(&list->rooms, &list->roomCount, tmpRoom, 0)) {
 				tmpRoom = NULL;
 				result = 0;
 			}
@@ -71,13 +82,23 @@
 	return result;
 }
 
-static int findRoom(flib_roomlist *list, const char *name) {
-	for(int i=0; i<list->roomCount; i++) {
-		if(!strcmp(name, list->rooms[i]->name)) {
-			return i;
+int flib_roomlist_delete(flib_roomlist *list, const char *name) {
+	int result = -1;
+	if(!list || !name) {
+		flib_log_e("null parameter in flib_roomlist_delete");
+	} else {
+		int roomid = findRoom(list, name);
+		if(roomid<0) {
+			flib_log_w("Attempt to delete unknown room %s", name);
+		} else {
+			flib_room *room = list->rooms[roomid];
+			if(!deleteRoom(&list->rooms, &list->roomCount, roomid)) {
+				flib_roomlist_room_destroy(room);
+				result = 0;
+			}
 		}
 	}
-	return -1;
+	return result;
 }
 
 int flib_roomlist_update(flib_roomlist *list, const char *name, char **params) {
@@ -85,7 +106,7 @@
 	if(!list || !name || !params) {
 		flib_log_e("null parameter in flib_roomlist_update");
 	} else {
-		flib_roomlist_room *tmpRoom = fillRoomFromParams(params);
+		flib_room *tmpRoom = fillRoomFromParams(params);
 		int roomid = findRoom(list, name);
 		if(tmpRoom && roomid>=0) {
 			flib_roomlist_room_destroy(list->rooms[roomid]);
@@ -98,8 +119,8 @@
 	return result;
 }
 
-flib_roomlist_room *flib_roomlist_find(flib_roomlist *list, const char *name) {
-	flib_roomlist_room *result = NULL;
+flib_room *flib_roomlist_find(const flib_roomlist *list, const char *name) {
+	flib_room *result = NULL;
 	if(!list || !name) {
 		flib_log_e("null parameter in flib_roomlist_find");
 	} else {
@@ -123,24 +144,3 @@
 		list->roomCount = 0;
 	}
 }
-
-int flib_roomlist_delete(flib_roomlist *list, const char *name) {
-	int result = -1;
-	if(!list || !name) {
-		flib_log_e("null parameter in flib_roomlist_delete");
-	} else {
-		int roomid = findRoom(list, name);
-		if(roomid<0) {
-			flib_log_w("Attempt to delete unknown room %s", name);
-		} else {
-			flib_roomlist_room *room = list->rooms[roomid];
-			flib_roomlist_room **rooms = flib_list_delete(list->rooms, &list->roomCount, sizeof(*list->rooms), roomid);
-			if(rooms || list->roomCount==0) {
-				list->rooms = rooms;
-				flib_roomlist_room_destroy(room);
-				result = 0;
-			}
-		}
-	}
-	return result;
-}
--- a/project_files/frontlib/model/roomlist.h	Mon Jun 25 15:21:18 2012 +0200
+++ b/project_files/frontlib/model/roomlist.h	Wed Jun 27 18:02:45 2012 +0200
@@ -16,11 +16,11 @@
     char *map;			// This is either a map name, or one of +rnd+, +maze+ or +drawn+.
     char *scheme;
     char *weapons;
-} flib_roomlist_room;
+} flib_room;
 
 typedef struct {
 	int roomCount;
-	flib_roomlist_room **rooms;
+	flib_room **rooms;
 } flib_roomlist;
 
 flib_roomlist *flib_roomlist_create();
@@ -45,7 +45,7 @@
 /**
  * Returns the room with the name [name] from the list if it exists, NULL otherwise
  */
-flib_roomlist_room *flib_roomlist_find(flib_roomlist *list, const char *name);
+flib_room *flib_roomlist_find(const flib_roomlist *list, const char *name);
 
 /**
  * Removes all rooms from the list
--- a/project_files/frontlib/model/schemelist.c	Mon Jun 25 15:21:18 2012 +0200
+++ b/project_files/frontlib/model/schemelist.c	Wed Jun 27 18:02:45 2012 +0200
@@ -16,6 +16,7 @@
 		for(int i=0; i<list->schemeCount; i++) {
 			flib_cfg_release(list->schemes[i]);
 		}
+		free(list->schemes);
 		free(list);
 	}
 }
@@ -192,29 +193,28 @@
 	return NULL;
 }
 
+GENERATE_STATIC_LIST_INSERT(insertScheme, flib_cfg*)
+GENERATE_STATIC_LIST_DELETE(deleteScheme, flib_cfg*)
+
 int flib_schemelist_insert(flib_schemelist *list, flib_cfg *cfg, int pos) {
-	flib_cfg **changedList = flib_list_insert(list->schemes, &list->schemeCount, sizeof(*list->schemes), &cfg, pos);
-	if(changedList) {
-		list->schemes = changedList;
+	if(!list) {
+		flib_log_e("Invalid parameter in flib_schemelist_insert");
+	} else if(!insertScheme(&list->schemes, &list->schemeCount, cfg, pos)) {
 		flib_cfg_retain(cfg);
 		return 0;
-	} else {
-		return -1;
 	}
+	return -1;
 }
 
 int flib_schemelist_delete(flib_schemelist *list, int pos) {
-	int result = -1;
-	if(!list || pos<0 || pos>=list->schemeCount) {
+	if(!list) {
 		flib_log_e("Invalid parameter in flib_schemelist_delete");
 	} else {
 		flib_cfg *elem = list->schemes[pos];
-		flib_cfg **changedList = flib_list_delete(list->schemes, &list->schemeCount, sizeof(*list->schemes), pos);
-		if(changedList || list->schemeCount==0) {
-			list->schemes = changedList;
+		if(!deleteScheme(&list->schemes, &list->schemeCount, pos)) {
 			flib_cfg_release(elem);
-			result = 0;
+			return 0;
 		}
 	}
-	return result;
+	return -1;
 }
--- a/project_files/frontlib/model/team.c	Mon Jun 25 15:21:18 2012 +0200
+++ b/project_files/frontlib/model/team.c	Wed Jun 27 18:02:45 2012 +0200
@@ -231,6 +231,14 @@
 	}
 }
 
+void flib_team_set_health(flib_team *team, int health) {
+	if(team) {
+		for(int i=0; i<HEDGEHOGS_PER_TEAM; i++) {
+			team->hogs[i].initialHealth = health;
+		}
+	}
+}
+
 char *strdupWithError(const char *in, bool *error) {
 	char *out = flib_strdupnull(in);
 	if(in && !out) {
@@ -239,7 +247,7 @@
 	return out;
 }
 
-flib_team *flib_team_copy(flib_team *team) {
+flib_team *flib_team_copy(const flib_team *team) {
 	flib_team *result = NULL;
 	if(team) {
 		flib_team *tmpTeam = flib_team_retain(flib_calloc(1, sizeof(flib_team)));
@@ -282,7 +290,7 @@
 			tmpTeam->wins = team->wins;
 			tmpTeam->campaignProgress = team->campaignProgress;
 
-			tmpTeam->color = team->color;
+			tmpTeam->colorIndex = team->colorIndex;
 			tmpTeam->hogsInGame = team->hogsInGame;
 			tmpTeam->remoteDriven = team->remoteDriven;
 
--- a/project_files/frontlib/model/team.h	Mon Jun 25 15:21:18 2012 +0200
+++ b/project_files/frontlib/model/team.h	Wed Jun 27 18:02:45 2012 +0200
@@ -62,7 +62,7 @@
 	int campaignProgress;
 
 	// Transient settings used in game setup
-	uint32_t color;
+	int colorIndex;		// Index into a color table
 	int hogsInGame;
 	bool remoteDriven;
 	char *ownerName;
@@ -98,6 +98,11 @@
 void flib_team_set_weaponset(flib_team *team, flib_weaponset *set);
 
 /**
+ * Set the same initial health for every hog.
+ */
+void flib_team_set_health(flib_team *team, int health);
+
+/**
  * Increase the reference count of the object. Call this if you store a pointer to it somewhere.
  * Returns the parameter.
  */
@@ -113,6 +118,6 @@
  * The referenced weaponsets are not copied, so the new
  * team references the same weaponsets.
  */
-flib_team *flib_team_copy(flib_team *team);
+flib_team *flib_team_copy(const flib_team *team);
 
 #endif /* TEAM_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/teamlist.c	Wed Jun 27 18:02:45 2012 +0200
@@ -0,0 +1,87 @@
+#include "teamlist.h"
+
+#include "../util/util.h"
+#include "../util/list.h"
+#include "../util/logging.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+flib_teamlist *flib_teamlist_create() {
+	return flib_calloc(1, sizeof(flib_teamlist));
+}
+
+void flib_teamlist_destroy(flib_teamlist *list) {
+	if(list) {
+		for(int i=0; i<list->teamCount; i++) {
+			flib_team_release(list->teams[i]);
+		}
+		free(list->teams);
+		free(list);
+	}
+}
+
+GENERATE_STATIC_LIST_INSERT(insertTeam, flib_team*)
+GENERATE_STATIC_LIST_DELETE(deleteTeam, flib_team*)
+
+static int findTeam(const flib_teamlist *list, const char *name) {
+	for(int i=0; i<list->teamCount; i++) {
+		if(!strcmp(name, list->teams[i]->name)) {
+			return i;
+		}
+	}
+	return -1;
+}
+
+int flib_teamlist_insert(flib_teamlist *list, flib_team *team, int pos) {
+	if(!list || !team) {
+		flib_log_e("null parameter in flib_teamlist_insert");
+	} else if(!insertTeam(&list->teams, &list->teamCount, team, pos)) {
+		flib_team_retain(team);
+		return 0;
+	}
+	return -1;
+}
+
+int flib_teamlist_delete(flib_teamlist *list, const char *name) {
+	int result = -1;
+	if(!list || !name) {
+		flib_log_e("null parameter in flib_teamlist_delete");
+	} else {
+		int itemid = findTeam(list, name);
+		if(itemid>=0) {
+			flib_team *team = list->teams[itemid];
+			if(!deleteTeam(&list->teams, &list->teamCount, itemid)) {
+				flib_team_release(team);
+				result = 0;
+			}
+		}
+	}
+	return result;
+}
+
+flib_team *flib_teamlist_find(const flib_teamlist *list, const char *name) {
+	flib_team *result = NULL;
+	if(!list || !name) {
+		flib_log_e("null parameter in flib_teamlist_find");
+	} else {
+		int itemid = findTeam(list, name);
+		if(itemid>=0) {
+			result = list->teams[itemid];
+		}
+	}
+	return result;
+}
+
+void flib_teamlist_clear(flib_teamlist *list) {
+	if(!list) {
+		flib_log_e("null parameter in flib_teamlist_clear");
+	} else {
+		for(int i=0; i<list->teamCount; i++) {
+			flib_team_release(list->teams[i]);
+		}
+		free(list->teams);
+		list->teams = NULL;
+		list->teamCount = 0;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/teamlist.h	Wed Jun 27 18:02:45 2012 +0200
@@ -0,0 +1,36 @@
+#ifndef TEAMLIST_H_
+#define TEAMLIST_H_
+
+#include "team.h"
+
+typedef struct {
+	int teamCount;
+	flib_team **teams;
+} flib_teamlist;
+
+flib_teamlist *flib_teamlist_create();
+
+void flib_teamlist_destroy(flib_teamlist *list);
+
+/**
+ * Insert a team into the list. Returns 0 on success.
+ */
+int flib_teamlist_insert(flib_teamlist *list, flib_team *team, int pos);
+
+/**
+ * Delete the item with the name [name] from the list.
+ * Returns 0 on success.
+ */
+int flib_teamlist_delete(flib_teamlist *list, const char *name);
+
+/**
+ * Returns the team with the name [name] from the list if it exists, NULL otherwise
+ */
+flib_team *flib_teamlist_find(const flib_teamlist *list, const char *name);
+
+/**
+ * Removes all items from the list and frees "teams".
+ */
+void flib_teamlist_clear(flib_teamlist *list);
+
+#endif
--- a/project_files/frontlib/model/weapon.c	Mon Jun 25 15:21:18 2012 +0200
+++ b/project_files/frontlib/model/weapon.c	Wed Jun 27 18:02:45 2012 +0200
@@ -72,11 +72,28 @@
 	}
 }
 
+flib_weaponset *flib_weaponset_copy(const flib_weaponset *weaponset) {
+	if(!weaponset) {
+		return NULL;
+	}
+
+	flib_weaponset *result = flib_weaponset_create(weaponset->name);
+	if(result) {
+		memcpy(result->loadout, weaponset->loadout, WEAPONS_COUNT+1);
+		memcpy(result->crateprob, weaponset->crateprob, WEAPONS_COUNT+1);
+		memcpy(result->delay, weaponset->delay, WEAPONS_COUNT+1);
+		memcpy(result->crateammo, weaponset->crateammo, WEAPONS_COUNT+1);
+	}
+
+	return result;
+}
+
 static void flib_weaponsetlist_destroy(flib_weaponsetlist *list) {
 	if(list) {
 		for(int i=0; i<list->weaponsetCount; i++) {
 			flib_weaponset_release(list->weaponsets[i]);
 		}
+		free(list->weaponsets);
 		free(list);
 	}
 }
@@ -196,31 +213,30 @@
 	return flib_weaponsetlist_retain(flib_calloc(1, sizeof(flib_weaponsetlist)));
 }
 
+GENERATE_STATIC_LIST_INSERT(insertWeaponset, flib_weaponset*)
+GENERATE_STATIC_LIST_DELETE(deleteWeaponset, flib_weaponset*)
+
 int flib_weaponsetlist_insert(flib_weaponsetlist *list, flib_weaponset *set, int pos) {
-	flib_weaponset **changedList = flib_list_insert(list->weaponsets, &list->weaponsetCount, sizeof(*list->weaponsets), &set, pos);
-	if(changedList) {
-		list->weaponsets = changedList;
+	if(!list) {
+		flib_log_e("Invalid parameter in flib_weaponsetlist_insert");
+	} else if(!insertWeaponset(&list->weaponsets, &list->weaponsetCount, set, pos)) {
 		flib_weaponset_retain(set);
 		return 0;
-	} else {
-		return -1;
 	}
+	return -1;
 }
 
 int flib_weaponsetlist_delete(flib_weaponsetlist *list, int pos) {
-	int result = -1;
-	if(!list || pos<0 || pos>=list->weaponsetCount) {
+	if(!list) {
 		flib_log_e("Invalid parameter in flib_weaponsetlist_delete");
 	} else {
 		flib_weaponset *elem = list->weaponsets[pos];
-		flib_weaponset **changedList = flib_list_delete(list->weaponsets, &list->weaponsetCount, sizeof(*list->weaponsets), pos);
-		if(changedList || list->weaponsetCount==0) {
-			list->weaponsets = changedList;
+		if(!deleteWeaponset(&list->weaponsets, &list->weaponsetCount, pos)) {
 			flib_weaponset_release(elem);
-			result = 0;
+			return 0;
 		}
 	}
-	return result;
+	return -1;
 }
 
 flib_weaponsetlist *flib_weaponsetlist_retain(flib_weaponsetlist *list) {
--- a/project_files/frontlib/model/weapon.h	Mon Jun 25 15:21:18 2012 +0200
+++ b/project_files/frontlib/model/weapon.h	Wed Jun 27 18:02:45 2012 +0200
@@ -44,6 +44,8 @@
  */
 void flib_weaponset_release(flib_weaponset *weaponset);
 
+flib_weaponset *flib_weaponset_copy(const flib_weaponset *weaponset);
+
 /**
  * Create a weaponset from an ammostring. This format is used both in the ini files
  * and in the net protocol.
--- a/project_files/frontlib/net/netbase.c	Mon Jun 25 15:21:18 2012 +0200
+++ b/project_files/frontlib/net/netbase.c	Wed Jun 27 18:02:45 2012 +0200
@@ -105,8 +105,7 @@
 		return 0;
 	} else {
 		int size = flib_socket_nbrecv(net->sock, buffer, sizeof(buffer));
-		if(size>=0) {
-			flib_vector_append(net->readBuffer, buffer, size);
+		if(size>=0 && !flib_vector_append(net->readBuffer, buffer, size)) {
 			return size;
 		} else {
 			flib_socket_close(net->sock);
--- a/project_files/frontlib/net/netconn.c	Mon Jun 25 15:21:18 2012 +0200
+++ b/project_files/frontlib/net/netconn.c	Wed Jun 27 18:02:45 2012 +0200
@@ -27,31 +27,44 @@
 #include "../model/roomlist.h"
 #include "../md5/md5.h"
 #include "../base64/base64.h"
+#include "../model/mapcfg.h"
 
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
 #include <ctype.h>
 
-flib_netconn *flib_netconn_create(const char *playerName, flib_cfg_meta *metacfg, const char *host, uint16_t port) {
+flib_netconn *flib_netconn_create(const char *playerName, flib_cfg_meta *metacfg, const char *dataDirPath, const char *host, uint16_t port) {
 	flib_netconn *result = NULL;
 	if(!playerName || !metacfg || !host) {
 		flib_log_e("null parameter in flib_netconn_create");
 	} else {
 		flib_netconn *newConn = flib_calloc(1, sizeof(flib_netconn));
 		if(newConn) {
+			newConn->netBase = flib_netbase_create(host, port);
+			newConn->playerName = flib_strdupnull(playerName);
+			newConn->dataDirPath = flib_strdupnull(dataDirPath);
+
 			newConn->netconnState = NETCONN_STATE_CONNECTING;
 			newConn->isAdmin = false;
+			newConn->metaCfg = flib_cfg_meta_retain(metacfg);
+			newConn->roomList.roomCount = 0;
+			newConn->roomList.rooms = NULL;
+
 			newConn->isChief = false;
-			newConn->metaCfg = flib_cfg_meta_retain(metacfg);
-			newConn->roomList = flib_roomlist_create();
 			newConn->map = flib_map_create_named("", "NoSuchMap");
+			newConn->pendingTeamlist.teamCount = 0;
+			newConn->pendingTeamlist.teams = NULL;
+			newConn->teamlist.teamCount = 0;
+			newConn->teamlist.teams = NULL;
+			newConn->scheme = NULL;
+			newConn->script = NULL;
+			newConn->weaponset = NULL;
+
 			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 && newConn->roomList) {
+			netconn_clearCallbacks(newConn);
+			if(newConn->netBase && newConn->playerName && newConn->dataDirPath && newConn->map) {
 				result = newConn;
 				newConn = NULL;
 			}
@@ -69,14 +82,23 @@
 			 * and we delay the actual destruction. We ensure no further callbacks will be
 			 * sent to prevent surprises.
 			 */
-			clearCallbacks(conn);
+			netconn_clearCallbacks(conn);
 			conn->destroyRequested = true;
 		} else {
 			flib_netbase_destroy(conn->netBase);
+			free(conn->playerName);
+			free(conn->dataDirPath);
+
 			flib_cfg_meta_release(conn->metaCfg);
-			flib_roomlist_destroy(conn->roomList);
+			flib_roomlist_clear(&conn->roomList);
+
 			flib_map_release(conn->map);
-			free(conn->playerName);
+			flib_teamlist_clear(&conn->pendingTeamlist);
+			flib_teamlist_clear(&conn->teamlist);
+			flib_cfg_release(conn->scheme);
+			free(conn->script);
+			flib_weaponset_release(conn->weaponset);
+
 			free(conn);
 		}
 	}
@@ -87,7 +109,7 @@
 	if(!conn) {
 		flib_log_e("null parameter in flib_netconn_get_roomlist");
 	} else {
-		result = conn->roomList;
+		result = &conn->roomList;
 	}
 	return result;
 }
@@ -102,16 +124,92 @@
 	return result;
 }
 
-void leaveRoom(flib_netconn *conn) {
+void netconn_leaveRoom(flib_netconn *conn) {
 	conn->netconnState = NETCONN_STATE_LOBBY;
 	conn->isChief = false;
-	flib_map *map = flib_map_create_named("", "NoSuchMap");
-	if(map) {
+	flib_map_release(conn->map);
+	conn->map = flib_map_create_named("", "NoSuchMap");
+	flib_teamlist_clear(&conn->pendingTeamlist);
+	flib_teamlist_clear(&conn->teamlist);
+	flib_cfg_release(conn->scheme);
+	conn->scheme = NULL;
+	free(conn->script);
+	conn->script = NULL;
+	flib_weaponset_release(conn->weaponset);
+	conn->weaponset = NULL;
+}
+
+bool flib_netconn_is_in_room_context(flib_netconn *conn) {
+	return conn && (conn->netconnState == NETCONN_STATE_ROOM || conn->netconnState == NETCONN_STATE_INGAME);
+}
+
+void netconn_setMap(flib_netconn *conn, const flib_map *map) {
+	flib_map *copy = flib_map_copy(map);
+	if(copy) {
 		flib_map_release(conn->map);
-		conn->map = map;
+		conn->map = copy;
+	}
+}
+
+void netconn_setWeaponset(flib_netconn *conn, const flib_weaponset *weaponset) {
+	flib_weaponset *copy = flib_weaponset_copy(weaponset);
+	if(copy) {
+		flib_weaponset_release(conn->weaponset);
+		conn->weaponset = copy;
+	}
+}
+
+void netconn_setScript(flib_netconn *conn, const char *script) {
+	char *copy = flib_strdupnull(script);
+	if(copy) {
+		free(conn->script);
+		conn->script = copy;
+	}
+}
+
+void netconn_setScheme(flib_netconn *conn, const flib_cfg *scheme) {
+	flib_cfg *copy = flib_cfg_copy(scheme);
+	if(copy) {
+		flib_cfg_release(conn->scheme);
+		conn->scheme = copy;
+	}
+}
+
+flib_gamesetup *flib_netconn_create_gameSetup(flib_netconn *conn) {
+	flib_gamesetup *result = NULL;
+	if(!conn) {
+		flib_log_e("null parameter in flib_netconn_create_gameSetup");
 	} else {
-		flib_log_e("Error resetting netconn.map");
+		if(conn->teamlist.teamCount==0 || !conn->scheme || !conn->weaponset) {
+			flib_log_e("Incomplete room state to create game setup.");
+		} else {
+			result = flib_calloc(1, sizeof(flib_gamesetup));
+			if(result) {
+				result->gamescheme = flib_cfg_copy(conn->scheme);
+				result->map = flib_map_copy(conn->map);
+				result->script = flib_strdupnull(conn->script);
+				result->teamlist = flib_teamlist_create();
+				for(int i=0; i<conn->teamlist.teamCount; i++) {
+					flib_team *copy = flib_team_copy(conn->teamlist.teams[i]);
+					if(copy) {
+						flib_team_set_weaponset(copy, conn->weaponset);
+						flib_team_set_health(copy, conn->scheme->settings[2]); // TODO by name
+						flib_teamlist_insert(result->teamlist, copy, result->teamlist->teamCount);
+					}
+					flib_team_release(copy);
+				}
+				if(result->map->mapgen == MAPGEN_NAMED && result->map->name) {
+					flib_mapcfg mapcfg;
+					if(!flib_mapcfg_read(conn->dataDirPath, result->map->name, &mapcfg)) {
+						free(result->map->theme);
+						result->map->theme = flib_strdupnull(mapcfg.theme);
+					}
+				}
+				// TODO handle errors
+			}
+		}
 	}
+	return result;
 }
 
 static void flib_netconn_wrappedtick(flib_netconn *conn) {
@@ -184,9 +282,9 @@
 	        if(netmsg->partCount % 8 != 1) {
 	        	flib_log_w("Net: Malformed ROOMS message");
 	        } else {
-	        	flib_roomlist_clear(conn->roomList);
+	        	flib_roomlist_clear(&conn->roomList);
 	        	for(int i=1; i<netmsg->partCount; i+=8) {
-	        		if(flib_roomlist_add(conn->roomList, netmsg->parts+i)) {
+	        		if(flib_roomlist_add(&conn->roomList, netmsg->parts+i)) {
 	        			flib_log_e("Error adding room to list in ROOMS message");
 	        		}
 	        	}
@@ -233,7 +331,7 @@
 					switch(flags[i]) {
 					case 'r':
 						for(int j = 2; j < netmsg->partCount; ++j) {
-							conn->onReadyStateCb(conn->onReadyStateCtx, netmsg->parts[i], setFlag);
+							conn->onReadyStateCb(conn->onReadyStateCtx, netmsg->parts[j], setFlag);
 						}
 						break;
 					default:
@@ -243,31 +341,36 @@
 				}
 	        }
 	    } else if (!strcmp(cmd, "ADD_TEAM")) {
-	        if(netmsg->partCount != 24) {
+	        if(netmsg->partCount != 24 || !flib_netconn_is_in_room_context(conn)) {
 	            flib_log_w("Net: Bad ADD_TEAM message");
 	        } else {
 	        	flib_team *team = flib_team_from_netmsg(netmsg->parts+1);
-	        	if(!team) {
+	        	flib_team *teamcopy = flib_team_from_netmsg(netmsg->parts+1);
+	        	if(!team || !teamcopy) {
 					conn->netconnState = NETCONN_STATE_DISCONNECTED;
 					conn->onDisconnectedCb(conn->onDisconnectedCtx, NETCONN_DISCONNECT_INTERNAL_ERROR, "Internal error");
 					exit = true;
 	        	} else {
 	        		team->remoteDriven = true;
+	        		teamcopy->remoteDriven = true;
+	        		flib_teamlist_insert(&conn->teamlist, teamcopy, conn->teamlist.teamCount);
 	        		conn->onTeamAddCb(conn->onTeamAddCtx, team);
 	        	}
 	        	flib_team_release(team);
+	        	flib_team_release(teamcopy);
 	        }
 	    } else if (!strcmp(cmd, "REMOVE_TEAM")) {
-	        if(netmsg->partCount != 2) {
+	        if(netmsg->partCount != 2 || !flib_netconn_is_in_room_context(conn)) {
 	            flib_log_w("Net: Bad REMOVETEAM message");
 	        } else {
+	        	flib_teamlist_delete(&conn->teamlist, netmsg->parts[1]);
 	        	conn->onTeamDeleteCb(conn->onTeamDeleteCtx, netmsg->parts[1]);
 	        }
 	    } else if(!strcmp(cmd, "ROOMABANDONED")) {
-	    	leaveRoom(conn);
+	    	netconn_leaveRoom(conn);
 	        conn->onLeaveRoomCb(conn->onLeaveRoomCtx, NETCONN_ROOMLEAVE_ABANDONED, "Room destroyed");
 	    } else if(!strcmp(cmd, "KICKED")) {
-	    	leaveRoom(conn);
+	    	netconn_leaveRoom(conn);
 	    	conn->onLeaveRoomCb(conn->onLeaveRoomCtx, NETCONN_ROOMLEAVE_KICKED, "You got kicked");
 	    } else if(!strcmp(cmd, "JOINED")) {
 	        if(netmsg->partCount < 2) {
@@ -312,19 +415,19 @@
 	    } else if(!strcmp(cmd, "ROOM") && netmsg->partCount >= 2) {
 	    	const char *subcmd = netmsg->parts[1];
 	    	if(!strcmp(subcmd, "ADD") && netmsg->partCount == 10) {
-	    		if(flib_roomlist_add(conn->roomList, netmsg->parts+2)) {
+	    		if(flib_roomlist_add(&conn->roomList, netmsg->parts+2)) {
 	    			flib_log_e("Error adding new room to list");
 	    		} else {
-	    			conn->onRoomAddCb(conn->onRoomAddCtx, conn->roomList->rooms[0]);
+	    			conn->onRoomAddCb(conn->onRoomAddCtx, conn->roomList.rooms[0]);
 	    		}
 			} else if(!strcmp(subcmd, "UPD") && netmsg->partCount == 11) {
-	    		if(flib_roomlist_update(conn->roomList, netmsg->parts[2], netmsg->parts+3)) {
+	    		if(flib_roomlist_update(&conn->roomList, netmsg->parts[2], netmsg->parts+3)) {
 	    			flib_log_e("Error updating room in list");
 	    		} else {
-	    			conn->onRoomUpdateCb(conn->onRoomUpdateCtx, netmsg->parts[2], flib_roomlist_find(conn->roomList, netmsg->parts[2]));
+	    			conn->onRoomUpdateCb(conn->onRoomUpdateCtx, netmsg->parts[2], flib_roomlist_find(&conn->roomList, netmsg->parts[2]));
 	    		}
 			} else if(!strcmp(subcmd, "DEL") && netmsg->partCount == 3) {
-	    		if(flib_roomlist_delete(conn->roomList, netmsg->parts[2])) {
+	    		if(flib_roomlist_delete(&conn->roomList, netmsg->parts[2])) {
 	    			flib_log_e("Error deleting room from list");
 	    		} else {
 	    			conn->onRoomDeleteCb(conn->onRoomDeleteCtx, netmsg->parts[2]);
@@ -340,6 +443,7 @@
 	        }
 	    } else if (!strcmp(cmd, "RUN_GAME")) {
 	        conn->netconnState = NETCONN_STATE_INGAME;
+	        // TODO send along the config
 	        conn->onRunGameCb(conn->onRunGameCtx);
 	    } else if (!strcmp(cmd, "ASKPASSWORD")) {
 	    	conn->onPasswordRequestCb(conn->onPasswordRequestCtx, conn->playerName);
@@ -358,19 +462,27 @@
 				}
 	        }
 	    } else if (!strcmp(cmd, "TEAM_ACCEPTED")) {
-	        if (netmsg->partCount != 2) {
+	        if (netmsg->partCount != 2 || !flib_netconn_is_in_room_context(conn)) {
 	            flib_log_w("Net: Bad TEAM_ACCEPTED message");
 	        } else {
+	        	flib_team *team = flib_teamlist_find(&conn->pendingTeamlist, netmsg->parts[1]);
+	        	if(team) {
+	        		flib_teamlist_insert(&conn->teamlist, team, conn->teamlist.teamCount);
+	        		flib_teamlist_delete(&conn->pendingTeamlist, netmsg->parts[1]);
+	        	} else {
+	        		flib_log_e("Team accepted that was not requested: %s", netmsg->parts[1]);
+	        	}
 	        	conn->onTeamAcceptedCb(conn->onTeamAcceptedCtx, netmsg->parts[1]);
 	        }
 	    } else if (!strcmp(cmd, "CFG")) {
-	        if(netmsg->partCount < 3) {
+	        if(netmsg->partCount < 3 || !flib_netconn_is_in_room_context(conn)) {
 	            flib_log_w("Net: Bad CFG message");
 	        } else {
 	        	const char *subcmd = netmsg->parts[1];
 				if(!strcmp(subcmd, "SCHEME") && netmsg->partCount == conn->metaCfg->modCount + conn->metaCfg->settingCount + 3) {
 					flib_cfg *cfg = flib_netmsg_to_cfg(conn->metaCfg, netmsg->parts+2);
 					if(cfg) {
+						netconn_setScheme(conn, cfg);
 						conn->onCfgSchemeCb(conn->onCfgSchemeCtx, cfg);
 					} else {
 						flib_log_e("Error processing CFG SCHEME message");
@@ -379,12 +491,12 @@
 				} else if(!strcmp(subcmd, "FULLMAPCONFIG") && netmsg->partCount == 7) {
 					flib_map *map = flib_netmsg_to_map(netmsg->parts+2);
 					if(map) {
-						flib_map_release(conn->map);
-						conn->map = map;
+						netconn_setMap(conn, map);
 						conn->onMapChangedCb(conn->onMapChangedCtx, conn->map, NETCONN_MAPCHANGE_FULL);
 					} else {
 						flib_log_e("Error processing CFG FULLMAPCONFIG message");
 					}
+					flib_map_release(map);
 				} else if(!strcmp(subcmd, "MAP") && netmsg->partCount == 3) {
 					char *mapname = flib_strdupnull(netmsg->parts[2]);
 					if(mapname) {
@@ -423,8 +535,8 @@
 					conn->onMapChangedCb(conn->onMapChangedCtx, conn->map, NETCONN_MAPCHANGE_MAZE_SIZE);
 				} else if(!strcmp(subcmd, "DRAWNMAP") && netmsg->partCount == 3) {
 					size_t drawnMapSize = 0;
-					uint8_t *drawnMapData = flib_netmsg_to_drawnmapdata(&drawnMapSize, netmsg->parts[2]);
-					if(drawnMapData) {
+					uint8_t *drawnMapData = NULL;
+					if(!flib_netmsg_to_drawnmapdata(netmsg->parts[2], &drawnMapData, &drawnMapSize)) {
 						free(conn->map->drawData);
 						conn->map->drawData = drawnMapData;
 						conn->map->drawDataSize = drawnMapSize;
@@ -433,10 +545,12 @@
 						flib_log_e("Error processing CFG DRAWNMAP message");
 					}
 				} else if(!strcmp(subcmd, "SCRIPT") && netmsg->partCount == 3) {
+					netconn_setScript(conn, netmsg->parts[2]);
 					conn->onScriptChangedCb(conn->onScriptChangedCtx, netmsg->parts[2]);
 				} else if(!strcmp(subcmd, "AMMO") && netmsg->partCount == 4) {
 					flib_weaponset *weapons = flib_weaponset_from_ammostring(netmsg->parts[2], netmsg->parts[3]);
 					if(weapons) {
+						netconn_setWeaponset(conn, weapons);
 						conn->onWeaponsetChangedCb(conn->onWeaponsetChangedCtx, weapons);
 					} else {
 						flib_log_e("Error processing CFG AMMO message");
@@ -447,23 +561,35 @@
 				}
 	        }
 	    } else if (!strcmp(cmd, "HH_NUM")) {
-	        if (netmsg->partCount != 3) {
+	        if (netmsg->partCount != 3 || !flib_netconn_is_in_room_context(conn)) {
 	            flib_log_w("Net: Bad HH_NUM message");
 	        } else {
 	        	int hogs = atoi(netmsg->parts[2]);
 	        	if(hogs<=0 || hogs>HEDGEHOGS_PER_TEAM) {
 	        		flib_log_w("Net: Bad HH_NUM message: %s hogs", netmsg->parts[2]);
 	        	} else {
+	        		flib_team *team = flib_teamlist_find(&conn->teamlist, netmsg->parts[1]);
+	        		if(team) {
+	        			team->hogsInGame = hogs;
+	        		} else {
+	        			flib_log_e("HH_NUM message for unknown team %s", netmsg->parts[1]);
+	        		}
 	        		conn->onHogCountChangedCb(conn->onHogCountChangedCtx, netmsg->parts[1], hogs);
 	        	}
 	        }
 	    } else if (!strcmp(cmd, "TEAM_COLOR")) {
-	        if (netmsg->partCount != 3) {
+	        if (netmsg->partCount != 3 || !flib_netconn_is_in_room_context(conn)) {
 	            flib_log_w("Net: Bad TEAM_COLOR message");
 	        } else {
 	        	long color;
-	        	if(sscanf(netmsg->parts[2], "#%lx", &color)) {
-	        		conn->onTeamColorChangedCb(conn->onTeamColorChangedCtx, netmsg->parts[1], (uint32_t)color);
+	        	if(sscanf(netmsg->parts[2], "%lu", &color) && color>=0 && color<flib_teamcolor_defaults_len) {
+	        		flib_team *team = flib_teamlist_find(&conn->teamlist, netmsg->parts[1]);
+	        		if(team) {
+	        			team->colorIndex = color;
+	        		} else {
+	        			flib_log_e("TEAM_COLOR message for unknown team %s", netmsg->parts[1]);
+	        		}
+	        		conn->onTeamColorChangedCb(conn->onTeamColorChangedCtx, netmsg->parts[1], color);
 	        	} else {
 	        		flib_log_w("Net: Bad TEAM_COLOR message: Color %s", netmsg->parts[2]);
 	        	}
@@ -477,7 +603,7 @@
 					size_t outlen;
 					bool ok = base64_decode_alloc(netmsg->parts[i], strlen(netmsg->parts[i]), &out, &outlen);
 					if(ok && outlen) {
-						conn->onEngineMessageCb(conn->onEngineMessageCtx, out, outlen);
+						conn->onEngineMessageCb(conn->onEngineMessageCtx, (uint8_t*)out, outlen);
 					} else {
 						flib_log_e("Net: Malformed engine message: %s", netmsg->parts[i]);
 					}
--- a/project_files/frontlib/net/netconn.h	Mon Jun 25 15:21:18 2012 +0200
+++ b/project_files/frontlib/net/netconn.h	Wed Jun 27 18:02:45 2012 +0200
@@ -40,7 +40,11 @@
 // TODO: Order of functions, and match the order in netconn.c
 typedef struct _flib_netconn flib_netconn;
 
-flib_netconn *flib_netconn_create(const char *playerName, flib_cfg_meta *metacfg, const char *host, uint16_t port);
+/**
+ * Create a new netplay connection with these parameters.
+ * The path to the data directory must end with a path delimiter (e.g. C:\Games\Hedgewars\Data\)
+ */
+flib_netconn *flib_netconn_create(const char *playerName, flib_cfg_meta *metacfg, const char *dataDirPath, const char *host, uint16_t port);
 void flib_netconn_destroy(flib_netconn *conn);
 
 /**
@@ -62,6 +66,20 @@
 bool flib_netconn_is_chief(flib_netconn *conn);
 
 /**
+ * Are you in the context of a room, i.e. either in room or ingame state?
+ */
+bool flib_netconn_is_in_room_context(flib_netconn *conn);
+
+/**
+ * Generate a game setup from the current room state.
+ * Returns NULL if the room state does not contain enough information
+ * for a complete game setup, or if an error occurs.
+ *
+ * The new gamesetup must be destroyed TODO function for that...
+ */
+flib_gamesetup *flib_netconn_create_gameSetup(flib_netconn *conn);
+
+/**
  * quitmsg may be null
  */
 int flib_netconn_send_quit(flib_netconn *conn, const char *quitmsg);
@@ -146,7 +164,7 @@
  * Set the teamcolor of a team. Only makes sense in room state and if you are chief.
  * The server does not send a reply.
  */
-int flib_netconn_send_teamColor(flib_netconn *conn, const char *teamname, uint32_t colorRGB);
+int flib_netconn_send_teamColor(flib_netconn *conn, const char *teamname, int colorIndex);
 
 /**
  * Set the weaponset for the room. Only makes sense in room state and if you are chief.
@@ -318,9 +336,9 @@
  * flib_netconn_get_roomlist() as soon as the onConnected callback is fired. These callbacks
  * provide notification about changes.
  */
-void flib_netconn_onRoomAdd(flib_netconn *conn, void (*callback)(void *context, const flib_roomlist_room *room), void* context);
+void flib_netconn_onRoomAdd(flib_netconn *conn, void (*callback)(void *context, const flib_room *room), void* context);
 void flib_netconn_onRoomDelete(flib_netconn *conn, void (*callback)(void *context, const char *name), void* context);
-void flib_netconn_onRoomUpdate(flib_netconn *conn, void (*callback)(void *context, const char *oldName, const flib_roomlist_room *room), void* context);
+void flib_netconn_onRoomUpdate(flib_netconn *conn, void (*callback)(void *context, const char *oldName, const flib_room *room), void* context);
 
 /**
  * Callbacks for players joining or leaving the lobby. If join is true it's a join, otherwise a leave.
@@ -417,9 +435,9 @@
  * The color of a team has been changed by the room chief. If you are the chief and change the color yourself,
  * you will not receive this callback!
  */
-void flib_netconn_onTeamColorChanged(flib_netconn *conn, void (*callback)(void *context, const char *teamName, uint32_t colorARGB), void *context);
+void flib_netconn_onTeamColorChanged(flib_netconn *conn, void (*callback)(void *context, const char *teamName, int colorIndex), void *context);
 
-void flib_netconn_onEngineMessage(flib_netconn *conn, void (*callback)(void *context, const char *message, int size), void *context);
+void flib_netconn_onEngineMessage(flib_netconn *conn, void (*callback)(void *context, const uint8_t *message, size_t size), void *context);
 
 void flib_netconn_onCfgScheme(flib_netconn *conn, void (*callback)(void *context, flib_cfg *scheme), void *context);
 
--- a/project_files/frontlib/net/netconn_callbacks.c	Mon Jun 25 15:21:18 2012 +0200
+++ b/project_files/frontlib/net/netconn_callbacks.c	Wed Jun 27 18:02:45 2012 +0200
@@ -41,7 +41,7 @@
 	flib_netconn_send_quit((flib_netconn*)context, "Authentication failed");
 }
 
-void clearCallbacks(flib_netconn *conn) {
+void netconn_clearCallbacks(flib_netconn *conn) {
 	flib_netconn_onMessage(conn, NULL, NULL);
 	flib_netconn_onConnected(conn, NULL, NULL);
 	flib_netconn_onDisconnected(conn, NULL, NULL);
@@ -102,9 +102,9 @@
 GENERATE_CB_SETTER(onMessage, (void *context, int msgtype, const char *msg), defaultCallback_onMessage);
 GENERATE_CB_SETTER_AND_DEFAULT(onConnected, (void *context));
 GENERATE_CB_SETTER_AND_DEFAULT(onDisconnected, (void *context, int reason, const char *message));
-GENERATE_CB_SETTER_AND_DEFAULT(onRoomAdd, (void *context, const flib_roomlist_room *room));
+GENERATE_CB_SETTER_AND_DEFAULT(onRoomAdd, (void *context, const flib_room *room));
 GENERATE_CB_SETTER_AND_DEFAULT(onRoomDelete, (void *context, const char *name));
-GENERATE_CB_SETTER_AND_DEFAULT(onRoomUpdate, (void *context, const char *oldName, const flib_roomlist_room *room));
+GENERATE_CB_SETTER_AND_DEFAULT(onRoomUpdate, (void *context, const char *oldName, const flib_room *room));
 GENERATE_CB_SETTER(onChat, (void *context, const char *nick, const char *msg), defaultCallback_onChat);
 GENERATE_CB_SETTER_AND_DEFAULT(onLobbyJoin, (void *context, const char *nick));
 GENERATE_CB_SETTER_AND_DEFAULT(onLobbyLeave, (void *context, const char *nick, const char *partMsg));
@@ -121,8 +121,8 @@
 GENERATE_CB_SETTER_AND_DEFAULT(onRunGame, (void *context));
 GENERATE_CB_SETTER_AND_DEFAULT(onTeamAccepted, (void *context, const char *teamName));
 GENERATE_CB_SETTER_AND_DEFAULT(onHogCountChanged, (void *context, const char *teamName, int hogs));
-GENERATE_CB_SETTER_AND_DEFAULT(onTeamColorChanged, (void *context, const char *teamName, uint32_t colorRGB));
-GENERATE_CB_SETTER_AND_DEFAULT(onEngineMessage, (void *context, const char *message, int size));
+GENERATE_CB_SETTER_AND_DEFAULT(onTeamColorChanged, (void *context, const char *teamName, int colorIndex));
+GENERATE_CB_SETTER_AND_DEFAULT(onEngineMessage, (void *context, const uint8_t *message, size_t size));
 GENERATE_CB_SETTER_AND_DEFAULT(onCfgScheme, (void *context, flib_cfg *scheme));
 GENERATE_CB_SETTER_AND_DEFAULT(onMapChanged, (void *context, const flib_map *map, int changetype));
 GENERATE_CB_SETTER_AND_DEFAULT(onScriptChanged, (void *context, const char *script));
--- a/project_files/frontlib/net/netconn_internal.h	Mon Jun 25 15:21:18 2012 +0200
+++ b/project_files/frontlib/net/netconn_internal.h	Wed Jun 27 18:02:45 2012 +0200
@@ -20,14 +20,21 @@
 struct _flib_netconn {
 	flib_netbase *netBase;
 	char *playerName;
+	char *dataDirPath;
+
+	int netconnState;			// One of the NETCONN_STATE constants
+	bool isAdmin;				// Player is server administrator
+
 	flib_cfg_meta *metaCfg;
-	flib_roomlist *roomList;
+	flib_roomlist roomList;
 
-	int netconnState;	// One of the NETCONN_STATE constants
-
-	bool isAdmin;			// Player is server administrator
-	bool isChief;			// Player can modify the current room
-	flib_map *map;			// Map settings in the current room.
+	bool isChief;				// Player can modify the current room
+	flib_map *map;
+	flib_teamlist pendingTeamlist;
+	flib_teamlist teamlist;
+	flib_cfg *scheme;
+	char *script;
+	flib_weaponset *weaponset;
 
 	void (*onMessageCb)(void *context, int msgtype, const char *msg);
 	void *onMessageCtx;
@@ -38,13 +45,13 @@
 	void (*onDisconnectedCb)(void *context, int reason, const char *message);
 	void *onDisconnectedCtx;
 
-	void (*onRoomAddCb)(void *context, const flib_roomlist_room *room);
+	void (*onRoomAddCb)(void *context, const flib_room *room);
 	void *onRoomAddCtx;
 
 	void (*onRoomDeleteCb)(void *context, const char *name);
 	void *onRoomDeleteCtx;
 
-	void (*onRoomUpdateCb)(void *context, const char *oldName, const flib_roomlist_room *room);
+	void (*onRoomUpdateCb)(void *context, const char *oldName, const flib_room *room);
 	void *onRoomUpdateCtx;
 
 	void (*onChatCb)(void *context, const char *nick, const char *msg);
@@ -95,10 +102,10 @@
 	void (*onHogCountChangedCb)(void *context, const char *teamName, int hogs);
 	void *onHogCountChangedCtx;
 
-	void (*onTeamColorChangedCb)(void *context, const char *teamName, uint32_t colorRGB);
+	void (*onTeamColorChangedCb)(void *context, const char *teamName, int colorIndex);
 	void *onTeamColorChangedCtx;
 
-	void (*onEngineMessageCb)(void *context, const char *message, int size);
+	void (*onEngineMessageCb)(void *context, const uint8_t *message, size_t size);
 	void *onEngineMessageCtx;
 
 	void (*onCfgSchemeCb)(void *context, flib_cfg *scheme);
@@ -123,6 +130,11 @@
 	bool destroyRequested;
 };
 
-void clearCallbacks(flib_netconn *conn);
+void netconn_clearCallbacks(flib_netconn *conn);
+void netconn_leaveRoom(flib_netconn *conn);
+void netconn_setMap(flib_netconn *conn, const flib_map *map);
+void netconn_setWeaponset(flib_netconn *conn, const flib_weaponset *weaponset);
+void netconn_setScript(flib_netconn *conn, const char *script);
+void netconn_setScheme(flib_netconn *conn, const flib_cfg *scheme);
 
 #endif
--- a/project_files/frontlib/net/netconn_send.c	Mon Jun 25 15:21:18 2012 +0200
+++ b/project_files/frontlib/net/netconn_send.c	Wed Jun 27 18:02:45 2012 +0200
@@ -10,50 +10,50 @@
 #include <string.h>
 #include <zlib.h>
 
-// TODO state changes
-
 // cmdname is always given as literal from functions in this file, so it is never null.
 static int sendVoid(flib_netconn *conn, const char *cmdname) {
-	if(!conn) {
-		flib_log_e("null parameter trying to send %s command.", cmdname);
+	if(log_e_if(!conn, "Invalid parameter sending %s command", cmdname)) {
 		return -1;
 	}
 	return flib_netbase_sendf(conn->netBase, "%s\n\n", cmdname);
 }
 
+// Testing for !*str prevents sending 0-length parameters (they trip up the protocol)
 static int sendStr(flib_netconn *conn, const char *cmdname, const char *str) {
-	if(!conn || !str) {
-		flib_log_e("null parameter trying to send %s command.", cmdname);
+	if(log_e_if(!conn || !str || !*str, "Invalid parameter sending %s command", cmdname)) {
 		return -1;
 	}
 	return flib_netbase_sendf(conn->netBase, "%s\n%s\n\n", cmdname, str);
 }
 
 static int sendInt(flib_netconn *conn, const char *cmdname, int param) {
-	if(!conn) {
-		flib_log_e("null parameter trying to send %s command.", cmdname);
+	if(log_e_if(!conn, "Invalid parameter sending %s command", cmdname)) {
 		return -1;
 	}
 	return flib_netbase_sendf(conn->netBase, "%s\n%i\n\n", cmdname, param);
 }
 
 int flib_netconn_send_quit(flib_netconn *conn, const char *quitmsg) {
-	return sendStr(conn, "QUIT", quitmsg ? quitmsg : "User quit");
+	return sendStr(conn, "QUIT", (quitmsg && *quitmsg) ? quitmsg : "User quit");
 }
 
 int flib_netconn_send_chat(flib_netconn *conn, const char *chat) {
-	return sendStr(conn, "CHAT", chat);
+	if(chat && *chat) {
+		return sendStr(conn, "CHAT", chat);
+	}
+	return 0;
 }
 
 int flib_netconn_send_teamchat(flib_netconn *conn, const char *chat) {
-	return sendStr(conn, "TEAMCHAT", chat);
+	if(chat && *chat) {
+		return sendStr(conn, "TEAMCHAT", chat);
+	}
+	return 0;
 }
 
 int flib_netconn_send_nick(flib_netconn *conn, const char *nick) {
 	int result = -1;
-	if(!conn || !nick) {
-		flib_log_e("null parameter in flib_netconn_send_nick");
-	} else {
+	if(!log_badparams_if(!conn || !nick || !*nick)) {
 		char *tmpName = flib_strdupnull(nick);
 		if(tmpName) {
 			if(!flib_netbase_sendf(conn->netBase, "%s\n%s\n\n", "NICK", nick)) {
@@ -70,9 +70,7 @@
 
 int flib_netconn_send_password(flib_netconn *conn, const char *latin1Passwd) {
 	int result = -1;
-	if(!conn || !latin1Passwd) {
-		flib_log_e("null parameter in flib_netconn_send_password");
-	} else {
+	if(!log_badparams_if(!conn || !latin1Passwd)) {
 		md5_state_t md5state;
 		uint8_t md5bytes[16];
 		char md5hex[33];
@@ -89,11 +87,19 @@
 }
 
 int flib_netconn_send_joinRoom(flib_netconn *conn, const char *room) {
-	return sendStr(conn, "JOIN_ROOM", room);
+	if(!sendStr(conn, "JOIN_ROOM", room)) {
+		conn->isChief = false;
+		return 0;
+	}
+	return -1;
 }
 
 int flib_netconn_send_createRoom(flib_netconn *conn, const char *room) {
-	return sendStr(conn, "CREATE_ROOM", room);
+	if(!sendStr(conn, "CREATE_ROOM", room)) {
+		conn->isChief = true;
+		return 0;
+	}
+	return -1;
 }
 
 int flib_netconn_send_renameRoom(flib_netconn *conn, const char *roomName) {
@@ -101,35 +107,50 @@
 }
 
 int flib_netconn_send_leaveRoom(flib_netconn *conn) {
-	return sendVoid(conn, "PART");
+	if(flib_netconn_is_in_room_context(conn) && !sendVoid(conn, "PART")) {
+		netconn_leaveRoom(conn);
+		return 0;
+	}
+	return -1;
 }
 
 int flib_netconn_send_toggleReady(flib_netconn *conn) {
 	return sendVoid(conn, "TOGGLE_READY");
 }
 
+static void addTeamToPendingList(flib_netconn *conn, const flib_team *team) {
+	flib_team *teamcopy = flib_team_copy(team);
+	if(teamcopy) {
+		teamcopy->remoteDriven = false;
+		free(teamcopy->ownerName);
+		teamcopy->ownerName = flib_strdupnull(conn->playerName);
+		if(teamcopy->ownerName) {
+			flib_teamlist_delete(&conn->pendingTeamlist, team->name);
+			flib_teamlist_insert(&conn->pendingTeamlist, teamcopy, 0);
+		}
+	}
+	flib_team_release(teamcopy);
+}
+
 int flib_netconn_send_addTeam(flib_netconn *conn, const flib_team *team) {
 	int result = -1;
-	if(!conn || !team) {
-		flib_log_e("null parameter in flib_netconn_send_addTeam");
-	} else {
-		bool missingInfo = !team->name || !team->color || !team->grave || !team->fort || !team->voicepack || !team->flag;
+	if(!log_badparams_if(!conn || !team)) {
+		bool missingInfo = !team->name || !team->grave || !team->fort || !team->voicepack || !team->flag;
 		for(int i=0; i<HEDGEHOGS_PER_TEAM; i++) {
 			missingInfo |= !team->hogs[i].name || !team->hogs[i].hat;
 		}
-		if(missingInfo) {
-			flib_log_e("Incomplete team definition for flib_netconn_send_addTeam");
-		} else {
+		if(!log_e_if(missingInfo, "Incomplete team definition")) {
 			flib_vector *vec = flib_vector_create();
 			if(vec) {
 				bool error = false;
-				error |= flib_vector_appendf(vec, "ADD_TEAM\n%s\n%lu\n%s\n%s\n%s\n%s\n%i\n", team->name, (unsigned long)team->color, team->grave, team->fort, team->voicepack, team->flag, team->hogs[0].difficulty);
+				error |= flib_vector_appendf(vec, "ADD_TEAM\n%s\n%i\n%s\n%s\n%s\n%s\n%i\n", team->name, team->colorIndex, team->grave, team->fort, team->voicepack, team->flag, team->hogs[0].difficulty);
 				for(int i=0; i<HEDGEHOGS_PER_TEAM; i++) {
 					error |= flib_vector_appendf(vec, "%s\n%s\n", team->hogs[i].name, team->hogs[i].hat);
 				}
 				error |= flib_vector_appendf(vec, "\n");
-				if(!error) {
-					result = flib_netbase_send_raw(conn->netBase, flib_vector_data(vec), flib_vector_size(vec));
+				if(!error && !flib_netbase_send_raw(conn->netBase, flib_vector_data(vec), flib_vector_size(vec))) {
+					addTeamToPendingList(conn, team);
+					result = 0;
 				}
 			}
 			flib_vector_destroy(vec);
@@ -139,14 +160,19 @@
 }
 
 int flib_netconn_send_removeTeam(flib_netconn *conn, const char *teamname) {
-	return sendStr(conn, "REMOVE_TEAM", teamname);
+	if(!sendStr(conn, "REMOVE_TEAM", teamname)) {
+		flib_team *team = flib_teamlist_find(&conn->teamlist, teamname);
+		if(team && !team->remoteDriven) {
+			flib_teamlist_delete(&conn->teamlist, teamname);
+		}
+		return 0;
+	}
+	return -1;
 }
 
 int flib_netconn_send_engineMessage(flib_netconn *conn, const uint8_t *message, size_t size) {
 	int result = -1;
-	if(!conn || (!message && size>0)) {
-		flib_log_e("null parameter in flib_netconn_send_engineMessage");
-	} else {
+	if(!log_badparams_if(!conn || (!message && size>0))) {
 		char *base64encout = NULL;
 		base64_encode_alloc((const char*)message, size, &base64encout);
 		if(base64encout) {
@@ -158,38 +184,52 @@
 }
 
 int flib_netconn_send_teamHogCount(flib_netconn *conn, const char *teamname, int hogcount) {
-	if(!conn || !teamname || hogcount<1 || hogcount>HEDGEHOGS_PER_TEAM) {
-		flib_log_e("invalid parameter in flib_netconn_send_teamHogCount");
-		return -1;
+	if(!log_badparams_if(!conn || !teamname || hogcount<1 || hogcount>HEDGEHOGS_PER_TEAM)
+			&& !flib_netbase_sendf(conn->netBase, "HH_NUM\n%s\n%i\n\n", teamname, hogcount)) {
+		if(conn->isChief) {
+			flib_team *team = flib_teamlist_find(&conn->teamlist, teamname);
+			if(team) {
+				team->hogsInGame = hogcount;
+			}
+		}
+		return 0;
 	}
-	return flib_netbase_sendf(conn->netBase, "HH_NUM\n%s\n%i\n\n", teamname, hogcount);
+	return -1;
 }
 
-int flib_netconn_send_teamColor(flib_netconn *conn, const char *teamname, uint32_t colorRGB) {
-	if(!conn || !teamname) {
-		flib_log_e("null parameter in flib_netconn_send_teamColor");
-		return -1;
+int flib_netconn_send_teamColor(flib_netconn *conn, const char *teamname, int colorIndex) {
+	if(!log_badparams_if(!conn || !teamname)
+			&& !flib_netbase_sendf(conn->netBase, "TEAM_COLOR\n%s\n%i\n\n", teamname, colorIndex)) {
+		if(conn->isChief) {
+			flib_team *team = flib_teamlist_find(&conn->teamlist, teamname);
+			if(team) {
+				team->colorIndex = colorIndex;
+			}
+		}
+		return 0;
 	}
-	return flib_netbase_sendf(conn->netBase, "TEAM_COLOR\n%s\n%lu\n\n", teamname, (unsigned long)colorRGB);
+	return -1;
 }
 
 int flib_netconn_send_weaponset(flib_netconn *conn, const flib_weaponset *weaponset) {
-	if(!conn || !weaponset) {
-		flib_log_e("null parameter in flib_netconn_send_weaponset");
-		return -1;
+	if(!log_badparams_if(!conn || !weaponset)) {
+		char ammostring[WEAPONS_COUNT*4+1];
+		strcpy(ammostring, weaponset->loadout);
+		strcat(ammostring, weaponset->crateprob);
+		strcat(ammostring, weaponset->delay);
+		strcat(ammostring, weaponset->crateammo);
+		if(!flib_netbase_sendf(conn->netBase, "CFG\nAMMO\n%s\n%s\n\n", weaponset->name, ammostring)) {
+			if(conn->isChief) {
+				netconn_setWeaponset(conn, weaponset);
+			}
+			return 0;
+		}
 	}
-
-	char ammostring[WEAPONS_COUNT*4+1];
-	strcpy(ammostring, weaponset->loadout);
-	strcat(ammostring, weaponset->crateprob);
-	strcat(ammostring, weaponset->delay);
-	strcat(ammostring, weaponset->crateammo);
-	return flib_netbase_sendf(conn->netBase, "CFG\nAMMO\n%s\n%s\n\n", weaponset->name, ammostring);
+	return -1;
 }
 
 int flib_netconn_send_map(flib_netconn *conn, const flib_map *map) {
-	if(!conn || !map) {
-		flib_log_e("null parameter in flib_netconn_send_map");
+	if(log_badparams_if(!conn || !map)) {
 		return -1;
 	}
 	bool error = false;
@@ -213,34 +253,80 @@
 }
 
 int flib_netconn_send_mapName(flib_netconn *conn, const char *mapName) {
-	return sendStr(conn, "CFG\nMAP", mapName);
+	if(!sendStr(conn, "CFG\nMAP", mapName)) {
+		if(conn->isChief) {
+			char *copy = flib_strdupnull(mapName);
+			if(copy) {
+				free(conn->map->name);
+				conn->map->name = copy;
+			}
+		}
+		return 0;
+	}
+	return -1;
 }
 
 int flib_netconn_send_mapGen(flib_netconn *conn, int mapGen) {
-	return sendInt(conn, "CFG\nMAPGEN", mapGen);
+	if(!sendInt(conn, "CFG\nMAPGEN", mapGen)) {
+		if(conn->isChief) {
+			conn->map->mapgen = mapGen;
+		}
+		return 0;
+	}
+	return -1;
 }
 
 int flib_netconn_send_mapTemplate(flib_netconn *conn, int templateFilter) {
-	return sendInt(conn, "CFG\nTEMPLATE", templateFilter);
+	if(!sendInt(conn, "CFG\nTEMPLATE", templateFilter)) {
+		if(conn->isChief) {
+			conn->map->templateFilter = templateFilter;
+		}
+		return 0;
+	}
+	return -1;
 }
 
 int flib_netconn_send_mapMazeSize(flib_netconn *conn, int mazeSize) {
-	return sendInt(conn, "CFG\nMAZE_SIZE", mazeSize);
+	if(!sendInt(conn, "CFG\nMAZE_SIZE", mazeSize)) {
+		if(conn->isChief) {
+			conn->map->mazeSize = mazeSize;
+		}
+		return 0;
+	}
+	return -1;
 }
 
 int flib_netconn_send_mapSeed(flib_netconn *conn, const char *seed) {
-	return sendStr(conn, "CFG\nSEED", seed);
+	if(!sendStr(conn, "CFG\nSEED", seed)) {
+		if(conn->isChief) {
+			char *copy = flib_strdupnull(seed);
+			if(copy) {
+				free(conn->map->seed);
+				conn->map->seed = copy;
+			}
+		}
+		return 0;
+	}
+	return -1;
 }
 
 int flib_netconn_send_mapTheme(flib_netconn *conn, const char *theme) {
-	return sendStr(conn, "CFG\nTHEME", theme);
+	if(!sendStr(conn, "CFG\nTHEME", theme)) {
+		if(conn->isChief) {
+			char *copy = flib_strdupnull(theme);
+			if(copy) {
+				free(conn->map->theme);
+				conn->map->theme = copy;
+			}
+		}
+		return 0;
+	}
+	return -1;
 }
 
 int flib_netconn_send_mapDrawdata(flib_netconn *conn, const uint8_t *drawData, size_t size) {
 	int result = -1;
-	if(!conn || (!drawData && size>0) || size>SIZE_MAX/2) {
-		flib_log_e("invalid parameter in flib_netconn_send_map");
-	} else {
+	if(!log_badparams_if(!conn || (!drawData && size>0) || size>SIZE_MAX/2)) {
 		uLongf zippedSize = compressBound(size);
 		uint8_t *zipped = flib_malloc(zippedSize+4); // 4 extra bytes for header
 		if(zipped) {
@@ -265,18 +351,31 @@
 		}
 		free(zipped);
 	}
+
+	if(!result && conn->isChief) {
+		uint8_t *copy = flib_bufdupnull(drawData, size);
+		if(copy) {
+			free(conn->map->drawData);
+			conn->map->drawData = copy;
+			conn->map->drawDataSize = size;
+		}
+	}
 	return result;
 }
 
 int flib_netconn_send_script(flib_netconn *conn, const char *scriptName) {
-	return sendStr(conn, "CFG\nSCRIPT", scriptName);
+	if(!sendStr(conn, "CFG\nSCRIPT", scriptName)) {
+		if(conn->isChief) {
+			netconn_setScript(conn, scriptName);
+		}
+		return 0;
+	}
+	return -1;
 }
 
 int flib_netconn_send_scheme(flib_netconn *conn, const flib_cfg *scheme) {
 	int result = -1;
-	if(!conn || !scheme) {
-		flib_log_e("null parameter in flib_netconn_send_scheme");
-	} else {
+	if(!log_badparams_if(!conn || !scheme)) {
 		flib_vector *vec = flib_vector_create();
 		if(vec) {
 			bool error = false;
@@ -294,11 +393,21 @@
 		}
 		flib_vector_destroy(vec);
 	}
+
+	if(!result && conn->isChief) {
+		netconn_setScheme(conn, scheme);
+	}
 	return result;
 }
 
 int flib_netconn_send_roundfinished(flib_netconn *conn, bool withoutError) {
-	return sendInt(conn, "ROUNDFINISHED", withoutError ? 1 : 0);
+	if(!sendInt(conn, "ROUNDFINISHED", withoutError ? 1 : 0)) {
+		if(conn->netconnState == NETCONN_STATE_INGAME) {
+			conn->netconnState = NETCONN_STATE_ROOM;
+		}
+		return 0;
+	}
+	return -1;
 }
 
 int flib_netconn_send_ban(flib_netconn *conn, const char *playerName) {
@@ -334,8 +443,7 @@
 }
 
 int flib_netconn_send_setServerVar(flib_netconn *conn, const char *name, const char *value) {
-	if(!conn || !name || !value) {
-		flib_log_e("null parameter trying to send SET_SERVER_VAR command.");
+	if(log_badparams_if(!conn || !name || !value)) {
 		return -1;
 	}
 	return flib_netbase_sendf(conn->netBase, "%s\n%s\n%s\n\n", "SET_SERVER_VAR", name, value);
--- a/project_files/frontlib/net/netprotocol.c	Mon Jun 25 15:21:18 2012 +0200
+++ b/project_files/frontlib/net/netprotocol.c	Wed Jun 27 18:02:45 2012 +0200
@@ -80,8 +80,8 @@
 }
 
 // TODO: Test with empty map
-uint8_t *flib_netmsg_to_drawnmapdata(size_t *outlen, char *netmsg) {
-	uint8_t *result = NULL;
+int flib_netmsg_to_drawnmapdata(char *netmsg, uint8_t** outbuf, size_t *outlen) {
+	int result = -1;
 
 	// First step: base64 decoding
 	char *base64decout = NULL;
@@ -90,24 +90,32 @@
 	if(ok && base64declen>3) {
 		// Second step: unzip with the QCompress header. That header is just a big-endian
 		// uint32 indicating the length of the uncompressed data.
+		uint8_t *ubyteBuf = (uint8_t*)base64decout;
 		uint32_t unzipLen =
-				(((uint32_t)base64decout[0])<<24)
-				+ (((uint32_t)base64decout[1])<<16)
-				+ (((uint32_t)base64decout[2])<<8)
-				+ base64decout[3];
-		uint8_t *out = flib_malloc(unzipLen);
-		if(out) {
-			uLongf actualUnzipLen = unzipLen;
-			int resultcode = uncompress(out, &actualUnzipLen, (Bytef*)(base64decout+4), base64declen-4);
-			if(resultcode == Z_OK) {
-				result = out;
-				*outlen = actualUnzipLen;
-				out = NULL;
-			} else {
-				flib_log_e("Uncompressing drawn map failed. Code: %i", resultcode);
+				(((uint32_t)ubyteBuf[0])<<24)
+				+ (((uint32_t)ubyteBuf[1])<<16)
+				+ (((uint32_t)ubyteBuf[2])<<8)
+				+ ubyteBuf[3];
+		if(unzipLen==0) {
+			*outbuf = NULL;
+			*outlen = 0;
+			result = 0;
+		} else {
+			uint8_t *out = flib_malloc(unzipLen);
+			if(out) {
+				uLongf actualUnzipLen = unzipLen;
+				int resultcode = uncompress(out, &actualUnzipLen, (Bytef*)(base64decout+4), base64declen-4);
+				if(resultcode == Z_OK) {
+					*outbuf = out;
+					*outlen = actualUnzipLen;
+					out = NULL;
+					result = 0;
+				} else {
+					flib_log_e("Uncompressing drawn map failed. Code: %i", resultcode);
+				}
 			}
+			free(out);
 		}
-		free(out);
 	} else {
 		flib_log_e("base64 decoding of drawn map failed.");
 	}
--- a/project_files/frontlib/net/netprotocol.h	Mon Jun 25 15:21:18 2012 +0200
+++ b/project_files/frontlib/net/netprotocol.h	Wed Jun 27 18:02:45 2012 +0200
@@ -31,6 +31,6 @@
  * is written to the variable pointed to by outlen.
  * Returns NULL on error.
  */
-uint8_t *flib_netmsg_to_drawnmapdata(size_t *outlen, char *netmsg);
+int flib_netmsg_to_drawnmapdata(char *netmsg, uint8_t **outbuf, size_t *outlen);
 
 #endif /* NETPROTOCOL_H_ */
--- a/project_files/frontlib/test.c	Mon Jun 25 15:21:18 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,337 +0,0 @@
-#include "frontlib.h"
-#include "util/logging.h"
-#include "util/buffer.h"
-#include "util/util.h"
-#include "util/list.h"
-#include "model/map.h"
-#include "model/weapon.h"
-#include "model/schemelist.h"
-#include "ipc/mapconn.h"
-#include "ipc/gameconn.h"
-#include "net/netconn.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) {
-	printf("Drawing map for %i brave little hogs...", numHedgehogs);
-
-	// Draw the map as ASCII art
-	for(int y=0; y<MAPIMAGE_HEIGHT; y++) {
-		for(int x=0; x<MAPIMAGE_WIDTH; x++) {
-			int pixelnum = x + y*MAPIMAGE_WIDTH;
-			bool pixel = bitmap[pixelnum>>3] & (1<<(7-(pixelnum&7)));
-			printf(pixel ? "#" : " ");
-		}
-		printf("\n");
-	}
-
-	// Destroy the connection object (this will end the "tick" loop below)
-	flib_mapconn **connptr = context;
-	flib_mapconn_destroy(*connptr);
-	*connptr = NULL;
-}
-
-static void onDisconnect(void *context, int reason) {
-	flib_log_i("Connection closed. Reason: %i", reason);
-	flib_gameconn **connptr = context;
-	flib_gameconn_destroy(*connptr);
-	*connptr = NULL;
-}
-
-static void onGameRecorded(void *context, const uint8_t *record, int size, bool isSavegame) {
-	flib_log_i("Writing %s (%i bytes)...", isSavegame ? "savegame" : "demo", size);
-	FILE *file = fopen(isSavegame ? "testsave.42.hws" : "testdemo.42.hwd", "wb");
-	fwrite(record, 1, size, file);
-	fclose(file);
-}
-
-// Callback function that will be called on error
-static void handleMapFailure(void *context, const char *errormessage) {
-	flib_log_e("Map rendering failed: %s", errormessage);
-
-	// Destroy the connection object (this will end the "tick" loop below)
-	flib_mapconn **connptr = context;
-	flib_mapconn_destroy(*connptr);
-	*connptr = NULL;
-}
-
-static void startEngineMap(int port) {
-	char commandbuffer[255];
-	const char *enginePath = "C:\\Programmieren\\Hedgewars\\bin";
-	const char *configPath = "C:\\Programmieren\\Hedgewars\\share\\hedgewars";
-	snprintf(commandbuffer, 255, "start %s\\hwengine.exe %s %i landpreview", enginePath, configPath, port);
-	system(commandbuffer);
-}
-
-static void startEngineGame(int port) {
-	char commandbuffer[255];
-	const char *enginePath = "C:\\Programmieren\\Hedgewars\\bin";
-	const char *configPath = "C:\\Programmieren\\Hedgewars\\share\\hedgewars";
-	const char *dataPath = "C:\\Programmieren\\Hedgewars\\share\\hedgewars\\Data";
-	snprintf(commandbuffer, 255, "start %s\\hwengine.exe %s 1024 768 32 %i 0 0 0 10 10 %s 0 0 TWVkbzQy 0 0 en.txt", enginePath, configPath, port, dataPath);
-	flib_log_d("Starting engine with CMD: %s", commandbuffer);
-	system(commandbuffer);
-}
-
-void testMapPreview() {
-	// Create a map description and check that there was no error
-	flib_map *map = flib_map_create_maze("This is the seed value", "Jungle", MAZE_SIZE_SMALL_TUNNELS);
-	assert(map);
-
-	// Create a new connection to the engine and check that there was no error
-	flib_mapconn *mapConnection = flib_mapconn_create(map);
-	assert(mapConnection);
-
-	// We don't need the map description anymore
-	flib_map_release(map);
-	map = NULL;
-
-	// Register the callback functions
-	flib_mapconn_onFailure(mapConnection, &handleMapFailure, &mapConnection);
-	flib_mapconn_onSuccess(mapConnection, &handleMapSuccess, &mapConnection);
-
-	// Start the engine process and tell it which port the frontlib is listening on
-	startEngineMap(flib_mapconn_getport(mapConnection));
-
-	// Usually, flib_mapconn_tick will be called in an event loop that runs several
-	// times per second. It handles I/O operations and progress, and calls
-	// callbacks when something interesting happens.
-	while(mapConnection) {
-		flib_mapconn_tick(mapConnection);
-	}
-}
-
-void testGame() {
-	flib_cfg_meta *metaconf = flib_cfg_meta_from_ini("metasettings.ini");
-	assert(metaconf);
-	flib_weaponset *weapons = flib_weaponset_create("Defaultweaps");
-	flib_schemelist *schemelist = flib_schemelist_from_ini(metaconf, "schemes.ini");
-
-	flib_gamesetup setup;
-	setup.gamescheme = flib_schemelist_find(schemelist, "Default");
-	setup.map = flib_map_create_maze("asparagus", "Jungle", MAZE_SIZE_MEDIUM_TUNNELS);
-	setup.script = NULL;
-	setup.teamCount = 2;
-	setup.teams = calloc(2, sizeof(flib_team*));
-	setup.teams[0] = calloc(1, sizeof(flib_team));
-	setup.teams[0]->color = 0xffff0000;
-	setup.teams[0]->flag = "australia";
-	setup.teams[0]->fort = "Plane";
-	setup.teams[0]->grave = "Bone";
-	setup.teams[0]->hogsInGame = 2;
-	setup.teams[0]->name = "Team Awesome";
-	setup.teams[0]->voicepack = "British";
-	setup.teams[0]->hogs[0].difficulty = 2;
-	setup.teams[0]->hogs[0].hat = "NoHat";
-	setup.teams[0]->hogs[0].initialHealth = 100;
-	setup.teams[0]->hogs[0].name = "Harry 120";
-	setup.teams[0]->hogs[1].difficulty = 2;
-	setup.teams[0]->hogs[1].hat = "chef";
-	setup.teams[0]->hogs[1].initialHealth = 100;
-	setup.teams[0]->hogs[1].name = "Chefkoch";
-	setup.teams[1] = flib_team_from_ini("Cave Dwellers.hwt");
-	setup.teams[1]->color = 0xFF0000F0;
-	setup.teams[1]->hogsInGame = 8;
-	flib_team_set_weaponset(setup.teams[0], weapons);
-	flib_team_set_weaponset(setup.teams[1], weapons);
-	flib_weaponset_release(weapons);
-
-	flib_gameconn *gameconn = flib_gameconn_create("Medo42", &setup, false);
-	assert(gameconn);
-
-	flib_gameconn_onDisconnect(gameconn, &onDisconnect, &gameconn);
-	//flib_gameconn_onGameRecorded(gameconn, &onGameRecorded, &gameconn);
-
-	startEngineGame(flib_gameconn_getport(gameconn));
-
-	while(gameconn) {
-		flib_gameconn_tick(gameconn);
-	}
-}
-
-void testDemo() {
-	FILE *demofile = fopen("testdemo.42.hwd", "rb");
-	assert(demofile);
-	flib_vector *vec = flib_vector_create();
-	uint8_t demobuf[512];
-	int len;
-	while((len=fread(demobuf, 1, 512, demofile))>0) {
-		flib_vector_append(vec, demobuf, len);
-	}
-	fclose(demofile);
-	flib_constbuffer constbuf = flib_vector_as_constbuffer(vec);
-	flib_gameconn *gameconn = flib_gameconn_create_playdemo(constbuf.data, constbuf.size);
-	flib_vector_destroy(vec);
-	assert(gameconn);
-	flib_gameconn_onDisconnect(gameconn, &onDisconnect, &gameconn);
-	flib_gameconn_onGameRecorded(gameconn, &onGameRecorded, &gameconn);
-	startEngineGame(flib_gameconn_getport(gameconn));
-
-	while(gameconn) {
-		flib_gameconn_tick(gameconn);
-	}
-}
-
-void testSave() {
-	FILE *demofile = fopen("testsave.42.hws", "rb");
-	assert(demofile);
-	flib_vector *vec = flib_vector_create();
-	uint8_t demobuf[512];
-	int len;
-	while((len=fread(demobuf, 1, 512, demofile))>0) {
-		flib_vector_append(vec, demobuf, len);
-	}
-	fclose(demofile);
-	flib_constbuffer constbuf = flib_vector_as_constbuffer(vec);
-	flib_gameconn *gameconn = flib_gameconn_create_loadgame("Medo42", constbuf.data, constbuf.size);
-	flib_vector_destroy(vec);
-	assert(gameconn);
-	flib_gameconn_onDisconnect(gameconn, &onDisconnect, &gameconn);
-	flib_gameconn_onGameRecorded(gameconn, &onGameRecorded, &gameconn);
-	startEngineGame(flib_gameconn_getport(gameconn));
-
-	while(gameconn) {
-		flib_gameconn_tick(gameconn);
-	}
-}
-
-void handleNetDisconnect(void *context, int reason, const char *message) {
-	flib_log_i("Disconnected: %s", message);
-	flib_netconn_destroy(*(flib_netconn**)context);
-	*(flib_netconn**)context = NULL;
-}
-
-void handleNetConnected(void *context) {
-	const flib_roomlist *roomlist = flib_netconn_get_roomlist(*(flib_netconn**)context);
-	flib_log_i("List of rooms:");
-	for(int i=0; i<roomlist->roomCount; i++) {
-		flib_roomlist_room *room = roomlist->rooms[i];
-		flib_log_i("%1s %20s %20s %2i %2i %20s %20s %20s", room->inProgress ? "X" : " ", room->name, room->owner, room->playerCount, room->teamCount, room->map, room->scheme, room->weapons);
-	}
-}
-
-void handleLobbyJoin(void *context, const char *nick) {
-	flib_log_i("%s joined", nick);
-}
-
-void handleChat(void *context, const char *nick, const char *msg) {
-	flib_log_i("%s: %s", nick, msg);
-	if(!memcmp("frontbot ", msg, strlen("frontbot "))) {
-		const char *command = msg+strlen("frontbot ");
-		if(!memcmp("quit", command, strlen("quit"))) {
-			flib_netconn_send_quit(*(flib_netconn**)context, "Yeth Mathter");
-		} else if(!memcmp("describe ", command, strlen("describe "))) {
-			const char *roomname = command+strlen("describe ");
-			const flib_roomlist *roomlist = flib_netconn_get_roomlist(*(flib_netconn**)context);
-			flib_roomlist_room *room = flib_roomlist_find((flib_roomlist*)roomlist, roomname);
-			if(!room) {
-				flib_netconn_send_chat(*(flib_netconn**)context, "Unknown room.");
-			} else {
-				char *text = flib_asprintf(
-						"%s is a room created by %s, where %i players (%i teams) are %s on %s%s, using the %s scheme and %s weaponset.",
-						room->name,
-						room->owner,
-						room->playerCount,
-						room->teamCount,
-						room->inProgress ? "fighting" : "preparing to fight",
-						room->map[0]=='+' ? "" : "the map ",
-						!strcmp("+rnd+", room->map) ? "a random map" :
-								!strcmp("+maze+", room->map) ? "a random maze" :
-								!strcmp("+drawn+", room->map) ? "a hand-drawn map" :
-								room->map,
-						room->scheme,
-						room->weapons);
-				if(text) {
-					flib_netconn_send_chat(*(flib_netconn**)context, text);
-				}
-				free(text);
-			}
-		} else if(!memcmp("join ", command, strlen("join "))) {
-			const char *roomname = command+strlen("join ");
-			flib_netconn_send_joinRoom(*(flib_netconn**)context, roomname);
-		} else if(!memcmp("ready", command, strlen("ready"))) {
-			flib_netconn_send_toggleReady(*(flib_netconn**)context);
-		}
-	}
-}
-
-static flib_gamesetup gGamesetup = {0};
-static flib_weaponset *gWeaponset = NULL;
-
-void handleEnterRoom(void *context, bool isChief) {
-	flib_netconn_send_toggleReady(*(flib_netconn**)context);
-}
-
-void handleMap(void *context, const flib_map *map, int changeType) {
-	flib_map_release(gGamesetup.map);
-	gGamesetup.map = flib_map_copy(map);
-}
-
-void handleCfgScheme(void *context, flib_cfg *cfg) {
-	flib_cfg_release(gGamesetup.gamescheme);
-	gGamesetup.gamescheme = flib_cfg_retain(cfg);
-}
-
-void handleWeaponset(void *context, flib_weaponset *weaponset) {
-	flib_weaponset_release(gWeaponset);
-	gWeaponset = flib_weaponset_retain(weaponset);
-}
-
-void handleScript(void *context, const char *script) {
-	free(gGamesetup.script);
-	gGamesetup.script = flib_strdupnull(script);
-}
-
-void handleTeamAdd(void *context, flib_team *team) {
-	flib_team *teamptr = flib_team_retain(team);
-	gGamesetup.teams = flib_list_insert(gGamesetup.teams, &gGamesetup.teamCount, sizeof(*gGamesetup.teams), &teamptr, 0);
-}
-
-void handleTeamRemove(void *context, const char *team) {
-	for(int i=0; i<gGamesetup.teamCount; i++) {
-		if(!strcmp(team, gGamesetup.teams[i]->name)) {
-			flib_team_release(gGamesetup.teams[i]);
-			gGamesetup.teams = flib_list_delete(gGamesetup.teams, &gGamesetup.teamCount, sizeof(*gGamesetup.teams), i);
-		}
-	}
-}
-
-int main(int argc, char *argv[]) {
-	flib_init(0);
-	flib_log_setLevel(FLIB_LOGLEVEL_ALL);
-
-	//testMapPreview();
-	//testDemo();
-	//testSave();
-	//testGame();
-
-	flib_cfg_meta *meta = flib_cfg_meta_from_ini("metasettings.ini");
-	assert(meta);
-	flib_netconn *conn = flib_netconn_create("frontbot", meta, "140.247.62.101", 46631);
-	assert(conn);
-	flib_cfg_meta_release(meta);
-
-	flib_netconn_onConnected(conn, handleNetConnected, &conn);
-	flib_netconn_onDisconnected(conn, handleNetDisconnect, &conn);
-	flib_netconn_onLobbyJoin(conn, handleLobbyJoin, &conn);
-	flib_netconn_onChat(conn, handleChat, &conn);
-	flib_netconn_onMapChanged(conn, handleMap, conn);
-	flib_netconn_onEnterRoom(conn, handleEnterRoom, conn);
-	flib_netconn_onCfgScheme(conn, handleCfgScheme, conn);
-	flib_netconn_onWeaponsetChanged(conn, handleWeaponset, conn);
-	flib_netconn_onScriptChanged(conn, handleScript, conn);
-	flib_netconn_onTeamAdd(conn, handleTeamAdd, conn);
-	flib_netconn_onTeamRemove(conn, handleTeamRemove, conn);
-	flib_netconn_onHogCountChanged(conn, handleHogCountChanged, conn);
-
-	while(conn) {
-		flib_netconn_tick(conn);
-	}
-
-	flib_quit();
-	return 0;
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/test.c.nocompile	Wed Jun 27 18:02:45 2012 +0200
@@ -0,0 +1,319 @@
+#include "frontlib.h"
+#include "util/logging.h"
+#include "util/buffer.h"
+#include "util/util.h"
+#include "util/list.h"
+#include "model/map.h"
+#include "model/weapon.h"
+#include "model/schemelist.h"
+#include "ipc/mapconn.h"
+#include "ipc/gameconn.h"
+#include "net/netconn.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) {
+	printf("Drawing map for %i brave little hogs...", numHedgehogs);
+
+	// Draw the map as ASCII art
+	for(int y=0; y<MAPIMAGE_HEIGHT; y++) {
+		for(int x=0; x<MAPIMAGE_WIDTH; x++) {
+			int pixelnum = x + y*MAPIMAGE_WIDTH;
+			bool pixel = bitmap[pixelnum>>3] & (1<<(7-(pixelnum&7)));
+			printf(pixel ? "#" : " ");
+		}
+		printf("\n");
+	}
+
+	// Destroy the connection object (this will end the "tick" loop below)
+	flib_mapconn **connptr = context;
+	flib_mapconn_destroy(*connptr);
+	*connptr = NULL;
+}
+
+static void onDisconnect(void *context, int reason) {
+	flib_log_i("Connection closed. Reason: %i", reason);
+	flib_gameconn **connptr = context;
+	flib_gameconn_destroy(*connptr);
+	*connptr = NULL;
+}
+
+static void onGameRecorded(void *context, const uint8_t *record, int size, bool isSavegame) {
+	flib_log_i("Writing %s (%i bytes)...", isSavegame ? "savegame" : "demo", size);
+	FILE *file = fopen(isSavegame ? "testsave.42.hws" : "testdemo.42.hwd", "wb");
+	fwrite(record, 1, size, file);
+	fclose(file);
+}
+
+// Callback function that will be called on error
+static void handleMapFailure(void *context, const char *errormessage) {
+	flib_log_e("Map rendering failed: %s", errormessage);
+
+	// Destroy the connection object (this will end the "tick" loop below)
+	flib_mapconn **connptr = context;
+	flib_mapconn_destroy(*connptr);
+	*connptr = NULL;
+}
+
+static void startEngineMap(int port) {
+	char commandbuffer[255];
+	const char *enginePath = "C:\\Programmieren\\Hedgewars\\bin";
+	const char *configPath = "C:\\Programmieren\\Hedgewars\\share\\hedgewars";
+	snprintf(commandbuffer, 255, "start %s\\hwengine.exe %s %i landpreview", enginePath, configPath, port);
+	system(commandbuffer);
+}
+
+static void startEngineGame(int port) {
+	char commandbuffer[255];
+	const char *enginePath = "C:\\Programmieren\\Hedgewars\\bin";
+	const char *configPath = "C:\\Programmieren\\Hedgewars\\share\\hedgewars";
+	const char *dataPath = "C:\\Programmieren\\Hedgewars\\share\\hedgewars\\Data";
+	snprintf(commandbuffer, 255, "start %s\\hwengine.exe %s 1024 768 32 %i 0 0 0 10 10 %s 0 0 TWVkbzQy 0 0 en.txt", enginePath, configPath, port, dataPath);
+	flib_log_d("Starting engine with CMD: %s", commandbuffer);
+	system(commandbuffer);
+}
+
+void testMapPreview() {
+	// Create a map description and check that there was no error
+	flib_map *map = flib_map_create_maze("This is the seed value", "Jungle", MAZE_SIZE_SMALL_TUNNELS);
+	assert(map);
+
+	// Create a new connection to the engine and check that there was no error
+	flib_mapconn *mapConnection = flib_mapconn_create(map);
+	assert(mapConnection);
+
+	// We don't need the map description anymore
+	flib_map_release(map);
+	map = NULL;
+
+	// Register the callback functions
+	flib_mapconn_onFailure(mapConnection, &handleMapFailure, &mapConnection);
+	flib_mapconn_onSuccess(mapConnection, &handleMapSuccess, &mapConnection);
+
+	// Start the engine process and tell it which port the frontlib is listening on
+	startEngineMap(flib_mapconn_getport(mapConnection));
+
+	// Usually, flib_mapconn_tick will be called in an event loop that runs several
+	// times per second. It handles I/O operations and progress, and calls
+	// callbacks when something interesting happens.
+	while(mapConnection) {
+		flib_mapconn_tick(mapConnection);
+	}
+}
+
+/*void testGame() {
+	flib_cfg_meta *metaconf = flib_cfg_meta_from_ini("metasettings.ini");
+	assert(metaconf);
+	flib_weaponset *weapons = flib_weaponset_create("Defaultweaps");
+	flib_schemelist *schemelist = flib_schemelist_from_ini(metaconf, "schemes.ini");
+
+	flib_gamesetup setup;
+	setup.gamescheme = flib_schemelist_find(schemelist, "Default");
+	setup.map = flib_map_create_maze("asparagus", "Jungle", MAZE_SIZE_MEDIUM_TUNNELS);
+	setup.script = NULL;
+	setup.teamCount = 2;
+	setup.teams = calloc(2, sizeof(flib_team*));
+	setup.teams[0] = calloc(1, sizeof(flib_team));
+	setup.teams[0]->color = 0xffff0000;
+	setup.teams[0]->flag = "australia";
+	setup.teams[0]->fort = "Plane";
+	setup.teams[0]->grave = "Bone";
+	setup.teams[0]->hogsInGame = 2;
+	setup.teams[0]->name = "Team Awesome";
+	setup.teams[0]->voicepack = "British";
+	setup.teams[0]->hogs[0].difficulty = 2;
+	setup.teams[0]->hogs[0].hat = "NoHat";
+	setup.teams[0]->hogs[0].initialHealth = 100;
+	setup.teams[0]->hogs[0].name = "Harry 120";
+	setup.teams[0]->hogs[1].difficulty = 2;
+	setup.teams[0]->hogs[1].hat = "chef";
+	setup.teams[0]->hogs[1].initialHealth = 100;
+	setup.teams[0]->hogs[1].name = "Chefkoch";
+	setup.teams[1] = flib_team_from_ini("Cave Dwellers.hwt");
+	setup.teams[1]->color = 0xFF0000F0;
+	setup.teams[1]->hogsInGame = 8;
+	flib_team_set_weaponset(setup.teams[0], weapons);
+	flib_team_set_weaponset(setup.teams[1], weapons);
+	flib_weaponset_release(weapons);
+
+	flib_gameconn *gameconn = flib_gameconn_create("Medo42", &setup, false);
+	assert(gameconn);
+
+	flib_gameconn_onDisconnect(gameconn, &onDisconnect, &gameconn);
+	//flib_gameconn_onGameRecorded(gameconn, &onGameRecorded, &gameconn);
+
+	startEngineGame(flib_gameconn_getport(gameconn));
+
+	while(gameconn) {
+		flib_gameconn_tick(gameconn);
+	}
+}*/
+
+void testDemo() {
+	FILE *demofile = fopen("testdemo.42.hwd", "rb");
+	assert(demofile);
+	flib_vector *vec = flib_vector_create();
+	uint8_t demobuf[512];
+	int len;
+	while((len=fread(demobuf, 1, 512, demofile))>0) {
+		flib_vector_append(vec, demobuf, len);
+	}
+	fclose(demofile);
+	flib_constbuffer constbuf = flib_vector_as_constbuffer(vec);
+	flib_gameconn *gameconn = flib_gameconn_create_playdemo(constbuf.data, constbuf.size);
+	flib_vector_destroy(vec);
+	assert(gameconn);
+	flib_gameconn_onDisconnect(gameconn, &onDisconnect, &gameconn);
+	flib_gameconn_onGameRecorded(gameconn, &onGameRecorded, &gameconn);
+	startEngineGame(flib_gameconn_getport(gameconn));
+
+	while(gameconn) {
+		flib_gameconn_tick(gameconn);
+	}
+}
+
+void testSave() {
+	FILE *demofile = fopen("testsave.42.hws", "rb");
+	assert(demofile);
+	flib_vector *vec = flib_vector_create();
+	uint8_t demobuf[512];
+	int len;
+	while((len=fread(demobuf, 1, 512, demofile))>0) {
+		flib_vector_append(vec, demobuf, len);
+	}
+	fclose(demofile);
+	flib_constbuffer constbuf = flib_vector_as_constbuffer(vec);
+	flib_gameconn *gameconn = flib_gameconn_create_loadgame("Medo42", constbuf.data, constbuf.size);
+	flib_vector_destroy(vec);
+	assert(gameconn);
+	flib_gameconn_onDisconnect(gameconn, &onDisconnect, &gameconn);
+	flib_gameconn_onGameRecorded(gameconn, &onGameRecorded, &gameconn);
+	startEngineGame(flib_gameconn_getport(gameconn));
+
+	while(gameconn) {
+		flib_gameconn_tick(gameconn);
+	}
+}
+
+void handleNetDisconnect(void *context, int reason, const char *message) {
+	flib_log_i("Disconnected: %s", message);
+	flib_netconn_destroy(*(flib_netconn**)context);
+	*(flib_netconn**)context = NULL;
+}
+
+void handleNetConnected(void *context) {
+	const flib_roomlist *roomlist = flib_netconn_get_roomlist(*(flib_netconn**)context);
+	flib_log_i("List of rooms:");
+	for(int i=0; i<roomlist->roomCount; i++) {
+		flib_roomlist_room *room = roomlist->rooms[i];
+		flib_log_i("%1s %20s %20s %2i %2i %20s %20s %20s", room->inProgress ? "X" : " ", room->name, room->owner, room->playerCount, room->teamCount, room->map, room->scheme, room->weapons);
+	}
+	flib_netconn_send_joinRoom(*(flib_netconn**)context, "frontlib test");
+}
+
+void handleLobbyJoin(void *context, const char *nick) {
+	flib_log_i("%s joined", nick);
+}
+
+void handleChat(void *context, const char *nick, const char *msg) {
+	flib_log_i("%s: %s", nick, msg);
+	if(!memcmp("frontbot ", msg, strlen("frontbot "))) {
+		const char *command = msg+strlen("frontbot ");
+		if(!memcmp("quit", command, strlen("quit"))) {
+			flib_netconn_send_quit(*(flib_netconn**)context, "Yeth Mathter");
+		} else if(!memcmp("describe ", command, strlen("describe "))) {
+			const char *roomname = command+strlen("describe ");
+			const flib_roomlist *roomlist = flib_netconn_get_roomlist(*(flib_netconn**)context);
+			flib_roomlist_room *room = flib_roomlist_find((flib_roomlist*)roomlist, roomname);
+			if(!room) {
+				flib_netconn_send_chat(*(flib_netconn**)context, "Unknown room.");
+			} else {
+				char *text = flib_asprintf(
+						"%s is a room created by %s, where %i players (%i teams) are %s on %s%s, using the %s scheme and %s weaponset.",
+						room->name,
+						room->owner,
+						room->playerCount,
+						room->teamCount,
+						room->inProgress ? "fighting" : "preparing to fight",
+						room->map[0]=='+' ? "" : "the map ",
+						!strcmp("+rnd+", room->map) ? "a random map" :
+								!strcmp("+maze+", room->map) ? "a random maze" :
+								!strcmp("+drawn+", room->map) ? "a hand-drawn map" :
+								room->map,
+						room->scheme,
+						room->weapons);
+				if(text) {
+					flib_netconn_send_chat(*(flib_netconn**)context, text);
+				}
+				free(text);
+			}
+		} else if(!memcmp("join ", command, strlen("join "))) {
+			const char *roomname = command+strlen("join ");
+			flib_netconn_send_joinRoom(*(flib_netconn**)context, roomname);
+		} else if(!memcmp("ready", command, strlen("ready"))) {
+			flib_netconn_send_toggleReady(*(flib_netconn**)context);
+		}
+	}
+}
+
+void handleEnterRoom(void *context, bool isChief) {
+	flib_netconn_send_toggleReady(*(flib_netconn**)context);
+}
+
+flib_gameconn *gameconn = NULL;
+
+void emFromNetHandler(void *context, const char *em, int size) {
+	flib_gameconn_send_enginemsg(gameconn, (const uint8_t*)em, size);
+}
+
+void emFromEngineHandler(void *context, const uint8_t *em, int size) {
+	flib_netconn_send_engineMessage((flib_netconn*)context, em, size);
+}
+
+void handleRunGame(void *context) {
+	flib_gamesetup *gamesetup = flib_netconn_create_gameSetup((flib_netconn*)context);
+	if(gamesetup) {
+		gameconn = flib_gameconn_create("frontbot", gamesetup, true);
+		flib_gameconn_onEngineMessage(gameconn, emFromEngineHandler, context);
+		flib_gameconn_onDisconnect(gameconn, onDisconnect, &gameconn);
+		startEngineGame(flib_gameconn_getport(gameconn));
+	}
+}
+
+int main(int argc, char *argv[]) {
+	flib_init(0);
+	flib_log_setLevel(FLIB_LOGLEVEL_ALL);
+
+	//testMapPreview();
+	//testDemo();
+	//testSave();
+	//testGame();
+
+	flib_cfg_meta *meta = flib_cfg_meta_from_ini("metasettings.ini");
+	assert(meta);
+	flib_netconn *conn = flib_netconn_create("frontbot", meta, "140.247.62.101", 46631);
+	assert(conn);
+	flib_cfg_meta_release(meta);
+
+	flib_netconn_onConnected(conn, handleNetConnected, &conn);
+	flib_netconn_onDisconnected(conn, handleNetDisconnect, &conn);
+	flib_netconn_onLobbyJoin(conn, handleLobbyJoin, &conn);
+	flib_netconn_onChat(conn, handleChat, &conn);
+	flib_netconn_onEnterRoom(conn, handleEnterRoom, conn);
+	flib_netconn_onRunGame(conn, handleRunGame, conn);
+	flib_netconn_onEngineMessage(conn, emFromNetHandler, NULL);
+
+	while(conn) {
+		flib_netconn_tick(conn);
+		if(gameconn) {
+			flib_gameconn_tick(gameconn);
+		}
+	}
+
+	flib_quit();
+	return 0;
+}
--- a/project_files/frontlib/util/buffer.c	Mon Jun 25 15:21:18 2012 +0200
+++ b/project_files/frontlib/util/buffer.c	Wed Jun 27 18:02:45 2012 +0200
@@ -94,29 +94,20 @@
 }
 
 int flib_vector_append(flib_vector *vec, const void *data, size_t len) {
-	if(!vec) {
-		flib_log_e("null parameter in flib_vector_append");
-		return 0;
-	}
-
-	if(len > SIZE_MAX-vec->size) {
-		return 0;
+	if(!log_badparams_if(!vec || (!data && len>0))
+			&& !log_oom_if(len > SIZE_MAX-vec->size)) {
+		size_t oldSize = vec->size;
+		if(!log_oom_if(flib_vector_resize(vec, vec->size+len))) {
+			memmove(((uint8_t*)vec->data) + oldSize, data, len);
+			return 0;
+		}
 	}
-
-	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;
+	return -1;
 }
 
 int flib_vector_appendf(flib_vector *vec, const char *fmt, ...) {
 	int result = -1;
-	if(!vec || !fmt) {
-		flib_log_e("null parameter in flib_vector_appendf");
-	} else {
+	if(!log_badparams_if(!vec || !fmt)) {
 		va_list argp;
 		va_start(argp, fmt);
 		char *formatted = flib_vasprintf(fmt, argp);
@@ -125,9 +116,7 @@
 
 		if(formatted) {
 			size_t len = strlen(formatted);
-			if(flib_vector_append(vec, formatted, len) == len) {
-				result = 0;
-			}
+			result = flib_vector_append(vec, formatted, len);
 		}
 	}
 	return result;
--- a/project_files/frontlib/util/buffer.h	Mon Jun 25 15:21:18 2012 +0200
+++ b/project_files/frontlib/util/buffer.h	Wed Jun 27 18:02:45 2012 +0200
@@ -48,8 +48,8 @@
 
 /**
  * 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.
+ * Returns 0 on success.
  */
 int flib_vector_append(flib_vector *vec, const void *data, size_t len);
 
--- a/project_files/frontlib/util/list.c	Mon Jun 25 15:21:18 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,42 +0,0 @@
-#include "list.h"
-
-#include <string.h>
-#include "util.h"
-#include "logging.h"
-
-void *flib_list_insert(void *list, int *listSizePtr, size_t elementSize, void *elementPtr, int pos) {
-	void *result = NULL;
-	if(!listSizePtr || !elementPtr || pos < 0 || pos > *listSizePtr) {
-		flib_log_e("Invalid parameter in flib_list_insert");
-	} else {
-		unsigned char *newList = flib_realloc(list, ((*listSizePtr)+1)*elementSize);
-		if(newList) {
-			memmove(newList + (pos+1)*elementSize, newList + pos*elementSize, ((*listSizePtr)-pos)*elementSize);
-			memmove(newList + pos*elementSize, elementPtr, elementSize);
-			(*listSizePtr)++;
-			result = newList;
-		}
-	}
-	return result;
-}
-
-void *flib_list_delete(void *list, int *listSizePtr, size_t elementSize, int pos) {
-	void *result = NULL;
-	if(!listSizePtr || pos < 0 || pos >= *listSizePtr) {
-		flib_log_e("Invalid parameter in flib_list_delete");
-	} else {
-		unsigned char *charList = list;
-		memmove(charList + (pos*elementSize), charList + (pos+1)*elementSize, (*listSizePtr-(pos+1))*elementSize);
-		(*listSizePtr)--;
-
-		// If the realloc fails, just keep using the old buffer...
-		size_t newCharSize = (*listSizePtr)*elementSize;
-		void *newList = flib_realloc(list, newCharSize);
-		if(newList || newCharSize==0) {
-			result = newList;
-		} else {
-			result = list;
-		}
-	}
-	return result;
-}
--- a/project_files/frontlib/util/list.h	Mon Jun 25 15:21:18 2012 +0200
+++ b/project_files/frontlib/util/list.h	Wed Jun 27 18:02:45 2012 +0200
@@ -6,19 +6,60 @@
 #define LIST_H_
 
 #include <stddef.h>
+#include <string.h>
+#include <stdlib.h>
+#include "util.h"
+#include "logging.h"
 
 /**
- * Insert element into the list and increase listSize.
- * Returns a pointer to the modified list on success, NULL on failure. On success, the old
- * pointer is no longer valid, and on failure the list remains unchanged (similar to realloc)
+ * Generate a static function that inserts a new value into a heap array of the given type,
+ * using realloc and memmove to increase the capacity and shift existing values.
+ * The function takes a pointer to the array variable and a pointer to the size variable
+ * because both can be changed by this operation (realloc / increment).
+ * The function returns 0 on success and leaves the array unchanged on error.
  */
-void *flib_list_insert(void *list, int *listSizePtr, size_t elementSize, void *elementPtr, int pos);
+#define GENERATE_STATIC_LIST_INSERT(fname, type) \
+	static int fname(type **listptr, int *listSizePtr, type element, int pos) { \
+		int result = -1; \
+		if(!listptr || !listSizePtr || pos < 0 || pos > *listSizePtr) { \
+			flib_log_e("Invalid parameter in "#fname); \
+		} else { \
+			type *newList = flib_realloc(*listptr, ((*listSizePtr)+1)*sizeof(type)); \
+			if(newList) { \
+				memmove(newList + (pos+1), newList + pos, ((*listSizePtr)-pos)*sizeof(type)); \
+				newList[pos] = element; \
+				(*listSizePtr)++; \
+				*listptr = newList; \
+				result = 0; \
+			} \
+		} \
+		return result; \
+	}
 
 /**
- * Remove an element from the list and decrease listSize.
- * Returns a pointer to the modified list on success, NULL on failure. On success, the old
- * pointer is no longer valid, and on failure the list remains unchanged (similar to realloc)
+ * Generate a static function that deletes a value from a heap array of the given type,
+ * using realloc and memmove to decrease the capacity and shift existing values.
+ * The function takes a pointer to the array variable and a pointer to the size variable
+ * because both can be changed by this operation (realloc / decrement).
+ * The function returns 0 on success and leaves the array unchanged on error.
  */
-void *flib_list_delete(void *list, int *listSizePtr, size_t elementSize, int pos);
+#define GENERATE_STATIC_LIST_DELETE(fname, type) \
+	static int fname(type **listPtr, int *listSizePtr, int pos) { \
+		int result = -1; \
+		if(!listPtr || !listSizePtr || pos < 0 || pos >= *listSizePtr) { \
+			flib_log_e("Invalid parameter in "#fname); \
+		} else { \
+			memmove((*listPtr) + pos, (*listPtr) + (pos+1), ((*listSizePtr)-(pos+1))*sizeof(type)); \
+			(*listSizePtr)--; \
+			\
+			size_t newCharSize = (*listSizePtr)*sizeof(type); \
+			type *newList = flib_realloc((*listPtr), newCharSize); \
+			if(newList || newCharSize==0) { \
+				(*listPtr) = newList; \
+			} /* If the realloc fails, just keep using the old buffer...*/ \
+			result = 0; \
+		} \
+		return result; \
+	}
 
 #endif /* LIST_H_ */
--- a/project_files/frontlib/util/logging.c	Mon Jun 25 15:21:18 2012 +0200
+++ b/project_files/frontlib/util/logging.c	Wed Jun 27 18:02:45 2012 +0200
@@ -34,44 +34,47 @@
     fprintf(flib_log_getfile(), "%s", buffer);
 }
 
-static void flib_vflog(const char *prefix, int level, const char *fmt, va_list args) {
+static const char *getPrefix(int level) {
+	switch(level) {
+	case FLIB_LOGLEVEL_ERROR: return "E";
+	case FLIB_LOGLEVEL_WARNING: return "W";
+	case FLIB_LOGLEVEL_INFO: return "I";
+	case FLIB_LOGLEVEL_DEBUG: return "D";
+	default: return "?";
+	}
+}
+
+static void _flib_vflog(const char *func, int level, const char *fmt, va_list args) {
 	FILE *logfile = flib_log_getfile();
 	if(level >= flib_loglevel) {
-		fprintf(logfile, "%s ", prefix);
+		fprintf(logfile, "%s ", getPrefix(level));
 		log_time(logfile);
-		fprintf(logfile, "  ", prefix);
+		fprintf(logfile, " [%-30s] ", func);
 		vfprintf(logfile, fmt, args);
 		fprintf(logfile, "\n");
 		fflush(logfile);
 	}
 }
 
-void flib_log_e(const char *fmt, ...) {
+void _flib_flog(const char *func, int level, const char *fmt, ...) {
 	va_list argp;
 	va_start(argp, fmt);
-	flib_vflog("E", FLIB_LOGLEVEL_ERROR, fmt, argp);
+	_flib_vflog(func, level, fmt, argp);
 	va_end(argp);
 }
 
-void flib_log_w(const char *fmt, ...) {
-	va_list argp;
-	va_start(argp, fmt);
-	flib_vflog("W", FLIB_LOGLEVEL_WARNING, fmt, argp);
-	va_end(argp);
+bool _flib_fassert(const char *func, int level, bool cond, const char *fmt, ...) {
+	if(!cond) {
+		va_list argp;
+		va_start(argp, fmt);
+		_flib_vflog(func, level, fmt, argp);
+		va_end(argp);
+	}
+	return !cond;
 }
 
-void flib_log_i(const char *fmt, ...) {
-	va_list argp;
-	va_start(argp, fmt);
-	flib_vflog("I", FLIB_LOGLEVEL_INFO, fmt, argp);
-	va_end(argp);
-}
-
-void flib_log_d(const char *fmt, ...) {
-	va_list argp;
-	va_start(argp, fmt);
-	flib_vflog("D", FLIB_LOGLEVEL_DEBUG, fmt, argp);
-	va_end(argp);
+bool _flib_assert_params(const char *func, bool cond) {
+	return _flib_fassert(func, FLIB_LOGLEVEL_ERROR, cond, "Invalid parameter to function");
 }
 
 int flib_log_getLevel() {
--- a/project_files/frontlib/util/logging.h	Mon Jun 25 15:21:18 2012 +0200
+++ b/project_files/frontlib/util/logging.h	Wed Jun 27 18:02:45 2012 +0200
@@ -17,10 +17,27 @@
  */
 char* flib_format_ip(uint32_t numip);
 
-void flib_log_e(const char *fmt, ...);
-void flib_log_w(const char *fmt, ...);
-void flib_log_i(const char *fmt, ...);
-void flib_log_d(const char *fmt, ...);
+/**
+ * Evaluates the expression cond. If it is true, a formatted error will be logged.
+ * Returns true if an error is logged, false otherwise (i.e. the boolean value of the argument)
+ */
+#define log_e_if(cond, ...) _flib_fassert(__func__, FLIB_LOGLEVEL_ERROR, !(bool)(cond), __VA_ARGS__)
+#define log_w_if(cond, ...) _flib_fassert(__func__, FLIB_LOGLEVEL_WARNING, !(bool)(cond), __VA_ARGS__)
+
+/**
+ * Shorthand for some common error types
+ */
+#define log_badparams_if(cond) log_e_if(cond, "Invalid Parameters")
+#define log_oom_if(cond) log_e_if(cond, "Out of Memory")
+
+#define flib_log_e(...) _flib_flog(__func__, FLIB_LOGLEVEL_ERROR, __VA_ARGS__)
+#define flib_log_w(...) _flib_flog(__func__, FLIB_LOGLEVEL_WARNING, __VA_ARGS__)
+#define flib_log_i(...) _flib_flog(__func__, FLIB_LOGLEVEL_INFO, __VA_ARGS__)
+#define flib_log_d(...) _flib_flog(__func__, FLIB_LOGLEVEL_DEBUG, __VA_ARGS__)
+
+bool _flib_assert_params(const char *func, bool cond);
+bool _flib_fassert(const char *func, int level, bool cond, const char *fmt, ...);
+void _flib_flog(const char *func, int level, const char *fmt, ...);
 
 int flib_log_getLevel();
 void flib_log_setLevel(int level);
--- a/project_files/frontlib/util/util.c	Mon Jun 25 15:21:18 2012 +0200
+++ b/project_files/frontlib/util/util.c	Wed Jun 27 18:02:45 2012 +0200
@@ -162,3 +162,27 @@
     char *shrunk = realloc(outbuf, outpos+1);
     return shrunk ? shrunk : outbuf;
 }
+
+bool flib_contains_dir_separator(const char *str) {
+	if(!log_badparams_if(!str)) {
+		for(;*str;str++) {
+			if(*str=='\\' || *str=='/') {
+				return true;
+			}
+		}
+	}
+	return false;
+}
+
+int flib_gets(char *str, size_t strlen) {
+	if(fgets(str, strlen, stdin)) {
+		for(char *s=str; *s; s++) {
+			if(*s=='\r' || *s=='\n') {
+				*s = 0;
+				break;
+			}
+		}
+		return 0;
+	}
+	return -1;
+}
--- a/project_files/frontlib/util/util.h	Mon Jun 25 15:21:18 2012 +0200
+++ b/project_files/frontlib/util/util.h	Wed Jun 27 18:02:45 2012 +0200
@@ -87,4 +87,12 @@
  */
 char *flib_urldecode(const char *str);
 
+/**
+ * Figure out if the string contains / or \. Useful in routines that
+ * construct filenames.
+ */
+bool flib_contains_dir_separator(const char *str);
+
+int flib_gets(char *str, size_t strlen);
+
 #endif