frontlib:
authorMedo <smaxein@googlemail.com>
Thu, 19 Jul 2012 17:56:38 +0200
changeset 7338 1ed603a54ebd
parent 7336 f821f7d727b7
child 7340 62043f5f7c67
frontlib: - Removed automatic roomlist handling, since the server doesn't inform of every change after all - Added flib_netconn_get_playername - Added a callback mechanism for logging
project_files/frontlib/Android.mk
project_files/frontlib/model/room.c
project_files/frontlib/model/room.h
project_files/frontlib/model/roomlist.c
project_files/frontlib/model/roomlist.h
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/util/logging.c
project_files/frontlib/util/logging.h
--- a/project_files/frontlib/Android.mk	Wed Jul 18 21:34:49 2012 +0200
+++ b/project_files/frontlib/Android.mk	Thu Jul 19 17:56:38 2012 +0200
@@ -9,7 +9,7 @@
 LOCAL_SRC_FILES := base64/base64.c iniparser/iniparser.c \
 	iniparser/dictionary.c ipc/gameconn.c ipc/ipcbase.c \
 	ipc/ipcprotocol.c ipc/mapconn.c md5/md5.c model/scheme.c \
-	model/gamesetup.c model/map.c model/mapcfg.c model/roomlist.c \
+	model/gamesetup.c model/map.c model/mapcfg.c model/room.c \
 	model/schemelist.c model/team.c model/teamlist.c model/weapon.c \
 	net/netbase.c net/netconn_callbacks.c net/netconn_send.c \
 	net/netconn.c net/netprotocol.c util/buffer.c util/inihelper.c \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/room.c	Thu Jul 19 17:56:38 2012 +0200
