frontlib: Intermittent commit. Things are still in flux but we're getting there :)
authorMedo <smaxein@googlemail.com>
Mon, 25 Jun 2012 00:42:07 +0200
changeset 7271 5608ac657362
parent 7269 5b0aeef8ba2a
child 7273 8eed495fd8da
frontlib: Intermittent commit. Things are still in flux but we're getting there :)
project_files/frontlib/base64/base64.c
project_files/frontlib/base64/base64.h
project_files/frontlib/ipc/gameconn.c
project_files/frontlib/ipc/gameconn.h
project_files/frontlib/ipc/ipcbase.c
project_files/frontlib/ipc/ipcbase.h
project_files/frontlib/ipc/ipcprotocol.c
project_files/frontlib/ipc/ipcprotocol.h
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.h
project_files/frontlib/model/map.c
project_files/frontlib/model/map.h
project_files/frontlib/model/schemelist.c
project_files/frontlib/model/team.c
project_files/frontlib/model/team.h
project_files/frontlib/model/weapon.c
project_files/frontlib/model/weapon.h
project_files/frontlib/net/netconn.c
project_files/frontlib/net/netconn.h
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/socket.c
project_files/frontlib/socket.h
project_files/frontlib/test.c
project_files/frontlib/util/buffer.c
project_files/frontlib/util/buffer.h
project_files/frontlib/util/inihelper.h
project_files/frontlib/util/refcounter.c
project_files/frontlib/util/refcounter.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/base64/base64.c	Mon Jun 25 00:42:07 2012 +0200
@@ -0,0 +1,423 @@
+/* base64.c -- Encode binary data using printable characters.
+   Copyright (C) 1999, 2000, 2001, 2004, 2005, 2006 Free Software
+   Foundation, Inc.
+
+   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, 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.  */
+
+/* Written by Simon Josefsson.  Partially adapted from GNU MailUtils
+ * (mailbox/filter_trans.c, as of 2004-11-28).  Improved by review
+ * from Paul Eggert, Bruno Haible, and Stepan Kasal.
+ *
+ * See also RFC 3548 <http://www.ietf.org/rfc/rfc3548.txt>.
+ *
+ * Be careful with error checking.  Here is how you would typically
+ * use these functions:
+ *
+ * bool ok = base64_decode_alloc (in, inlen, &out, &outlen);
+ * if (!ok)
+ *   FAIL: input was not valid base64
+ * if (out == NULL)
+ *   FAIL: memory allocation error
+ * OK: data in OUT/OUTLEN
+ *
+ * size_t outlen = base64_encode_alloc (in, inlen, &out);
+ * if (out == NULL && outlen == 0 && inlen != 0)
+ *   FAIL: input too long
+ * if (out == NULL)
+ *   FAIL: memory allocation error
+ * OK: data in OUT/OUTLEN.
+ *
+ */
+
+/* Get prototype. */
+#include "base64.h"
+
+/* Get malloc. */
+#include <stdlib.h>
+
+/* Get UCHAR_MAX. */
+#include <limits.h>
+
+/* C89 compliant way to cast 'char' to 'unsigned char'. */
+static inline unsigned char
+to_uchar (char ch)
+{
+  return ch;
+}
+
+/* Base64 encode IN array of size INLEN into OUT array of size OUTLEN.
+   If OUTLEN is less than BASE64_LENGTH(INLEN), write as many bytes as
+   possible.  If OUTLEN is larger than BASE64_LENGTH(INLEN), also zero
+   terminate the output buffer. */
+void
+base64_encode (const char *restrict in, size_t inlen,
+	       char *restrict out, size_t outlen)
+{
+  static const char b64str[64] =
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+  while (inlen && outlen)
+    {
+      *out++ = b64str[(to_uchar (in[0]) >> 2) & 0x3f];
+      if (!--outlen)
+	break;
+      *out++ = b64str[((to_uchar (in[0]) << 4)
+		       + (--inlen ? to_uchar (in[1]) >> 4 : 0))
+		      & 0x3f];
+      if (!--outlen)
+	break;
+      *out++ =
+	(inlen
+	 ? b64str[((to_uchar (in[1]) << 2)
+		   + (--inlen ? to_uchar (in[2]) >> 6 : 0))
+		  & 0x3f]
+	 : '=');
+      if (!--outlen)
+	break;
+      *out++ = inlen ? b64str[to_uchar (in[2]) & 0x3f] : '=';
+      if (!--outlen)
+	break;
+      if (inlen)
+	inlen--;
+      if (inlen)
+	in += 3;
+    }
+
+  if (outlen)
+    *out = '\0';
+}
+
+/* Allocate a buffer and store zero terminated base64 encoded data
+   from array IN of size INLEN, returning BASE64_LENGTH(INLEN), i.e.,
+   the length of the encoded data, excluding the terminating zero.  On
+   return, the OUT variable will hold a pointer to newly allocated
+   memory that must be deallocated by the caller.  If output string
+   length would overflow, 0 is returned and OUT is set to NULL.  If
+   memory allocation failed, OUT is set to NULL, and the return value
+   indicates length of the requested memory block, i.e.,
+   BASE64_LENGTH(inlen) + 1. */
+size_t
+base64_encode_alloc (const char *in, size_t inlen, char **out)
+{
+  size_t outlen = 1 + BASE64_LENGTH (inlen);
+
+  /* Check for overflow in outlen computation.
+   *
+   * If there is no overflow, outlen >= inlen.
+   *
+   * If the operation (inlen + 2) overflows then it yields at most +1, so
+   * outlen is 0.
+   *
+   * If the multiplication overflows, we lose at least half of the
+   * correct value, so the result is < ((inlen + 2) / 3) * 2, which is
+   * less than (inlen + 2) * 0.66667, which is less than inlen as soon as
+   * (inlen > 4).
+   */
+  if (inlen > outlen)
+    {
+      *out = NULL;
+      return 0;
+    }
+
+  *out = malloc (outlen);
+  if (!*out)
+    return outlen;
+
+  base64_encode (in, inlen, *out, outlen);
+
+  return outlen - 1;
+}
+
+/* With this approach this file works independent of the charset used
+   (think EBCDIC).  However, it does assume that the characters in the
+   Base64 alphabet (A-Za-z0-9+/) are encoded in 0..255.  POSIX
+   1003.1-2001 require that char and unsigned char are 8-bit
+   quantities, though, taking care of that problem.  But this may be a
+   potential problem on non-POSIX C99 platforms.
+
+   IBM C V6 for AIX mishandles "#define B64(x) ...'x'...", so use "_"
+   as the formal parameter rather than "x".  */
+#define B64(_)					\
+  ((_) == 'A' ? 0				\
+   : (_) == 'B' ? 1				\
+   : (_) == 'C' ? 2				\
+   : (_) == 'D' ? 3				\
+   : (_) == 'E' ? 4				\
+   : (_) == 'F' ? 5				\
+   : (_) == 'G' ? 6				\
+   : (_) == 'H' ? 7				\
+   : (_) == 'I' ? 8				\
+   : (_) == 'J' ? 9				\
+   : (_) == 'K' ? 10				\
+   : (_) == 'L' ? 11				\
+   : (_) == 'M' ? 12				\
+   : (_) == 'N' ? 13				\
+   : (_) == 'O' ? 14				\
+   : (_) == 'P' ? 15				\
+   : (_) == 'Q' ? 16				\
+   : (_) == 'R' ? 17				\
+   : (_) == 'S' ? 18				\
+   : (_) == 'T' ? 19				\
+   : (_) == 'U' ? 20				\
+   : (_) == 'V' ? 21				\
+   : (_) == 'W' ? 22				\
+   : (_) == 'X' ? 23				\
+   : (_) == 'Y' ? 24				\
+   : (_) == 'Z' ? 25				\
+   : (_) == 'a' ? 26				\
+   : (_) == 'b' ? 27				\
+   : (_) == 'c' ? 28				\
+   : (_) == 'd' ? 29				\
+   : (_) == 'e' ? 30				\
+   : (_) == 'f' ? 31				\
+   : (_) == 'g' ? 32				\
+   : (_) == 'h' ? 33				\
+   : (_) == 'i' ? 34				\
+   : (_) == 'j' ? 35				\
+   : (_) == 'k' ? 36				\
+   : (_) == 'l' ? 37				\
+   : (_) == 'm' ? 38				\
+   : (_) == 'n' ? 39				\
+   : (_) == 'o' ? 40				\
+   : (_) == 'p' ? 41				\
+   : (_) == 'q' ? 42				\
+   : (_) == 'r' ? 43				\
+   : (_) == 's' ? 44				\
+   : (_) == 't' ? 45				\
+   : (_) == 'u' ? 46				\
+   : (_) == 'v' ? 47				\
+   : (_) == 'w' ? 48				\
+   : (_) == 'x' ? 49				\
+   : (_) == 'y' ? 50				\
+   : (_) == 'z' ? 51				\
+   : (_) == '0' ? 52				\
+   : (_) == '1' ? 53				\
+   : (_) == '2' ? 54				\
+   : (_) == '3' ? 55				\
+   : (_) == '4' ? 56				\
+   : (_) == '5' ? 57				\
+   : (_) == '6' ? 58				\
+   : (_) == '7' ? 59				\
+   : (_) == '8' ? 60				\
+   : (_) == '9' ? 61				\
+   : (_) == '+' ? 62				\
+   : (_) == '/' ? 63				\
+   : -1)
+
+static const signed char b64[0x100] = {
+  B64 (0), B64 (1), B64 (2), B64 (3),
+  B64 (4), B64 (5), B64 (6), B64 (7),
+  B64 (8), B64 (9), B64 (10), B64 (11),
+  B64 (12), B64 (13), B64 (14), B64 (15),
+  B64 (16), B64 (17), B64 (18), B64 (19),
+  B64 (20), B64 (21), B64 (22), B64 (23),
+  B64 (24), B64 (25), B64 (26), B64 (27),
+  B64 (28), B64 (29), B64 (30), B64 (31),
+  B64 (32), B64 (33), B64 (34), B64 (35),
+  B64 (36), B64 (37), B64 (38), B64 (39),
+  B64 (40), B64 (41), B64 (42), B64 (43),
+  B64 (44), B64 (45), B64 (46), B64 (47),
+  B64 (48), B64 (49), B64 (50), B64 (51),
+  B64 (52), B64 (53), B64 (54), B64 (55),
+  B64 (56), B64 (57), B64 (58), B64 (59),
+  B64 (60), B64 (61), B64 (62), B64 (63),
+  B64 (64), B64 (65), B64 (66), B64 (67),
+  B64 (68), B64 (69), B64 (70), B64 (71),
+  B64 (72), B64 (73), B64 (74), B64 (75),
+  B64 (76), B64 (77), B64 (78), B64 (79),
+  B64 (80), B64 (81), B64 (82), B64 (83),
+  B64 (84), B64 (85), B64 (86), B64 (87),
+  B64 (88), B64 (89), B64 (90), B64 (91),
+  B64 (92), B64 (93), B64 (94), B64 (95),
+  B64 (96), B64 (97), B64 (98), B64 (99),
+  B64 (100), B64 (101), B64 (102), B64 (103),
+  B64 (104), B64 (105), B64 (106), B64 (107),
+  B64 (108), B64 (109), B64 (110), B64 (111),
+  B64 (112), B64 (113), B64 (114), B64 (115),
+  B64 (116), B64 (117), B64 (118), B64 (119),
+  B64 (120), B64 (121), B64 (122), B64 (123),
+  B64 (124), B64 (125), B64 (126), B64 (127),
+  B64 (128), B64 (129), B64 (130), B64 (131),
+  B64 (132), B64 (133), B64 (134), B64 (135),
+  B64 (136), B64 (137), B64 (138), B64 (139),
+  B64 (140), B64 (141), B64 (142), B64 (143),
+  B64 (144), B64 (145), B64 (146), B64 (147),
+  B64 (148), B64 (149), B64 (150), B64 (151),
+  B64 (152), B64 (153), B64 (154), B64 (155),
+  B64 (156), B64 (157), B64 (158), B64 (159),
+  B64 (160), B64 (161), B64 (162), B64 (163),
+  B64 (164), B64 (165), B64 (166), B64 (167),
+  B64 (168), B64 (169), B64 (170), B64 (171),
+  B64 (172), B64 (173), B64 (174), B64 (175),
+  B64 (176), B64 (177), B64 (178), B64 (179),
+  B64 (180), B64 (181), B64 (182), B64 (183),
+  B64 (184), B64 (185), B64 (186), B64 (187),
+  B64 (188), B64 (189), B64 (190), B64 (191),
+  B64 (192), B64 (193), B64 (194), B64 (195),
+  B64 (196), B64 (197), B64 (198), B64 (199),
+  B64 (200), B64 (201), B64 (202), B64 (203),
+  B64 (204), B64 (205), B64 (206), B64 (207),
+  B64 (208), B64 (209), B64 (210), B64 (211),
+  B64 (212), B64 (213), B64 (214), B64 (215),
+  B64 (216), B64 (217), B64 (218), B64 (219),
+  B64 (220), B64 (221), B64 (222), B64 (223),
+  B64 (224), B64 (225), B64 (226), B64 (227),
+  B64 (228), B64 (229), B64 (230), B64 (231),
+  B64 (232), B64 (233), B64 (234), B64 (235),
+  B64 (236), B64 (237), B64 (238), B64 (239),
+  B64 (240), B64 (241), B64 (242), B64 (243),
+  B64 (244), B64 (245), B64 (246), B64 (247),
+  B64 (248), B64 (249), B64 (250), B64 (251),
+  B64 (252), B64 (253), B64 (254), B64 (255)
+};
+
+#if UCHAR_MAX == 255
+# define uchar_in_range(c) true
+#else
+# define uchar_in_range(c) ((c) <= 255)
+#endif
+
+/* Return true if CH is a character from the Base64 alphabet, and
+   false otherwise.  Note that '=' is padding and not considered to be
+   part of the alphabet.  */
+bool
+isbase64 (char ch)
+{
+  return uchar_in_range (to_uchar (ch)) && 0 <= b64[to_uchar (ch)];
+}
+
+/* Decode base64 encoded input array IN of length INLEN to output
+   array OUT that can hold *OUTLEN bytes.  Return true if decoding was
+   successful, i.e. if the input was valid base64 data, false
+   otherwise.  If *OUTLEN is too small, as many bytes as possible will
+   be written to OUT.  On return, *OUTLEN holds the length of decoded
+   bytes in OUT.  Note that as soon as any non-alphabet characters are
+   encountered, decoding is stopped and false is returned.  This means
+   that, when applicable, you must remove any line terminators that is
+   part of the data stream before calling this function.  */
+bool
+base64_decode (const char *restrict in, size_t inlen,
+	       char *restrict out, size_t *outlen)
+{
+  size_t outleft = *outlen;
+
+  while (inlen >= 2)
+    {
+      if (!isbase64 (in[0]) || !isbase64 (in[1]))
+	break;
+
+      if (outleft)
+	{
+	  *out++ = ((b64[to_uchar (in[0])] << 2)
+		    | (b64[to_uchar (in[1])] >> 4));
+	  outleft--;
+	}
+
+      if (inlen == 2)
+	break;
+
+      if (in[2] == '=')
+	{
+	  if (inlen != 4)
+	    break;
+
+	  if (in[3] != '=')
+	    break;
+
+	}
+      else
+	{
+	  if (!isbase64 (in[2]))
+	    break;
+
+	  if (outleft)
+	    {
+	      *out++ = (((b64[to_uchar (in[1])] << 4) & 0xf0)
+			| (b64[to_uchar (in[2])] >> 2));
+	      outleft--;
+	    }
+
+	  if (inlen == 3)
+	    break;
+
+	  if (in[3] == '=')
+	    {
+	      if (inlen != 4)
+		break;
+	    }
+	  else
+	    {
+	      if (!isbase64 (in[3]))
+		break;
+
+	      if (outleft)
+		{
+		  *out++ = (((b64[to_uchar (in[2])] << 6) & 0xc0)
+			    | b64[to_uchar (in[3])]);
+		  outleft--;
+		}
+	    }
+	}
+
+      in += 4;
+      inlen -= 4;
+    }
+
+  *outlen -= outleft;
+
+  if (inlen != 0)
+    return false;
+
+  return true;
+}
+
+/* Allocate an output buffer in *OUT, and decode the base64 encoded
+   data stored in IN of size INLEN to the *OUT buffer.  On return, the
+   size of the decoded data is stored in *OUTLEN.  OUTLEN may be NULL,
+   if the caller is not interested in the decoded length.  *OUT may be
+   NULL to indicate an out of memory error, in which case *OUTLEN
+   contains the size of the memory block needed.  The function returns
+   true on successful decoding and memory allocation errors.  (Use the
+   *OUT and *OUTLEN parameters to differentiate between successful
+   decoding and memory error.)  The function returns false if the
+   input was invalid, in which case *OUT is NULL and *OUTLEN is
+   undefined. */
+bool
+base64_decode_alloc (const char *in, size_t inlen, char **out,
+		     size_t *outlen)
+{
+  /* This may allocate a few bytes too much, depending on input,
+     but it's not worth the extra CPU time to compute the exact amount.
+     The exact amount is 3 * inlen / 4, minus 1 if the input ends
+     with "=" and minus another 1 if the input ends with "==".
+     Dividing before multiplying avoids the possibility of overflow.  */
+  size_t needlen = 3 * (inlen / 4) + 2;
+
+  *out = malloc (needlen);
+  if (!*out)
+    return true;
+
+  if (!base64_decode (in, inlen, *out, &needlen))
+    {
+      free (*out);
+      *out = NULL;
+      return false;
+    }
+
+  if (outlen)
+    *outlen = needlen;
+
+  return true;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/base64/base64.h	Mon Jun 25 00:42:07 2012 +0200
@@ -0,0 +1,45 @@
+/* base64.h -- Encode binary data using printable characters.
+   Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc.
+   Written by Simon Josefsson.
+
+   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, 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.  */
+
+#ifndef BASE64_H
+# define BASE64_H
+
+/* Get size_t. */
+# include <stddef.h>
+
+/* Get bool. */
+# include <stdbool.h>
+
+/* This uses that the expression (n+(k-1))/k means the smallest
+   integer >= n/k, i.e., the ceiling of n/k.  */
+# define BASE64_LENGTH(inlen) ((((inlen) + 2) / 3) * 4)
+
+extern bool isbase64 (char ch);
+
+extern void base64_encode (const char *restrict in, size_t inlen,
+			   char *restrict out, size_t outlen);
+
+extern size_t base64_encode_alloc (const char *in, size_t inlen, char **out);
+
+extern bool base64_decode (const char *restrict in, size_t inlen,
+			   char *restrict out, size_t *outlen);
+
+extern bool base64_decode_alloc (const char *in, size_t inlen,
+				 char **out, size_t *outlen);
+
+#endif /* BASE64_H */
--- a/project_files/frontlib/ipc/gameconn.c	Thu Jun 21 21:32:12 2012 +0200
+++ b/project_files/frontlib/ipc/gameconn.c	Mon Jun 25 00:42:07 2012 +0200
@@ -85,7 +85,7 @@
 	return result;
 }
 
