project_files/frontlib/net/netconn.c
changeset 10017 de822cd3df3a
parent 9998 736015b847e3
child 10108 c68cf030eded
equal deleted inserted replaced
10015:4feced261c68 10017:de822cd3df3a
    30 #include <string.h>
    30 #include <string.h>
    31 #include <errno.h>
    31 #include <errno.h>
    32 #include <ctype.h>
    32 #include <ctype.h>
    33 
    33 
    34 flib_netconn *flib_netconn_create(const char *playerName, const char *dataDirPath, const char *host, int port) {
    34 flib_netconn *flib_netconn_create(const char *playerName, const char *dataDirPath, const char *host, int port) {
    35 	flib_netconn *result = NULL;
    35     flib_netconn *result = NULL;
    36 	if(!log_badargs_if4(playerName==NULL, host==NULL, port<1, port>65535)) {
    36     if(!log_badargs_if4(playerName==NULL, host==NULL, port<1, port>65535)) {
    37 		flib_netconn *newConn = flib_calloc(1, sizeof(flib_netconn));
    37         flib_netconn *newConn = flib_calloc(1, sizeof(flib_netconn));
    38 		if(newConn) {
    38         if(newConn) {
    39 			newConn->netBase = flib_netbase_create(host, port);
    39             newConn->netBase = flib_netbase_create(host, port);
    40 			newConn->playerName = flib_strdupnull(playerName);
    40             newConn->playerName = flib_strdupnull(playerName);
    41 			newConn->dataDirPath = flib_strdupnull(dataDirPath);
    41             newConn->dataDirPath = flib_strdupnull(dataDirPath);
    42 
    42 
    43 			newConn->netconnState = NETCONN_STATE_CONNECTING;
    43             newConn->netconnState = NETCONN_STATE_CONNECTING;
    44 
    44 
    45 			newConn->isChief = false;
    45             newConn->isChief = false;
    46 			newConn->map = flib_map_create_named("", "NoSuchMap");
    46             newConn->map = flib_map_create_named("", "NoSuchMap");
    47 			newConn->pendingTeamlist.teamCount = 0;
    47             newConn->pendingTeamlist.teamCount = 0;
    48 			newConn->pendingTeamlist.teams = NULL;
    48             newConn->pendingTeamlist.teams = NULL;
    49 			newConn->teamlist.teamCount = 0;
    49             newConn->teamlist.teamCount = 0;
    50 			newConn->teamlist.teams = NULL;
    50             newConn->teamlist.teams = NULL;
    51 			newConn->scheme = NULL;
    51             newConn->scheme = NULL;
    52 			newConn->style = NULL;
    52             newConn->style = NULL;
    53 			newConn->weaponset = NULL;
    53             newConn->weaponset = NULL;
    54 
    54 
    55 			newConn->running = false;
    55             newConn->running = false;
    56 			newConn->destroyRequested = false;
    56             newConn->destroyRequested = false;
    57 			netconn_clearCallbacks(newConn);
    57             netconn_clearCallbacks(newConn);
    58 			if(newConn->netBase && newConn->playerName && newConn->dataDirPath && newConn->map) {
    58             if(newConn->netBase && newConn->playerName && newConn->dataDirPath && newConn->map) {
    59 				result = newConn;
    59                 result = newConn;
    60 				newConn = NULL;
    60                 newConn = NULL;
    61 			}
    61             }
    62 		}
    62         }
    63 		flib_netconn_destroy(newConn);
    63         flib_netconn_destroy(newConn);
    64 	}
    64     }
    65 	return result;
    65     return result;
    66 }
    66 }
    67 
    67 
    68 void flib_netconn_destroy(flib_netconn *conn) {
    68 void flib_netconn_destroy(flib_netconn *conn) {
    69 	if(conn) {
    69     if(conn) {
    70 		if(conn->running) {
    70         if(conn->running) {
    71 			/*
    71             /*
    72 			 * The function was called from a callback, so the tick function is still running
    72              * The function was called from a callback, so the tick function is still running
    73 			 * and we delay the actual destruction. We ensure no further callbacks will be
    73              * and we delay the actual destruction. We ensure no further callbacks will be
    74 			 * sent to prevent surprises.
    74              * sent to prevent surprises.
    75 			 */
    75              */
    76 			netconn_clearCallbacks(conn);
    76             netconn_clearCallbacks(conn);
    77 			conn->destroyRequested = true;
    77             conn->destroyRequested = true;
    78 		} else {
    78         } else {
    79 			flib_netbase_destroy(conn->netBase);
    79             flib_netbase_destroy(conn->netBase);
    80 			free(conn->playerName);
    80             free(conn->playerName);
    81 			free(conn->dataDirPath);
    81             free(conn->dataDirPath);
    82 
    82 
    83 			flib_map_destroy(conn->map);
    83             flib_map_destroy(conn->map);
    84 			flib_teamlist_clear(&conn->pendingTeamlist);
    84             flib_teamlist_clear(&conn->pendingTeamlist);
    85 			flib_teamlist_clear(&conn->teamlist);
    85             flib_teamlist_clear(&conn->teamlist);
    86 			flib_scheme_destroy(conn->scheme);
    86             flib_scheme_destroy(conn->scheme);
    87 			free(conn->style);
    87             free(conn->style);
    88 			flib_weaponset_destroy(conn->weaponset);
    88             flib_weaponset_destroy(conn->weaponset);
    89 
    89 
    90 			free(conn);
    90             free(conn);
    91 		}
    91         }
    92 	}
    92     }
    93 }
    93 }
    94 
    94 
    95 bool flib_netconn_is_chief(flib_netconn *conn) {
    95 bool flib_netconn_is_chief(flib_netconn *conn) {
    96 	if(!log_badargs_if(conn==NULL) && conn->netconnState==NETCONN_STATE_ROOM) {
    96     if(!log_badargs_if(conn==NULL) && conn->netconnState==NETCONN_STATE_ROOM) {
    97 		return conn->isChief;
    97         return conn->isChief;
    98 	}
    98     }
    99 	return false;
    99     return false;
   100 }
   100 }
   101 
   101 
   102 const char *flib_netconn_get_playername(flib_netconn *conn) {
   102 const char *flib_netconn_get_playername(flib_netconn *conn) {
   103 	if(!log_badargs_if(conn==NULL)) {
   103     if(!log_badargs_if(conn==NULL)) {
   104 		return conn->playerName;
   104         return conn->playerName;
   105 	}
   105     }
   106 	return NULL;
   106     return NULL;
   107 }
   107 }
   108 
   108 
   109 void netconn_leaveRoom(flib_netconn *conn) {
   109 void netconn_leaveRoom(flib_netconn *conn) {
   110 	conn->netconnState = NETCONN_STATE_LOBBY;
   110     conn->netconnState = NETCONN_STATE_LOBBY;
   111 	conn->isChief = false;
   111     conn->isChief = false;
   112 	flib_map_destroy(conn->map);
   112     flib_map_destroy(conn->map);
   113 	conn->map = flib_map_create_named("", "NoSuchMap");
   113     conn->map = flib_map_create_named("", "NoSuchMap");
   114 	flib_teamlist_clear(&conn->pendingTeamlist);
   114     flib_teamlist_clear(&conn->pendingTeamlist);
   115 	flib_teamlist_clear(&conn->teamlist);
   115     flib_teamlist_clear(&conn->teamlist);
   116 	flib_scheme_destroy(conn->scheme);
   116     flib_scheme_destroy(conn->scheme);
   117 	conn->scheme = NULL;
   117     conn->scheme = NULL;
   118 	free(conn->style);
   118     free(conn->style);
   119 	conn->style = NULL;
   119     conn->style = NULL;
   120 	flib_weaponset_destroy(conn->weaponset);
   120     flib_weaponset_destroy(conn->weaponset);
   121 	conn->weaponset = NULL;
   121     conn->weaponset = NULL;
   122 }
   122 }
   123 
   123 
   124 void netconn_setMap(flib_netconn *conn, const flib_map *map) {
   124 void netconn_setMap(flib_netconn *conn, const flib_map *map) {
   125 	flib_map *copy = flib_map_copy(map);
   125     flib_map *copy = flib_map_copy(map);
   126 	if(copy) {
   126     if(copy) {
   127 		flib_map_destroy(conn->map);
   127         flib_map_destroy(conn->map);
   128 		conn->map = copy;
   128         conn->map = copy;
   129 	}
   129     }
   130 }
   130 }
   131 
   131 
   132 void netconn_setWeaponset(flib_netconn *conn, const flib_weaponset *weaponset) {
   132 void netconn_setWeaponset(flib_netconn *conn, const flib_weaponset *weaponset) {
   133 	flib_weaponset *copy = flib_weaponset_copy(weaponset);
   133     flib_weaponset *copy = flib_weaponset_copy(weaponset);
   134 	if(copy) {
   134     if(copy) {
   135 		flib_weaponset_destroy(conn->weaponset);
   135         flib_weaponset_destroy(conn->weaponset);
   136 		conn->weaponset = copy;
   136         conn->weaponset = copy;
   137 	}
   137     }
   138 }
   138 }
   139 
   139 
   140 void netconn_setScript(flib_netconn *conn, const char *script) {
   140 void netconn_setScript(flib_netconn *conn, const char *script) {
   141 	char *copy = flib_strdupnull(script);
   141     char *copy = flib_strdupnull(script);
   142 	if(copy) {
   142     if(copy) {
   143 		free(conn->style);
   143         free(conn->style);
   144 		conn->style = copy;
   144         conn->style = copy;
   145 	}
   145     }
   146 }
   146 }
   147 
   147 
   148 void netconn_setScheme(flib_netconn *conn, const flib_scheme *scheme) {
   148 void netconn_setScheme(flib_netconn *conn, const flib_scheme *scheme) {
   149 	flib_scheme *copy = flib_scheme_copy(scheme);
   149     flib_scheme *copy = flib_scheme_copy(scheme);
   150 	if(copy) {
   150     if(copy) {
   151 		flib_scheme_destroy(conn->scheme);
   151         flib_scheme_destroy(conn->scheme);
   152 		conn->scheme = copy;
   152         conn->scheme = copy;
   153 	}
   153     }
   154 }
   154 }
   155 
   155 
   156 flib_gamesetup *flib_netconn_create_gamesetup(flib_netconn *conn) {
   156 flib_gamesetup *flib_netconn_create_gamesetup(flib_netconn *conn) {
   157 	flib_gamesetup *result = NULL;
   157     flib_gamesetup *result = NULL;
   158 	if(!log_badargs_if(conn==NULL)) {
   158     if(!log_badargs_if(conn==NULL)) {
   159 		if(conn->teamlist.teamCount==0 || !conn->scheme || !conn->weaponset) {
   159         if(conn->teamlist.teamCount==0 || !conn->scheme || !conn->weaponset) {
   160 			flib_log_e("Incomplete room state");
   160             flib_log_e("Incomplete room state");
   161 		} else {
   161         } else {
   162 			flib_gamesetup stackSetup = {0};
   162             flib_gamesetup stackSetup = {0};
   163 			stackSetup.gamescheme = conn->scheme;
   163             stackSetup.gamescheme = conn->scheme;
   164 			stackSetup.map = conn->map;
   164             stackSetup.map = conn->map;
   165 			stackSetup.style = conn->style;
   165             stackSetup.style = conn->style;
   166 			stackSetup.teamlist = &conn->teamlist;
   166             stackSetup.teamlist = &conn->teamlist;
   167 			result = flib_gamesetup_copy(&stackSetup);
   167             result = flib_gamesetup_copy(&stackSetup);
   168 			if(result) {
   168             if(result) {
   169 				bool error = false;
   169                 bool error = false;
   170 				for(int i=0; i<result->teamlist->teamCount; i++) {
   170                 for(int i=0; i<result->teamlist->teamCount; i++) {
   171 					if(flib_team_set_weaponset(result->teamlist->teams[i], conn->weaponset)) {
   171                     if(flib_team_set_weaponset(result->teamlist->teams[i], conn->weaponset)) {
   172 						error = true;
   172                         error = true;
   173 					}
   173                     }
   174 					flib_team_set_health(result->teamlist->teams[i], flib_scheme_get_setting(conn->scheme, "health", 100));
   174                     flib_team_set_health(result->teamlist->teams[i], flib_scheme_get_setting(conn->scheme, "health", 100));
   175 				}
   175                 }
   176 				if(result->map->mapgen == MAPGEN_NAMED && result->map->name) {
   176                 if(result->map->mapgen == MAPGEN_NAMED && result->map->name) {
   177 					flib_mapcfg mapcfg;
   177                     flib_mapcfg mapcfg;
   178 					if(!flib_mapcfg_read(conn->dataDirPath, result->map->name, &mapcfg)) {
   178                     if(!flib_mapcfg_read(conn->dataDirPath, result->map->name, &mapcfg)) {
   179 						free(result->map->theme);
   179                         free(result->map->theme);
   180 						result->map->theme = flib_strdupnull(mapcfg.theme);
   180                         result->map->theme = flib_strdupnull(mapcfg.theme);
   181 						if(!result->map->theme) {
   181                         if(!result->map->theme) {
   182 							error = true;
   182                             error = true;
   183 						}
   183                         }
   184 					} else {
   184                     } else {
   185 						flib_log_e("Unable to read map config for map %s", result->map->name);
   185                         flib_log_e("Unable to read map config for map %s", result->map->name);
   186 					}
   186                     }
   187 				}
   187                 }
   188 				if(error) {
   188                 if(error) {
   189 					flib_gamesetup_destroy(result);
   189                     flib_gamesetup_destroy(result);
   190 					result = NULL;
   190                     result = NULL;
   191 				}
   191                 }
   192 			}
   192             }
   193 		}
   193         }
   194 	}
   194     }
   195 	return result;
   195     return result;
   196 }
   196 }
   197 
   197 
   198 static void flib_netconn_wrappedtick(flib_netconn *conn) {
   198 static void flib_netconn_wrappedtick(flib_netconn *conn) {
   199 	flib_netmsg *netmsg;
   199     flib_netmsg *netmsg;
   200 	flib_netbase *net = conn->netBase;
   200     flib_netbase *net = conn->netBase;
   201 	bool exit = false;
   201     bool exit = false;
   202 
   202 
   203 	while(!exit && !conn->destroyRequested && (netmsg=flib_netbase_recv_message(conn->netBase))) {
   203     while(!exit && !conn->destroyRequested && (netmsg=flib_netbase_recv_message(conn->netBase))) {
   204 		if(netmsg->partCount==0) {
   204         if(netmsg->partCount==0) {
   205 			flib_log_w("Empty server message");
   205             flib_log_w("Empty server message");
   206 			continue;
   206             continue;
   207 		}
   207         }
   208 
   208 
   209 		if(flib_log_isActive(FLIB_LOGLEVEL_DEBUG)) {
   209         if(flib_log_isActive(FLIB_LOGLEVEL_DEBUG)) {
   210 			char *buf = flib_join(netmsg->parts, netmsg->partCount, "|");
   210             char *buf = flib_join(netmsg->parts, netmsg->partCount, "|");
   211 			if(buf) {
   211             if(buf) {
   212 				flib_log_d("[Net In]%s", buf);
   212                 flib_log_d("[Net In]%s", buf);
   213 			}
   213             }
   214 			free(buf);
   214             free(buf);
   215 		}
   215         }
   216 
   216 
   217 		const char *cmd = netmsg->parts[0];
   217         const char *cmd = netmsg->parts[0];
   218 
   218 
   219 	    if (!strcmp(cmd, "NICK") && netmsg->partCount>=2) {
   219         if (!strcmp(cmd, "NICK") && netmsg->partCount>=2) {
   220 	    	if(netmsg->partCount<2) {
   220             if(netmsg->partCount<2) {
   221 	    		flib_log_w("Net: Malformed NICK message");
   221                 flib_log_w("Net: Malformed NICK message");
   222 	    	} else {
   222             } else {
   223 	    		char *nick = flib_strdupnull(netmsg->parts[1]);
   223                 char *nick = flib_strdupnull(netmsg->parts[1]);
   224 	    		if(nick) {
   224                 if(nick) {
   225 					free(conn->playerName);
   225                     free(conn->playerName);
   226 					conn->playerName = nick;
   226                     conn->playerName = nick;
   227 	    		} else {
   227                 } else {
   228 					conn->netconnState = NETCONN_STATE_DISCONNECTED;
   228                     conn->netconnState = NETCONN_STATE_DISCONNECTED;
   229 					conn->onDisconnectedCb(conn->onDisconnectedCtx, NETCONN_DISCONNECT_INTERNAL_ERROR, "Out of memory");
   229                     conn->onDisconnectedCb(conn->onDisconnectedCtx, NETCONN_DISCONNECT_INTERNAL_ERROR, "Out of memory");
   230 					exit = true;
   230                     exit = true;
   231 				}
   231                 }
   232 	    	}
   232             }
   233 	    } else if (!strcmp(cmd, "PROTO")) {
   233         } else if (!strcmp(cmd, "PROTO")) {
   234 	        // The server just echoes this back apparently
   234             // The server just echoes this back apparently
   235 		} else if (!strcmp(cmd, "ERROR")) {
   235         } else if (!strcmp(cmd, "ERROR")) {
   236 			if (netmsg->partCount >= 2) {
   236             if (netmsg->partCount >= 2) {
   237 				conn->onMessageCb(conn->onMessageCtx, NETCONN_MSG_TYPE_ERROR, netmsg->parts[1]);
   237                 conn->onMessageCb(conn->onMessageCtx, NETCONN_MSG_TYPE_ERROR, netmsg->parts[1]);
   238 			} else {
   238             } else {
   239 				conn->onMessageCb(conn->onMessageCtx, NETCONN_MSG_TYPE_ERROR, "Unknown Error");
   239                 conn->onMessageCb(conn->onMessageCtx, NETCONN_MSG_TYPE_ERROR, "Unknown Error");
   240 			}
   240             }
   241 		} else if(!strcmp(cmd, "WARNING")) {
   241         } else if(!strcmp(cmd, "WARNING")) {
   242 			if (netmsg->partCount >= 2) {
   242             if (netmsg->partCount >= 2) {
   243 				conn->onMessageCb(conn->onMessageCtx, NETCONN_MSG_TYPE_WARNING, netmsg->parts[1]);
   243                 conn->onMessageCb(conn->onMessageCtx, NETCONN_MSG_TYPE_WARNING, netmsg->parts[1]);
   244 			} else {
   244             } else {
   245 				conn->onMessageCb(conn->onMessageCtx, NETCONN_MSG_TYPE_WARNING, "Unknown Warning");
   245                 conn->onMessageCb(conn->onMessageCtx, NETCONN_MSG_TYPE_WARNING, "Unknown Warning");
   246 			}
   246             }
   247 	    } else if(!strcmp(cmd, "CONNECTED")) {
   247         } else if(!strcmp(cmd, "CONNECTED")) {
   248 			if(netmsg->partCount<3 || atol(netmsg->parts[2])<MIN_SERVER_VERSION) {
   248             if(netmsg->partCount<3 || atol(netmsg->parts[2])<MIN_SERVER_VERSION) {
   249 				flib_log_w("Net: Server too old");
   249                 flib_log_w("Net: Server too old");
   250 				flib_netbase_sendf(net, "%s\n%s\n\n", "QUIT", "Server too old");
   250                 flib_netbase_sendf(net, "%s\n%s\n\n", "QUIT", "Server too old");
   251 				conn->netconnState = NETCONN_STATE_DISCONNECTED;
   251                 conn->netconnState = NETCONN_STATE_DISCONNECTED;
   252 				conn->onDisconnectedCb(conn->onDisconnectedCtx, NETCONN_DISCONNECT_SERVER_TOO_OLD, "Server too old");
   252                 conn->onDisconnectedCb(conn->onDisconnectedCtx, NETCONN_DISCONNECT_SERVER_TOO_OLD, "Server too old");
   253 				exit = true;
   253                 exit = true;
   254 			} else {
   254             } else {
   255 				flib_netbase_sendf(net, "%s\n%s\n\n", "NICK", conn->playerName);
   255                 flib_netbase_sendf(net, "%s\n%s\n\n", "NICK", conn->playerName);
   256 				flib_netbase_sendf(net, "%s\n%i\n\n", "PROTO", (int)PROTOCOL_VERSION);
   256                 flib_netbase_sendf(net, "%s\n%i\n\n", "PROTO", (int)PROTOCOL_VERSION);
   257 			}
   257             }
   258 		} else if(!strcmp(cmd, "PING")) {
   258         } else if(!strcmp(cmd, "PING")) {
   259 	        if (netmsg->partCount > 1) {
   259             if (netmsg->partCount > 1) {
   260 	        	flib_netbase_sendf(net, "%s\n%s\n\n", "PONG", netmsg->parts[1]);
   260                 flib_netbase_sendf(net, "%s\n%s\n\n", "PONG", netmsg->parts[1]);
   261 	        } else {
   261             } else {
   262 	        	flib_netbase_sendf(net, "%s\n\n", "PONG");
   262                 flib_netbase_sendf(net, "%s\n\n", "PONG");
   263 	        }
   263             }
   264 	    } else if(!strcmp(cmd, "ROOMS")) {
   264         } else if(!strcmp(cmd, "ROOMS")) {
   265 	        if(netmsg->partCount % 8 != 1) {
   265             if(netmsg->partCount % 8 != 1) {
   266 	        	flib_log_w("Net: Malformed ROOMS message");
   266                 flib_log_w("Net: Malformed ROOMS message");
   267 	        } else {
   267             } else {
   268 	        	int roomCount = netmsg->partCount/8;
   268                 int roomCount = netmsg->partCount/8;
   269 	        	flib_room **rooms = flib_room_array_from_netmsg(netmsg->parts+1, roomCount);
   269                 flib_room **rooms = flib_room_array_from_netmsg(netmsg->parts+1, roomCount);
   270 	        	if(rooms) {
   270                 if(rooms) {
   271 	        		conn->onRoomlistCb(conn->onRoomlistCtx, (const flib_room**)rooms, roomCount);
   271                     conn->onRoomlistCb(conn->onRoomlistCtx, (const flib_room**)rooms, roomCount);
   272 	        		for(int i=0; i<roomCount; i++) {
   272                     for(int i=0; i<roomCount; i++) {
   273 	        			flib_room_destroy(rooms[i]);
   273                         flib_room_destroy(rooms[i]);
   274 	        		}
   274                     }
   275 	        		free(rooms);
   275                     free(rooms);
   276 	        	}
   276                 }
   277 	        }
   277             }
   278 	    } else if (!strcmp(cmd, "SERVER_MESSAGE")) {
   278         } else if (!strcmp(cmd, "SERVER_MESSAGE")) {
   279 	        if(netmsg->partCount < 2) {
   279             if(netmsg->partCount < 2) {
   280 	        	flib_log_w("Net: Empty SERVERMESSAGE message");
   280                 flib_log_w("Net: Empty SERVERMESSAGE message");
   281 	        } else {
   281             } else {
   282 	        	conn->onMessageCb(conn->onMessageCtx, NETCONN_MSG_TYPE_SERVERMESSAGE, netmsg->parts[1]);
   282                 conn->onMessageCb(conn->onMessageCtx, NETCONN_MSG_TYPE_SERVERMESSAGE, netmsg->parts[1]);
   283 	        }
   283             }
   284 	    } else if (!strcmp(cmd, "CHAT")) {
   284         } else if (!strcmp(cmd, "CHAT")) {
   285 	        if(netmsg->partCount < 3) {
   285             if(netmsg->partCount < 3) {
   286 	        	flib_log_w("Net: Empty CHAT message");
   286                 flib_log_w("Net: Empty CHAT message");
   287 	        } else {
   287             } else {
   288 	        	conn->onChatCb(conn->onChatCtx, netmsg->parts[1], netmsg->parts[2]);
   288                 conn->onChatCb(conn->onChatCtx, netmsg->parts[1], netmsg->parts[2]);
   289 	        }
   289             }
   290 	    } else if (!strcmp(cmd, "INFO")) {
   290         } else if (!strcmp(cmd, "INFO")) {
   291 	        if(netmsg->partCount < 5) {
   291             if(netmsg->partCount < 5) {
   292 	        	flib_log_w("Net: Malformed INFO message");
   292                 flib_log_w("Net: Malformed INFO message");
   293 	        } else {
   293             } else {
   294 	        	char *joined = flib_join(netmsg->parts+1, netmsg->partCount-1, "\n");
   294                 char *joined = flib_join(netmsg->parts+1, netmsg->partCount-1, "\n");
   295 	        	if(joined) {
   295                 if(joined) {
   296 	        		conn->onMessageCb(conn->onMessageCtx, NETCONN_MSG_TYPE_PLAYERINFO, joined);
   296                     conn->onMessageCb(conn->onMessageCtx, NETCONN_MSG_TYPE_PLAYERINFO, joined);
   297 	        	}
   297                 }
   298 	        	free(joined);
   298                 free(joined);
   299 	        }
   299             }
   300 	    } else if(!strcmp(cmd, "SERVER_VARS")) {
   300         } else if(!strcmp(cmd, "SERVER_VARS")) {
   301 	    	for(int offset=1; offset+2<netmsg->partCount; offset+=2) {
   301             for(int offset=1; offset+2<netmsg->partCount; offset+=2) {
   302 	    		conn->onServerVarCb(conn->onServerVarCtx, netmsg->parts[offset], netmsg->parts[offset+1]);
   302                 conn->onServerVarCb(conn->onServerVarCtx, netmsg->parts[offset], netmsg->parts[offset+1]);
   303 	    	}
   303             }
   304 	    } else if (!strcmp(cmd, "CLIENT_FLAGS")) {
   304         } else if (!strcmp(cmd, "CLIENT_FLAGS")) {
   305 	        if(netmsg->partCount < 3 || strlen(netmsg->parts[1]) < 2) {
   305             if(netmsg->partCount < 3 || strlen(netmsg->parts[1]) < 2) {
   306 	        	flib_log_w("Net: Malformed CLIENT_FLAGS message");
   306                 flib_log_w("Net: Malformed CLIENT_FLAGS message");
   307 	        } else {
   307             } else {
   308 				const char *flags = netmsg->parts[1];
   308                 const char *flags = netmsg->parts[1];
   309 				bool setFlag = flags[0] == '+';
   309                 bool setFlag = flags[0] == '+';
   310 
   310 
   311 				for(int j = 2; j < netmsg->partCount; ++j) {
   311                 for(int j = 2; j < netmsg->partCount; ++j) {
   312 					bool isSelf = !strcmp(conn->playerName, netmsg->parts[j]);
   312                     bool isSelf = !strcmp(conn->playerName, netmsg->parts[j]);
   313 					if(isSelf && strchr(flags, 'h')) {
   313                     if(isSelf && strchr(flags, 'h')) {
   314 						conn->isChief = setFlag;
   314                         conn->isChief = setFlag;
   315 					}
   315                     }
   316 					conn->onClientFlagsCb(conn->onClientFlagsCtx, netmsg->parts[j], flags+1, setFlag);
   316                     conn->onClientFlagsCb(conn->onClientFlagsCtx, netmsg->parts[j], flags+1, setFlag);
   317 				}
   317                 }
   318 	        }
   318             }
   319 	    } else if (!strcmp(cmd, "ADD_TEAM")) {
   319         } else if (!strcmp(cmd, "ADD_TEAM")) {
   320 	        if(netmsg->partCount != 24 || conn->netconnState!=NETCONN_STATE_ROOM) {
   320             if(netmsg->partCount != 24 || conn->netconnState!=NETCONN_STATE_ROOM) {
   321 	            flib_log_w("Net: Bad ADD_TEAM message");
   321                 flib_log_w("Net: Bad ADD_TEAM message");
   322 	        } else {
   322             } else {
   323 	        	flib_team *team = flib_team_from_netmsg(netmsg->parts+1);
   323                 flib_team *team = flib_team_from_netmsg(netmsg->parts+1);
   324 	        	if(!team || flib_teamlist_insert(&conn->teamlist, team, conn->teamlist.teamCount)) {
   324                 if(!team || flib_teamlist_insert(&conn->teamlist, team, conn->teamlist.teamCount)) {
   325 					flib_team_destroy(team);
   325                     flib_team_destroy(team);
   326 					conn->netconnState = NETCONN_STATE_DISCONNECTED;
   326                     conn->netconnState = NETCONN_STATE_DISCONNECTED;
   327 					conn->onDisconnectedCb(conn->onDisconnectedCtx, NETCONN_DISCONNECT_INTERNAL_ERROR, "Internal error");
   327                     conn->onDisconnectedCb(conn->onDisconnectedCtx, NETCONN_DISCONNECT_INTERNAL_ERROR, "Internal error");
   328 					exit = true;
   328                     exit = true;
   329 	        	} else {
   329                 } else {
   330 	        		conn->onTeamAddCb(conn->onTeamAddCtx, team);
   330                     conn->onTeamAddCb(conn->onTeamAddCtx, team);
   331 	        	}
   331                 }
   332 	        }
   332             }
   333 	    } else if (!strcmp(cmd, "REMOVE_TEAM")) {
   333         } else if (!strcmp(cmd, "REMOVE_TEAM")) {
   334 	        if(netmsg->partCount != 2 || conn->netconnState!=NETCONN_STATE_ROOM) {
   334             if(netmsg->partCount != 2 || conn->netconnState!=NETCONN_STATE_ROOM) {
   335 	            flib_log_w("Net: Bad REMOVETEAM message");
   335                 flib_log_w("Net: Bad REMOVETEAM message");
   336 	        } else {
   336             } else {
   337 	        	flib_teamlist_delete(&conn->teamlist, netmsg->parts[1]);
   337                 flib_teamlist_delete(&conn->teamlist, netmsg->parts[1]);
   338 	        	conn->onTeamDeleteCb(conn->onTeamDeleteCtx, netmsg->parts[1]);
   338                 conn->onTeamDeleteCb(conn->onTeamDeleteCtx, netmsg->parts[1]);
   339 	        }
   339             }
   340 	    } else if(!strcmp(cmd, "ROOMABANDONED")) {
   340         } else if(!strcmp(cmd, "ROOMABANDONED")) {
   341 	    	netconn_leaveRoom(conn);
   341             netconn_leaveRoom(conn);
   342 	        conn->onLeaveRoomCb(conn->onLeaveRoomCtx, NETCONN_ROOMLEAVE_ABANDONED, "Room destroyed");
   342             conn->onLeaveRoomCb(conn->onLeaveRoomCtx, NETCONN_ROOMLEAVE_ABANDONED, "Room destroyed");
   343 	    } else if(!strcmp(cmd, "KICKED")) {
   343         } else if(!strcmp(cmd, "KICKED")) {
   344 	    	netconn_leaveRoom(conn);
   344             netconn_leaveRoom(conn);
   345 	    	conn->onLeaveRoomCb(conn->onLeaveRoomCtx, NETCONN_ROOMLEAVE_KICKED, "You got kicked");
   345             conn->onLeaveRoomCb(conn->onLeaveRoomCtx, NETCONN_ROOMLEAVE_KICKED, "You got kicked");
   346 	    } else if(!strcmp(cmd, "JOINED")) {
   346         } else if(!strcmp(cmd, "JOINED")) {
   347 	        if(netmsg->partCount < 2) {
   347             if(netmsg->partCount < 2) {
   348 	            flib_log_w("Net: Bad JOINED message");
   348                 flib_log_w("Net: Bad JOINED message");
   349 	        } else {
   349             } else {
   350 				for(int i = 1; i < netmsg->partCount; ++i)
   350                 for(int i = 1; i < netmsg->partCount; ++i)
   351 				{
   351                 {
   352 					bool isMe = !strcmp(conn->playerName, netmsg->parts[i]);
   352                     bool isMe = !strcmp(conn->playerName, netmsg->parts[i]);
   353 					if (isMe) {
   353                     if (isMe) {
   354 						conn->netconnState = NETCONN_STATE_ROOM;
   354                         conn->netconnState = NETCONN_STATE_ROOM;
   355 						conn->onEnterRoomCb(conn->onEnterRoomCtx, conn->isChief);
   355                         conn->onEnterRoomCb(conn->onEnterRoomCtx, conn->isChief);
   356 					}
   356                     }
   357 
   357 
   358 					conn->onRoomJoinCb(conn->onRoomJoinCtx, netmsg->parts[i]);
   358                     conn->onRoomJoinCb(conn->onRoomJoinCtx, netmsg->parts[i]);
   359 				}
   359                 }
   360 	        }
   360             }
   361 	    } else if(!strcmp(cmd, "LOBBY:JOINED")) {
   361         } else if(!strcmp(cmd, "LOBBY:JOINED")) {
   362 	        if(netmsg->partCount < 2) {
   362             if(netmsg->partCount < 2) {
   363 	            flib_log_w("Net: Bad JOINED message");
   363                 flib_log_w("Net: Bad JOINED message");
   364 	        } else {
   364             } else {
   365 				for(int i = 1; i < netmsg->partCount; ++i)
   365                 for(int i = 1; i < netmsg->partCount; ++i)
   366 				{
   366                 {
   367 					bool isMe = !strcmp(conn->playerName, netmsg->parts[i]);
   367                     bool isMe = !strcmp(conn->playerName, netmsg->parts[i]);
   368 					if (isMe && conn->netconnState == NETCONN_STATE_CONNECTING) {
   368                     if (isMe && conn->netconnState == NETCONN_STATE_CONNECTING) {
   369 						conn->onConnectedCb(conn->onConnectedCtx);
   369                         conn->onConnectedCb(conn->onConnectedCtx);
   370 						conn->netconnState = NETCONN_STATE_LOBBY;
   370                         conn->netconnState = NETCONN_STATE_LOBBY;
   371 					}
   371                     }
   372 					conn->onLobbyJoinCb(conn->onLobbyJoinCtx, netmsg->parts[i]);
   372                     conn->onLobbyJoinCb(conn->onLobbyJoinCtx, netmsg->parts[i]);
   373 				}
   373                 }
   374 	        }
   374             }
   375 	    } else if(!strcmp(cmd, "LEFT")) {
   375         } else if(!strcmp(cmd, "LEFT")) {
   376 	        if(netmsg->partCount < 2) {
   376             if(netmsg->partCount < 2) {
   377 	            flib_log_w("Net: Bad LEFT message");
   377                 flib_log_w("Net: Bad LEFT message");
   378 	        } else {
   378             } else {
   379 	        	conn->onRoomLeaveCb(conn->onRoomLeaveCtx, netmsg->parts[1], netmsg->partCount>2 ? netmsg->parts[2] : NULL);
   379                 conn->onRoomLeaveCb(conn->onRoomLeaveCtx, netmsg->parts[1], netmsg->partCount>2 ? netmsg->parts[2] : NULL);
   380 	        }
   380             }
   381 	    } else if(!strcmp(cmd, "ROOM") && netmsg->partCount >= 2) {
   381         } else if(!strcmp(cmd, "ROOM") && netmsg->partCount >= 2) {
   382 	    	const char *subcmd = netmsg->parts[1];
   382             const char *subcmd = netmsg->parts[1];
   383 	    	if(!strcmp(subcmd, "ADD") && netmsg->partCount == 10) {
   383             if(!strcmp(subcmd, "ADD") && netmsg->partCount == 10) {
   384 	    		flib_room *room = flib_room_from_netmsg(netmsg->parts+2);
   384                 flib_room *room = flib_room_from_netmsg(netmsg->parts+2);
   385 	    		if(room) {
   385                 if(room) {
   386 	    			conn->onRoomAddCb(conn->onRoomAddCtx, room);
   386                     conn->onRoomAddCb(conn->onRoomAddCtx, room);
   387 	    		}
   387                 }
   388 	    		flib_room_destroy(room);
   388                 flib_room_destroy(room);
   389 			} else if(!strcmp(subcmd, "UPD") && netmsg->partCount == 11) {
   389             } else if(!strcmp(subcmd, "UPD") && netmsg->partCount == 11) {
   390 				flib_room *room = flib_room_from_netmsg(netmsg->parts+3);
   390                 flib_room *room = flib_room_from_netmsg(netmsg->parts+3);
   391 				if(room) {
   391                 if(room) {
   392 	    			conn->onRoomUpdateCb(conn->onRoomUpdateCtx, netmsg->parts[2], room);
   392                     conn->onRoomUpdateCb(conn->onRoomUpdateCtx, netmsg->parts[2], room);
   393 	    		}
   393                 }
   394 				flib_room_destroy(room);
   394                 flib_room_destroy(room);
   395 			} else if(!strcmp(subcmd, "DEL") && netmsg->partCount == 3) {
   395             } else if(!strcmp(subcmd, "DEL") && netmsg->partCount == 3) {
   396 				conn->onRoomDeleteCb(conn->onRoomDeleteCtx, netmsg->parts[2]);
   396                 conn->onRoomDeleteCb(conn->onRoomDeleteCtx, netmsg->parts[2]);
   397 			} else {
   397             } else {
   398 				flib_log_w("Net: Unknown or malformed ROOM subcommand: %s", subcmd);
   398                 flib_log_w("Net: Unknown or malformed ROOM subcommand: %s", subcmd);
   399 			}
   399             }
   400 	    } else if(!strcmp(cmd, "LOBBY:LEFT")) {
   400         } else if(!strcmp(cmd, "LOBBY:LEFT")) {
   401 	        if(netmsg->partCount < 2) {
   401             if(netmsg->partCount < 2) {
   402 	            flib_log_w("Net: Bad LOBBY:LEFT message");
   402                 flib_log_w("Net: Bad LOBBY:LEFT message");
   403 	        } else {
   403             } else {
   404 	        	conn->onLobbyLeaveCb(conn->onLobbyLeaveCtx, netmsg->parts[1], netmsg->partCount>2 ? netmsg->parts[2] : NULL);
   404                 conn->onLobbyLeaveCb(conn->onLobbyLeaveCtx, netmsg->parts[1], netmsg->partCount>2 ? netmsg->parts[2] : NULL);
   405 	        }
   405             }
   406 	    } else if (!strcmp(cmd, "RUN_GAME")) {
   406         } else if (!strcmp(cmd, "RUN_GAME")) {
   407 	        conn->onRunGameCb(conn->onRunGameCtx);
   407             conn->onRunGameCb(conn->onRunGameCtx);
   408 	    } else if (!strcmp(cmd, "ASKPASSWORD")) {
   408         } else if (!strcmp(cmd, "ASKPASSWORD")) {
   409 	    	conn->onPasswordRequestCb(conn->onPasswordRequestCtx, conn->playerName);
   409             conn->onPasswordRequestCb(conn->onPasswordRequestCtx, conn->playerName);
   410 	    } else if (!strcmp(cmd, "NOTICE")) {
   410         } else if (!strcmp(cmd, "NOTICE")) {
   411 	        if(netmsg->partCount < 2) {
   411             if(netmsg->partCount < 2) {
   412 	            flib_log_w("Net: Bad NOTICE message");
   412                 flib_log_w("Net: Bad NOTICE message");
   413 	        } else {
   413             } else {
   414 				errno = 0;
   414                 errno = 0;
   415 				long n = strtol(netmsg->parts[1], NULL, 10);
   415                 long n = strtol(netmsg->parts[1], NULL, 10);
   416 				if(errno) {
   416                 if(errno) {
   417 					flib_log_w("Net: Bad NOTICE message");
   417                     flib_log_w("Net: Bad NOTICE message");
   418 				} else if(n==0) {
   418                 } else if(n==0) {
   419 					conn->onNickTakenCb(conn->onNickTakenCtx, conn->playerName);
   419                     conn->onNickTakenCb(conn->onNickTakenCtx, conn->playerName);
   420 				} else {
   420                 } else {
   421 					flib_log_w("Net: Unknown NOTICE message: %l", n);
   421                     flib_log_w("Net: Unknown NOTICE message: %l", n);
   422 				}
   422                 }
   423 	        }
   423             }
   424 	    } else if (!strcmp(cmd, "TEAM_ACCEPTED")) {
   424         } else if (!strcmp(cmd, "TEAM_ACCEPTED")) {
   425 	        if (netmsg->partCount != 2 || conn->netconnState!=NETCONN_STATE_ROOM) {
   425             if (netmsg->partCount != 2 || conn->netconnState!=NETCONN_STATE_ROOM) {
   426 	            flib_log_w("Net: Bad TEAM_ACCEPTED message");
   426                 flib_log_w("Net: Bad TEAM_ACCEPTED message");
   427 	        } else {
   427             } else {
   428 	        	flib_team *team = flib_team_copy(flib_teamlist_find(&conn->pendingTeamlist, netmsg->parts[1]));
   428                 flib_team *team = flib_team_copy(flib_teamlist_find(&conn->pendingTeamlist, netmsg->parts[1]));
   429 	        	if(team) {
   429                 if(team) {
   430 	        		flib_teamlist_insert(&conn->teamlist, team, conn->teamlist.teamCount);
   430                     flib_teamlist_insert(&conn->teamlist, team, conn->teamlist.teamCount);
   431 	        		flib_teamlist_delete(&conn->pendingTeamlist, netmsg->parts[1]);
   431                     flib_teamlist_delete(&conn->pendingTeamlist, netmsg->parts[1]);
   432 	        	} else {
   432                 } else {
   433 	        		flib_log_e("Team accepted that was not requested: %s", netmsg->parts[1]);
   433                     flib_log_e("Team accepted that was not requested: %s", netmsg->parts[1]);
   434 	        	}
   434                 }
   435 	        	conn->onTeamAcceptedCb(conn->onTeamAcceptedCtx, netmsg->parts[1]);
   435                 conn->onTeamAcceptedCb(conn->onTeamAcceptedCtx, netmsg->parts[1]);
   436 	        }
   436             }
   437 	    } else if (!strcmp(cmd, "CFG")) {
   437         } else if (!strcmp(cmd, "CFG")) {
   438 	        if(netmsg->partCount < 3 || conn->netconnState!=NETCONN_STATE_ROOM) {
   438             if(netmsg->partCount < 3 || conn->netconnState!=NETCONN_STATE_ROOM) {
   439 	            flib_log_w("Net: Bad CFG message");
   439                 flib_log_w("Net: Bad CFG message");
   440 	        } else {
   440             } else {
   441 	        	const char *subcmd = netmsg->parts[1];
   441                 const char *subcmd = netmsg->parts[1];
   442 				if(!strcmp(subcmd, "SCHEME") && netmsg->partCount == flib_meta.modCount + flib_meta.settingCount + 3) {
   442                 if(!strcmp(subcmd, "SCHEME") && netmsg->partCount == flib_meta.modCount + flib_meta.settingCount + 3) {
   443 					flib_scheme *cfg = flib_scheme_from_netmsg(netmsg->parts+2);
   443                     flib_scheme *cfg = flib_scheme_from_netmsg(netmsg->parts+2);
   444 					if(cfg) {
   444                     if(cfg) {
   445 						flib_scheme_destroy(conn->scheme);
   445                         flib_scheme_destroy(conn->scheme);
   446 						conn->scheme = cfg;
   446                         conn->scheme = cfg;
   447 						conn->onSchemeChangedCb(conn->onSchemeChangedCtx, cfg);
   447                         conn->onSchemeChangedCb(conn->onSchemeChangedCtx, cfg);
   448 					} else {
   448                     } else {
   449 						flib_log_e("Error processing CFG SCHEME message");
   449                         flib_log_e("Error processing CFG SCHEME message");
   450 					}
   450                     }
   451 				} else if(!strcmp(subcmd, "FULLMAPCONFIG") && netmsg->partCount == 7) {
   451                 } else if(!strcmp(subcmd, "FULLMAPCONFIG") && netmsg->partCount == 7) {
   452 					flib_map *map = flib_map_from_netmsg(netmsg->parts+2);
   452                     flib_map *map = flib_map_from_netmsg(netmsg->parts+2);
   453 					if(map) {
   453                     if(map) {
   454 						flib_map_destroy(conn->map);
   454                         flib_map_destroy(conn->map);
   455 						conn->map = map;
   455                         conn->map = map;
   456 						conn->onMapChangedCb(conn->onMapChangedCtx, conn->map, NETCONN_MAPCHANGE_FULL);
   456                         conn->onMapChangedCb(conn->onMapChangedCtx, conn->map, NETCONN_MAPCHANGE_FULL);
   457 					} else {
   457                     } else {
   458 						flib_log_e("Error processing CFG FULLMAPCONFIG message");
   458                         flib_log_e("Error processing CFG FULLMAPCONFIG message");
   459 					}
   459                     }
   460 				} else if(!strcmp(subcmd, "MAP") && netmsg->partCount == 3) {
   460                 } else if(!strcmp(subcmd, "MAP") && netmsg->partCount == 3) {
   461 					char *mapname = flib_strdupnull(netmsg->parts[2]);
   461                     char *mapname = flib_strdupnull(netmsg->parts[2]);
   462 					if(mapname) {
   462                     if(mapname) {
   463 						free(conn->map->name);
   463                         free(conn->map->name);
   464 						conn->map->name = mapname;
   464                         conn->map->name = mapname;
   465 						conn->onMapChangedCb(conn->onMapChangedCtx, conn->map, NETCONN_MAPCHANGE_MAP);
   465                         conn->onMapChangedCb(conn->onMapChangedCtx, conn->map, NETCONN_MAPCHANGE_MAP);
   466 					} else {
   466                     } else {
   467 						flib_log_e("Error processing CFG MAP message");
   467                         flib_log_e("Error processing CFG MAP message");
   468 					}
   468                     }
   469 				} else if(!strcmp(subcmd, "THEME") && netmsg->partCount == 3) {
   469                 } else if(!strcmp(subcmd, "THEME") && netmsg->partCount == 3) {
   470 					char *themename = flib_strdupnull(netmsg->parts[2]);
   470                     char *themename = flib_strdupnull(netmsg->parts[2]);
   471 					if(themename) {
   471                     if(themename) {
   472 						free(conn->map->theme);
   472                         free(conn->map->theme);
   473 						conn->map->theme = themename;
   473                         conn->map->theme = themename;
   474 						conn->onMapChangedCb(conn->onMapChangedCtx, conn->map, NETCONN_MAPCHANGE_THEME);
   474                         conn->onMapChangedCb(conn->onMapChangedCtx, conn->map, NETCONN_MAPCHANGE_THEME);
   475 					} else {
   475                     } else {
   476 						flib_log_e("Error processing CFG THEME message");
   476                         flib_log_e("Error processing CFG THEME message");
   477 					}
   477                     }
   478 				} else if(!strcmp(subcmd, "SEED") && netmsg->partCount == 3) {
   478                 } else if(!strcmp(subcmd, "SEED") && netmsg->partCount == 3) {
   479 					char *seed = flib_strdupnull(netmsg->parts[2]);
   479                     char *seed = flib_strdupnull(netmsg->parts[2]);
   480 					if(seed) {
   480                     if(seed) {
   481 						free(conn->map->seed);
   481                         free(conn->map->seed);
   482 						conn->map->seed = seed;
   482                         conn->map->seed = seed;
   483 						conn->onMapChangedCb(conn->onMapChangedCtx, conn->map, NETCONN_MAPCHANGE_SEED);
   483                         conn->onMapChangedCb(conn->onMapChangedCtx, conn->map, NETCONN_MAPCHANGE_SEED);
   484 					} else {
   484                     } else {
   485 						flib_log_e("Error processing CFG SEED message");
   485                         flib_log_e("Error processing CFG SEED message");
   486 					}
   486                     }
   487 				} else if(!strcmp(subcmd, "TEMPLATE") && netmsg->partCount == 3) {
   487                 } else if(!strcmp(subcmd, "TEMPLATE") && netmsg->partCount == 3) {
   488 					conn->map->templateFilter = atoi(netmsg->parts[2]);
   488                     conn->map->templateFilter = atoi(netmsg->parts[2]);
   489 					conn->onMapChangedCb(conn->onMapChangedCtx, conn->map, NETCONN_MAPCHANGE_TEMPLATE);
   489                     conn->onMapChangedCb(conn->onMapChangedCtx, conn->map, NETCONN_MAPCHANGE_TEMPLATE);
   490 				} else if(!strcmp(subcmd, "MAPGEN") && netmsg->partCount == 3) {
   490                 } else if(!strcmp(subcmd, "MAPGEN") && netmsg->partCount == 3) {
   491 					conn->map->mapgen = atoi(netmsg->parts[2]);
   491                     conn->map->mapgen = atoi(netmsg->parts[2]);
   492 					conn->onMapChangedCb(conn->onMapChangedCtx, conn->map, NETCONN_MAPCHANGE_MAPGEN);
   492                     conn->onMapChangedCb(conn->onMapChangedCtx, conn->map, NETCONN_MAPCHANGE_MAPGEN);
   493 				} else if(!strcmp(subcmd, "MAZE_SIZE") && netmsg->partCount == 3) {
   493                 } else if(!strcmp(subcmd, "MAZE_SIZE") && netmsg->partCount == 3) {
   494 					conn->map->mazeSize = atoi(netmsg->parts[2]);
   494                     conn->map->mazeSize = atoi(netmsg->parts[2]);
   495 					conn->onMapChangedCb(conn->onMapChangedCtx, conn->map, NETCONN_MAPCHANGE_MAZE_SIZE);
   495                     conn->onMapChangedCb(conn->onMapChangedCtx, conn->map, NETCONN_MAPCHANGE_MAZE_SIZE);
   496 				} else if(!strcmp(subcmd, "DRAWNMAP") && netmsg->partCount == 3) {
   496                 } else if(!strcmp(subcmd, "DRAWNMAP") && netmsg->partCount == 3) {
   497 					size_t drawnMapSize = 0;
   497                     size_t drawnMapSize = 0;
   498 					uint8_t *drawnMapData = NULL;
   498                     uint8_t *drawnMapData = NULL;
   499 					if(!flib_drawnmapdata_from_netmsg(netmsg->parts[2], &drawnMapData, &drawnMapSize)) {
   499                     if(!flib_drawnmapdata_from_netmsg(netmsg->parts[2], &drawnMapData, &drawnMapSize)) {
   500 						free(conn->map->drawData);
   500                         free(conn->map->drawData);
   501 						conn->map->drawData = drawnMapData;
   501                         conn->map->drawData = drawnMapData;
   502 						conn->map->drawDataSize = drawnMapSize;
   502                         conn->map->drawDataSize = drawnMapSize;
   503 						conn->onMapChangedCb(conn->onMapChangedCtx, conn->map, NETCONN_MAPCHANGE_DRAWNMAP);
   503                         conn->onMapChangedCb(conn->onMapChangedCtx, conn->map, NETCONN_MAPCHANGE_DRAWNMAP);
   504 					} else {
   504                     } else {
   505 						flib_log_e("Error processing CFG DRAWNMAP message");
   505                         flib_log_e("Error processing CFG DRAWNMAP message");
   506 					}
   506                     }
   507 				} else if(!strcmp(subcmd, "SCRIPT") && netmsg->partCount == 3) {
   507                 } else if(!strcmp(subcmd, "SCRIPT") && netmsg->partCount == 3) {
   508 					netconn_setScript(conn, netmsg->parts[2]);
   508                     netconn_setScript(conn, netmsg->parts[2]);
   509 					conn->onScriptChangedCb(conn->onScriptChangedCtx, netmsg->parts[2]);
   509                     conn->onScriptChangedCb(conn->onScriptChangedCtx, netmsg->parts[2]);
   510 				} else if(!strcmp(subcmd, "AMMO") && netmsg->partCount == 4) {
   510                 } else if(!strcmp(subcmd, "AMMO") && netmsg->partCount == 4) {
   511 					flib_weaponset *weapons = flib_weaponset_from_ammostring(netmsg->parts[2], netmsg->parts[3]);
   511                     flib_weaponset *weapons = flib_weaponset_from_ammostring(netmsg->parts[2], netmsg->parts[3]);
   512 					if(weapons) {
   512                     if(weapons) {
   513 						flib_weaponset_destroy(conn->weaponset);
   513                         flib_weaponset_destroy(conn->weaponset);
   514 						conn->weaponset = weapons;
   514                         conn->weaponset = weapons;
   515 						conn->onWeaponsetChangedCb(conn->onWeaponsetChangedCtx, weapons);
   515                         conn->onWeaponsetChangedCb(conn->onWeaponsetChangedCtx, weapons);
   516 					} else {
   516                     } else {
   517 						flib_log_e("Error processing CFG AMMO message");
   517                         flib_log_e("Error processing CFG AMMO message");
   518 					}
   518                     }
   519 				} else {
   519                 } else {
   520 					flib_log_w("Net: Unknown or malformed CFG subcommand: %s", subcmd);
   520                     flib_log_w("Net: Unknown or malformed CFG subcommand: %s", subcmd);
   521 				}
   521                 }
   522 	        }
   522             }
   523 	    } else if (!strcmp(cmd, "HH_NUM")) {
   523         } else if (!strcmp(cmd, "HH_NUM")) {
   524 	        if (netmsg->partCount != 3 || conn->netconnState!=NETCONN_STATE_ROOM) {
   524             if (netmsg->partCount != 3 || conn->netconnState!=NETCONN_STATE_ROOM) {
   525 	            flib_log_w("Net: Bad HH_NUM message");
   525                 flib_log_w("Net: Bad HH_NUM message");
   526 	        } else {
   526             } else {
   527 	        	int hogs = atoi(netmsg->parts[2]);
   527                 int hogs = atoi(netmsg->parts[2]);
   528 	        	if(hogs<=0 || hogs>HEDGEHOGS_PER_TEAM) {
   528                 if(hogs<=0 || hogs>HEDGEHOGS_PER_TEAM) {
   529 	        		flib_log_w("Net: Bad HH_NUM message: %s hogs", netmsg->parts[2]);
   529                     flib_log_w("Net: Bad HH_NUM message: %s hogs", netmsg->parts[2]);
   530 	        	} else {
   530                 } else {
   531 	        		flib_team *team = flib_teamlist_find(&conn->teamlist, netmsg->parts[1]);
   531                     flib_team *team = flib_teamlist_find(&conn->teamlist, netmsg->parts[1]);
   532 	        		if(team) {
   532                     if(team) {
   533 	        			team->hogsInGame = hogs;
   533                         team->hogsInGame = hogs;
   534 	        		} else {
   534                     } else {
   535 	        			flib_log_e("HH_NUM message for unknown team %s", netmsg->parts[1]);
   535                         flib_log_e("HH_NUM message for unknown team %s", netmsg->parts[1]);
   536 	        		}
   536                     }
   537 	        		conn->onHogCountChangedCb(conn->onHogCountChangedCtx, netmsg->parts[1], hogs);
   537                     conn->onHogCountChangedCb(conn->onHogCountChangedCtx, netmsg->parts[1], hogs);
   538 	        	}
   538                 }
   539 	        }
   539             }
   540 	    } else if (!strcmp(cmd, "TEAM_COLOR")) {
   540         } else if (!strcmp(cmd, "TEAM_COLOR")) {
   541 	        if (netmsg->partCount != 3 || conn->netconnState!=NETCONN_STATE_ROOM) {
   541             if (netmsg->partCount != 3 || conn->netconnState!=NETCONN_STATE_ROOM) {
   542 	            flib_log_w("Net: Bad TEAM_COLOR message");
   542                 flib_log_w("Net: Bad TEAM_COLOR message");
   543 	        } else {
   543             } else {
   544 	        	long color;
   544                 long color;
   545 	        	if(sscanf(netmsg->parts[2], "%lu", &color) && color>=0 && color<flib_teamcolor_count) {
   545                 if(sscanf(netmsg->parts[2], "%lu", &color) && color>=0 && color<flib_teamcolor_count) {
   546 	        		flib_team *team = flib_teamlist_find(&conn->teamlist, netmsg->parts[1]);
   546                     flib_team *team = flib_teamlist_find(&conn->teamlist, netmsg->parts[1]);
   547 	        		if(team) {
   547                     if(team) {
   548 	        			team->colorIndex = color;
   548                         team->colorIndex = color;
   549 	        		} else {
   549                     } else {
   550 	        			flib_log_e("TEAM_COLOR message for unknown team %s", netmsg->parts[1]);
   550                         flib_log_e("TEAM_COLOR message for unknown team %s", netmsg->parts[1]);
   551 	        		}
   551                     }
   552 	        		conn->onTeamColorChangedCb(conn->onTeamColorChangedCtx, netmsg->parts[1], color);
   552                     conn->onTeamColorChangedCb(conn->onTeamColorChangedCtx, netmsg->parts[1], color);
   553 	        	} else {
   553                 } else {
   554 	        		flib_log_w("Net: Bad TEAM_COLOR message: Color %s", netmsg->parts[2]);
   554                     flib_log_w("Net: Bad TEAM_COLOR message: Color %s", netmsg->parts[2]);
   555 	        	}
   555                 }
   556 	        }
   556             }
   557 	    } else if (!strcmp(cmd, "EM")) {
   557         } else if (!strcmp(cmd, "EM")) {
   558 	        if(netmsg->partCount < 2) {
   558             if(netmsg->partCount < 2) {
   559 	            flib_log_w("Net: Bad EM message");
   559                 flib_log_w("Net: Bad EM message");
   560 	        } else {
   560             } else {
   561 	        	for(int i = 1; i < netmsg->partCount; ++i) {
   561                 for(int i = 1; i < netmsg->partCount; ++i) {
   562 					char *out = NULL;
   562                     char *out = NULL;
   563 					size_t outlen;
   563                     size_t outlen;
   564 					bool ok = base64_decode_alloc(netmsg->parts[i], strlen(netmsg->parts[i]), &out, &outlen);
   564                     bool ok = base64_decode_alloc(netmsg->parts[i], strlen(netmsg->parts[i]), &out, &outlen);
   565 					if(ok && outlen) {
   565                     if(ok && outlen) {
   566 						conn->onEngineMessageCb(conn->onEngineMessageCtx, (uint8_t*)out, outlen);
   566                         conn->onEngineMessageCb(conn->onEngineMessageCtx, (uint8_t*)out, outlen);
   567 					} else {
   567                     } else {
   568 						flib_log_e("Net: Malformed engine message: %s", netmsg->parts[i]);
   568                         flib_log_e("Net: Malformed engine message: %s", netmsg->parts[i]);
   569 					}
   569                     }
   570 					free(out);
   570                     free(out);
   571 	        	}
   571                 }
   572 	        }
   572             }
   573 	    } else if (!strcmp(cmd, "BYE")) {
   573         } else if (!strcmp(cmd, "BYE")) {
   574 	        if (netmsg->partCount < 2) {
   574             if (netmsg->partCount < 2) {
   575 	            flib_log_w("Net: Bad BYE message");
   575                 flib_log_w("Net: Bad BYE message");
   576 	        } else {
   576             } else {
   577 				conn->netconnState = NETCONN_STATE_DISCONNECTED;
   577                 conn->netconnState = NETCONN_STATE_DISCONNECTED;
   578 				if (!strcmp(netmsg->parts[1], "Authentication failed")) {
   578                 if (!strcmp(netmsg->parts[1], "Authentication failed")) {
   579 					conn->onDisconnectedCb(conn->onDisconnectedCtx, NETCONN_DISCONNECT_AUTH_FAILED, netmsg->parts[1]);
   579                     conn->onDisconnectedCb(conn->onDisconnectedCtx, NETCONN_DISCONNECT_AUTH_FAILED, netmsg->parts[1]);
   580 				} else {
   580                 } else {
   581 					conn->onDisconnectedCb(conn->onDisconnectedCtx, NETCONN_DISCONNECT_NORMAL, netmsg->parts[1]);
   581                     conn->onDisconnectedCb(conn->onDisconnectedCtx, NETCONN_DISCONNECT_NORMAL, netmsg->parts[1]);
   582 				}
   582                 }
   583 				exit = true;
   583                 exit = true;
   584 	        }
   584             }
   585 	    } else if (!strcmp(cmd, "ADMIN_ACCESS")) {
   585         } else if (!strcmp(cmd, "ADMIN_ACCESS")) {
   586 	    	// deprecated
   586             // deprecated
   587 	    } else if (!strcmp(cmd, "ROOM_CONTROL_ACCESS")) {
   587         } else if (!strcmp(cmd, "ROOM_CONTROL_ACCESS")) {
   588 	    	// deprecated
   588             // deprecated
   589 	    } else {
   589         } else {
   590 	    	flib_log_w("Unknown server command: %s", cmd);
   590             flib_log_w("Unknown server command: %s", cmd);
   591 	    }
   591         }
   592 		flib_netmsg_destroy(netmsg);
   592         flib_netmsg_destroy(netmsg);
   593 	}
   593     }
   594 
   594 
   595 	if(!exit && !conn->destroyRequested && !flib_netbase_connected(net)) {
   595     if(!exit && !conn->destroyRequested && !flib_netbase_connected(net)) {
   596 		conn->netconnState = NETCONN_STATE_DISCONNECTED;
   596         conn->netconnState = NETCONN_STATE_DISCONNECTED;
   597 		conn->onDisconnectedCb(conn->onDisconnectedCtx, NETCONN_DISCONNECT_CONNLOST, "Connection lost");
   597         conn->onDisconnectedCb(conn->onDisconnectedCtx, NETCONN_DISCONNECT_CONNLOST, "Connection lost");
   598 	}
   598     }
   599 }
   599 }
   600 
   600 
   601 void flib_netconn_tick(flib_netconn *conn) {
   601 void flib_netconn_tick(flib_netconn *conn) {
   602 	if(!log_badargs_if(conn==NULL)
   602     if(!log_badargs_if(conn==NULL)
   603 			&& !log_w_if(conn->running, "Call to flib_netconn_tick from a callback")
   603             && !log_w_if(conn->running, "Call to flib_netconn_tick from a callback")
   604 			&& !log_w_if(conn->netconnState == NETCONN_STATE_DISCONNECTED, "We are already done.")) {
   604             && !log_w_if(conn->netconnState == NETCONN_STATE_DISCONNECTED, "We are already done.")) {
   605 		conn->running = true;
   605         conn->running = true;
   606 		flib_netconn_wrappedtick(conn);
   606         flib_netconn_wrappedtick(conn);
   607 		conn->running = false;
   607         conn->running = false;
   608 
   608 
   609 		if(conn->destroyRequested) {
   609         if(conn->destroyRequested) {
   610 			flib_netconn_destroy(conn);
   610             flib_netconn_destroy(conn);
   611 		}
   611         }
   612 	}
   612     }
   613 }
   613 }