@@ -0,0 +1,34 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * 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; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include "room.h"
+#include "../util/logging.h"
+
+#include <stdlib.h>
+
+void flib_room_destroy(flib_room *room) {
+	if(room) {
+		free(room->map);
+		free(room->name);
+		free(room->owner);
+		free(room->scheme);
+		free(room->weapons);
+		free(room);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/room.h	Thu Jul 19 17:56:38 2012 +0200
@@ -0,0 +1,42 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * 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; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+/**
+ * Models the room information for the lobby roomlist.
+ */
+
+#ifndef ROOM_H_
+#define ROOM_H_
+
+#include <stdbool.h>
+
+typedef struct {
+    bool inProgress;	// true if the game is running
+    char *name;
+    int playerCount;
+    int teamCount;
+    char *owner;
+    char *map;			// This is either a map name, or one of +rnd+, +maze+ or +drawn+.
+    char *scheme;
+    char *weapons;
+} flib_room;
+
+void flib_room_destroy();
+
+#endif
--- a/project_files/frontlib/model/roomlist.c	Wed Jul 18 21:34:49 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,155 +0,0 @@
-/*
- * Hedgewars, a free turn based strategy game
- * 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; either version 2
- * of the License, or (at your option) any later version.
- *
- * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-
-#include "roomlist.h"
-
-#include "../util/util.h"
-#include "../util/list.h"
-#include "../util/logging.h"
-
-#include <stdlib.h>
-#include <string.h>
-
-flib_roomlist *flib_roomlist_create() {
-	return flib_calloc(1, sizeof(flib_roomlist));
-}
-
-static void flib_roomlist_room_destroy(flib_room *room) {
-	if(room) {
-		free(room->map);
-		free(room->name);
-		free(room->owner);
-		free(room->scheme);
-		free(room->weapons);
-		free(room);
-	}
-}
-
-void flib_roomlist_destroy(flib_roomlist *list) {
-	if(list) {
-		for(int i=0; i<list->roomCount; i++) {
-			flib_roomlist_room_destroy(list->rooms[i]);
-		}
-		free(list->rooms);
-		free(list);
-	}
-}
-
-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]);
-		tmpRoom->playerCount = atoi(params[2]);
-		tmpRoom->teamCount = atoi(params[3]);
-		tmpRoom->owner = flib_strdupnull(params[4]);
-		tmpRoom->map = flib_strdupnull(params[5]);
-		tmpRoom->scheme = flib_strdupnull(params[6]);
-		tmpRoom->weapons = flib_strdupnull(params[7]);
-		if(tmpRoom->name && tmpRoom->owner && tmpRoom->map && tmpRoom->scheme && tmpRoom->weapons) {
-			result = tmpRoom;
-			tmpRoom = NULL;
-		}
-	}
-	flib_roomlist_room_destroy(tmpRoom);
-	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(!log_badargs_if2(list==NULL, params==NULL)) {
-		flib_room *tmpRoom = fillRoomFromParams(params);
-		if(tmpRoom) {
-			if(!insertRoom(&list->rooms, &list->roomCount, tmpRoom, 0)) {
-				tmpRoom = NULL;
-				result = 0;
-			}
-		}
-		flib_roomlist_room_destroy(tmpRoom);
-	}
-	return result;
-}
-
-int flib_roomlist_delete(flib_roomlist *list, const char *name) {
-	int result = -1;
-	if(!log_badargs_if2(list==NULL, name==NULL)) {
-		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 result;
-}
-
-int flib_roomlist_update(flib_roomlist *list, const char *name, char **params) {
-	int result = -1;
-	if(!log_badargs_if3(list==NULL, name==NULL, params==NULL)) {
-		flib_room *tmpRoom = fillRoomFromParams(params);
-		int roomid = findRoom(list, name);
-		if(tmpRoom && roomid>=0) {
-			flib_roomlist_room_destroy(list->rooms[roomid]);
-			list->rooms[roomid] = tmpRoom;
-			tmpRoom = NULL;
-			result = 0;
-		}
-		flib_roomlist_room_destroy(tmpRoom);
-	}
-	return result;
-}
-
-flib_room *flib_roomlist_find(const flib_roomlist *list, const char *name) {
-	flib_room *result = NULL;
-	if(!log_badargs_if2(list==NULL, name==NULL)) {
-		int roomid = findRoom(list, name);
-		if(roomid>=0) {
-			result = list->rooms[roomid];
-		}
-	}
-	return result;
-}
-
-void flib_roomlist_clear(flib_roomlist *list) {
-	if(!log_badargs_if(list==NULL)) {
-		for(int i=0; i<list->roomCount; i++) {
-			flib_roomlist_room_destroy(list->rooms[i]);
-		}
-		free(list->rooms);
-		list->rooms = NULL;
-		list->roomCount = 0;
-	}
-}
--- a/project_files/frontlib/model/roomlist.h	Wed Jul 18 21:34:49 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,80 +0,0 @@
-/*
- * Hedgewars, a free turn based strategy game
- * 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; either version 2
- * of the License, or (at your option) any later version.
- *
- * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-
-/**
- * Models the list of rooms on a server for netplay.
- */
-
-#ifndef ROOMLIST_H_
-#define ROOMLIST_H_
-
-#include <stdbool.h>
-
-typedef struct {
-    bool inProgress;	// true if the game is running
-    char *name;
-    int playerCount;
-    int teamCount;
-    char *owner;
-    char *map;			// This is either a map name, or one of +rnd+, +maze+ or +drawn+.
-    char *scheme;
-    char *weapons;
-} flib_room;
-
-typedef struct {
-	int roomCount;
-	flib_room **rooms;
-} flib_roomlist;
-
-flib_roomlist *flib_roomlist_create();
-
-void flib_roomlist_destroy(flib_roomlist *list);
-
-/**
- * Insert a new room at the start of the list. The room is defined by the params-array,
- * which must consist of 8 non-null strings, as sent by the server in netplay.
- *
- * Returns 0 on success.
- */
-int flib_roomlist_add(flib_roomlist *list, char **params);
-
-/**
- * Update the room with the name [name] with parameters sent by the server.
- *
- * Returns 0 on success.
- */
-int flib_roomlist_update(flib_roomlist *list, const char *name, char **params);
-
-/**
- * Returns the room with the name [name] from the list if it exists, NULL otherwise
- */
-flib_room *flib_roomlist_find(const flib_roomlist *list, const char *name);
-
-/**
- * Removes all rooms from the list
- */
-void flib_roomlist_clear(flib_roomlist *list);
-
-/**
- * Delete the room with the name [name] from the room list.
- * Returns 0 on success.
- */
-int flib_roomlist_delete(flib_roomlist *list, const char *name);
-
-#endif
--- a/project_files/frontlib/net/netconn.c	Wed Jul 18 21:34:49 2012 +0200
+++ b/project_files/frontlib/net/netconn.c	Thu Jul 19 17:56:38 2012 +0200
@@ -24,7 +24,6 @@
 #include "netprotocol.h"
 #include "../util/logging.h"
 #include "../util/util.h"
-#include "../model/roomlist.h"
 #include "../md5/md5.h"
 #include "../base64/base64.h"
 #include "../model/mapcfg.h"
@@ -46,8 +45,6 @@
 			newConn->netconnState = NETCONN_STATE_CONNECTING;
 			newConn->isAdmin = false;
 			newConn->metaCfg = flib_metascheme_retain(metacfg);
-			newConn->roomList.roomCount = 0;
-			newConn->roomList.rooms = NULL;
 
 			newConn->isChief = false;
 			newConn->map = flib_map_create_named("", "NoSuchMap");
@@ -88,7 +85,6 @@
 			free(conn->dataDirPath);
 
 			flib_metascheme_release(conn->metaCfg);
-			flib_roomlist_clear(&conn->roomList);
 
 			flib_map_release(conn->map);
 			flib_teamlist_clear(&conn->pendingTeamlist);
@@ -102,13 +98,6 @@
 	}
 }
 