-flib_gameconn *flib_gameconn_create(const char *playerName, flib_gamesetup *setup, bool netgame) {
+flib_gameconn *flib_gameconn_create(const char *playerName, const flib_gamesetup *setup, bool netgame) {
 	flib_gameconn *result = NULL;
 	flib_gameconn *tempConn = flib_gameconn_create_partial(true, playerName, netgame);
 	if(tempConn) {
@@ -124,6 +124,22 @@
 	return result;
 }
 
+flib_gameconn *flib_gameconn_create_campaign(const char *playerName, const char *seed, const char *script) {
+	flib_gameconn *result = NULL;
+	flib_gameconn *tempConn = flib_gameconn_create_partial(true, playerName, false);
+	if(tempConn) {
+		if(!flib_ipc_append_message(tempConn->configBuffer, "TL")
+				&& !flib_ipc_append_seed(tempConn->configBuffer, seed)
+				&& !flib_ipc_append_script(tempConn->configBuffer, script)
+				&& !flib_ipc_append_message(tempConn->configBuffer, "!")) {
+			result = tempConn;
+			tempConn = NULL;
+		}
+	}
+	flib_gameconn_destroy(tempConn);
+	return result;
+}
+
 void flib_gameconn_destroy(flib_gameconn *conn) {
 	if(conn) {
 		if(conn->running) {
--- a/project_files/frontlib/ipc/gameconn.h	Thu Jun 21 21:32:12 2012 +0200
+++ b/project_files/frontlib/ipc/gameconn.h	Mon Jun 25 00:42:07 2012 +0200
@@ -15,9 +15,11 @@
 struct _flib_gameconn;
 typedef struct _flib_gameconn flib_gameconn;
 
-flib_gameconn *flib_gameconn_create(const char *playerName, flib_gamesetup *setup, bool netgame);
+flib_gameconn *flib_gameconn_create(const char *playerName, const flib_gamesetup *setup, bool netgame);
 flib_gameconn *flib_gameconn_create_playdemo(const uint8_t *demo, int size);
 flib_gameconn *flib_gameconn_create_loadgame(const char *playerName, const uint8_t *save, int size);
+flib_gameconn *flib_gameconn_create_campaign(const char *playerName, const char *seed, const char *script);
+
 void flib_gameconn_destroy(flib_gameconn *conn);
 
 /**
--- a/project_files/frontlib/ipc/ipcbase.c	Thu Jun 21 21:32:12 2012 +0200
+++ b/project_files/frontlib/ipc/ipcbase.c	Mon Jun 25 00:42:07 2012 +0200
@@ -73,10 +73,6 @@
 	}
 }
 
-static bool isMessageReady(flib_ipcbase *ipc) {
-	return ipc->readBufferSize >= ipc->readBuffer[0]+1;
-}
-
 static void receiveToBuffer(flib_ipcbase *ipc) {
 	if(ipc->sock) {
 		int size = flib_socket_nbrecv(ipc->sock, ipc->readBuffer+ipc->readBufferSize, sizeof(ipc->readBuffer)-ipc->readBufferSize);
@@ -89,6 +85,10 @@
 	}
 }
 
+static bool isMessageReady(flib_ipcbase *ipc) {
+	return ipc->readBufferSize >= ipc->readBuffer[0]+1;
+}
+
 int flib_ipcbase_recv_message(flib_ipcbase *ipc, void *data) {
 	if(!ipc || !data) {
 		flib_log_e("null parameter in flib_ipcbase_recv_message");
@@ -183,10 +183,6 @@
 	return flib_ipcbase_send_raw(ipc, sendbuf, len+1);
 }
 
-int flib_ipcbase_send_messagestr(flib_ipcbase *ipc, char *data) {
-	return flib_ipcbase_send_message(ipc, data, strlen(data));
-}
-
 void flib_ipcbase_accept(flib_ipcbase *ipc) {
 	if(!ipc) {
 		flib_log_e("null parameter in flib_ipcbase_accept");
--- a/project_files/frontlib/ipc/ipcbase.h	Thu Jun 21 21:32:12 2012 +0200
+++ b/project_files/frontlib/ipc/ipcbase.h	Mon Jun 25 00:42:07 2012 +0200
@@ -13,7 +13,6 @@
 
 typedef enum {IPC_NOT_CONNECTED, IPC_LISTENING, IPC_CONNECTED} IpcState;
 
-struct _flib_ipcbase;
 typedef struct _flib_ipcbase flib_ipcbase;
 
 /**
@@ -27,10 +26,13 @@
  */
 flib_ipcbase *flib_ipcbase_create();
 
+/**
+ * Return the listening port
+ */
 uint16_t flib_ipcbase_port(flib_ipcbase *ipc);
 
 /**
- * Free resources and close sockets.
+ * Free resources and close sockets. NULL safe.
  */
 void flib_ipcbase_destroy(flib_ipcbase *ipc);
 
@@ -61,6 +63,10 @@
  */
 int flib_ipcbase_recv_map(flib_ipcbase *ipc, void *data);
 
+/**
+ * Blocking send bytes over the socket. No message framing will be added.
+ * Returns 0 on success.
+ */
 int flib_ipcbase_send_raw(flib_ipcbase *ipc, const void *data, size_t len);
 
 /**
@@ -68,16 +74,11 @@
  * message is completely written or the connection is closed or an error occurs.
  *
  * Calling this function in a state other than IPC_CONNECTED will fail immediately.
- * Returns a negative value on failure.
+ * Returns 0 on success.
  */
 int flib_ipcbase_send_message(flib_ipcbase *ipc, void *data, size_t len);
 
 /**
- * Convenience function for sending a 0-delimited string.
- */
-int flib_ipcbase_send_messagestr(flib_ipcbase *ipc, char *data);
-
-/**
  * Try to accept a connection. Only has an effect in state IPC_LISTENING.
  */
 void flib_ipcbase_accept(flib_ipcbase *ipc);
--- a/project_files/frontlib/ipc/ipcprotocol.c	Thu Jun 21 21:32:12 2012 +0200
+++ b/project_files/frontlib/ipc/ipcprotocol.c	Mon Jun 25 00:42:07 2012 +0200
@@ -48,11 +48,22 @@
 		bool error = false;
 
 		if(map->mapgen == MAPGEN_NAMED) {
-			error |= flib_ipc_append_message(tempvector, "emap %s", map->name);
+			if(map->name) {
+				error |= flib_ipc_append_message(tempvector, "emap %s", map->name);
+			} else {
+				flib_log_e("Missing map name");
+				error = true;
+			}
 		}
 		if(map->theme && !mappreview) {
-			error |= flib_ipc_append_message(tempvector, "etheme %s", map->theme);
+			if(map->theme) {
+				error |= flib_ipc_append_message(tempvector, "etheme %s", map->theme);
+			} else {
+				flib_log_e("Missing map theme");
+				error = true;
+			}
 		}
+		error |= flib_ipc_append_seed(tempvector, map->seed);
 		error |= flib_ipc_append_message(tempvector, "e$template_filter %i", map->templateFilter);
 		error |= flib_ipc_append_message(tempvector, "e$mapgen %i", map->mapgen);
 
@@ -97,6 +108,15 @@
 	}
 }
 
+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 flib_ipc_append_gamescheme(flib_vector *vec, const flib_cfg *scheme) {
 	int result = -1;
 	flib_vector *tempvector = flib_vector_create();
@@ -155,7 +175,8 @@
 			error |= flib_ipc_append_message(tempvector, "eammstore");
 		}
 
-		char *hash = team->hash ? team->hash : "00000000000000000000000000000000";
+		// 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->remoteDriven) {
@@ -212,7 +233,6 @@
 		bool sharedAmmo = false;
 
 		error |= flib_ipc_append_message(vec, netgame ? "TN" : "TL");
-		error |= flib_ipc_append_seed(vec, setup->seed);
 		if(setup->map) {
 			error |= flib_ipc_append_mapconf(tempvector, setup->map, false);
 		}
@@ -225,13 +245,13 @@
 			// Shared ammo has priority over per-hog ammo
 			perHogAmmo = !sharedAmmo && getGameMod(setup->gamescheme, GAMEMOD_PERHOGAMMO_MASKBIT);
 		}
-		if(setup->teams && setup->teamcount>0) {
-			uint32_t *clanColors = flib_calloc(setup->teamcount, sizeof(uint32_t));
+		if(setup->teams && setup->teamCount>0) {
+			uint32_t *clanColors = flib_calloc(setup->teamCount, sizeof(uint32_t));
 			if(!clanColors) {
 				error = true;
 			} else {
 				int clanCount = 0;
-				for(int i=0; i<setup->teamcount; i++) {
+				for(int i=0; i<setup->teamCount; i++) {
 					flib_team *team = setup->teams[i];
 					bool newClan = false;
 
--- a/project_files/frontlib/ipc/ipcprotocol.h	Thu Jun 21 21:32:12 2012 +0200
+++ b/project_files/frontlib/ipc/ipcprotocol.h	Mon Jun 25 00:42:07 2012 +0200
@@ -39,6 +39,14 @@
 int flib_ipc_append_seed(flib_vector *vec, const char *seed);
 
 /**
+ * Append a script message to the buffer.
+ *
+ * Returns nonzero if something goes wrong. In that case the buffer
+ * contents are unaffected.
+ */
+int flib_ipc_append_script(flib_vector *vec, const char *script);
+
+/**
  * Append the game scheme to the buffer.
  *
  * Returns nonzero if something goes wrong. In that case the buffer
--- a/project_files/frontlib/ipc/mapconn.c	Thu Jun 21 21:32:12 2012 +0200
+++ b/project_files/frontlib/ipc/mapconn.c	Mon Jun 25 00:42:07 2012 +0200
@@ -39,12 +39,11 @@
 	conn->onFailureCb = &noop_handleFailure;
 }
 
-static flib_vector *createConfigBuffer(char *seed, flib_map *mapdesc) {
+static flib_vector *createConfigBuffer(flib_map *mapdesc) {
 	flib_vector *result = NULL;
 	flib_vector *tempbuffer = flib_vector_create();
 	if(tempbuffer) {
 		bool error = false;
-		error |= flib_ipc_append_seed(tempbuffer, seed);
 		error |= flib_ipc_append_mapconf(tempbuffer, mapdesc, true);
 		error |= flib_ipc_append_message(tempbuffer, "!");
 		if(!error) {
@@ -56,12 +55,12 @@
 	return result;
 }
 
-flib_mapconn *flib_mapconn_create(char *seed, flib_map *mapdesc) {
+flib_mapconn *flib_mapconn_create(flib_map *mapdesc) {
 	flib_mapconn *result = NULL;
 	flib_mapconn *tempConn = flib_calloc(1, sizeof(flib_mapconn));
 	if(tempConn) {
 		tempConn->ipcBase = flib_ipcbase_create();
-		tempConn->configBuffer = createConfigBuffer(seed, mapdesc);
+		tempConn->configBuffer = createConfigBuffer(mapdesc);
 		if(tempConn->ipcBase && tempConn->configBuffer) {
 			tempConn->progress = AWAIT_CONNECTION;
 			clearCallbacks(tempConn);
--- a/project_files/frontlib/ipc/mapconn.h	Thu Jun 21 21:32:12 2012 +0200
+++ b/project_files/frontlib/ipc/mapconn.h	Mon Jun 25 00:42:07 2012 +0200
@@ -9,7 +9,6 @@
 #define MAPIMAGE_HEIGHT 128
 #define MAPIMAGE_BYTES (MAPIMAGE_WIDTH/8*MAPIMAGE_HEIGHT)
 
-struct _flib_mapconn;
 typedef struct _flib_mapconn flib_mapconn;
 
 /**
@@ -21,7 +20,7 @@
  * No NULL parameters allowed, returns NULL on failure.
  * Use flib_mapconn_destroy to free the returned object.
  */
-flib_mapconn *flib_mapconn_create(char *seed, flib_map *mapdesc);
+flib_mapconn *flib_mapconn_create(flib_map *mapdesc);
 
 /**
  * Destroy the mapconn object. Passing NULL is allowed and does nothing.
--- a/project_files/frontlib/model/cfg.c	Thu Jun 21 21:32:12 2012 +0200
+++ b/project_files/frontlib/model/cfg.c	Mon Jun 25 00:42:07 2012 +0200
@@ -34,7 +34,7 @@
 		flib_cfg_meta_release(cfg->meta);
 		free(cfg->mods);
 		free(cfg->settings);
-		free(cfg->schemeName);
+		free(cfg->name);
 		free(cfg);
 	}
 }
@@ -155,11 +155,11 @@
 	}
 
 	result->meta = flib_cfg_meta_retain(meta);
-	result->schemeName = flib_strdupnull(schemeName);
+	result->name = flib_strdupnull(schemeName);
 	result->mods = flib_calloc(meta->modCount, sizeof(*result->mods));
 	result->settings = flib_calloc(meta->settingCount, sizeof(*result->settings));
 
-	if(!result->mods || !result->settings || !result->schemeName) {
+	if(!result->mods || !result->settings || !result->name) {
 		flib_cfg_destroy(result);
 		return NULL;
 	}
@@ -173,7 +173,7 @@
 flib_cfg *flib_cfg_copy(flib_cfg *cfg) {
 	flib_cfg *result = NULL;
 	if(cfg) {
-		result = flib_cfg_create(cfg->meta, cfg->schemeName);
+		result = flib_cfg_create(cfg->meta, cfg->name);
 		if(result) {
 			memcpy(result->mods, cfg->mods, cfg->meta->modCount * sizeof(*cfg->mods));
 			memcpy(result->settings, cfg->settings, cfg->meta->settingCount * sizeof(*cfg->settings));
--- a/project_files/frontlib/model/cfg.h	Thu Jun 21 21:32:12 2012 +0200
+++ b/project_files/frontlib/model/cfg.h	Mon Jun 25 00:42:07 2012 +0200
@@ -24,6 +24,10 @@
     int bitmaskIndex;
 } flib_cfg_mod_meta;
 
+/**
+ * The order of the meta information in the arrays is the same as the order
+ * of the mod/setting information in the net protocol messages.
+ */
 typedef struct {
 	int _referenceCount;
 	int settingCount;
@@ -36,7 +40,7 @@
 	int _referenceCount;
     flib_cfg_meta *meta;
 
-    char *schemeName;
+    char *name;
     int *settings;
     bool *mods;
 } flib_cfg;
--- a/project_files/frontlib/model/gamesetup.h	Thu Jun 21 21:32:12 2012 +0200
+++ b/project_files/frontlib/model/gamesetup.h	Mon Jun 25 00:42:07 2012 +0200
@@ -1,8 +1,6 @@
 /**
  * A complete game configuration that contains all settings for a
  * local or networked game.
- *
- * It should be noted that the meta-configuration is not included.
  */
 
 #ifndef MODEL_GAMESETUP_H_
@@ -14,12 +12,11 @@
 #include "team.h"
 
 typedef struct {
-    char *seed;						// required
-    char *script;					// optional
-    flib_cfg *gamescheme;			// optional
-    flib_map *map;					// optional
-	flib_team **teams;				// optional
-	int teamcount;
+    char *script;
+    flib_cfg *gamescheme;
+    flib_map *map;
+	int teamCount;
+	flib_team **teams;
 } flib_gamesetup;
 
 #endif
--- a/project_files/frontlib/model/map.c	Thu Jun 21 21:32:12 2012 +0200
+++ b/project_files/frontlib/model/map.c	Mon Jun 25 00:42:07 2012 +0200
@@ -9,6 +9,7 @@
 
 static void flib_map_destroy(flib_map *map) {
 	if(map) {
+		free(map->seed);
 		free(map->drawData);
 		free(map->name);
 		free(map->theme);
@@ -16,85 +17,88 @@
 	}
 }
 
-flib_map *flib_map_create_regular(const char *theme, int templateFilter) {
+flib_map *flib_map_create_regular(const char *seed, const char *theme, int templateFilter) {
 	flib_map *result = NULL;
-	if(!theme) {
+	if(!seed || !theme) {
 		flib_log_e("null parameter in flib_map_create_regular");
 	} else {
-		flib_map *newmap = flib_calloc(1, sizeof(flib_map));
-		if(newmap) {
-			newmap->_referenceCount = 1;
-			newmap->mapgen = MAPGEN_REGULAR;
-			newmap->templateFilter = templateFilter;
-			newmap->theme = flib_strdupnull(theme);
-			if(newmap->theme) {
-				result = newmap;
-				newmap = NULL;
-			}
-		}
-		flib_map_destroy(newmap);
+		flib_map newmap = {0};
+		newmap.mapgen = MAPGEN_REGULAR;
+		newmap.name = "+rnd+";
+		newmap.seed = (char*)seed;
+		newmap.theme = (char*)theme;
+		newmap.templateFilter = templateFilter;
+		result = flib_map_copy(&newmap);
+	}
+	return result;
+}
+
+flib_map *flib_map_create_maze(const char *seed, const char *theme, int mazeSize) {
+	flib_map *result = NULL;
+	if(!seed || !theme) {
+		flib_log_e("null parameter in flib_map_create_maze");
+	} else {
+		flib_map newmap = {0};
+		newmap.mapgen = MAPGEN_MAZE;
+		newmap.name = "+maze+";
+		newmap.seed = (char*)seed;
+		newmap.theme = (char*)theme;
+		newmap.mazeSize = mazeSize;
+		result = flib_map_copy(&newmap);
 	}
 	return result;
 }
 
-flib_map *flib_map_create_maze(const char *theme, int mazeSize) {
+flib_map *flib_map_create_named(const char *seed, const char *name) {
 	flib_map *result = NULL;
-	if(!theme) {
-		flib_log_e("null parameter in flib_map_create_maze");
+	if(!seed || !name) {
+		flib_log_e("null parameter in flib_map_create_named");
+	} else {
+		flib_map newmap = {0};
+		newmap.mapgen = MAPGEN_NAMED;
+		newmap.name = (char*)name;
+		newmap.seed = (char*)seed;
+		result = flib_map_copy(&newmap);
+	}
+	return result;
+}
+
+flib_map *flib_map_create_drawn(const char *seed, const char *theme, const uint8_t *drawData, int drawDataSize) {
+	flib_map *result = NULL;
+	if(!seed || !theme || (!drawData && drawDataSize)) {
+		flib_log_e("null parameter in flib_map_create_drawn");
 	} else {
-		flib_map *newmap = flib_calloc(1, sizeof(flib_map));
+		flib_map newmap = {0};
+		newmap.mapgen = MAPGEN_DRAWN;
+		newmap.name = "+drawn+";
+		newmap.seed = (char*)seed;
+		newmap.theme = (char*)theme;
+		newmap.drawData = (uint8_t*) drawData;
+		newmap.drawDataSize = drawDataSize;
+		result = flib_map_copy(&newmap);
+	}
+	return result;
+}
+
+flib_map *flib_map_copy(const flib_map *map) {
+	flib_map *result = NULL;
+	if(map) {
+		flib_map *newmap = flib_map_retain(flib_calloc(1, sizeof(flib_map)));
 		if(newmap) {
-			newmap->_referenceCount = 1;
-			newmap->mapgen = MAPGEN_MAZE;
-			newmap->mazeSize = mazeSize;
-			newmap->theme = flib_strdupnull(theme);
-			if(newmap->theme) {
+			newmap->mapgen = map->mapgen;
+			newmap->drawDataSize = map->drawDataSize;
+			newmap->drawData = flib_bufdupnull(map->drawData, map->drawDataSize);
+			newmap->mazeSize = map->mazeSize;
+			newmap->name = flib_strdupnull(map->name);
+			newmap->seed = flib_strdupnull(map->seed);
+			newmap->templateFilter = map->templateFilter;
+			newmap->theme = flib_strdupnull(map->theme);
+			if((newmap->drawData || !map->drawData) && (newmap->name || !map->name) && (newmap->seed || !map->seed) && (newmap->theme || !map->theme)) {
 				result = newmap;
 				newmap = NULL;
 			}
 		}
-		flib_map_destroy(newmap);
-	}
-	return result;
-}
-
-flib_map *flib_map_create_named(const char *name) {
-	flib_map *result = NULL;
-	if(!name) {
-		flib_log_e("null parameter in flib_map_create_named");
-	} else {
-		flib_map *newmap = flib_calloc(1, sizeof(flib_map));
-		if(newmap) {
-			newmap->_referenceCount = 1;
-			newmap->mapgen = MAPGEN_NAMED;
-			newmap->name = flib_strdupnull(name);
-			if(newmap->name) {
-				result = newmap;
-				newmap = NULL;
-			}
-		}
-		flib_map_destroy(newmap);
-	}
-	return result;
-}
-
-flib_map *flib_map_create_drawn(const char *theme, const uint8_t *drawData, int drawDataSize) {
-	flib_map *result = NULL;
-	if(!theme || !drawData) {
-		flib_log_e("null parameter in flib_map_create_named");
-	} else {
-		flib_map *newmap = flib_calloc(1, sizeof(flib_map));
-		if(newmap) {
-			newmap->_referenceCount = 1;
-			newmap->mapgen = MAPGEN_DRAWN;
-			newmap->drawData = flib_bufdupnull(drawData, drawDataSize);
-			newmap->drawDataSize = drawDataSize;
-			if(newmap->drawData) {
-				result = newmap;
-				newmap = NULL;
-			}
-		}
-		flib_map_destroy(newmap);
+		flib_map_release(newmap);
 	}
 	return result;
 }
--- a/project_files/frontlib/model/map.h	Thu Jun 21 21:32:12 2012 +0200
+++ b/project_files/frontlib/model/map.h	Mon Jun 25 00:42:07 2012 +0200
@@ -1,14 +1,17 @@
 /**
- * Data structure for defining a map. Note that most maps also depend on the
- * random seed passed to the engine, if you store that in addition to the
- * flib_map structure you have the whole recipe to exactly recreate a particular
- * map. For named maps, you also need the corresponding files.
+ * 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_
 
 #include <stdint.h>
+#include <stdbool.h>
 
 #define MAPGEN_REGULAR 0
 #define MAPGEN_MAZE 1
@@ -32,8 +35,9 @@
 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 *name;				// Used for MAPGEN_NAMED
 	uint8_t *drawData;		// Used for MAPGEN_DRAWN
 	int drawDataSize;		// Used for MAPGEN_DRAWN
 	int templateFilter;		// Used for MAPGEN_REGULAR
@@ -49,7 +53,7 @@
  * Use flib_map_destroy to free the returned object.
  * No NULL parameters allowed, returns NULL on failure.
  */
-flib_map *flib_map_create_regular(const char *theme, int templateFilter);
+flib_map *flib_map_create_regular(const char *seed, const char *theme, int templateFilter);
 
 /**
  * Create a generated maze-type map. theme should be the name of a
@@ -60,7 +64,7 @@
  * Use flib_map_destroy to free the returned object.
  * No NULL parameters allowed, returns NULL on failure.
  */
-flib_map *flib_map_create_maze(const char *theme, int mazeSize);
+flib_map *flib_map_create_maze(const char *seed, const char *theme, int mazeSize);
 
 /**
  * Create a map from the Maps-Directory. name should be the name of a
@@ -71,13 +75,18 @@
  * Use flib_map_destroy to free the returned object.
  * No NULL parameters allowed, returns NULL on failure.
  */
-flib_map *flib_map_create_named(const char *name);
+flib_map *flib_map_create_named(const char *seed, const char *name);
 
 /**
  * Create a hand-drawn map. Use flib_map_destroy to free the returned object.
  * No NULL parameters allowed, returns NULL on failure.
  */
-flib_map *flib_map_create_drawn(const char *theme, const uint8_t *drawData, int drawDataSize);
+flib_map *flib_map_create_drawn(const char *seed, const char *theme, const uint8_t *drawData, int drawDataSize);
+
+/**
+ * Create a deep copy of the map. Returns NULL on failure or if NULL was passed.
+ */
+flib_map *flib_map_copy(const flib_map *map);
 
 /**
  * Increase the reference count of the object. Call this if you store a pointer to it somewhere.
--- a/project_files/frontlib/model/schemelist.c	Thu Jun 21 21:32:12 2012 +0200
+++ b/project_files/frontlib/model/schemelist.c	Mon Jun 25 00:42:07 2012 +0200
@@ -125,7 +125,7 @@
 	bool error = false;
 
 	char *key = makePrefixedName(index+1, "name");
-	error |= !key || flib_ini_set_str(ini, key, scheme->schemeName);
+	error |= !key || flib_ini_set_str(ini, key, scheme->name);
 	free(key);
 
 	for(int i=0; i<meta->modCount && !error; i++) {
@@ -184,7 +184,7 @@
 flib_cfg *flib_schemelist_find(flib_schemelist *list, const char *name) {
 	if(list && name) {
 		for(int i=0; i<list->schemeCount; i++) {
-			if(!strcmp(name, list->schemes[i]->schemeName)) {
+			if(!strcmp(name, list->schemes[i]->name)) {
 				return list->schemes[i];
 			}
 		}
--- a/project_files/frontlib/model/team.c	Thu Jun 21 21:32:12 2012 +0200
+++ b/project_files/frontlib/model/team.c	Mon Jun 25 00:42:07 2012 +0200
@@ -3,17 +3,19 @@
 #include "../util/inihelper.h"
 #include "../util/util.h"
 #include "../util/logging.h"
+#include "../util/refcounter.h"
+
 #include <string.h>
 #include <stdlib.h>
 
 static flib_team *from_ini_handleError(flib_team *result, flib_ini *settingfile) {
 	flib_ini_destroy(settingfile);
-	flib_team_destroy(result);
+	flib_team_release(result);
 	return NULL;
 }
 
 flib_team *flib_team_from_ini(const char *filename) {
-	flib_team *result = flib_calloc(1, sizeof(flib_team));
+	flib_team *result = flib_team_retain(flib_calloc(1, sizeof(flib_team)));
 	flib_ini *ini = NULL;
 
 	if(!filename) {
@@ -189,8 +191,15 @@
 	return result;
 }
 
-void flib_team_destroy(flib_team *team) {
+flib_team *flib_team_retain(flib_team *team) {
 	if(team) {
+		flib_retain(&team->_referenceCount, "flib_team");
+	}
+	return team;
+}
+
+void flib_team_release(flib_team *team) {
+	if(team && flib_release(&team->_referenceCount, "flib_team")) {
 		for(int i=0; i<HEDGEHOGS_PER_TEAM; i++) {
 			free(team->hogs[i].name);
 			free(team->hogs[i].hat);
@@ -208,7 +217,7 @@
 			}
 		}
 		free(team->bindings);
-		free(team->hash);
+		free(team->ownerName);
 		free(team);
 	}
 }
@@ -221,3 +230,68 @@
 		}
 	}
 }
+
+char *strdupWithError(const char *in, bool *error) {
+	char *out = flib_strdupnull(in);
+	if(in && !out) {
+		*error = true;
+	}
+	return out;
+}
+
+flib_team *flib_team_copy(flib_team *team) {
+	flib_team *result = NULL;
+	if(team) {
+		flib_team *tmpTeam = flib_team_retain(flib_calloc(1, sizeof(flib_team)));
+		if(tmpTeam) {
+			bool error = false;
+
+			for(int i=0; i<HEDGEHOGS_PER_TEAM; i++) {
+				tmpTeam->hogs[i].name = strdupWithError(team->hogs[i].name, &error);
+				tmpTeam->hogs[i].hat = strdupWithError(team->hogs[i].hat, &error);
+				tmpTeam->hogs[i].rounds = team->hogs[i].rounds;
+				tmpTeam->hogs[i].kills = team->hogs[i].kills;
+				tmpTeam->hogs[i].deaths = team->hogs[i].deaths;
+				tmpTeam->hogs[i].suicides = team->hogs[i].suicides;
+				tmpTeam->hogs[i].difficulty = team->hogs[i].difficulty;
+				tmpTeam->hogs[i].initialHealth = team->hogs[i].initialHealth;
+				tmpTeam->hogs[i].weaponset = flib_weaponset_retain(team->hogs[i].weaponset);
+			}
+
+			tmpTeam->name = strdupWithError(team->name, &error);
+			tmpTeam->grave = strdupWithError(team->grave, &error);
+			tmpTeam->fort = strdupWithError(team->fort, &error);
+			tmpTeam->voicepack = strdupWithError(team->voicepack, &error);
+			tmpTeam->flag = strdupWithError(team->flag, &error);
+			tmpTeam->ownerName = strdupWithError(team->ownerName, &error);
+
+			tmpTeam->bindingCount = team->bindingCount;
+			if(team->bindings) {
+				tmpTeam->bindings = flib_calloc(team->bindingCount, sizeof(flib_binding));
+				if(tmpTeam->bindings) {
+					for(int i=0; i<tmpTeam->bindingCount; i++) {
+						tmpTeam->bindings[i].action = strdupWithError(team->bindings[i].action, &error);
+						tmpTeam->bindings[i].binding = strdupWithError(team->bindings[i].binding, &error);
+					}
+				} else {
+					error = true;
+				}
+			}
+
+			tmpTeam->rounds = team->rounds;
+			tmpTeam->wins = team->wins;
+			tmpTeam->campaignProgress = team->campaignProgress;
+
+			tmpTeam->color = team->color;
+			tmpTeam->hogsInGame = team->hogsInGame;
+			tmpTeam->remoteDriven = team->remoteDriven;
+
+			if(!error) {
+				result = tmpTeam;
+				tmpTeam = 0;
+			}
+		}
+		flib_team_release(tmpTeam);
+	}
+	return result;
+}
--- a/project_files/frontlib/model/team.h	Thu Jun 21 21:32:12 2012 +0200
+++ b/project_files/frontlib/model/team.h	Mon Jun 25 00:42:07 2012 +0200
@@ -44,6 +44,7 @@
 } flib_hog;
 
 typedef struct {
+	int _referenceCount;
 	flib_hog hogs[HEDGEHOGS_PER_TEAM];
 	char *name;
 	char *grave;
@@ -64,7 +65,7 @@
 	uint32_t color;
 	int hogsInGame;
 	bool remoteDriven;
-	char *hash; // TODO calculate at the appropriate time... i.e. before trying to send the config to the engine
+	char *ownerName;
 } flib_team;
 
 /**
@@ -96,6 +97,22 @@
  */
 void flib_team_set_weaponset(flib_team *team, flib_weaponset *set);
 
-void flib_team_destroy(flib_team *team);
+/**
+ * Increase the reference count of the object. Call this if you store a pointer to it somewhere.
+ * Returns the parameter.
+ */
+flib_team *flib_team_retain(flib_team *team);
+
+/**
+ * Decrease the reference count of the object and free it if this was the last reference.
+ */
+void flib_team_release(flib_team *team);
+
+/**
+ * Create a deep copy of a team. Returns NULL on failure.
+ * The referenced weaponsets are not copied, so the new
+ * team references the same weaponsets.
+ */
+flib_team *flib_team_copy(flib_team *team);
 
 #endif /* TEAM_H_ */
--- a/project_files/frontlib/model/weapon.c	Thu Jun 21 21:32:12 2012 +0200
+++ b/project_files/frontlib/model/weapon.c	Mon Jun 25 00:42:07 2012 +0200
@@ -81,30 +81,38 @@
 	}
 }
 
+flib_weaponset *flib_weaponset_from_ammostring(const char *name, const char *ammostring) {
+	flib_weaponset *result = NULL;
+	if(!name || !ammostring) {
+		flib_log_e("null parameter in flib_weaponset_from_ammostring");
+	} else {
+		result = flib_weaponset_create(name);
+		if(result) {
+			int fieldlen = strlen(ammostring)/4;
+			setField(result->loadout, ammostring, fieldlen, false);
+			setField(result->crateprob, ammostring + fieldlen, fieldlen, true);
+			setField(result->delay, ammostring + 2*fieldlen, fieldlen, true);
+			setField(result->crateammo, ammostring + 3*fieldlen, fieldlen, true);
+		}
+	}
+	return result;
+}
+
 static int fillWeaponsetFromIni(flib_weaponsetlist *list, flib_ini *ini, int index) {
 	int result = -1;
 	char *keyname = flib_ini_get_keyname(ini, index);
 	char *decodedKeyname = flib_urldecode(keyname);
-
-	if(decodedKeyname) {
-		flib_weaponset *set = flib_weaponset_create(decodedKeyname);
+	char *ammostring = NULL;
+	if(decodedKeyname && !flib_ini_get_str(ini, &ammostring, keyname)) {
+		flib_weaponset *set = flib_weaponset_from_ammostring(decodedKeyname, ammostring);
 		if(set) {
-			char *value = NULL;
-			if(!flib_ini_get_str(ini, &value, keyname)) {
-				int fieldlen = strlen(value)/4;
-				setField(set->loadout, value, fieldlen, false);
-				setField(set->crateprob, value+1*fieldlen, fieldlen, true);
-				setField(set->delay, value+2*fieldlen, fieldlen, true);
-				setField(set->crateammo, value+3*fieldlen, fieldlen, true);
-				result = flib_weaponsetlist_insert(list, set, list->weaponsetCount);
-			}
-			free(value);
+			result = flib_weaponsetlist_insert(list, set, list->weaponsetCount);
 		}
 		flib_weaponset_release(set);
 	}
-
+	free(ammostring);
+	free(decodedKeyname);
 	free(keyname);
-	free(decodedKeyname);
 	return result;
 }
 
--- a/project_files/frontlib/model/weapon.h	Thu Jun 21 21:32:12 2012 +0200
+++ b/project_files/frontlib/model/weapon.h	Mon Jun 25 00:42:07 2012 +0200
@@ -45,6 +45,12 @@
 void flib_weaponset_release(flib_weaponset *weaponset);
 
 /**
+ * Create a weaponset from an ammostring. This format is used both in the ini files
+ * and in the net protocol.
+ */
+flib_weaponset *flib_weaponset_from_ammostring(const char *name, const char *ammostring);
+
+/**
  * Load a list of weaponsets from the ini file.
  * Returns NULL on error.
  */
--- a/project_files/frontlib/net/netconn.c	Thu Jun 21 21:32:12 2012 +0200
+++ b/project_files/frontlib/net/netconn.c	Mon Jun 25 00:42:07 2012 +0200
@@ -18,93 +18,21 @@
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
  */
 
-#include "netconn.h"
-#include "netbase.h"
+// TODO: Check the state transitions. Document with a diagram or something
+
+#include "netconn_internal.h"
 #include "netprotocol.h"
 #include "../util/logging.h"
 #include "../util/util.h"
 #include "../model/roomlist.h"
 #include "../md5/md5.h"
+#include "../base64/base64.h"
 
-#include <stdbool.h>
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
 #include <ctype.h>
 
-struct _flib_netconn {
-	flib_netbase *netBase;
-	char *playerName;
-	flib_cfg_meta *metaCfg;
-	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
-
-
-	void (*onMessageCb)(void *context, int msgtype, const char *msg);
-	void *onMessageCtx;
-
-	void (*onConnectedCb)(void *context);
-	void *onConnectedCtx;
-
-	void (*onDisconnectedCb)(void *context, int reason, const char *message);
-	void *onDisconnectedCtx;
-
-	void (*onRoomAddCb)(void *context, const flib_roomlist_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 *onRoomUpdateCtx;
-
-	void (*onChatCb)(void *context, const char *nick, const char *msg);
-	void *onChatCtx;
-
-	void (*onLobbyJoinCb)(void *context, const char *nick);
-	void *onLobbyJoinCtx;
-
-	void (*onLobbyLeaveCb)(void *context, const char *nick, const char *partMessage);
-	void *onLobbyLeaveCtx;
-
-	void (*onRoomJoinCb)(void *context, const char *nick);
-	void *onRoomJoinCtx;
-
-	void (*onRoomLeaveCb)(void *context, const char *nick, const char *partMessage);
-	void *onRoomLeaveCtx;
-
-	void (*onNickTakenCb)(void *context, const char *nick);
-	void *onNickTakenCtx;
-
-	void (*onNickAcceptCb)(void *context, const char *nick);
-	void *onNickAcceptCtx;
-
-	void (*onPasswordRequestCb)(void *context, const char *nick);
-	void *onPasswordRequestCtx;
-
-	void (*onRoomChiefStatusCb)(void *context, bool isChief);
-	void *onRoomChiefStatusCtx;
-
-	void (*onReadyStateCb)(void *context, const char *nick, bool ready);
-	void *onReadyStateCtx;
-
-	void (*onEnterRoomCb)(void *context, bool chief);
-	void *onEnterRoomCtx;
-
-	void (*onLeaveRoomCb)(void *context, int reason, const char *message);
-	void *onLeaveRoomCtx;
-
-	void (*onTeamAddCb)(void *context, flib_team *team);
-	void *onTeamAddCtx;
-
-	bool running;
-	bool destroyRequested;
-};
-
 static void defaultCallback_onMessage(void *context, int msgtype, const char *msg) {
 	flib_log_i("Net: [%i] %s", msgtype, msg);
 }
@@ -115,6 +43,7 @@
 static void defaultCallback_int_str(void *context, int i, const char *str) {}
 static void defaultCallback_str_str(void *context, const char *str1, const char *str2) {}
 static void defaultCallback_str_bool(void *context, const char *str, bool b) {}
+static void defaultCallback_str_int(void *context, const char *str, int i) {}
 
 static void defaultCallback_onRoomAdd(void *context, const flib_roomlist_room *room) {}
 static void defaultCallback_onRoomUpdate(void *context, const char *oldName, const flib_roomlist_room *room) {}
@@ -149,6 +78,10 @@
 }
 
 static void defaultCallback_onTeamAdd(void *context, flib_team *team) {}
+static void defaultCallback_onTeamColorChanged(void *context, const char *teamName, uint32_t color) {}
+static void defaultCallback_onCfgScheme(void *context, flib_cfg *scheme) {}
+static void defaultCallback_onMapChanged(void *context, const flib_map *map, int changetype) {}
+static void defaultCallback_onWeaponsetChanged(void *context, flib_weaponset *weaponset) {}
 
 static void clearCallbacks(flib_netconn *conn) {
 	flib_netconn_onMessage(conn, NULL, NULL);
@@ -163,12 +96,24 @@
 	flib_netconn_onRoomJoin(conn, NULL, NULL);
 	flib_netconn_onRoomLeave(conn, NULL, NULL);
 	flib_netconn_onNickTaken(conn, NULL, NULL);
-	flib_netconn_onNickAccept(conn, NULL, NULL);
 	flib_netconn_onPasswordRequest(conn, NULL, NULL);
 	flib_netconn_onRoomChiefStatus(conn, NULL, NULL);
-	flib_netconn_onReadyStateCb(conn, NULL, NULL);
-	flib_netconn_onEnterRoomCb(conn, NULL, NULL);
-	flib_netconn_onTeamAddCb(conn, NULL, NULL);
+	flib_netconn_onReadyState(conn, NULL, NULL);
+	flib_netconn_onEnterRoom(conn, NULL, NULL);
+	flib_netconn_onLeaveRoom(conn, NULL, NULL);
+	flib_netconn_onTeamAdd(conn, NULL, NULL);
+	flib_netconn_onTeamDelete(conn, NULL, NULL);
+	flib_netconn_onRunGame(conn, NULL, NULL);
+	flib_netconn_onTeamAccepted(conn, NULL, NULL);
+	flib_netconn_onHogCountChanged(conn, NULL, NULL);
+	flib_netconn_onTeamColorChanged(conn, NULL, NULL);
+	flib_netconn_onEngineMessage(conn, NULL, NULL);
+	flib_netconn_onCfgScheme(conn, NULL, NULL);
+	flib_netconn_onMapChanged(conn, NULL, NULL);
+	flib_netconn_onScriptChanged(conn, NULL, NULL);
+	flib_netconn_onWeaponsetChanged(conn, NULL, NULL);
+	flib_netconn_onAdminAccess(conn, NULL, NULL);
+	flib_netconn_onServerVar(conn, NULL, NULL);
 }
 
 flib_netconn *flib_netconn_create(const char *playerName, flib_cfg_meta *metacfg, const char *host, uint16_t port) {
@@ -183,6 +128,7 @@
 			newConn->isChief = false;
 			newConn->metaCfg = flib_cfg_meta_retain(metacfg);
 			newConn->roomList = flib_roomlist_create();
+			newConn->map = flib_map_create_named("", "NoSuchMap");
 			newConn->running = false;
 			newConn->destroyRequested = false;
 			clearCallbacks(newConn);
@@ -212,6 +158,7 @@
 			flib_netbase_destroy(conn->netBase);
 			flib_cfg_meta_release(conn->metaCfg);
 			flib_roomlist_destroy(conn->roomList);
+			flib_map_release(conn->map);
 			free(conn->playerName);
 			free(conn);
 		}
@@ -238,65 +185,6 @@
 	return result;
 }
 
-int flib_netconn_send_quit(flib_netconn *conn, const char *quitmsg) {
-	int result = -1;
-	if(!conn) {
-		flib_log_e("null parameter in flib_netconn_send_quit");
-	} else {
-		result = flib_netbase_sendf(conn->netBase, "%s\n%s\n\n", "QUIT", quitmsg ? quitmsg : "User quit");
-	}
-	return result;
-}
-
-int flib_netconn_send_chat(flib_netconn *conn, const char *chat) {
-	int result = -1;
-	if(!conn || !chat) {
-		flib_log_e("null parameter in flib_netconn_send_chat");
-	} else {
-		result = flib_netbase_sendf(conn->netBase, "%s\n%s\n\n", "CHAT", chat);
-	}
-	return result;
-}
-
-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 {
-		char *tmpName = flib_strdupnull(nick);
-		if(tmpName) {
-			if(!flib_netbase_sendf(conn->netBase, "%s\n%s\n\n", "NICK", nick)) {
-				free(conn->playerName);
-				conn->playerName = tmpName;
-				tmpName = NULL;
-				result = 0;
-			}
-		}
-		free(tmpName);
-	}
-	return result;
-}
-
-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 {
-		md5_state_t md5state;
-		uint8_t md5bytes[16];
-		char md5hex[33];
-		md5_init(&md5state);
-		md5_append(&md5state, (unsigned char*)latin1Passwd, strlen(latin1Passwd));
-		md5_finish(&md5state, md5bytes);
-		for(int i=0;i<sizeof(md5bytes); i++) {
-			// Needs to be lowercase - server checks case sensitive
-			snprintf(md5hex+i*2, 3, "%02x", (unsigned)md5bytes[i]);
-		}
-		result = flib_netbase_sendf(conn->netBase, "%s\n%s\n\n", "PASSWORD", md5hex);
-	}
-	return result;
-}
-
 /*
  * Callback registration functions
  */
@@ -412,15 +300,6 @@
 	}
 }
 
-void flib_netconn_onNickAccept(flib_netconn *conn, void (*callback)(void *context, const char *nick), void* context) {
-	if(!conn) {
-		flib_log_e("null parameter in flib_netconn_onNickAccept");
-	} else {
-		conn->onNickAcceptCb = callback ? callback : &defaultCallback_str;
-		conn->onNickAcceptCtx = context;
-	}
-}
-
 void flib_netconn_onPasswordRequest(flib_netconn *conn, void (*callback)(void *context, const char *nick), void* context) {
 	if(!conn) {
 		flib_log_e("null parameter in flib_netconn_onPasswordRequest");
@@ -442,42 +321,162 @@
 	}
 }
 
-void flib_netconn_onReadyStateCb(flib_netconn *conn, void (*callback)(void *context, const char *nick, bool ready), void* context) {
+void flib_netconn_onReadyState(flib_netconn *conn, void (*callback)(void *context, const char *nick, bool ready), void* context) {
 	if(!conn) {
-		flib_log_e("null parameter in flib_netconn_onReadyStateCb");
+		flib_log_e("null parameter in flib_netconn_onReadyState");
 	} else {
 		conn->onReadyStateCb = callback ? callback : &defaultCallback_str_bool;
 		conn->onReadyStateCtx = context;
 	}
 }
 
-void flib_netconn_onEnterRoomCb(flib_netconn *conn, void (*callback)(void *context, bool chief), void *context) {
+void flib_netconn_onEnterRoom(flib_netconn *conn, void (*callback)(void *context, bool chief), void *context) {
 	if(!conn) {
-		flib_log_e("null parameter in flib_netconn_onEnterRoomCb");
+		flib_log_e("null parameter in flib_netconn_onEnterRoom");
 	} else {
 		conn->onEnterRoomCb = callback ? callback : &defaultCallback_bool;
 		conn->onEnterRoomCtx = context;
 	}
 }
 
-void flib_netconn_onLeaveRoomCb(flib_netconn *conn, void (*callback)(void *context, int reason, const char *message), void *context) {
+void flib_netconn_onLeaveRoom(flib_netconn *conn, void (*callback)(void *context, int reason, const char *message), void *context) {
 	if(!conn) {
-		flib_log_e("null parameter in flib_netconn_onLeaveRoomCb");
+		flib_log_e("null parameter in flib_netconn_onLeaveRoom");
 	} else {
 		conn->onLeaveRoomCb = callback ? callback : &defaultCallback_int_str;
 		conn->onLeaveRoomCtx = context;
 	}
 }
 
-void flib_netconn_onTeamAddCb(flib_netconn *conn, void (*callback)(void *context, flib_team *team), void *context) {
+void flib_netconn_onTeamAdd(flib_netconn *conn, void (*callback)(void *context, flib_team *team), void *context) {
 	if(!conn) {
-		flib_log_e("null parameter in flib_netconn_onTeamAddCb");
+		flib_log_e("null parameter in flib_netconn_onTeamAdd");
 	} else {
 		conn->onTeamAddCb = callback ? callback : &defaultCallback_onTeamAdd;
 		conn->onTeamAddCtx = context;
 	}
 }
 
+void flib_netconn_onTeamDelete(flib_netconn *conn, void (*callback)(void *context, const char *teamname), void *context) {
+	if(!conn) {
+		flib_log_e("null parameter in flib_netconn_onTeamDelete");
+	} else {
+		conn->onTeamDeleteCb = callback ? callback : &defaultCallback_str;
+		conn->onTeamDeleteCtx = context;
+	}
+}
+
+void flib_netconn_onRunGame(flib_netconn *conn, void (*callback)(void *context), void *context) {
+	if(!conn) {
+		flib_log_e("null parameter in flib_netconn_onRunGame");
+	} else {
+		conn->onRunGameCb = callback ? callback : &defaultCallback_void;
+		conn->onRunGameCtx = context;
+	}
+}
+
+void flib_netconn_onTeamAccepted(flib_netconn *conn, void (*callback)(void *context, const char *teamName), void *context) {
+	if(!conn) {
+		flib_log_e("null parameter in flib_netconn_onTeamAccepted");
+	} else {
+		conn->onTeamAcceptedCb = callback ? callback : &defaultCallback_str;
+		conn->onTeamAcceptedCtx = context;
+	}
+}
+
+void flib_netconn_onHogCountChanged(flib_netconn *conn, void (*callback)(void *context, const char *teamName, int hogs), void *context) {
+	if(!conn) {
+		flib_log_e("null parameter in flib_netconn_onHogCountChanged");
+	} else {
+		conn->onHogCountChangedCb = callback ? callback : &defaultCallback_str_int;
+		conn->onHogCountChangedCtx = context;
+	}
+}
+
+void flib_netconn_onTeamColorChanged(flib_netconn *conn, void (*callback)(void *context, const char *teamName, uint32_t colorARGB), void *context) {
+	if(!conn) {
+		flib_log_e("null parameter in flib_netconn_onTeamColorChanged");
+	} else {
+		conn->onTeamColorChangedCb = callback ? callback : &defaultCallback_onTeamColorChanged;
+		conn->onTeamColorChangedCtx = context;
+	}
+}
+
+void flib_netconn_onEngineMessage(flib_netconn *conn, void (*callback)(void *context, const char *message, int size), void *context) {
+	if(!conn) {
+		flib_log_e("null parameter in flib_netconn_onEngineMessage");
+	} else {
+		conn->onEngineMessageCb = callback ? callback : &defaultCallback_str_int;
+		conn->onEngineMessageCtx = context;
+	}
+}
+
+void flib_netconn_onCfgScheme(flib_netconn *conn, void (*callback)(void *context, flib_cfg *scheme), void *context) {
+	if(!conn) {
+		flib_log_e("null parameter in flib_netconn_onCfgScheme");
+	} else {
+		conn->onCfgSchemeCb = callback ? callback : &defaultCallback_onCfgScheme;
+		conn->onCfgSchemeCtx = context;
+	}
+}
+
+void flib_netconn_onMapChanged(flib_netconn *conn, void (*callback)(void *context, const flib_map *map, int changetype), void *context) {
+	if(!conn) {
+		flib_log_e("null parameter in flib_netconn_onMapChanged");
+	} else {
+		conn->onMapChangedCb = callback ? callback : &defaultCallback_onMapChanged;
+		conn->onMapChangedCtx = context;
+	}
+}
+
+void flib_netconn_onScriptChanged(flib_netconn *conn, void (*callback)(void *context, const char *script), void *context) {
+	if(!conn) {
+		flib_log_e("null parameter in flib_netconn_onScriptChanged");
+	} else {
+		conn->onScriptChangedCb = callback ? callback : &defaultCallback_str;
+		conn->onScriptChangedCtx = context;
+	}
+}
+
+void flib_netconn_onWeaponsetChanged(flib_netconn *conn, void (*callback)(void *context, flib_weaponset *weaponset), void *context) {
+	if(!conn) {
+		flib_log_e("null parameter in flib_netconn_onWeaponsetChanged");
+	} else {
+		conn->onWeaponsetChangedCb = callback ? callback : &defaultCallback_onWeaponsetChanged;
+		conn->onWeaponsetChangedCtx = context;
+	}
+}
+
+void flib_netconn_onAdminAccess(flib_netconn *conn, void (*callback)(void *context), void *context) {
+	if(!conn) {
+		flib_log_e("null parameter in flib_netconn_onAdminAccess");
+	} else {
+		conn->onAdminAccessCb = callback ? callback : &defaultCallback_void;
+		conn->onAdminAccessCtx = context;
+	}
+}
+
+void flib_netconn_onServerVar(flib_netconn *conn, void (*callback)(void *context, const char *name, const char *value), void *context) {
+	if(!conn) {
+		flib_log_e("null parameter in flib_netconn_onServerVar");
+	} else {
+		conn->onServerVarCb = callback ? callback : &defaultCallback_str_str;
+		conn->onServerVarCtx = context;
+	}
+}
+
+void 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 = map;
+	} else {
+		flib_log_e("Error resetting netconn.map");
+	}
+}
+
 static void flib_netconn_wrappedtick(flib_netconn *conn) {
 	flib_netmsg *netmsg;
 	flib_netbase *net = conn->netBase;
@@ -503,14 +502,14 @@
 	    	if(netmsg->partCount<2) {
 	    		flib_log_w("Net: Malformed NICK message");
 	    	} else {
-				free(conn->playerName);
-				conn->playerName = flib_strdupnull(netmsg->parts[1]);
-				if(!conn->playerName) {
+	    		char *nick = flib_strdupnull(netmsg->parts[1]);
+	    		if(nick) {
+					free(conn->playerName);
+					conn->playerName = nick;
+	    		} else {
 					conn->netconnState = NETCONN_STATE_DISCONNECTED;
 					conn->onDisconnectedCb(conn->onDisconnectedCtx, NETCONN_DISCONNECT_INTERNAL_ERROR, "Out of memory");
 					exit = true;
-				} else {
-					conn->onNickAcceptCb(conn->onNickAcceptCtx, conn->playerName);
 				}
 	    	}
 	    } else if (!strcmp(cmd, "PROTO")) {
@@ -583,18 +582,9 @@
 	        	free(joined);
 	        }
 	    } else if(!strcmp(cmd, "SERVER_VARS")) {
-	    	// TODO
-//	        QStringList tmp = lst;
-//	        tmp.removeFirst();
-//	        while (tmp.size() >= 2)
-//	        {
-//	            if(tmp[0] == "MOTD_NEW") emit serverMessageNew(tmp[1]);
-//	            else if(tmp[0] == "MOTD_OLD") emit serverMessageOld(tmp[1]);
-//	            else if(tmp[0] == "LATEST_PROTO") emit latestProtocolVar(tmp[1].toInt());
-//
-//	            tmp.removeFirst();
-//	            tmp.removeFirst();
-//	        }
+	    	for(int offset=1; offset+2<netmsg->partCount; offset+=2) {
+	    		conn->onServerVarCb(conn->onServerVarCtx, netmsg->parts[offset], netmsg->parts[offset+1]);
+	    	}
 	    } else if (!strcmp(cmd, "CLIENT_FLAGS")) {
 	        if(netmsg->partCount < 3 || strlen(netmsg->parts[1]) < 2) {
 	        	flib_log_w("Net: Malformed CLIENT_FLAGS message");
@@ -606,12 +596,6 @@
 					switch(flags[i]) {
 					case 'r':
 						for(int j = 2; j < netmsg->partCount; ++j) {
-							if (!strcmp(conn->playerName, netmsg->parts[i])) {
-								// TODO what is the reason behind this (copied from QtFrontend)?
-								if (conn->isChief && !setFlag) {
-									flib_netbase_sendf(conn->netBase, "%s\n\n", "TOGGLE_READY");
-								}
-							}
 							conn->onReadyStateCb(conn->onReadyStateCtx, netmsg->parts[i], setFlag);
 						}
 						break;
@@ -631,21 +615,22 @@
 					conn->onDisconnectedCb(conn->onDisconnectedCtx, NETCONN_DISCONNECT_INTERNAL_ERROR, "Internal error");
 					exit = true;
 	        	} else {
+	        		team->remoteDriven = true;
 	        		conn->onTeamAddCb(conn->onTeamAddCtx, team);
 	        	}
+	        	flib_team_release(team);
 	        }
 	    } else if (!strcmp(cmd, "REMOVE_TEAM")) {
 	        if(netmsg->partCount != 2) {
 	            flib_log_w("Net: Bad REMOVETEAM message");
 	        } else {
-	        	// TODO
-	        	// emit RemoveNetTeam(HWTeam(lst[1]));
+	        	conn->onTeamDeleteCb(conn->onTeamDeleteCtx, netmsg->parts[1]);
 	        }
 	    } else if(!strcmp(cmd, "ROOMABANDONED")) {
-	        conn->netconnState = NETCONN_STATE_LOBBY;
+	    	leaveRoom(conn);
 	        conn->onLeaveRoomCb(conn->onLeaveRoomCtx, NETCONN_ROOMLEAVE_ABANDONED, "Room destroyed");
 	    } else if(!strcmp(cmd, "KICKED")) {
-	    	conn->netconnState = NETCONN_STATE_LOBBY;
+	    	leaveRoom(conn);
 	    	conn->onLeaveRoomCb(conn->onLeaveRoomCtx, NETCONN_ROOMLEAVE_KICKED, "You got kicked");
 	    } else if(!strcmp(cmd, "JOINED")) {
 	        if(netmsg->partCount < 2) {
@@ -718,8 +703,7 @@
 	        }
 	    } else if (!strcmp(cmd, "RUN_GAME")) {
 	        conn->netconnState = NETCONN_STATE_INGAME;
-	        // TODO
-	        // emit AskForRunGame();
+	        conn->onRunGameCb(conn->onRunGameCtx);
 	    } else if (!strcmp(cmd, "ASKPASSWORD")) {
 	    	conn->onPasswordRequestCb(conn->onPasswordRequestCtx, conn->playerName);
 	    } else if (!strcmp(cmd, "NOTICE")) {
@@ -740,49 +724,128 @@
 	        if (netmsg->partCount != 2) {
 	            flib_log_w("Net: Bad TEAM_ACCEPTED message");
 	        } else {
-	        	// TODO
-	        	// emit TeamAccepted(lst[1]);
+	        	conn->onTeamAcceptedCb(conn->onTeamAcceptedCtx, netmsg->parts[1]);
 	        }
 	    } else if (!strcmp(cmd, "CFG")) {
 	        if(netmsg->partCount < 3) {
 	            flib_log_w("Net: Bad CFG message");
 	        } else {
-	        	// TODO
-//				QStringList tmp = lst;
-//				tmp.removeFirst();
-//				tmp.removeFirst();
-//				if (lst[1] == "SCHEME")
-//					emit netSchemeConfig(tmp);
-//				else
-//					emit paramChanged(lst[1], tmp);
+	        	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) {
+						conn->onCfgSchemeCb(conn->onCfgSchemeCtx, cfg);
+					} else {
+						flib_log_e("Error processing CFG SCHEME message");
+					}
+					flib_cfg_release(cfg);
+				} 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;
+						conn->onMapChangedCb(conn->onMapChangedCtx, conn->map, NETCONN_MAPCHANGE_FULL);
+					} else {
+						flib_log_e("Error processing CFG FULLMAPCONFIG message");
+					}
+				} else if(!strcmp(subcmd, "MAP") && netmsg->partCount == 3) {
+					char *mapname = flib_strdupnull(netmsg->parts[2]);
+					if(mapname) {
+						free(conn->map->name);
+						conn->map->name = mapname;
+						conn->onMapChangedCb(conn->onMapChangedCtx, conn->map, NETCONN_MAPCHANGE_MAP);
+					} else {
+						flib_log_e("Error processing CFG MAP message");
+					}
+				} else if(!strcmp(subcmd, "THEME") && netmsg->partCount == 3) {
+					char *themename = flib_strdupnull(netmsg->parts[2]);
+					if(themename) {
+						free(conn->map->theme);
+						conn->map->theme = themename;
+						conn->onMapChangedCb(conn->onMapChangedCtx, conn->map, NETCONN_MAPCHANGE_THEME);
+					} else {
+						flib_log_e("Error processing CFG THEME message");
+					}
+				} else if(!strcmp(subcmd, "SEED") && netmsg->partCount == 3) {
+					char *seed = flib_strdupnull(netmsg->parts[2]);
+					if(seed) {
+						free(conn->map->seed);
+						conn->map->seed = seed;
+						conn->onMapChangedCb(conn->onMapChangedCtx, conn->map, NETCONN_MAPCHANGE_SEED);
+					} else {
+						flib_log_e("Error processing CFG SEED message");
+					}
+				} else if(!strcmp(subcmd, "TEMPLATE") && netmsg->partCount == 3) {
+					conn->map->templateFilter = atoi(netmsg->parts[2]);
+					conn->onMapChangedCb(conn->onMapChangedCtx, conn->map, NETCONN_MAPCHANGE_TEMPLATE);
+				} else if(!strcmp(subcmd, "MAPGEN") && netmsg->partCount == 3) {
+					conn->map->mapgen = atoi(netmsg->parts[2]);
+					conn->onMapChangedCb(conn->onMapChangedCtx, conn->map, NETCONN_MAPCHANGE_MAPGEN);
+				} else if(!strcmp(subcmd, "MAZE_SIZE") && netmsg->partCount == 3) {
+					conn->map->mazeSize = atoi(netmsg->parts[2]);
+					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) {
+						free(conn->map->drawData);
+						conn->map->drawData = drawnMapData;
+						conn->map->drawDataSize = drawnMapSize;
+						conn->onMapChangedCb(conn->onMapChangedCtx, conn->map, NETCONN_MAPCHANGE_DRAWNMAP);
+					} else {
+						flib_log_e("Error processing CFG DRAWNMAP message");
+					}
+				} else if(!strcmp(subcmd, "SCRIPT") && netmsg->partCount == 3) {
+					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) {
+						conn->onWeaponsetChangedCb(conn->onWeaponsetChangedCtx, weapons);
+					} else {
+						flib_log_e("Error processing CFG AMMO message");
+					}
+					flib_weaponset_release(weapons);
+				} else {
+					flib_log_w("Net: Unknown or malformed CFG subcommand: %s", subcmd);
+				}
 	        }
 	    } else if (!strcmp(cmd, "HH_NUM")) {
 	        if (netmsg->partCount != 3) {
-	            flib_log_w("Net: Bad TEAM_ACCEPTED message");
+	            flib_log_w("Net: Bad HH_NUM message");
 	        } else {
-	        	// TODO
-//				HWTeam tmptm(lst[1]);
-//				tmptm.setNumHedgehogs(lst[2].toUInt());
-//				emit hhnumChanged(tmptm);
+	        	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 {
+	        		conn->onHogCountChangedCb(conn->onHogCountChangedCtx, netmsg->parts[1], hogs);
+	        	}
 	        }
 	    } else if (!strcmp(cmd, "TEAM_COLOR")) {
 	        if (netmsg->partCount != 3) {
 	            flib_log_w("Net: Bad TEAM_COLOR message");
 	        } else {
-	        	// TODO
-//				HWTeam tmptm(lst[1]);
-//				tmptm.setColor(lst[2].toInt());
-//				emit teamColorChanged(tmptm);
+	        	long color;
+	        	if(sscanf(netmsg->parts[2], "#%lx", &color)) {
+	        		conn->onTeamColorChangedCb(conn->onTeamColorChangedCtx, netmsg->parts[1], (uint32_t)color);
+	        	} else {
+	        		flib_log_w("Net: Bad TEAM_COLOR message: Color %s", netmsg->parts[2]);
+	        	}
 	        }
 	    } else if (!strcmp(cmd, "EM")) {
 	        if(netmsg->partCount < 2) {
 	            flib_log_w("Net: Bad EM message");
 	        } else {
-	        	// TODO
-//				for(int i = 1; i < netmsg->partCount; ++i) {
-//					QByteArray em = QByteArray::fromBase64(lst[i].toAscii());
-//					emit FromNet(em);
-//				}
+	        	for(int i = 1; i < netmsg->partCount; ++i) {
+					char *out = NULL;
+					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);
+					} else {
+						flib_log_e("Net: Malformed engine message: %s", netmsg->parts[i]);
+					}
+					free(out);
+	        	}
 	        }
 	    } else if (!strcmp(cmd, "BYE")) {
 	        if (netmsg->partCount < 2) {
@@ -797,7 +860,7 @@
 				exit = true;
 	        }
 	    } else if (!strcmp(cmd, "ADMIN_ACCESS")) {
-	    	// TODO callback?
+	    	conn->onAdminAccessCb(conn->onAdminAccessCtx);
 	    	conn->isAdmin = true;
 	    } else if (!strcmp(cmd, "ROOM_CONTROL_ACCESS")) {
 	        if (netmsg->partCount < 2) {
--- a/project_files/frontlib/net/netconn.h	Thu Jun 21 21:32:12 2012 +0200
+++ b/project_files/frontlib/net/netconn.h	Mon Jun 25 00:42:07 2012 +0200
@@ -28,8 +28,16 @@
 #define NETCONN_MSG_TYPE_WARNING 2
 #define NETCONN_MSG_TYPE_ERROR 3
 
+#define NETCONN_MAPCHANGE_FULL 0
+#define NETCONN_MAPCHANGE_MAP 1
+#define NETCONN_MAPCHANGE_MAPGEN 2
+#define NETCONN_MAPCHANGE_DRAWNMAP 3
+#define NETCONN_MAPCHANGE_MAZE_SIZE 4
+#define NETCONN_MAPCHANGE_TEMPLATE 5
+#define NETCONN_MAPCHANGE_THEME 6
+#define NETCONN_MAPCHANGE_SEED 7
 
-struct _flib_netconn;
+// 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);
@@ -60,6 +68,12 @@
 int flib_netconn_send_chat(flib_netconn *conn, const char *chat);
 
 /**
+ * Send a teamchat message, forwarded from the engine. Only makes sense ingame.
+ * The server does not send a reply. TODO figure out details
+ */
+int flib_netconn_send_teamchat(flib_netconn *conn, const char *msg);
+
+/**
  * Note: Most other functions in this lib accept UTF-8, but the password needs to be
  * sent as latin1
  */
@@ -68,16 +82,212 @@
 /**
  * Request a different nickname.
  * This function only makes sense in reaction to an onNickTaken callback, because the netconn automatically
- * requests the nickname you provide on creation, and once the server accepts the nickname (onNickAccept)
- * it can no longer be changed.
- *
- * As a response to the nick change request, the server will either reply with a confirmation (onNickAccept)
- * or a rejection (onNickTaken). Note that the server confirms a nick even if it is password protected, the
- * password request happens afterwards.
+ * requests the nickname you provide on creation, and once the server accepts the nickname it can no longer
+ * be changed.
  */
 int flib_netconn_send_nick(flib_netconn *conn, const char *nick);
 
 /**
+ * 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.
+ */
+int flib_netconn_send_joinRoom(flib_netconn *conn, const char *room);
+
+/**
+ * Create and join a new room. Only makes sense when in lobby state. If the action succeeds, you will
+ * receive an onEnterRoom callback with chief=true.
+ */
+int flib_netconn_send_createRoom(flib_netconn *conn, const char *room);
+
+/**
+ * Rename the current room. Only makes sense in room state and if you are chief.
+ * TODO: reply
+ */
+int flib_netconn_send_renameRoom(flib_netconn *conn, const char *roomName);
+
+/**
+ * Leave the room for the lobby. Only makes sense in room state.
+ * TODO: reply, TODO can you send a message?
+ */
+int flib_netconn_send_leaveRoom(flib_netconn *conn);
+
+/**
+ * Change your "ready" status in the room. Only makes sense when in room state.
+ * TODO: reply
+ */
+int flib_netconn_send_toggleReady(flib_netconn *conn);
+
+/**
+ * Add a team to the current room. The message includes the team color, but not
+ * the number of hogs. Only makes sense when in room state. If the action succeeds, you will
+ * receive an onTeamAccepted callback with the name of the team.
+ */
+int flib_netconn_send_addTeam(flib_netconn *conn, const flib_team *team);
+
+/**
+ * Remove the team with the name teamname. Only makes sense when in room state.
+ * TODO: reply
+ */
+int flib_netconn_send_removeTeam(flib_netconn *conn, const char *teamname);
+
+/**
+ * Send an engine message. Only makes sense when ingame.
+ * TODO: reply
+ */
+int flib_netconn_send_engineMessage(flib_netconn *conn, const uint8_t *message, size_t size);
+
+/**
+ * Set the number of hogs for a team. Only makes sense in room state and if you are chief.
+ * The server does not send a reply.
+ */
+int flib_netconn_send_teamHogCount(flib_netconn *conn, const char *teamname, int hogcount);
+
+/**
+ * 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);
+
+/**
+ * Set the weaponset for the room. Only makes sense in room state and if you are chief.
+ * The server does not send a reply.
+ */
+int flib_netconn_send_weaponset(flib_netconn *conn, const flib_weaponset *weaponset);
+
+/**
+ * Set the map for the room. Only makes sense in room state and if you are chief.
+ * The server does not send a reply.
+ */
+int flib_netconn_send_map(flib_netconn *conn, const flib_map *map);
+
+/**
+ * Set the mapname. Only makes sense in room state and if you are chief.
+ * The server does not send a reply.
+ */
+int flib_netconn_send_mapName(flib_netconn *conn, const char *mapName);
+
+/**
+ * Set the map generator. Only makes sense in room state and if you are chief.
+ * The server does not send a reply.
+ */
+int flib_netconn_send_mapGen(flib_netconn *conn, int mapGen);
+
+/**
+ * Set the map template for regular maps. Only makes sense in room state and if you are chief.
+ * The server does not send a reply.
+ */
+int flib_netconn_send_mapTemplate(flib_netconn *conn, int templateFilter);
+
+/**
+ * Set the maze template (maze size) for mazes. Only makes sense in room state and if you are chief.
+ * The server does not send a reply.
+ */
+int flib_netconn_send_mapMazeSize(flib_netconn *conn, int mazeSize);
+
+/**
+ * Set the seed for the map. Only makes sense in room state and if you are chief.
+ * The server does not send a reply.
+ */
+int flib_netconn_send_mapSeed(flib_netconn *conn, const char *seed);
+
+/**
+ * Set the theme for the map. Only makes sense in room state and if you are chief.
+ * The server does not send a reply.
+ */
+int flib_netconn_send_mapTheme(flib_netconn *conn, const char *theme);
+
+/**
+ * Set the draw data for the drawn map. Only makes sense in room state and if you are chief.
+ * The server does not send a reply.
+ */
+int flib_netconn_send_mapDrawdata(flib_netconn *conn, const uint8_t *drawData, size_t size);
+
+/**
+ * Set the script (game style). Only makes sense in room state and if you are chief.
+ * The server does not send a reply.
+ */
+int flib_netconn_send_script(flib_netconn *conn, const char *scriptName);
+
+/**
+ * Set the scheme. Only makes sense in room state and if you are chief.
+ * The server does not send a reply.
+ */
+int flib_netconn_send_scheme(flib_netconn *conn, const flib_cfg *scheme);
+
+/**
+ * Inform the server that the round has ended. TODO: Figure out details
+ */
+int flib_netconn_send_roundfinished(flib_netconn *conn, bool withoutError);
+
+/**
+ * Ban a player. TODO: Figure out details
+ */
+int flib_netconn_send_ban(flib_netconn *conn, const char *playerName);
+
+/**
+ * Kick a player. TODO: Figure out details
+ */
+int flib_netconn_send_kick(flib_netconn *conn, const char *playerName);
+
+/**
+ * Request information about a player. If the action succeeds, you will
+ * receive an onMessage callback with NETCONN_MSG_TYPE_PLAYERINFO containing
+ * the requested information.
+ */
+int flib_netconn_send_playerInfo(flib_netconn *conn, const char *playerName);
+
+/**
+ * Follow a player. TODO figure out details
+ */
+int flib_netconn_send_playerFollow(flib_netconn *conn, const char *playerName);
+
+/**
+ * Signal that you want to start the game. Only makes sense in room state and if you are chief.
+ * TODO figure out details
+ */
+int flib_netconn_send_startGame(flib_netconn *conn);
+
+/**
+ * Allow/forbid players to join the room. Only makes sense in room state and if you are chief.
+ * The server does not send a reply.
+ */
+int flib_netconn_send_toggleRestrictJoins(flib_netconn *conn);
+
+/**
+ * Allow/forbid adding teams to the room. Only makes sense in room state and if you are chief.
+ * The server does not send a reply.
+ */
+int flib_netconn_send_toggleRestrictTeams(flib_netconn *conn);
+
+/**
+ * Probably does something administrator-y.
+ */
+int flib_netconn_send_clearAccountsCache(flib_netconn *conn);
+
+/**
+ * Sets a server variable to the indicated value. Only makes sense if you are server admin.
+ * Known variables are MOTD_NEW, MOTD_OLD and LATEST_PROTO.
+ * TODO reply?
+ */
+int flib_netconn_send_setServerVar(flib_netconn *conn, const char *name, const char *value);
+
+/**
+ * Queries all server variables. Only makes sense if you are server admin. (TODO: try)
+ * If the action succeeds, you will receive several onServerVar callbacks with the
+ * current values of all server variables.
+ */
+int flib_netconn_send_getServerVars(flib_netconn *conn);
+
+
+
+
+
+
+
+
+
+
+/**
  * Callback for several informational messages that should be displayed to the user
  * (e.g. in the chat window), but do not require a reaction. If a game is running, you might
  * want to redirect some of these messages to the engine as well so the user will see them.
@@ -113,13 +323,11 @@
 void flib_netconn_onRoomUpdate(flib_netconn *conn, void (*callback)(void *context, const char *oldName, const flib_roomlist_room *room), void* context);
 
 /**
- * Callbacks for players joining or leaving a room or the lobby. If join is true it's a join, otherwise a leave.
+ * Callbacks for players joining or leaving the lobby. If join is true it's a join, otherwise a leave.
  * NOTE: partMessage is null if no parting message was given.
  */
 void flib_netconn_onLobbyJoin(flib_netconn *conn, void (*callback)(void *context, const char *nick), void* context);
 void flib_netconn_onLobbyLeave(flib_netconn *conn, void (*callback)(void *context, const char *nick, const char *partMessage), void* context);
-void flib_netconn_onRoomJoin(flib_netconn *conn, void (*callback)(void *context, const char *nick), void* context);
-void flib_netconn_onRoomLeave(flib_netconn *conn, void (*callback)(void *context, const char *nick, const char *partMessage), void* context);
 
 /**
  * onNickTaken is called on connecting to the server, if it turns out that there is already a player with the same nick.
@@ -130,14 +338,6 @@
 void flib_netconn_onNickTaken(flib_netconn *conn, void (*callback)(void *context, const char *nick), void* context);
 
 /**
- * onNickAccept informs that your nickname has been accepted by the server, i.e. there was nobody with that nick already
- * on the server.
- * Note that a nick request is sent automatically by the netconn when you join the server, so you should receive this
- * callback shortly after connecting.
- */
-void flib_netconn_onNickAccept(flib_netconn *conn, void (*callback)(void *context, const char *nick), void* context);
-
-/**
  * When connecting with a registered nickname, the server will ask for a password before admitting you in.
  * This callback is called when that happens. As a reaction, you can send the password using
  * flib_netconn_send_password or choose a different nick. If you don't register a callback,
@@ -146,6 +346,18 @@
 void flib_netconn_onPasswordRequest(flib_netconn *conn, void (*callback)(void *context, const char *nick), void* context);
 
 /**
+ * You just left the lobby and entered a room.
+ * If chief is true, you can and should send a full configuration for the room now.
+ * This consists of TODO
+ */
+void flib_netconn_onEnterRoom(flib_netconn *conn, void (*callback)(void *context, bool chief), void *context);
+
+
+/**
+ * The following callbacks are only relevant in room state.
+ */
+
+/**
  * This callback informs about changes to your room chief status, i.e. whether you are allowed to
  * modify the current room. Generally when you create a room you start out being room chief, and
  * when you join an existing room you are not. However, in some situations room ownership can change,
@@ -159,21 +371,91 @@
 /**
  * One of the players in the room (possibly you!) changed their ready state.
  */
-void flib_netconn_onReadyStateCb(flib_netconn *conn, void (*callback)(void *context, const char *nick, bool ready), void* context);
-
-/**
- * You just left the lobby and entered a room.
- * If chief is true, you can and should send a full configuration for the room now.
- */
-void flib_netconn_onEnterRoomCb(flib_netconn *conn, void (*callback)(void *context, bool chief), void *context);
+void flib_netconn_onReadyState(flib_netconn *conn, void (*callback)(void *context, const char *nick, bool ready), void* context);
 
 /**
  * You just left a room and entered the lobby again.
  * reason is one of the NETCONN_ROOMLEAVE_ constants.
  * This will not be called when you actively leave a room using PART.
  */
-void flib_netconn_onLeaveRoomCb(flib_netconn *conn, void (*callback)(void *context, int reason, const char *message), void *context);
+void flib_netconn_onLeaveRoom(flib_netconn *conn, void (*callback)(void *context, int reason, const char *message), void *context);
+
+/**
+ * A new team was added to the room. The person who adds a team does NOT receive this callback (he gets onTeamAccepted instead).
+ * The team does not contain bindings, stats, weaponset, color or the number of hogs.
+ */
+void flib_netconn_onTeamAdd(flib_netconn *conn, void (*callback)(void *context, flib_team *team), void *context);
+
+/**
+ * A team was removed from the room.
+ */
+void flib_netconn_onTeamDelete(flib_netconn *conn, void (*callback)(void *context, const char *teamname), void *context);
+
+void flib_netconn_onRoomJoin(flib_netconn *conn, void (*callback)(void *context, const char *nick), void* context);
+void flib_netconn_onRoomLeave(flib_netconn *conn, void (*callback)(void *context, const char *nick, const char *partMessage), void* context);
+
+/**
+ * The game is starting. Fire up the engine and join in!
+ * TODO: How?
+ */
+void flib_netconn_onRunGame(flib_netconn *conn, void (*callback)(void *context), void *context);
+
+/**
+ * When you ask for a team to be added, the server might reject it for several reasons, e.g. because it has the same name
+ * as an existing team, or because the room chief restricted adding new teams. If the team is accepted by the server,
+ * this callback is fired.
+ */
+void flib_netconn_onTeamAccepted(flib_netconn *conn, void (*callback)(void *context, const char *teamName), void *context);
+
+/**
+ * The number of hogs in a team has been changed by the room chief. If you are the chief and change the number of hogs yourself,
+ * you will not receive this callback!
+ */
+void flib_netconn_onHogCountChanged(flib_netconn *conn, void (*callback)(void *context, const char *teamName, int hogs), void *context);
 
-void flib_netconn_onTeamAddCb(flib_netconn *conn, void (*callback)(void *context, flib_team *team), void *context);
+/**
+ * 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_onEngineMessage(flib_netconn *conn, void (*callback)(void *context, const char *message, int size), void *context);
+
+void flib_netconn_onCfgScheme(flib_netconn *conn, void (*callback)(void *context, flib_cfg *scheme), void *context);
+
+/**
+ * This is called when the map configuration in a room is changed (or first received). Only non-chiefs receive these messages.
+ * To reduce the number of callback functions, the netconn keeps track of the current map settings and always passes the entire
+ * current map config, but informs the callee about what has changed (see the NETCONN_MAPCHANGE_ constants).
+ * The map parameter passed to the callback is an internally held map config. If you want to keep it around, best make a copy
+ * or it may or may not change while you are not looking.
+ *
+ * Caution: Due to the way the protocol works, the map might not be complete at this point if it is a hand-drawn map, because
+ * the "full" map config does not include the drawn map data.
+ */
+void flib_netconn_onMapChanged(flib_netconn *conn, void (*callback)(void *context, const flib_map *map, int changetype), void *context);
+
+/**
+ * The "game style" script has been changed by the room chief. If you are the chief and change the script yourself,
+ * you will not receive this callback!
+ */
+void flib_netconn_onScriptChanged(flib_netconn *conn, void (*callback)(void *context, const char *script), void *context);
+
+/**
+ * The weaponset has been changed by the room chief. If you are the chief and change the weaponset yourself,
+ * you will not receive this callback!
+ */
+void flib_netconn_onWeaponsetChanged(flib_netconn *conn, void (*callback)(void *context, flib_weaponset *weaponset), void *context);
+
+/**
+ * This callback is called if the server informs us that we have admin rights.
+ */
+void flib_netconn_onAdminAccess(flib_netconn *conn, void (*callback)(void *context), void *context);
+
+/**
+ * When you query the server vars with GET_SERVER_VAR (TODO probably only works as admin), the server
+ * replies with a list of them. This callback is called for each entry in that list.
+ */
+void flib_netconn_onServerVar(flib_netconn *conn, void (*callback)(void *context, const char *name, const char *value), void *context);
 
 #endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/net/netconn_internal.h	Mon Jun 25 00:42:07 2012 +0200
@@ -0,0 +1,126 @@
+/**
+ * Common definitions needed by netconn functions, to allow splitting them into several files.
+ */
+
+#ifndef NETCONN_INTERNAL_H_
+#define NETCONN_INTERNAL_H_
+
+#include "netconn.h"
+#include "netbase.h"
+#include "../model/cfg.h"
+#include "../model/roomlist.h"
+#include "../model/map.h"
+#include "../model/team.h"
+#include "../model/weapon.h"
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stddef.h>
+
+struct _flib_netconn {
+	flib_netbase *netBase;
+	char *playerName;
+	flib_cfg_meta *metaCfg;
+	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.
+
+	void (*onMessageCb)(void *context, int msgtype, const char *msg);
+	void *onMessageCtx;
+
+	void (*onConnectedCb)(void *context);
+	void *onConnectedCtx;
+
+	void (*onDisconnectedCb)(void *context, int reason, const char *message);
+	void *onDisconnectedCtx;
+
+	void (*onRoomAddCb)(void *context, const flib_roomlist_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 *onRoomUpdateCtx;
+
+	void (*onChatCb)(void *context, const char *nick, const char *msg);
+	void *onChatCtx;
+
+	void (*onLobbyJoinCb)(void *context, const char *nick);
+	void *onLobbyJoinCtx;
+
+	void (*onLobbyLeaveCb)(void *context, const char *nick, const char *partMessage);
+	void *onLobbyLeaveCtx;
+
+	void (*onRoomJoinCb)(void *context, const char *nick);
+	void *onRoomJoinCtx;
+
+	void (*onRoomLeaveCb)(void *context, const char *nick, const char *partMessage);
+	void *onRoomLeaveCtx;
+
+	void (*onNickTakenCb)(void *context, const char *nick);
+	void *onNickTakenCtx;
+
+	void (*onPasswordRequestCb)(void *context, const char *nick);
+	void *onPasswordRequestCtx;
+
+	void (*onRoomChiefStatusCb)(void *context, bool isChief);
+	void *onRoomChiefStatusCtx;
+
+	void (*onReadyStateCb)(void *context, const char *nick, bool ready);
+	void *onReadyStateCtx;
+
+	void (*onEnterRoomCb)(void *context, bool chief);
+	void *onEnterRoomCtx;
+
+	void (*onLeaveRoomCb)(void *context, int reason, const char *message);
+	void *onLeaveRoomCtx;
+
+	void (*onTeamAddCb)(void *context, flib_team *team);
+	void *onTeamAddCtx;
+
+	void (*onTeamDeleteCb)(void *context, const char *teamname);
+	void *onTeamDeleteCtx;
+
+	void (*onRunGameCb)(void *context);
+	void *onRunGameCtx;
+
+	void (*onTeamAcceptedCb)(void *context, const char *teamName);
+	void *onTeamAcceptedCtx;
+
+	void (*onHogCountChangedCb)(void *context, const char *teamName, int hogs);
+	void *onHogCountChangedCtx;
+
+	void (*onTeamColorChangedCb)(void *context, const char *teamName, uint32_t colorRGB);
+	void *onTeamColorChangedCtx;
+
+	void (*onEngineMessageCb)(void *context, const char *message, int size);
+	void *onEngineMessageCtx;
+
+	void (*onCfgSchemeCb)(void *context, flib_cfg *scheme);
+	void *onCfgSchemeCtx;
+
+	void (*onMapChangedCb)(void *context, const flib_map *map, int changetype);
+	void *onMapChangedCtx;
+
+	void (*onScriptChangedCb)(void *context, const char *script);
+	void *onScriptChangedCtx;
+
+	void (*onWeaponsetChangedCb)(void *context, flib_weaponset *weaponset);
+	void *onWeaponsetChangedCtx;
+
+	void (*onAdminAccessCb)(void *context);
+	void *onAdminAccessCtx;
+
+	void (*onServerVarCb)(void *context, const char *name, const char *value);
+	void *onServerVarCtx;
+
+	bool running;
+	bool destroyRequested;
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/net/netconn_send.c	Mon Jun 25 00:42:07 2012 +0200
@@ -0,0 +1,346 @@
+#include "netconn_internal.h"
+
+#include "../util/logging.h"
+#include "../util/util.h"
+#include "../util/buffer.h"
+#include "../md5/md5.h"
+#include "../base64/base64.h"
+
+#include <stdlib.h>
+#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);
+		return -1;
+	}
+	return flib_netbase_sendf(conn->netBase, "%s\n\n", cmdname);
+}
+
+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);
+		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);
+		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");
+}
+
+int flib_netconn_send_chat(flib_netconn *conn, const char *chat) {
+	return sendStr(conn, "CHAT", chat);
+}
+
+int flib_netconn_send_teamchat(flib_netconn *conn, const char *chat) {
+	return sendStr(conn, "TEAMCHAT", chat);
+}
+
+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 {
+		char *tmpName = flib_strdupnull(nick);
+		if(tmpName) {
+			if(!flib_netbase_sendf(conn->netBase, "%s\n%s\n\n", "NICK", nick)) {
+				free(conn->playerName);
+				conn->playerName = tmpName;
+				tmpName = NULL;
+				result = 0;
+			}
+		}
+		free(tmpName);
+	}
+	return result;
+}
+
+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 {
+		md5_state_t md5state;
+		uint8_t md5bytes[16];
+		char md5hex[33];
+		md5_init(&md5state);
+		md5_append(&md5state, (unsigned char*)latin1Passwd, strlen(latin1Passwd));
+		md5_finish(&md5state, md5bytes);
+		for(int i=0;i<sizeof(md5bytes); i++) {
+			// Needs to be lowercase - server checks case sensitive
+			snprintf(md5hex+i*2, 3, "%02x", (unsigned)md5bytes[i]);
+		}
+		result = flib_netbase_sendf(conn->netBase, "%s\n%s\n\n", "PASSWORD", md5hex);
+	}
+	return result;
+}
+
+int flib_netconn_send_joinRoom(flib_netconn *conn, const char *room) {
+	return sendStr(conn, "JOIN_ROOM", room);
+}
+
+int flib_netconn_send_createRoom(flib_netconn *conn, const char *room) {
+	return sendStr(conn, "CREATE_ROOM", room);
+}
+
+int flib_netconn_send_renameRoom(flib_netconn *conn, const char *roomName) {
+	return sendStr(conn, "ROOM_NAME", roomName);
+}
+
+int flib_netconn_send_leaveRoom(flib_netconn *conn) {
+	return sendVoid(conn, "PART");
+}
+
+int flib_netconn_send_toggleReady(flib_netconn *conn) {
+	return sendVoid(conn, "TOGGLE_READY");
+}
+
+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;
+		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 {
+			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);
+				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));
+				}
+			}
+			flib_vector_destroy(vec);
+		}
+	}
+	return result;
+}
+
+int flib_netconn_send_removeTeam(flib_netconn *conn, const char *teamname) {
+	return sendStr(conn, "REMOVE_TEAM", teamname);
+}
+
+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 {
+		char *base64encout = NULL;
+		base64_encode_alloc((const char*)message, size, &base64encout);
+		if(base64encout) {
+			result = flib_netbase_sendf(conn->netBase, "EM\n%s\n\n", base64encout);
+		}
+		free(base64encout);
+	}
+	return result;
+}
+
+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;
+	}
+	return flib_netbase_sendf(conn->netBase, "HH_NUM\n%s\n%i\n\n", teamname, hogcount);
+}
+
+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;
+	}
+	return flib_netbase_sendf(conn->netBase, "TEAM_COLOR\n%s\n%lu\n\n", teamname, (unsigned long)colorRGB);
+}
+
+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;
+	}
+
+	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);
+}
+
+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");
+		return -1;
+	}
+	bool error = false;
+
+	if(map->seed) {
+		error |= flib_netconn_send_mapSeed(conn, map->seed);
+	}
+	error |= flib_netconn_send_mapTemplate(conn, map->templateFilter);
+	if(map->theme) {
+		error |= flib_netconn_send_mapTheme(conn, map->theme);
+	}
+	error |= flib_netconn_send_mapGen(conn, map->mapgen);
+	error |= flib_netconn_send_mapMazeSize(conn, map->mazeSize);
+	if(map->name) {
+		error |= flib_netconn_send_mapName(conn, map->name);
+	}
+	if(map->drawData && map->drawDataSize>0) {
+		error |= flib_netconn_send_mapDrawdata(conn, map->drawData, map->drawDataSize);
+	}
+	return error;
+}
+
+int flib_netconn_send_mapName(flib_netconn *conn, const char *mapName) {
+	return sendStr(conn, "CFG\nMAP", mapName);
+}
+
+int flib_netconn_send_mapGen(flib_netconn *conn, int mapGen) {
+	return sendInt(conn, "CFG\nMAPGEN", mapGen);
+}
+
+int flib_netconn_send_mapTemplate(flib_netconn *conn, int templateFilter) {
+	return sendInt(conn, "CFG\nTEMPLATE", templateFilter);
+}
+
+int flib_netconn_send_mapMazeSize(flib_netconn *conn, int mazeSize) {
+	return sendInt(conn, "CFG\nMAZE_SIZE", mazeSize);
+}
+
+int flib_netconn_send_mapSeed(flib_netconn *conn, const char *seed) {
+	return sendStr(conn, "CFG\nSEED", seed);
+}
+
+int flib_netconn_send_mapTheme(flib_netconn *conn, const char *theme) {
+	return sendStr(conn, "CFG\nTHEME", theme);
+}
+
+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 {
+		uLongf zippedSize = compressBound(size);
+		uint8_t *zipped = flib_malloc(zippedSize+4); // 4 extra bytes for header
+		if(zipped) {
+			// Create the QCompress size header (uint32 big endian)
+			zipped[0] = (size>>24) & 0xff;
+			zipped[1] = (size>>16) & 0xff;
+			zipped[2] = (size>>8) & 0xff;
+			zipped[3] = (size) & 0xff;
+
+			if(compress(zipped+4, &zippedSize, drawData, size) != Z_OK) {
+				flib_log_e("Error compressing drawn map data.");
+			} else {
+				char *base64encout = NULL;
+				base64_encode_alloc((const char*)zipped, zippedSize+4, &base64encout);
+				if(!base64encout) {
+					flib_log_e("Error base64-encoding drawn map data.");
+				} else {
+					result = flib_netbase_sendf(conn->netBase, "CFG\nDRAWNMAP\n%s\n\n", base64encout);
+				}
+				free(base64encout);
+			}
+		}
+		free(zipped);
+	}
+	return result;
+}
+
+int flib_netconn_send_script(flib_netconn *conn, const char *scriptName) {
+	return sendStr(conn, "CFG\nSCRIPT", scriptName);
+}
+
+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 {
+		flib_vector *vec = flib_vector_create();
+		if(vec) {
+			bool error = false;
+			error |= flib_vector_appendf(vec, "CFG\nSCHEME\n%s\n", scheme->name);
+			for(int i=0; i<scheme->meta->modCount; i++) {
+				error |= flib_vector_appendf(vec, "%s\n", scheme->mods[i] ? "true" : "false");
+			}
+			for(int i=0; i<scheme->meta->settingCount; i++) {
+				error |= flib_vector_appendf(vec, "%i\n", scheme->settings[i]);
+			}
+			error |= flib_vector_appendf(vec, "\n");
+			if(!error) {
+				result = flib_netbase_send_raw(conn->netBase, flib_vector_data(vec), flib_vector_size(vec));
+			}
+		}
+		flib_vector_destroy(vec);
+	}
+	return result;
+}
+
+int flib_netconn_send_roundfinished(flib_netconn *conn, bool withoutError) {
+	return sendInt(conn, "ROUNDFINISHED", withoutError ? 1 : 0);
+}
+
+int flib_netconn_send_ban(flib_netconn *conn, const char *playerName) {
+	return sendStr(conn, "BAN", playerName);
+}
+
+int flib_netconn_send_kick(flib_netconn *conn, const char *playerName) {
+	return sendStr(conn, "KICK", playerName);
+}
+
+int flib_netconn_send_playerInfo(flib_netconn *conn, const char *playerName) {
+	return sendStr(conn, "INFO", playerName);
+}
+
+int flib_netconn_send_playerFollow(flib_netconn *conn, const char *playerName) {
+	return sendStr(conn, "FOLLOW", playerName);
+}
+
+int flib_netconn_send_startGame(flib_netconn *conn) {
+	return sendVoid(conn, "START_GAME");
+}
+
+int flib_netconn_send_toggleRestrictJoins(flib_netconn *conn) {
+	return sendVoid(conn, "TOGGLE_RESTRICT_JOINS");
+}
+
+int flib_netconn_send_toggleRestrictTeams(flib_netconn *conn) {
+	return sendVoid(conn, "TOGGLE_RESTRICT_TEAMS");
+}
+
+int flib_netconn_send_clearAccountsCache(flib_netconn *conn) {
+	return sendVoid(conn, "CLEAR_ACCOUNTS_CACHE");
+}
+
+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.");
+		return -1;
+	}
+	return flib_netbase_sendf(conn->netBase, "%s\n%s\n%s\n\n", "SET_SERVER_VAR", name, value);
+}
+
+int flib_netconn_send_getServerVars(flib_netconn *conn) {
+	return sendVoid(conn, "GET_SERVER_VAR");
+}
--- a/project_files/frontlib/net/netprotocol.c	Thu Jun 21 21:32:12 2012 +0200
+++ b/project_files/frontlib/net/netprotocol.c	Mon Jun 25 00:42:07 2012 +0200
@@ -3,6 +3,10 @@
 #include "../util/util.h"
 #include "../util/logging.h"
 