-const flib_roomlist *flib_netconn_get_roomlist(flib_netconn *conn) {
-	if(!log_badargs_if(conn==NULL)) {
-		return &conn->roomList;
-	}
-	return NULL;
-}
-
 bool flib_netconn_is_chief(flib_netconn *conn) {
 	if(!log_badargs_if(conn==NULL) && flib_netconn_is_in_room_context(conn)) {
 		return conn->isChief;
@@ -116,6 +105,13 @@
 	return false;
 }
 
+const char *flib_netconn_get_playername(flib_netconn *conn) {
+	if(!log_badargs_if(conn==NULL)) {
+		return conn->playerName;
+	}
+	return NULL;
+}
+
 void netconn_leaveRoom(flib_netconn *conn) {
 	conn->netconnState = NETCONN_STATE_LOBBY;
 	conn->isChief = false;
@@ -269,16 +265,14 @@
 	        if(netmsg->partCount % 8 != 1) {
 	        	flib_log_w("Net: Malformed ROOMS message");
 	        } else {
-	        	flib_roomlist_clear(&conn->roomList);
-	        	for(int i=1; i<netmsg->partCount; i+=8) {
-	        		if(flib_roomlist_add(&conn->roomList, netmsg->parts+i)) {
-	        			flib_log_e("Error adding room to list in ROOMS message");
+	        	int roomCount = netmsg->partCount/8;
+	        	flib_room **rooms = flib_room_array_from_netmsg(netmsg->parts+1, roomCount);
+	        	if(rooms) {
+	        		conn->onRoomlistCb(conn->onRoomlistCtx, (const flib_room**)rooms, roomCount);
+	        		for(int i=0; i<roomCount; i++) {
+	        			flib_room_destroy(rooms[i]);
 	        		}
-	        	}
-	        	if(conn->netconnState == NETCONN_STATE_CONNECTING) {
-	        		// We delay the "connected" callback until now to ensure the room list is avaliable.
-	        		conn->onConnectedCb(conn->onConnectedCtx);
-					conn->netconnState = NETCONN_STATE_LOBBY;
+	        		free(rooms);
 	        	}
 	        }
 	    } else if (!strcmp(cmd, "SERVER_MESSAGE")) {
@@ -377,14 +371,9 @@
 				for(int i = 1; i < netmsg->partCount; ++i)
 				{
 					bool isMe = !strcmp(conn->playerName, netmsg->parts[i]);
-					if (isMe) {
-						if(flib_netbase_sendf(conn->netBase, "%s\n\n", "LIST")) {
-							// If sending this fails, the protocol breaks (we'd be waiting infinitely for the room list)
-							flib_netbase_sendf(net, "%s\n%s\n\n", "QUIT", "Client error");
-							conn->netconnState = NETCONN_STATE_DISCONNECTED;
-							conn->onDisconnectedCb(conn->onDisconnectedCtx, NETCONN_DISCONNECT_INTERNAL_ERROR, "Failed to send a critical message.");
-							exit = true;
-						}
+					if (isMe && conn->netconnState == NETCONN_STATE_CONNECTING) {
+						conn->onConnectedCb(conn->onConnectedCtx);
+						conn->netconnState = NETCONN_STATE_LOBBY;
 					}
 					conn->onLobbyJoinCb(conn->onLobbyJoinCtx, netmsg->parts[i]);
 				}
@@ -398,24 +387,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)) {
-	    			flib_log_e("Error adding new room to list");
-	    		} else {
-	    			conn->onRoomAddCb(conn->onRoomAddCtx, conn->roomList.rooms[0]);
+	    		flib_room *room = flib_room_from_netmsg(netmsg->parts+2);
+	    		if(room) {
+	    			conn->onRoomAddCb(conn->onRoomAddCtx, room);
 	    		}
+	    		flib_room_destroy(room);
 			} else if(!strcmp(subcmd, "UPD") && netmsg->partCount == 11) {
-				char *newName = netmsg->parts[4];
-	    		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, newName));
+				flib_room *room = flib_room_from_netmsg(netmsg->parts+3);
+				if(room) {
+	    			conn->onRoomUpdateCb(conn->onRoomUpdateCtx, netmsg->parts[2], room);
 	    		}
+				flib_room_destroy(room);
 			} else if(!strcmp(subcmd, "DEL") && netmsg->partCount == 3) {
-	    		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]);
-	    		}
+				conn->onRoomDeleteCb(conn->onRoomDeleteCtx, netmsg->parts[2]);
 			} else {
 				flib_log_w("Net: Unknown or malformed ROOM subcommand: %s", subcmd);
 			}