+#include "../base64/base64.h"
+
+#include <zlib.h>
+
 #include <string.h>
 #include <stdio.h>
 #include <errno.h>
@@ -10,18 +14,12 @@
 
 static int fillTeamFromMsg(flib_team *team, char **parts) {
 	team->name = flib_strdupnull(parts[0]);
-	team->grave = flib_strdupnull(parts[2]);
-	team->fort = flib_strdupnull(parts[3]);
-	team->voicepack = flib_strdupnull(parts[4]);
-	team->flag = flib_strdupnull(parts[5]);
-	if(!team->name || !team->grave || !team->fort || !team->voicepack || !team->flag) {
-		return -1;
-	}
-
-	long color;
-	if(sscanf(parts[1], "#%lx", &color)) {
-		team->color = color;
-	} else {
+	team->grave = flib_strdupnull(parts[1]);
+	team->fort = flib_strdupnull(parts[2]);
+	team->voicepack = flib_strdupnull(parts[3]);
+	team->flag = flib_strdupnull(parts[4]);
+	team->ownerName = flib_strdupnull(parts[5]);
+	if(!team->name || !team->grave || !team->fort || !team->voicepack || !team->flag || !team->ownerName) {
 		return -1;
 	}
 
@@ -45,7 +43,7 @@
 
 flib_team *flib_team_from_netmsg(char **parts) {
 	flib_team *result = NULL;
-	flib_team *tmpTeam = flib_calloc(1, sizeof(flib_team));
+	flib_team *tmpTeam = flib_team_retain(flib_calloc(1, sizeof(flib_team)));
 	if(tmpTeam) {
 		if(!fillTeamFromMsg(tmpTeam, parts)) {
 			result = tmpTeam;
@@ -54,6 +52,65 @@
 			flib_log_e("Error parsing team from net.");
 		}
 	}
-	flib_team_destroy(tmpTeam);
+	flib_team_release(tmpTeam);
+	return result;
+}
+
+flib_cfg *flib_netmsg_to_cfg(flib_cfg_meta *meta, char **parts) {
+	flib_cfg *result = flib_cfg_create(meta, parts[0]);
+	if(result) {
+		for(int i=0; i<meta->modCount; i++) {
+			result->mods[i] = !strcmp(parts[i+1], "true");
+		}
+		for(int i=0; i<meta->settingCount; i++) {
+			result->settings[i] = atoi(parts[i+meta->modCount+1]);
+		}
+	}
+	return result;
+}
+
+flib_map *flib_netmsg_to_map(char **parts) {
+	flib_map *result = flib_map_create_named(parts[3], parts[0]);
+	if(result) {
+		result->mapgen = atoi(parts[1]);
+		result->mazeSize = atoi(parts[2]);
+		result->templateFilter = atoi(parts[4]);
+	}
 	return result;
 }
+
+// TODO: Test with empty map
+uint8_t *flib_netmsg_to_drawnmapdata(size_t *outlen, char *netmsg) {
+	uint8_t *result = NULL;
+
+	// First step: base64 decoding
+	char *base64decout = NULL;
+	size_t base64declen;
+	bool ok = base64_decode_alloc(netmsg, strlen(netmsg), &base64decout, &base64declen);
+	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.
+		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);
+			}
+		}
+		free(out);
+	} else {
+		flib_log_e("base64 decoding of drawn map failed.");
+	}
+	free(base64decout);
+	return result;
+}
--- a/project_files/frontlib/net/netprotocol.h	Thu Jun 21 21:32:12 2012 +0200
+++ b/project_files/frontlib/net/netprotocol.h	Mon Jun 25 00:42:07 2012 +0200
@@ -2,11 +2,35 @@
 #define NETPROTOCOL_H_
 
 #include "../model/team.h"
+#include "../model/cfg.h"
+#include "../model/map.h"
+
+#include <stddef.h>
 
 /**
  * Create a new team from this 23-part net message
  */
 flib_team *flib_team_from_netmsg(char **parts);
 
+/**
+ * Create a new scheme from this net message, which must have
+ * meta->modCount+meta->settingCount+1 parts.
+ */
+flib_cfg *flib_netmsg_to_cfg(flib_cfg_meta *meta, char **parts);
+
+/**
+ * Create a new map from this five-part netmsg
+ */
+flib_map *flib_netmsg_to_map(char **parts);
+
+/**
+ * Decode the drawn map data from this netmessage line.
+ *
+ * The data is first base64 decoded and then quncompress()ed.
+ * The return value is a newly allocated byte buffer, the length
+ * is written to the variable pointed to by outlen.
+ * Returns NULL on error.
+ */
+uint8_t *flib_netmsg_to_drawnmapdata(size_t *outlen, char *netmsg);
 
 #endif /* NETPROTOCOL_H_ */
--- a/project_files/frontlib/socket.c	Thu Jun 21 21:32:12 2012 +0200
+++ b/project_files/frontlib/socket.c	Mon Jun 25 00:42:07 2012 +0200
@@ -5,84 +5,77 @@
 #include <SDL_net.h>
 #include <time.h>
 