--- a/project_files/frontlib/net/netconn.h	Wed Jul 18 21:34:49 2012 +0200
+++ b/project_files/frontlib/net/netconn.h	Thu Jul 19 17:56:38 2012 +0200
@@ -22,7 +22,7 @@
 
 #include "../model/gamesetup.h"
 #include "../model/scheme.h"
-#include "../model/roomlist.h"
+#include "../model/room.h"
 
 #include <stddef.h>
 #include <stdint.h>
@@ -73,12 +73,6 @@
  */
 void flib_netconn_tick(flib_netconn *conn);
 
-
-/**
- * Return the current roomlist. Don't free or modify.
- */
-const flib_roomlist *flib_netconn_get_roomlist(flib_netconn *conn);
-
 /**
  * Are you currently the owner of this room? The return value only makes sense in
  * NETCONN_STATE_ROOM and NETCONN_STATE_INGAME states.
@@ -91,6 +85,13 @@
 bool flib_netconn_is_in_room_context(flib_netconn *conn);
 
 /**
+ * Returns the playername. This is *probably* the one provided on creation, but
+ * if that name was already taken, a different one could have been set by the
+ * onNickTaken callback or its default implementation.
+ */
+const char *flib_netconn_get_playername(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.
@@ -127,6 +128,12 @@
 int flib_netconn_send_nick(flib_netconn *conn, const char *nick);
 
 /**
+ * Request an update of the room list. Only makes sense when in lobby state.
+ * If the action succeeds, you will receive an onRoomlist callback containing the current room data.
+ */
+int flib_netconn_send_request_roomlist(flib_netconn *conn);
+
+/**
  * Join a room as guest (not chief). Only makes sense when in lobby state. If the action succeeds, you will
  * receive an onEnterRoom callback with chief=false.
  */
@@ -356,10 +363,13 @@
 void flib_netconn_onDisconnected(flib_netconn *conn, void (*callback)(void *context, int reason, const char *message), void* context);
 
 /**
- * Callbacks for room list updates. The room list is managed automatically and can be queried with
- * flib_netconn_get_roomlist() as soon as the onConnected callback is fired. These callbacks
- * provide notification about changes.
+ * Callbacks for room list updates. The roomlist can be queried with flib_netconn_send_request_roomlist(), which will
+ * trigger flib_netconn_onRoomlist once the server replies. Additionally, the roomAdd/delete/update callbacks will fire
+ * whenever the server informs about these events, which can happen *before* the roomlist is first received - so be sure
+ * not to blindly reference your room list in these callbacks. The server currently only sends updates when a room changes
+ * its name, so in order to update other room information you need to query the roomlist again.
  */
+void flib_netconn_onRoomlist(flib_netconn *conn, void (*callback)(void *context, const flib_room **rooms, int roomCount), 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_room *room), void* context);
--- a/project_files/frontlib/net/netconn_callbacks.c	Wed Jul 18 21:34:49 2012 +0200
+++ b/project_files/frontlib/net/netconn_callbacks.c	Thu Jul 19 17:56:38 2012 +0200
@@ -64,6 +64,7 @@
 	flib_netconn_onMessage(conn, NULL, NULL);
 	flib_netconn_onConnected(conn, NULL, NULL);
 	flib_netconn_onDisconnected(conn, NULL, NULL);
+	flib_netconn_onRoomlist(conn, NULL, NULL);
 	flib_netconn_onRoomAdd(conn, NULL, NULL);
 	flib_netconn_onRoomDelete(conn, NULL, NULL);
 	flib_netconn_onRoomUpdate(conn, NULL, NULL);
@@ -120,6 +121,7 @@
 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(onRoomlist, (void *context, const flib_room **rooms, int roomCount));
 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_room *room));
--- a/project_files/frontlib/net/netconn_internal.h	Wed Jul 18 21:34:49 2012 +0200
+++ b/project_files/frontlib/net/netconn_internal.h	Thu Jul 19 17:56:38 2012 +0200
@@ -26,10 +26,10 @@
 
 #include "netconn.h"
 #include "netbase.h"
-#include "../model/roomlist.h"
 #include "../model/map.h"
 #include "../model/team.h"
 #include "../model/weapon.h"
+#include "../model/room.h"
 
 #include <stdbool.h>
 #include <stdint.h>
@@ -44,7 +44,6 @@
 	bool isAdmin;				// Player is server administrator
 
 	flib_metascheme *metaCfg;
-	flib_roomlist roomList;
 
 	bool isChief;				// Player can modify the current room
 	flib_map *map;
@@ -63,6 +62,9 @@
 	void (*onDisconnectedCb)(void *context, int reason, const char *message);
 	void *onDisconnectedCtx;
 
+	void (*onRoomlistCb)(void *context, const flib_room **rooms, int roomCount);
+	void *onRoomlistCtx;
+
 	void (*onRoomAddCb)(void *context, const flib_room *room);
 	void *onRoomAddCtx;
 
--- a/project_files/frontlib/net/netconn_send.c	Wed Jul 18 21:34:49 2012 +0200
+++ b/project_files/frontlib/net/netconn_send.c	Thu Jul 19 17:56:38 2012 +0200
@@ -107,6 +107,10 @@
 	return result;
 }
 
+int flib_netconn_send_request_roomlist(flib_netconn *conn) {
+	return sendVoid(conn, "LIST");
+}
+
 int flib_netconn_send_joinRoom(flib_netconn *conn, const char *room) {
 	if(!sendStr(conn, "JOIN_ROOM", room)) {
 		conn->isChief = false;
--- a/project_files/frontlib/net/netprotocol.c	Wed Jul 18 21:34:49 2012 +0200
+++ b/project_files/frontlib/net/netprotocol.c	Thu Jul 19 17:56:38 2012 +0200
@@ -140,3 +140,48 @@
 	free(base64decout);
 	return result;
 }
+
+flib_room *flib_room_from_netmsg(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]);
+		tmpRoom->playerCount = atoi(params[2]);
+		tmpRoom->teamCount = atoi(params[3]);
+		tmpRoom->owner = flib_strdupnull(params[4]);
+		tmpRoom->map = flib_strdupnull(params[5]);
+		tmpRoom->scheme = flib_strdupnull(params[6]);
+		tmpRoom->weapons = flib_strdupnull(params[7]);
+		if(tmpRoom->name && tmpRoom->owner && tmpRoom->map && tmpRoom->scheme && tmpRoom->weapons) {
+			result = tmpRoom;
+			tmpRoom = NULL;
+		}
+	}
+	flib_room_destroy(tmpRoom);
+	return result;
+}
+
+int fillRoomArray(flib_room **array, char **params, int count) {
+	for(int i=0; i<count; i++) {
+		array[i] = flib_room_from_netmsg(params + 8*i);
+		if(!array[i]) {
+			return -1;
+		}
+	}
+	return 0;
+}
+
+flib_room **flib_room_array_from_netmsg(char **params, int count) {
+	flib_room **result = flib_calloc(count, sizeof(flib_room*));
+	if(result) {
+		if(fillRoomArray(result, params, count)) {
+			for(int i=0; i<count; i++) {
+				flib_room_destroy(result[i]);
+			}
+			free(result);
+			result = NULL;
+		}
+	}
+	return result;
+}
--- a/project_files/frontlib/net/netprotocol.h	Wed Jul 18 21:34:49 2012 +0200
+++ b/project_files/frontlib/net/netprotocol.h	Thu Jul 19 17:56:38 2012 +0200
@@ -23,9 +23,12 @@
 #include "../model/team.h"
 #include "../model/scheme.h"
 #include "../model/map.h"