-typedef struct _flib_tcpsocket {
+struct _flib_tcpsocket {
 	TCPsocket sock;
 	SDLNet_SocketSet sockset;
-} _flib_tcpsocket;
+};
 
-typedef struct _flib_acceptor {
+struct _flib_acceptor {
 	TCPsocket sock;
 	uint16_t port;
-} _flib_acceptor;
+};
 
-static uint32_t get_peer_ip(TCPsocket sock) {
+static uint32_t getPeerIp(TCPsocket sock) {
 	IPaddress *addr = SDLNet_TCP_GetPeerAddress(sock);
 	return SDLNet_Read32(&addr->host);
 }
 
-static bool connection_is_local(TCPsocket sock) {
-	return get_peer_ip(sock) == (uint32_t)((127UL<<24)+1); // 127.0.0.1
+static bool connectionIsLocal(TCPsocket sock) {
+	return getPeerIp(sock) == (uint32_t)((127UL<<24)+1); // 127.0.0.1
 }
 
-static flib_tcpsocket *flib_socket_create(TCPsocket sdlsock) {
-	flib_tcpsocket *result = flib_calloc(1, sizeof(_flib_tcpsocket));
-	if(!result) {
-		return NULL;
-	}
-	result->sock = sdlsock;
-	result->sockset = SDLNet_AllocSocketSet(1);
+static flib_tcpsocket *createSocket(TCPsocket sdlsock) {
+	flib_tcpsocket *result = flib_calloc(1, sizeof(flib_tcpsocket));
+	if(result) {
+		result->sock = sdlsock;
+		result->sockset = SDLNet_AllocSocketSet(1);
 
-	if(!result->sockset) {
-		flib_log_e("Can't allocate socket: Out of memory!");
-		SDLNet_FreeSocketSet(result->sockset);
-		free(result);
-		return NULL;
+		if(!result->sockset) {
+			flib_log_e("Can't allocate socket: Out of memory!");
+			SDLNet_FreeSocketSet(result->sockset);
+			free(result);
+			result = NULL;
+		} else {
+			SDLNet_AddSocket(result->sockset, (SDLNet_GenericSocket)result->sock);
+		}
 	}
+	return result;
+}
 
-	SDLNet_AddSocket(result->sockset, (SDLNet_GenericSocket)result->sock);
-	return result;
+TCPsocket listen(uint16_t port) {
+	IPaddress addr;
+	addr.host = INADDR_ANY;
+	SDLNet_Write16(port, &addr.port);
+	TCPsocket sock = SDLNet_TCP_Open(&addr);
+	if(!sock) {
+		flib_log_w("Unable to listen on port %u: %s", (unsigned)port, SDLNet_GetError());
+	}
+	return sock;
 }
 
 flib_acceptor *flib_acceptor_create(uint16_t port) {
-	flib_acceptor *result = flib_calloc(1, sizeof(_flib_acceptor));
-	if(!result) {
-		return NULL;
-	}
-
-	IPaddress addr;
-	addr.host = INADDR_ANY;
-
-	if(port > 0) {
-		result->port = port;
-		SDLNet_Write16(port, &addr.port);
-		result->sock = SDLNet_TCP_Open(&addr);
-		if(result->sock) {
-			return result;
+	flib_acceptor *result = flib_calloc(1, sizeof(flib_acceptor));
+	if(result) {
+		if(port > 0) {
+			result->port = port;
+			result->sock = listen(result->port);
 		} else {
-			flib_log_e("Unable to listen on port %u: %s", (unsigned)port, SDLNet_GetError());
-			free(result);
-			return NULL;
-		}
-	} else {
-		/* SDL_net does not seem to have a way to listen on a random unused port
-		   and find out which port that is, so let's try to find one ourselves. */
-		srand(time(NULL));
-		rand();
-		for(int i=0; i<1000; i++) {
-			// IANA suggests using ports in the range 49152-65535 for things like this
-			result->port = 49152+(rand()%(65535-49152));
-			SDLNet_Write16(result->port, &addr.port);
-			result->sock = SDLNet_TCP_Open(&addr);
-			if(result->sock) {
-				return result;
-			} else {
-				flib_log_w("Unable to listen on port %u: %s", (unsigned)result->port, SDLNet_GetError());
+			/* SDL_net does not seem to have a way to listen on a random unused port
+			   and find out which port that is, so let's try to find one ourselves. */
+			srand(time(NULL));
+			for(int i=0; !result->sock && i<1000; i++) {
+				// IANA suggests using ports in the range 49152-65535 for things like this
+				result->port = 49152+(rand()%(65535-49152));
+				result->sock = listen(result->port);
 			}
 		}
-		flib_log_e("Unable to listen on a random unused port.");
-		free(result);
-		return NULL;
+		if(!result->sock) {
+			flib_log_e("Failed to create acceptor.");
+			free(result);
+			result = NULL;
+		}
 	}
+	return result;
 }
 
 uint16_t flib_acceptor_listenport(flib_acceptor *acceptor) {
@@ -101,18 +94,17 @@
 }
 
 flib_tcpsocket *flib_socket_accept(flib_acceptor *acceptor, bool localOnly) {
+	flib_tcpsocket *result = NULL;
 	if(!acceptor) {
 		flib_log_e("Call to flib_socket_accept with acceptor==null");
-		return NULL;
-	}
-	flib_tcpsocket *result = NULL;
-	TCPsocket sock = NULL;
-	while(!result && (sock = SDLNet_TCP_Accept(acceptor->sock))) {
-		if(localOnly && !connection_is_local(sock)) {
-			flib_log_i("Rejected nonlocal connection attempt from %s", flib_format_ip(get_peer_ip(sock)));
-			SDLNet_TCP_Close(sock);
-		} else {
-			result = flib_socket_create(sock);
+	} else {
+		TCPsocket sock = NULL;
+		while(!result && (sock = SDLNet_TCP_Accept(acceptor->sock))) {
+			if(localOnly && !connectionIsLocal(sock)) {
+				flib_log_i("Rejected nonlocal connection attempt from %s", flib_format_ip(getPeerIp(sock)));
+			} else {
+				result = createSocket(sock);
+			}
 			if(!result) {
 				SDLNet_TCP_Close(sock);
 			}
@@ -134,7 +126,7 @@
 			if(!sock) {
 				flib_log_e("SDLNet_TCP_Open: %s\n", SDLNet_GetError());
 			} else {
-				result = flib_socket_create(sock);
+				result = createSocket(sock);
 				if(result) {
 					sock = NULL;
 				}
--- a/project_files/frontlib/socket.h	Thu Jun 21 21:32:12 2012 +0200
+++ b/project_files/frontlib/socket.h	Mon Jun 25 00:42:07 2012 +0200
@@ -15,10 +15,7 @@
 #include <stdbool.h>
 #include <stdint.h>
 
-struct _flib_tcpsocket;
 typedef struct _flib_tcpsocket flib_tcpsocket;
-
-struct _flib_acceptor;
 typedef struct _flib_acceptor flib_acceptor;
 
 /**
@@ -26,7 +23,7 @@
  * on the given port. If port is 0, this will listen on a random
  * unused port which can then be queried with flib_acceptor_listenport.
  *
- * Can return NULL on error.
+ * Returns NULL on error.
  */
 flib_acceptor *flib_acceptor_create(uint16_t port);
 
@@ -36,8 +33,7 @@
 uint16_t flib_acceptor_listenport(flib_acceptor *acceptor);
 
 /**
- * Close the acceptor and free its memory.
- * If the acceptor is already NULL, nothing happens.
+ * Close the acceptor and free its memory. NULL-safe.
  */
 void flib_acceptor_close(flib_acceptor *acceptor);
 
@@ -54,8 +50,7 @@
 flib_tcpsocket *flib_socket_connect(const char *host, uint16_t port);
 
 /**
- * Close the socket and free its memory.
- * If the socket is already NULL, nothing happens.
+ * Close the socket and free its memory. NULL-safe.
  */
 void flib_socket_close(flib_tcpsocket *socket);
 
@@ -67,6 +62,11 @@
  */
 int flib_socket_nbrecv(flib_tcpsocket *sock, void *data, int maxlen);
 
+/**
+ * Blocking send all the data in the data buffer. Returns the actual ammount
+ * of data sent, or a negative value on error. If the value returned here
+ * is less than len, either the connection closed or an error occurred.
+ */
 int flib_socket_send(flib_tcpsocket *sock, const void *data, int len);
 
 #endif /* SOCKET_H_ */
--- a/project_files/frontlib/test.c	Thu Jun 21 21:32:12 2012 +0200
+++ b/project_files/frontlib/test.c	Mon Jun 25 00:42:07 2012 +0200
@@ -2,6 +2,7 @@
 #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"
@@ -78,11 +79,11 @@
 
 void testMapPreview() {
 	// Create a map description and check that there was no error
-	flib_map *map = flib_map_create_maze("Jungle", MAZE_SIZE_SMALL_TUNNELS);
+	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("This is the seed value", map);
+	flib_mapconn *mapConnection = flib_mapconn_create(map);
 	assert(mapConnection);
 
 	// We don't need the map description anymore
@@ -112,10 +113,9 @@
 
 	flib_gamesetup setup;
 	setup.gamescheme = flib_schemelist_find(schemelist, "Default");
-	setup.map = flib_map_create_maze("Jungle", MAZE_SIZE_MEDIUM_TUNNELS);
-	setup.seed = "asparagus";
+	setup.map = flib_map_create_maze("asparagus", "Jungle", MAZE_SIZE_MEDIUM_TUNNELS);
 	setup.script = NULL;
-	setup.teamcount = 2;
+	setup.teamCount = 2;
 	setup.teams = calloc(2, sizeof(flib_team*));
 	setup.teams[0] = calloc(1, sizeof(flib_team));
 	setup.teams[0]->color = 0xffff0000;
@@ -250,12 +250,54 @@
 				}
 				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 handleAskPass(void *context, const char *nick) {
-	flib_netconn_send_password((flib_netconn*)context, "Lorem");
+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[]) {
@@ -269,7 +311,7 @@
 
 	flib_cfg_meta *meta = flib_cfg_meta_from_ini("metasettings.ini");
 	assert(meta);
-	flib_netconn *conn = flib_netconn_create("Medo42", meta, "140.247.62.101", 46631);
+	flib_netconn *conn = flib_netconn_create("frontbot", meta, "140.247.62.101", 46631);
 	assert(conn);
 	flib_cfg_meta_release(meta);
 
@@ -277,7 +319,14 @@
 	flib_netconn_onDisconnected(conn, handleNetDisconnect, &conn);
 	flib_netconn_onLobbyJoin(conn, handleLobbyJoin, &conn);
 	flib_netconn_onChat(conn, handleChat, &conn);
-	flib_netconn_onPasswordRequest(conn, handleAskPass, 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);
--- a/project_files/frontlib/util/buffer.c	Thu Jun 21 21:32:12 2012 +0200
+++ b/project_files/frontlib/util/buffer.c	Mon Jun 25 00:42:07 2012 +0200
@@ -8,15 +8,15 @@
 
 #define MIN_VECTOR_CAPACITY 16
 
-typedef struct _flib_vector {
+struct _flib_vector {
 	void *data;
 	size_t size;
 	size_t capacity;
-} _flib_vector;
+};
 
 flib_vector *flib_vector_create() {
 	flib_vector *result = NULL;
-	flib_vector *tmpVector = flib_calloc(1, sizeof(_flib_vector));
+	flib_vector *tmpVector = flib_calloc(1, sizeof(flib_vector));
 	if(tmpVector) {
 		tmpVector->data = flib_malloc(MIN_VECTOR_CAPACITY);
 		if(tmpVector->data) {
@@ -67,8 +67,7 @@
 
 	if(vec->capacity < newSize) {
 		// Resize exponentially for constant amortized time,
-		// But at least by as much as we need of course,
-		// and be extra careful with integer overflows...
+		// But at least by as much as we need of course
 		size_t extraCapacity = (vec->capacity)/2;
 		size_t minExtraCapacity = newSize - vec->capacity;
 		if(extraCapacity < minExtraCapacity) {
@@ -113,6 +112,27 @@
 	return len;
 }
 
+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 {
+		va_list argp;
+		va_start(argp, fmt);
+		char *formatted = flib_vasprintf(fmt, argp);
+		va_end(argp);
+
+
+		if(formatted) {
+			size_t len = strlen(formatted);
+			if(flib_vector_append(vec, formatted, len) == len) {
+				result = 0;
+			}
+		}
+	}
+	return result;
+}
+
 flib_buffer flib_vector_as_buffer(flib_vector *vec) {
 	if(!vec) {
 		flib_log_e("null parameter in flib_vector_as_buffer");
--- a/project_files/frontlib/util/buffer.h	Thu Jun 21 21:32:12 2012 +0200
+++ b/project_files/frontlib/util/buffer.h	Mon Jun 25 00:42:07 2012 +0200
@@ -26,7 +26,6 @@
 /**
  * Simple variable-capacity data structure that can be efficiently appended to.
  */
-struct _flib_vector;
 typedef struct _flib_vector flib_vector;
 
 /**
@@ -55,6 +54,12 @@
 int flib_vector_append(flib_vector *vec, const void *data, size_t len);
 
 /**
+ * Append data from a format string to the buffer (without trailing 0)
+ * Returns 0 on success.
+ */
+int flib_vector_appendf(flib_vector *vec, const char *template, ...);
+
+/**
  * Return a pointer to the current data buffer of the vector. This pointer can
  * become invalid if the vector size or capacity is changed.
  */
--- a/project_files/frontlib/util/inihelper.h	Thu Jun 21 21:32:12 2012 +0200
+++ b/project_files/frontlib/util/inihelper.h	Mon Jun 25 00:42:07 2012 +0200
@@ -15,7 +15,6 @@
 #define INI_ERROR_FORMAT -2
 #define INI_ERROR_OTHER -100
 
-struct _flib_ini;
 typedef struct _flib_ini flib_ini;
 
 /**
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/util/refcounter.c	Mon Jun 25 00:42:07 2012 +0200
@@ -0,0 +1,37 @@
+#include "refcounter.h"
+
+#include "logging.h"
+
+void flib_retain(int *referenceCountPtr, const char *objName) {
+	if(!referenceCountPtr || !objName) {
+		flib_log_e("null parameter to flib_retain");
+	} else {
+		if((*referenceCountPtr)  >= 0) {
+			(*referenceCountPtr)++;
+			flib_log_d("retaining %s, now %i references", objName, (*referenceCountPtr));
+		}
+		if((*referenceCountPtr) < 0) {
+			flib_log_e("Memory leak: Reference count overflow in %s object!", objName);
+		}
+	}
+}
+
+/**
+ * Returns true if the struct should be freed.
+ */
+bool flib_release(int *referenceCountPtr, const char *objName) {
+	bool result = false;
+	if(!referenceCountPtr) {
+		flib_log_e("null parameter to flib_release");
+	} else if((*referenceCountPtr) > 0) {
+		if(--(*referenceCountPtr) == 0) {
+			flib_log_d("releasing and destroying %s", objName);
+			result = true;
+		} else {
+			flib_log_d("releasing %s, now %i references", objName, (*referenceCountPtr));
+		}
+	} else if((*referenceCountPtr) == 0) {
+		flib_log_e("Attempt to release a %s with zero references!", objName);
+	}
+	return result;
+}
--- a/project_files/frontlib/util/refcounter.h	Thu Jun 21 21:32:12 2012 +0200
+++ b/project_files/frontlib/util/refcounter.h	Mon Jun 25 00:42:07 2012 +0200
@@ -11,41 +11,20 @@
 #ifndef REFCOUNTER_H_
 #define REFCOUNTER_H_
 
-#include "logging.h"
 #include <stdbool.h>
 
-static inline void flib_retain(int *referenceCountPtr, const char *objName) {
-	if(!referenceCountPtr || !objName) {
-		flib_log_e("null parameter to flib_retain");
-	} else {
-		if((*referenceCountPtr)  >= 0) {
-			(*referenceCountPtr)++;
-			flib_log_d("retaining %s, now %i references", objName, (*referenceCountPtr));
-		}
-		if((*referenceCountPtr) < 0) {
-			flib_log_e("Memory leak: Reference count overflow in %s object!", objName);
-		}
-	}
-}
-
 /**
- * Returns true if the struct should be freed.
+ * Pass a pointer to the counter variable to be incremented, and the name of the
+ * object for logging purposes. On overflow an error will be logged and the
+ * counter will get "stuck" so neither retain nor release will modify it anymore.
  */
-static inline bool flib_release(int *referenceCountPtr, const char *objName) {
-	bool result = false;
-	if(!referenceCountPtr) {
-		flib_log_e("null parameter to flib_release");
-	} else if((*referenceCountPtr) > 0) {
-		if(--(*referenceCountPtr) == 0) {
-			flib_log_d("releasing and destroying %s", objName);
-			result = true;
-		} else {
-			flib_log_d("releasing %s, now %i references", objName, (*referenceCountPtr));
-		}
-	} else if((*referenceCountPtr) == 0) {
-		flib_log_e("Attempt to release a %s with zero references!", objName);
-	}
-	return result;
-}
+void flib_retain(int *referenceCountPtr, const char *objName);
+
+/**
+ * Pass a pointer to the counter variable to be decremented and the name
+ * of the object for logging purposes.
+ * Returns true if the object should be freed.
+ */
+bool flib_release(int *referenceCountPtr, const char *objName);
 
 #endif /* REFCOUNTER_H_ */