+#include "../model/room.h"
 
 #include <stddef.h>
 
+// TODO unify naming
+
 /**
  * Create a new team from this 23-part net message
  */
@@ -52,4 +55,14 @@
  */
 int flib_netmsg_to_drawnmapdata(char *netmsg, uint8_t **outbuf, size_t *outlen);
 
+/**
+ * Create a new room from this 8-part net message
+ */
+flib_room *flib_room_from_netmsg(char **params);
+
+/**
+ * Create an array of count rooms from count*8 netmessage parts
+ */
+flib_room **flib_room_array_from_netmsg(char **params, int count);
+
 #endif /* NETPROTOCOL_H_ */
--- a/project_files/frontlib/util/logging.c	Wed Jul 18 21:34:49 2012 +0200
+++ b/project_files/frontlib/util/logging.c	Thu Jul 19 17:56:38 2012 +0200
@@ -26,6 +26,7 @@
 
 static int flib_loglevel = FLIB_LOGLEVEL_INFO;
 static FILE *flib_logfile = NULL;
+void (*flib_logCallback)(int level, const char *msg) = NULL;
 
 char* flib_format_ip(uint32_t numip) {
 	static char ip[16];
@@ -41,37 +42,66 @@
 	}
 }
 
-static void log_time() {
+static int log_time(char *buffer) {
     time_t timer;
-    char buffer[25];
     struct tm* tm_info;
 
     time(&timer);
     tm_info = localtime(&timer);
 
-    strftime(buffer, 25, "%Y-%m-%d %H:%M:%S", tm_info);
-    fprintf(flib_log_getfile(), "%s", buffer);
+    return strftime(buffer, 25, "%Y-%m-%d %H:%M:%S", tm_info);
 }
 
-static const char *getPrefix(int level) {
+static 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 "?";
+	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 ", getPrefix(level));
-		log_time(logfile);
-		fprintf(logfile, " [%-30s] ", func);
-		vfprintf(logfile, fmt, args);
-		fprintf(logfile, "\n");
-		fflush(logfile);
+		char logbuffer[1024];
+		logbuffer[0] = getPrefix(level);
+		logbuffer[1] = ' ';
+
+		int pos = 2;
+
+		int len = log_time(logbuffer+pos);
+		if(len>=0) {
+			pos += len;
+			if(pos>sizeof(logbuffer)-1) pos = sizeof(logbuffer)-1;
+		} else {
+			return;
+		}
+
+		len = snprintf(logbuffer+pos, sizeof(logbuffer)-pos, " [%-30s] ", func);
+		if(len>=0) {
+			pos += len;
+			if(pos>sizeof(logbuffer)-1) pos = sizeof(logbuffer)-1;
+		} else {
+			return;
+		}
+
+		len = vsnprintf(logbuffer+pos, sizeof(logbuffer)-pos, fmt, args);
+		if(len>=0) {
+			pos += len;
+			if(pos>sizeof(logbuffer)-1) pos = sizeof(logbuffer)-1;
+		} else {
+			return;
+		}
+
+		if(flib_logCallback != NULL) {
+			flib_logCallback(level, logbuffer);
+		} else {
+			FILE *logfile = flib_log_getfile();
+			fputs(logbuffer, logfile);
+			fputc('\n', logfile);
+			fflush(logfile);
+		}
 	}
 }
 
@@ -102,8 +132,14 @@
 
 void flib_log_setFile(FILE *file) {
 	flib_logfile = file;
+	flib_logCallback = NULL;
 }
 
 bool flib_log_isActive(int level) {
 	return level >= flib_log_getLevel();
 }
+
+void flib_log_setCallback(void (*logCallback)(int level, const char *msg)) {
+	flib_logCallback = logCallback;
+	flib_logfile = NULL;
+}
--- a/project_files/frontlib/util/logging.h	Wed Jul 18 21:34:49 2012 +0200
+++ b/project_files/frontlib/util/logging.h	Thu Jul 19 17:56:38 2012 +0200
@@ -84,4 +84,10 @@
 void flib_log_setFile(FILE *logfile);
 bool flib_log_isActive(int level);
 
+/**
+ * Allows logging through an arbitrary callback function. Useful for integrating into an
+ * existing logging system. This overrides setFile and vice versa.
+ */
+void flib_log_setCallback(void (*logCallback)(int level, const char *msg));
+
 #endif /* LOGGING_H_ */