project_files/frontlib/net/netconn.c
changeset 7271 5608ac657362
parent 7269 5b0aeef8ba2a
child 7273 8eed495fd8da
equal deleted inserted replaced
7269:5b0aeef8ba2a 7271:5608ac657362
    16  * You should have received a copy of the GNU General Public License
    16  * You should have received a copy of the GNU General Public License
    17  * along with this program; if not, write to the Free Software
    17  * along with this program; if not, write to the Free Software
    18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
    18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
    19  */
    19  */
    20 
    20 
    21 #include "netconn.h"
    21 // TODO: Check the state transitions. Document with a diagram or something
    22 #include "netbase.h"
    22 
       
    23 #include "netconn_internal.h"
    23 #include "netprotocol.h"
    24 #include "netprotocol.h"
    24 #include "../util/logging.h"
    25 #include "../util/logging.h"
    25 #include "../util/util.h"
    26 #include "../util/util.h"
    26 #include "../model/roomlist.h"
    27 #include "../model/roomlist.h"
    27 #include "../md5/md5.h"
    28 #include "../md5/md5.h"
    28 
    29 #include "../base64/base64.h"
    29 #include <stdbool.h>
    30 
    30 #include <stdlib.h>
    31 #include <stdlib.h>
    31 #include <string.h>
    32 #include <string.h>
    32 #include <errno.h>
    33 #include <errno.h>
    33 #include <ctype.h>
    34 #include <ctype.h>
    34 
       
    35 struct _flib_netconn {
       
    36 	flib_netbase *netBase;
       
    37 	char *playerName;
       
    38 	flib_cfg_meta *metaCfg;
       
    39 	flib_roomlist *roomList;
       
    40 
       
    41 	int netconnState;	// One of the NETCONN_STATE constants
       
    42 
       
    43 	bool isAdmin;			// Player is server administrator
       
    44 	bool isChief;			// Player can modify the current room
       
    45 
       
    46 
       
    47 	void (*onMessageCb)(void *context, int msgtype, const char *msg);
       
    48 	void *onMessageCtx;
       
    49 
       
    50 	void (*onConnectedCb)(void *context);
       
    51 	void *onConnectedCtx;
       
    52 
       
    53 	void (*onDisconnectedCb)(void *context, int reason, const char *message);
       
    54 	void *onDisconnectedCtx;
       
    55 
       
    56 	void (*onRoomAddCb)(void *context, const flib_roomlist_room *room);
       
    57 	void *onRoomAddCtx;
       
    58 
       
    59 	void (*onRoomDeleteCb)(void *context, const char *name);
       
    60 	void *onRoomDeleteCtx;
       
    61 
       
    62 	void (*onRoomUpdateCb)(void *context, const char *oldName, const flib_roomlist_room *room);
       
    63 	void *onRoomUpdateCtx;
       
    64 
       
    65 	void (*onChatCb)(void *context, const char *nick, const char *msg);
       
    66 	void *onChatCtx;
       
    67 
       
    68 	void (*onLobbyJoinCb)(void *context, const char *nick);
       
    69 	void *onLobbyJoinCtx;
       
    70 
       
    71 	void (*onLobbyLeaveCb)(void *context, const char *nick, const char *partMessage);
       
    72 	void *onLobbyLeaveCtx;
       
    73 
       
    74 	void (*onRoomJoinCb)(void *context, const char *nick);
       
    75 	void *onRoomJoinCtx;
       
    76 
       
    77 	void (*onRoomLeaveCb)(void *context, const char *nick, const char *partMessage);
       
    78 	void *onRoomLeaveCtx;
       
    79 
       
    80 	void (*onNickTakenCb)(void *context, const char *nick);
       
    81 	void *onNickTakenCtx;
       
    82 
       
    83 	void (*onNickAcceptCb)(void *context, const char *nick);
       
    84 	void *onNickAcceptCtx;
       
    85 
       
    86 	void (*onPasswordRequestCb)(void *context, const char *nick);
       
    87 	void *onPasswordRequestCtx;
       
    88 
       
    89 	void (*onRoomChiefStatusCb)(void *context, bool isChief);
       
    90 	void *onRoomChiefStatusCtx;
       
    91 
       
    92 	void (*onReadyStateCb)(void *context, const char *nick, bool ready);
       
    93 	void *onReadyStateCtx;
       
    94 
       
    95 	void (*onEnterRoomCb)(void *context, bool chief);
       
    96 	void *onEnterRoomCtx;
       
    97 
       
    98 	void (*onLeaveRoomCb)(void *context, int reason, const char *message);
       
    99 	void *onLeaveRoomCtx;
       
   100 
       
   101 	void (*onTeamAddCb)(void *context, flib_team *team);
       
   102 	void *onTeamAddCtx;
       
   103 
       
   104 	bool running;
       
   105 	bool destroyRequested;
       
   106 };
       
   107 
    35 
   108 static void defaultCallback_onMessage(void *context, int msgtype, const char *msg) {
    36 static void defaultCallback_onMessage(void *context, int msgtype, const char *msg) {
   109 	flib_log_i("Net: [%i] %s", msgtype, msg);
    37 	flib_log_i("Net: [%i] %s", msgtype, msg);
   110 }
    38 }
   111 
    39 
   113 static void defaultCallback_bool(void *context, bool isChief) {}
    41 static void defaultCallback_bool(void *context, bool isChief) {}
   114 static void defaultCallback_str(void *context, const char *str) {}
    42 static void defaultCallback_str(void *context, const char *str) {}
   115 static void defaultCallback_int_str(void *context, int i, const char *str) {}
    43 static void defaultCallback_int_str(void *context, int i, const char *str) {}
   116 static void defaultCallback_str_str(void *context, const char *str1, const char *str2) {}
    44 static void defaultCallback_str_str(void *context, const char *str1, const char *str2) {}
   117 static void defaultCallback_str_bool(void *context, const char *str, bool b) {}
    45 static void defaultCallback_str_bool(void *context, const char *str, bool b) {}
       
    46 static void defaultCallback_str_int(void *context, const char *str, int i) {}
   118 
    47 
   119 static void defaultCallback_onRoomAdd(void *context, const flib_roomlist_room *room) {}
    48 static void defaultCallback_onRoomAdd(void *context, const flib_roomlist_room *room) {}
   120 static void defaultCallback_onRoomUpdate(void *context, const char *oldName, const flib_roomlist_room *room) {}
    49 static void defaultCallback_onRoomUpdate(void *context, const char *oldName, const flib_roomlist_room *room) {}
   121 static void defaultCallback_onChat(void *context, const char *nick, const char *msg) {
    50 static void defaultCallback_onChat(void *context, const char *nick, const char *msg) {
   122 	flib_log_i("%s: %s", nick, msg);
    51 	flib_log_i("%s: %s", nick, msg);
   147 static void defaultCallback_onPasswordRequest(void *context, const char *requestedNick) {
    76 static void defaultCallback_onPasswordRequest(void *context, const char *requestedNick) {
   148 	flib_netconn_send_quit((flib_netconn*)context, "Authentication failed");
    77 	flib_netconn_send_quit((flib_netconn*)context, "Authentication failed");
   149 }
    78 }
   150 
    79 
   151 static void defaultCallback_onTeamAdd(void *context, flib_team *team) {}
    80 static void defaultCallback_onTeamAdd(void *context, flib_team *team) {}
       
    81 static void defaultCallback_onTeamColorChanged(void *context, const char *teamName, uint32_t color) {}
       
    82 static void defaultCallback_onCfgScheme(void *context, flib_cfg *scheme) {}
       
    83 static void defaultCallback_onMapChanged(void *context, const flib_map *map, int changetype) {}
       
    84 static void defaultCallback_onWeaponsetChanged(void *context, flib_weaponset *weaponset) {}
   152 
    85 
   153 static void clearCallbacks(flib_netconn *conn) {
    86 static void clearCallbacks(flib_netconn *conn) {
   154 	flib_netconn_onMessage(conn, NULL, NULL);
    87 	flib_netconn_onMessage(conn, NULL, NULL);
   155 	flib_netconn_onConnected(conn, NULL, NULL);
    88 	flib_netconn_onConnected(conn, NULL, NULL);
   156 	flib_netconn_onDisconnected(conn, NULL, NULL);
    89 	flib_netconn_onDisconnected(conn, NULL, NULL);
   161 	flib_netconn_onLobbyJoin(conn, NULL, NULL);
    94 	flib_netconn_onLobbyJoin(conn, NULL, NULL);
   162 	flib_netconn_onLobbyLeave(conn, NULL, NULL);
    95 	flib_netconn_onLobbyLeave(conn, NULL, NULL);
   163 	flib_netconn_onRoomJoin(conn, NULL, NULL);
    96 	flib_netconn_onRoomJoin(conn, NULL, NULL);
   164 	flib_netconn_onRoomLeave(conn, NULL, NULL);
    97 	flib_netconn_onRoomLeave(conn, NULL, NULL);
   165 	flib_netconn_onNickTaken(conn, NULL, NULL);
    98 	flib_netconn_onNickTaken(conn, NULL, NULL);
   166 	flib_netconn_onNickAccept(conn, NULL, NULL);
       
   167 	flib_netconn_onPasswordRequest(conn, NULL, NULL);
    99 	flib_netconn_onPasswordRequest(conn, NULL, NULL);
   168 	flib_netconn_onRoomChiefStatus(conn, NULL, NULL);
   100 	flib_netconn_onRoomChiefStatus(conn, NULL, NULL);
   169 	flib_netconn_onReadyStateCb(conn, NULL, NULL);
   101 	flib_netconn_onReadyState(conn, NULL, NULL);
   170 	flib_netconn_onEnterRoomCb(conn, NULL, NULL);
   102 	flib_netconn_onEnterRoom(conn, NULL, NULL);
   171 	flib_netconn_onTeamAddCb(conn, NULL, NULL);
   103 	flib_netconn_onLeaveRoom(conn, NULL, NULL);
       
   104 	flib_netconn_onTeamAdd(conn, NULL, NULL);
       
   105 	flib_netconn_onTeamDelete(conn, NULL, NULL);
       
   106 	flib_netconn_onRunGame(conn, NULL, NULL);
       
   107 	flib_netconn_onTeamAccepted(conn, NULL, NULL);
       
   108 	flib_netconn_onHogCountChanged(conn, NULL, NULL);
       
   109 	flib_netconn_onTeamColorChanged(conn, NULL, NULL);
       
   110 	flib_netconn_onEngineMessage(conn, NULL, NULL);
       
   111 	flib_netconn_onCfgScheme(conn, NULL, NULL);
       
   112 	flib_netconn_onMapChanged(conn, NULL, NULL);
       
   113 	flib_netconn_onScriptChanged(conn, NULL, NULL);
       
   114 	flib_netconn_onWeaponsetChanged(conn, NULL, NULL);
       
   115 	flib_netconn_onAdminAccess(conn, NULL, NULL);
       
   116 	flib_netconn_onServerVar(conn, NULL, NULL);
   172 }
   117 }
   173 
   118 
   174 flib_netconn *flib_netconn_create(const char *playerName, flib_cfg_meta *metacfg, const char *host, uint16_t port) {
   119 flib_netconn *flib_netconn_create(const char *playerName, flib_cfg_meta *metacfg, const char *host, uint16_t port) {
   175 	flib_netconn *result = NULL;
   120 	flib_netconn *result = NULL;
   176 	if(!playerName || !metacfg || !host) {
   121 	if(!playerName || !metacfg || !host) {
   181 			newConn->netconnState = NETCONN_STATE_CONNECTING;
   126 			newConn->netconnState = NETCONN_STATE_CONNECTING;
   182 			newConn->isAdmin = false;
   127 			newConn->isAdmin = false;
   183 			newConn->isChief = false;
   128 			newConn->isChief = false;
   184 			newConn->metaCfg = flib_cfg_meta_retain(metacfg);
   129 			newConn->metaCfg = flib_cfg_meta_retain(metacfg);
   185 			newConn->roomList = flib_roomlist_create();
   130 			newConn->roomList = flib_roomlist_create();
       
   131 			newConn->map = flib_map_create_named("", "NoSuchMap");
   186 			newConn->running = false;
   132 			newConn->running = false;
   187 			newConn->destroyRequested = false;
   133 			newConn->destroyRequested = false;
   188 			clearCallbacks(newConn);
   134 			clearCallbacks(newConn);
   189 			newConn->netBase = flib_netbase_create(host, port);
   135 			newConn->netBase = flib_netbase_create(host, port);
   190 			newConn->playerName = flib_strdupnull(playerName);
   136 			newConn->playerName = flib_strdupnull(playerName);
   210 			conn->destroyRequested = true;
   156 			conn->destroyRequested = true;
   211 		} else {
   157 		} else {
   212 			flib_netbase_destroy(conn->netBase);
   158 			flib_netbase_destroy(conn->netBase);
   213 			flib_cfg_meta_release(conn->metaCfg);
   159 			flib_cfg_meta_release(conn->metaCfg);
   214 			flib_roomlist_destroy(conn->roomList);
   160 			flib_roomlist_destroy(conn->roomList);
       
   161 			flib_map_release(conn->map);
   215 			free(conn->playerName);
   162 			free(conn->playerName);
   216 			free(conn);
   163 			free(conn);
   217 		}
   164 		}
   218 	}
   165 	}
   219 }
   166 }
   232 	bool result = false;
   179 	bool result = false;
   233 	if(!conn) {
   180 	if(!conn) {
   234 		flib_log_e("null parameter in flib_netconn_is_chief");
   181 		flib_log_e("null parameter in flib_netconn_is_chief");
   235 	} else if(conn->netconnState == NETCONN_STATE_ROOM || conn->netconnState == NETCONN_STATE_INGAME) {
   182 	} else if(conn->netconnState == NETCONN_STATE_ROOM || conn->netconnState == NETCONN_STATE_INGAME) {
   236 		result = conn->isChief;
   183 		result = conn->isChief;
   237 	}
       
   238 	return result;
       
   239 }
       
   240 
       
   241 int flib_netconn_send_quit(flib_netconn *conn, const char *quitmsg) {
       
   242 	int result = -1;
       
   243 	if(!conn) {
       
   244 		flib_log_e("null parameter in flib_netconn_send_quit");
       
   245 	} else {
       
   246 		result = flib_netbase_sendf(conn->netBase, "%s\n%s\n\n", "QUIT", quitmsg ? quitmsg : "User quit");
       
   247 	}
       
   248 	return result;
       
   249 }
       
   250 
       
   251 int flib_netconn_send_chat(flib_netconn *conn, const char *chat) {
       
   252 	int result = -1;
       
   253 	if(!conn || !chat) {
       
   254 		flib_log_e("null parameter in flib_netconn_send_chat");
       
   255 	} else {
       
   256 		result = flib_netbase_sendf(conn->netBase, "%s\n%s\n\n", "CHAT", chat);
       
   257 	}
       
   258 	return result;
       
   259 }
       
   260 
       
   261 int flib_netconn_send_nick(flib_netconn *conn, const char *nick) {
       
   262 	int result = -1;
       
   263 	if(!conn || !nick) {
       
   264 		flib_log_e("null parameter in flib_netconn_send_nick");
       
   265 	} else {
       
   266 		char *tmpName = flib_strdupnull(nick);
       
   267 		if(tmpName) {
       
   268 			if(!flib_netbase_sendf(conn->netBase, "%s\n%s\n\n", "NICK", nick)) {
       
   269 				free(conn->playerName);
       
   270 				conn->playerName = tmpName;
       
   271 				tmpName = NULL;
       
   272 				result = 0;
       
   273 			}
       
   274 		}
       
   275 		free(tmpName);
       
   276 	}
       
   277 	return result;
       
   278 }
       
   279 
       
   280 int flib_netconn_send_password(flib_netconn *conn, const char *latin1Passwd) {
       
   281 	int result = -1;
       
   282 	if(!conn || !latin1Passwd) {
       
   283 		flib_log_e("null parameter in flib_netconn_send_password");
       
   284 	} else {
       
   285 		md5_state_t md5state;
       
   286 		uint8_t md5bytes[16];
       
   287 		char md5hex[33];
       
   288 		md5_init(&md5state);
       
   289 		md5_append(&md5state, (unsigned char*)latin1Passwd, strlen(latin1Passwd));
       
   290 		md5_finish(&md5state, md5bytes);
       
   291 		for(int i=0;i<sizeof(md5bytes); i++) {
       
   292 			// Needs to be lowercase - server checks case sensitive
       
   293 			snprintf(md5hex+i*2, 3, "%02x", (unsigned)md5bytes[i]);
       
   294 		}
       
   295 		result = flib_netbase_sendf(conn->netBase, "%s\n%s\n\n", "PASSWORD", md5hex);
       
   296 	}
   184 	}
   297 	return result;
   185 	return result;
   298 }
   186 }
   299 
   187 
   300 /*
   188 /*
   410 		conn->onNickTakenCb = callback;
   298 		conn->onNickTakenCb = callback;
   411 		conn->onNickTakenCtx = context;
   299 		conn->onNickTakenCtx = context;
   412 	}
   300 	}
   413 }
   301 }
   414 
   302 
   415 void flib_netconn_onNickAccept(flib_netconn *conn, void (*callback)(void *context, const char *nick), void* context) {
       
   416 	if(!conn) {
       
   417 		flib_log_e("null parameter in flib_netconn_onNickAccept");
       
   418 	} else {
       
   419 		conn->onNickAcceptCb = callback ? callback : &defaultCallback_str;
       
   420 		conn->onNickAcceptCtx = context;
       
   421 	}
       
   422 }
       
   423 
       
   424 void flib_netconn_onPasswordRequest(flib_netconn *conn, void (*callback)(void *context, const char *nick), void* context) {
   303 void flib_netconn_onPasswordRequest(flib_netconn *conn, void (*callback)(void *context, const char *nick), void* context) {
   425 	if(!conn) {
   304 	if(!conn) {
   426 		flib_log_e("null parameter in flib_netconn_onPasswordRequest");
   305 		flib_log_e("null parameter in flib_netconn_onPasswordRequest");
   427 	} else if(!callback) {
   306 	} else if(!callback) {
   428 		conn->onPasswordRequestCb = &defaultCallback_onPasswordRequest;
   307 		conn->onPasswordRequestCb = &defaultCallback_onPasswordRequest;
   440 		conn->onRoomChiefStatusCb = callback ? callback : &defaultCallback_bool;
   319 		conn->onRoomChiefStatusCb = callback ? callback : &defaultCallback_bool;
   441 		conn->onRoomChiefStatusCtx = context;
   320 		conn->onRoomChiefStatusCtx = context;
   442 	}
   321 	}
   443 }
   322 }
   444 
   323 
   445 void flib_netconn_onReadyStateCb(flib_netconn *conn, void (*callback)(void *context, const char *nick, bool ready), void* context) {
   324 void flib_netconn_onReadyState(flib_netconn *conn, void (*callback)(void *context, const char *nick, bool ready), void* context) {
   446 	if(!conn) {
   325 	if(!conn) {
   447 		flib_log_e("null parameter in flib_netconn_onReadyStateCb");
   326 		flib_log_e("null parameter in flib_netconn_onReadyState");
   448 	} else {
   327 	} else {
   449 		conn->onReadyStateCb = callback ? callback : &defaultCallback_str_bool;
   328 		conn->onReadyStateCb = callback ? callback : &defaultCallback_str_bool;
   450 		conn->onReadyStateCtx = context;
   329 		conn->onReadyStateCtx = context;
   451 	}
   330 	}
   452 }
   331 }
   453 
   332 
   454 void flib_netconn_onEnterRoomCb(flib_netconn *conn, void (*callback)(void *context, bool chief), void *context) {
   333 void flib_netconn_onEnterRoom(flib_netconn *conn, void (*callback)(void *context, bool chief), void *context) {
   455 	if(!conn) {
   334 	if(!conn) {
   456 		flib_log_e("null parameter in flib_netconn_onEnterRoomCb");
   335 		flib_log_e("null parameter in flib_netconn_onEnterRoom");
   457 	} else {
   336 	} else {
   458 		conn->onEnterRoomCb = callback ? callback : &defaultCallback_bool;
   337 		conn->onEnterRoomCb = callback ? callback : &defaultCallback_bool;
   459 		conn->onEnterRoomCtx = context;
   338 		conn->onEnterRoomCtx = context;
   460 	}
   339 	}
   461 }
   340 }
   462 
   341 
   463 void flib_netconn_onLeaveRoomCb(flib_netconn *conn, void (*callback)(void *context, int reason, const char *message), void *context) {
   342 void flib_netconn_onLeaveRoom(flib_netconn *conn, void (*callback)(void *context, int reason, const char *message), void *context) {
   464 	if(!conn) {
   343 	if(!conn) {
   465 		flib_log_e("null parameter in flib_netconn_onLeaveRoomCb");
   344 		flib_log_e("null parameter in flib_netconn_onLeaveRoom");
   466 	} else {
   345 	} else {
   467 		conn->onLeaveRoomCb = callback ? callback : &defaultCallback_int_str;
   346 		conn->onLeaveRoomCb = callback ? callback : &defaultCallback_int_str;
   468 		conn->onLeaveRoomCtx = context;
   347 		conn->onLeaveRoomCtx = context;
   469 	}
   348 	}
   470 }
   349 }
   471 
   350 
   472 void flib_netconn_onTeamAddCb(flib_netconn *conn, void (*callback)(void *context, flib_team *team), void *context) {
   351 void flib_netconn_onTeamAdd(flib_netconn *conn, void (*callback)(void *context, flib_team *team), void *context) {
   473 	if(!conn) {
   352 	if(!conn) {
   474 		flib_log_e("null parameter in flib_netconn_onTeamAddCb");
   353 		flib_log_e("null parameter in flib_netconn_onTeamAdd");
   475 	} else {
   354 	} else {
   476 		conn->onTeamAddCb = callback ? callback : &defaultCallback_onTeamAdd;
   355 		conn->onTeamAddCb = callback ? callback : &defaultCallback_onTeamAdd;
   477 		conn->onTeamAddCtx = context;
   356 		conn->onTeamAddCtx = context;
       
   357 	}
       
   358 }
       
   359 
       
   360 void flib_netconn_onTeamDelete(flib_netconn *conn, void (*callback)(void *context, const char *teamname), void *context) {
       
   361 	if(!conn) {
       
   362 		flib_log_e("null parameter in flib_netconn_onTeamDelete");
       
   363 	} else {
       
   364 		conn->onTeamDeleteCb = callback ? callback : &defaultCallback_str;
       
   365 		conn->onTeamDeleteCtx = context;
       
   366 	}
       
   367 }
       
   368 
       
   369 void flib_netconn_onRunGame(flib_netconn *conn, void (*callback)(void *context), void *context) {
       
   370 	if(!conn) {
       
   371 		flib_log_e("null parameter in flib_netconn_onRunGame");
       
   372 	} else {
       
   373 		conn->onRunGameCb = callback ? callback : &defaultCallback_void;
       
   374 		conn->onRunGameCtx = context;
       
   375 	}
       
   376 }
       
   377 
       
   378 void flib_netconn_onTeamAccepted(flib_netconn *conn, void (*callback)(void *context, const char *teamName), void *context) {
       
   379 	if(!conn) {
       
   380 		flib_log_e("null parameter in flib_netconn_onTeamAccepted");
       
   381 	} else {
       
   382 		conn->onTeamAcceptedCb = callback ? callback : &defaultCallback_str;
       
   383 		conn->onTeamAcceptedCtx = context;
       
   384 	}
       
   385 }
       
   386 
       
   387 void flib_netconn_onHogCountChanged(flib_netconn *conn, void (*callback)(void *context, const char *teamName, int hogs), void *context) {
       
   388 	if(!conn) {
       
   389 		flib_log_e("null parameter in flib_netconn_onHogCountChanged");
       
   390 	} else {
       
   391 		conn->onHogCountChangedCb = callback ? callback : &defaultCallback_str_int;
       
   392 		conn->onHogCountChangedCtx = context;
       
   393 	}
       
   394 }
       
   395 
       
   396 void flib_netconn_onTeamColorChanged(flib_netconn *conn, void (*callback)(void *context, const char *teamName, uint32_t colorARGB), void *context) {
       
   397 	if(!conn) {
       
   398 		flib_log_e("null parameter in flib_netconn_onTeamColorChanged");
       
   399 	} else {
       
   400 		conn->onTeamColorChangedCb = callback ? callback : &defaultCallback_onTeamColorChanged;
       
   401 		conn->onTeamColorChangedCtx = context;
       
   402 	}
       
   403 }
       
   404 
       
   405 void flib_netconn_onEngineMessage(flib_netconn *conn, void (*callback)(void *context, const char *message, int size), void *context) {
       
   406 	if(!conn) {
       
   407 		flib_log_e("null parameter in flib_netconn_onEngineMessage");
       
   408 	} else {
       
   409 		conn->onEngineMessageCb = callback ? callback : &defaultCallback_str_int;
       
   410 		conn->onEngineMessageCtx = context;
       
   411 	}
       
   412 }
       
   413 
       
   414 void flib_netconn_onCfgScheme(flib_netconn *conn, void (*callback)(void *context, flib_cfg *scheme), void *context) {
       
   415 	if(!conn) {
       
   416 		flib_log_e("null parameter in flib_netconn_onCfgScheme");
       
   417 	} else {
       
   418 		conn->onCfgSchemeCb = callback ? callback : &defaultCallback_onCfgScheme;
       
   419 		conn->onCfgSchemeCtx = context;
       
   420 	}
       
   421 }
       
   422 
       
   423 void flib_netconn_onMapChanged(flib_netconn *conn, void (*callback)(void *context, const flib_map *map, int changetype), void *context) {
       
   424 	if(!conn) {
       
   425 		flib_log_e("null parameter in flib_netconn_onMapChanged");
       
   426 	} else {
       
   427 		conn->onMapChangedCb = callback ? callback : &defaultCallback_onMapChanged;
       
   428 		conn->onMapChangedCtx = context;
       
   429 	}
       
   430 }
       
   431 
       
   432 void flib_netconn_onScriptChanged(flib_netconn *conn, void (*callback)(void *context, const char *script), void *context) {
       
   433 	if(!conn) {
       
   434 		flib_log_e("null parameter in flib_netconn_onScriptChanged");
       
   435 	} else {
       
   436 		conn->onScriptChangedCb = callback ? callback : &defaultCallback_str;
       
   437 		conn->onScriptChangedCtx = context;
       
   438 	}
       
   439 }
       
   440 
       
   441 void flib_netconn_onWeaponsetChanged(flib_netconn *conn, void (*callback)(void *context, flib_weaponset *weaponset), void *context) {
       
   442 	if(!conn) {
       
   443 		flib_log_e("null parameter in flib_netconn_onWeaponsetChanged");
       
   444 	} else {
       
   445 		conn->onWeaponsetChangedCb = callback ? callback : &defaultCallback_onWeaponsetChanged;
       
   446 		conn->onWeaponsetChangedCtx = context;
       
   447 	}
       
   448 }
       
   449 
       
   450 void flib_netconn_onAdminAccess(flib_netconn *conn, void (*callback)(void *context), void *context) {
       
   451 	if(!conn) {
       
   452 		flib_log_e("null parameter in flib_netconn_onAdminAccess");
       
   453 	} else {
       
   454 		conn->onAdminAccessCb = callback ? callback : &defaultCallback_void;
       
   455 		conn->onAdminAccessCtx = context;
       
   456 	}
       
   457 }
       
   458 
       
   459 void flib_netconn_onServerVar(flib_netconn *conn, void (*callback)(void *context, const char *name, const char *value), void *context) {
       
   460 	if(!conn) {
       
   461 		flib_log_e("null parameter in flib_netconn_onServerVar");
       
   462 	} else {
       
   463 		conn->onServerVarCb = callback ? callback : &defaultCallback_str_str;
       
   464 		conn->onServerVarCtx = context;
       
   465 	}
       
   466 }
       
   467 
       
   468 void leaveRoom(flib_netconn *conn) {
       
   469 	conn->netconnState = NETCONN_STATE_LOBBY;
       
   470 	conn->isChief = false;
       
   471 	flib_map *map = flib_map_create_named("", "NoSuchMap");
       
   472 	if(map) {
       
   473 		flib_map_release(conn->map);
       
   474 		conn->map = map;
       
   475 	} else {
       
   476 		flib_log_e("Error resetting netconn.map");
   478 	}
   477 	}
   479 }
   478 }
   480 
   479 
   481 static void flib_netconn_wrappedtick(flib_netconn *conn) {
   480 static void flib_netconn_wrappedtick(flib_netconn *conn) {
   482 	flib_netmsg *netmsg;
   481 	flib_netmsg *netmsg;
   501 
   500 
   502 	    if (!strcmp(cmd, "NICK") && netmsg->partCount>=2) {
   501 	    if (!strcmp(cmd, "NICK") && netmsg->partCount>=2) {
   503 	    	if(netmsg->partCount<2) {
   502 	    	if(netmsg->partCount<2) {
   504 	    		flib_log_w("Net: Malformed NICK message");
   503 	    		flib_log_w("Net: Malformed NICK message");
   505 	    	} else {
   504 	    	} else {
   506 				free(conn->playerName);
   505 	    		char *nick = flib_strdupnull(netmsg->parts[1]);
   507 				conn->playerName = flib_strdupnull(netmsg->parts[1]);
   506 	    		if(nick) {
   508 				if(!conn->playerName) {
   507 					free(conn->playerName);
       
   508 					conn->playerName = nick;
       
   509 	    		} else {
   509 					conn->netconnState = NETCONN_STATE_DISCONNECTED;
   510 					conn->netconnState = NETCONN_STATE_DISCONNECTED;
   510 					conn->onDisconnectedCb(conn->onDisconnectedCtx, NETCONN_DISCONNECT_INTERNAL_ERROR, "Out of memory");
   511 					conn->onDisconnectedCb(conn->onDisconnectedCtx, NETCONN_DISCONNECT_INTERNAL_ERROR, "Out of memory");
   511 					exit = true;
   512 					exit = true;
   512 				} else {
       
   513 					conn->onNickAcceptCb(conn->onNickAcceptCtx, conn->playerName);
       
   514 				}
   513 				}
   515 	    	}
   514 	    	}
   516 	    } else if (!strcmp(cmd, "PROTO")) {
   515 	    } else if (!strcmp(cmd, "PROTO")) {
   517 	        // The server just echoes this back apparently
   516 	        // The server just echoes this back apparently
   518 		} else if (!strcmp(cmd, "ERROR")) {
   517 		} else if (!strcmp(cmd, "ERROR")) {
   581 	        		conn->onMessageCb(conn->onMessageCtx, NETCONN_MSG_TYPE_PLAYERINFO, joined);
   580 	        		conn->onMessageCb(conn->onMessageCtx, NETCONN_MSG_TYPE_PLAYERINFO, joined);
   582 	        	}
   581 	        	}
   583 	        	free(joined);
   582 	        	free(joined);
   584 	        }
   583 	        }
   585 	    } else if(!strcmp(cmd, "SERVER_VARS")) {
   584 	    } else if(!strcmp(cmd, "SERVER_VARS")) {
   586 	    	// TODO
   585 	    	for(int offset=1; offset+2<netmsg->partCount; offset+=2) {
   587 //	        QStringList tmp = lst;
   586 	    		conn->onServerVarCb(conn->onServerVarCtx, netmsg->parts[offset], netmsg->parts[offset+1]);
   588 //	        tmp.removeFirst();
   587 	    	}
   589 //	        while (tmp.size() >= 2)
       
   590 //	        {
       
   591 //	            if(tmp[0] == "MOTD_NEW") emit serverMessageNew(tmp[1]);
       
   592 //	            else if(tmp[0] == "MOTD_OLD") emit serverMessageOld(tmp[1]);
       
   593 //	            else if(tmp[0] == "LATEST_PROTO") emit latestProtocolVar(tmp[1].toInt());
       
   594 //
       
   595 //	            tmp.removeFirst();
       
   596 //	            tmp.removeFirst();
       
   597 //	        }
       
   598 	    } else if (!strcmp(cmd, "CLIENT_FLAGS")) {
   588 	    } else if (!strcmp(cmd, "CLIENT_FLAGS")) {
   599 	        if(netmsg->partCount < 3 || strlen(netmsg->parts[1]) < 2) {
   589 	        if(netmsg->partCount < 3 || strlen(netmsg->parts[1]) < 2) {
   600 	        	flib_log_w("Net: Malformed CLIENT_FLAGS message");
   590 	        	flib_log_w("Net: Malformed CLIENT_FLAGS message");
   601 	        } else {
   591 	        } else {
   602 				const char *flags = netmsg->parts[1];
   592 				const char *flags = netmsg->parts[1];
   604 
   594 
   605 				for(int i=1; flags[i]; i++) {
   595 				for(int i=1; flags[i]; i++) {
   606 					switch(flags[i]) {
   596 					switch(flags[i]) {
   607 					case 'r':
   597 					case 'r':
   608 						for(int j = 2; j < netmsg->partCount; ++j) {
   598 						for(int j = 2; j < netmsg->partCount; ++j) {
   609 							if (!strcmp(conn->playerName, netmsg->parts[i])) {
       
   610 								// TODO what is the reason behind this (copied from QtFrontend)?
       
   611 								if (conn->isChief && !setFlag) {
       
   612 									flib_netbase_sendf(conn->netBase, "%s\n\n", "TOGGLE_READY");
       
   613 								}
       
   614 							}
       
   615 							conn->onReadyStateCb(conn->onReadyStateCtx, netmsg->parts[i], setFlag);
   599 							conn->onReadyStateCb(conn->onReadyStateCtx, netmsg->parts[i], setFlag);
   616 						}
   600 						}
   617 						break;
   601 						break;
   618 					default:
   602 					default:
   619 						flib_log_w("Net: Unknown flag %c in CLIENT_FLAGS message", flags[i]);
   603 						flib_log_w("Net: Unknown flag %c in CLIENT_FLAGS message", flags[i]);
   629 	        	if(!team) {
   613 	        	if(!team) {
   630 					conn->netconnState = NETCONN_STATE_DISCONNECTED;
   614 					conn->netconnState = NETCONN_STATE_DISCONNECTED;
   631 					conn->onDisconnectedCb(conn->onDisconnectedCtx, NETCONN_DISCONNECT_INTERNAL_ERROR, "Internal error");
   615 					conn->onDisconnectedCb(conn->onDisconnectedCtx, NETCONN_DISCONNECT_INTERNAL_ERROR, "Internal error");
   632 					exit = true;
   616 					exit = true;
   633 	        	} else {
   617 	        	} else {
       
   618 	        		team->remoteDriven = true;
   634 	        		conn->onTeamAddCb(conn->onTeamAddCtx, team);
   619 	        		conn->onTeamAddCb(conn->onTeamAddCtx, team);
   635 	        	}
   620 	        	}
       
   621 	        	flib_team_release(team);
   636 	        }
   622 	        }
   637 	    } else if (!strcmp(cmd, "REMOVE_TEAM")) {
   623 	    } else if (!strcmp(cmd, "REMOVE_TEAM")) {
   638 	        if(netmsg->partCount != 2) {
   624 	        if(netmsg->partCount != 2) {
   639 	            flib_log_w("Net: Bad REMOVETEAM message");
   625 	            flib_log_w("Net: Bad REMOVETEAM message");
   640 	        } else {
   626 	        } else {
   641 	        	// TODO
   627 	        	conn->onTeamDeleteCb(conn->onTeamDeleteCtx, netmsg->parts[1]);
   642 	        	// emit RemoveNetTeam(HWTeam(lst[1]));
       
   643 	        }
   628 	        }
   644 	    } else if(!strcmp(cmd, "ROOMABANDONED")) {
   629 	    } else if(!strcmp(cmd, "ROOMABANDONED")) {
   645 	        conn->netconnState = NETCONN_STATE_LOBBY;
   630 	    	leaveRoom(conn);
   646 	        conn->onLeaveRoomCb(conn->onLeaveRoomCtx, NETCONN_ROOMLEAVE_ABANDONED, "Room destroyed");
   631 	        conn->onLeaveRoomCb(conn->onLeaveRoomCtx, NETCONN_ROOMLEAVE_ABANDONED, "Room destroyed");
   647 	    } else if(!strcmp(cmd, "KICKED")) {
   632 	    } else if(!strcmp(cmd, "KICKED")) {
   648 	    	conn->netconnState = NETCONN_STATE_LOBBY;
   633 	    	leaveRoom(conn);
   649 	    	conn->onLeaveRoomCb(conn->onLeaveRoomCtx, NETCONN_ROOMLEAVE_KICKED, "You got kicked");
   634 	    	conn->onLeaveRoomCb(conn->onLeaveRoomCtx, NETCONN_ROOMLEAVE_KICKED, "You got kicked");
   650 	    } else if(!strcmp(cmd, "JOINED")) {
   635 	    } else if(!strcmp(cmd, "JOINED")) {
   651 	        if(netmsg->partCount < 2) {
   636 	        if(netmsg->partCount < 2) {
   652 	            flib_log_w("Net: Bad JOINED message");
   637 	            flib_log_w("Net: Bad JOINED message");
   653 	        } else {
   638 	        } else {
   716 	        } else {
   701 	        } else {
   717 	        	conn->onLobbyLeaveCb(conn->onLobbyLeaveCtx, netmsg->parts[1], netmsg->partCount>2 ? netmsg->parts[2] : NULL);
   702 	        	conn->onLobbyLeaveCb(conn->onLobbyLeaveCtx, netmsg->parts[1], netmsg->partCount>2 ? netmsg->parts[2] : NULL);
   718 	        }
   703 	        }
   719 	    } else if (!strcmp(cmd, "RUN_GAME")) {
   704 	    } else if (!strcmp(cmd, "RUN_GAME")) {
   720 	        conn->netconnState = NETCONN_STATE_INGAME;
   705 	        conn->netconnState = NETCONN_STATE_INGAME;
   721 	        // TODO
   706 	        conn->onRunGameCb(conn->onRunGameCtx);
   722 	        // emit AskForRunGame();
       
   723 	    } else if (!strcmp(cmd, "ASKPASSWORD")) {
   707 	    } else if (!strcmp(cmd, "ASKPASSWORD")) {
   724 	    	conn->onPasswordRequestCb(conn->onPasswordRequestCtx, conn->playerName);
   708 	    	conn->onPasswordRequestCb(conn->onPasswordRequestCtx, conn->playerName);
   725 	    } else if (!strcmp(cmd, "NOTICE")) {
   709 	    } else if (!strcmp(cmd, "NOTICE")) {
   726 	        if(netmsg->partCount < 2) {
   710 	        if(netmsg->partCount < 2) {
   727 	            flib_log_w("Net: Bad NOTICE message");
   711 	            flib_log_w("Net: Bad NOTICE message");
   738 	        }
   722 	        }
   739 	    } else if (!strcmp(cmd, "TEAM_ACCEPTED")) {
   723 	    } else if (!strcmp(cmd, "TEAM_ACCEPTED")) {
   740 	        if (netmsg->partCount != 2) {
   724 	        if (netmsg->partCount != 2) {
   741 	            flib_log_w("Net: Bad TEAM_ACCEPTED message");
   725 	            flib_log_w("Net: Bad TEAM_ACCEPTED message");
   742 	        } else {
   726 	        } else {
   743 	        	// TODO
   727 	        	conn->onTeamAcceptedCb(conn->onTeamAcceptedCtx, netmsg->parts[1]);
   744 	        	// emit TeamAccepted(lst[1]);
       
   745 	        }
   728 	        }
   746 	    } else if (!strcmp(cmd, "CFG")) {
   729 	    } else if (!strcmp(cmd, "CFG")) {
   747 	        if(netmsg->partCount < 3) {
   730 	        if(netmsg->partCount < 3) {
   748 	            flib_log_w("Net: Bad CFG message");
   731 	            flib_log_w("Net: Bad CFG message");
   749 	        } else {
   732 	        } else {
   750 	        	// TODO
   733 	        	const char *subcmd = netmsg->parts[1];
   751 //				QStringList tmp = lst;
   734 				if(!strcmp(subcmd, "SCHEME") && netmsg->partCount == conn->metaCfg->modCount + conn->metaCfg->settingCount + 3) {
   752 //				tmp.removeFirst();
   735 					flib_cfg *cfg = flib_netmsg_to_cfg(conn->metaCfg, netmsg->parts+2);
   753 //				tmp.removeFirst();
   736 					if(cfg) {
   754 //				if (lst[1] == "SCHEME")
   737 						conn->onCfgSchemeCb(conn->onCfgSchemeCtx, cfg);
   755 //					emit netSchemeConfig(tmp);
   738 					} else {
   756 //				else
   739 						flib_log_e("Error processing CFG SCHEME message");
   757 //					emit paramChanged(lst[1], tmp);
   740 					}
       
   741 					flib_cfg_release(cfg);
       
   742 				} else if(!strcmp(subcmd, "FULLMAPCONFIG") && netmsg->partCount == 7) {
       
   743 					flib_map *map = flib_netmsg_to_map(netmsg->parts+2);
       
   744 					if(map) {
       
   745 						flib_map_release(conn->map);
       
   746 						conn->map = map;
       
   747 						conn->onMapChangedCb(conn->onMapChangedCtx, conn->map, NETCONN_MAPCHANGE_FULL);
       
   748 					} else {
       
   749 						flib_log_e("Error processing CFG FULLMAPCONFIG message");
       
   750 					}
       
   751 				} else if(!strcmp(subcmd, "MAP") && netmsg->partCount == 3) {
       
   752 					char *mapname = flib_strdupnull(netmsg->parts[2]);
       
   753 					if(mapname) {
       
   754 						free(conn->map->name);
       
   755 						conn->map->name = mapname;
       
   756 						conn->onMapChangedCb(conn->onMapChangedCtx, conn->map, NETCONN_MAPCHANGE_MAP);
       
   757 					} else {
       
   758 						flib_log_e("Error processing CFG MAP message");
       
   759 					}
       
   760 				} else if(!strcmp(subcmd, "THEME") && netmsg->partCount == 3) {
       
   761 					char *themename = flib_strdupnull(netmsg->parts[2]);
       
   762 					if(themename) {
       
   763 						free(conn->map->theme);
       
   764 						conn->map->theme = themename;
       
   765 						conn->onMapChangedCb(conn->onMapChangedCtx, conn->map, NETCONN_MAPCHANGE_THEME);
       
   766 					} else {
       
   767 						flib_log_e("Error processing CFG THEME message");
       
   768 					}
       
   769 				} else if(!strcmp(subcmd, "SEED") && netmsg->partCount == 3) {
       
   770 					char *seed = flib_strdupnull(netmsg->parts[2]);
       
   771 					if(seed) {
       
   772 						free(conn->map->seed);
       
   773 						conn->map->seed = seed;
       
   774 						conn->onMapChangedCb(conn->onMapChangedCtx, conn->map, NETCONN_MAPCHANGE_SEED);
       
   775 					} else {
       
   776 						flib_log_e("Error processing CFG SEED message");
       
   777 					}
       
   778 				} else if(!strcmp(subcmd, "TEMPLATE") && netmsg->partCount == 3) {
       
   779 					conn->map->templateFilter = atoi(netmsg->parts[2]);
       
   780 					conn->onMapChangedCb(conn->onMapChangedCtx, conn->map, NETCONN_MAPCHANGE_TEMPLATE);
       
   781 				} else if(!strcmp(subcmd, "MAPGEN") && netmsg->partCount == 3) {
       
   782 					conn->map->mapgen = atoi(netmsg->parts[2]);
       
   783 					conn->onMapChangedCb(conn->onMapChangedCtx, conn->map, NETCONN_MAPCHANGE_MAPGEN);
       
   784 				} else if(!strcmp(subcmd, "MAZE_SIZE") && netmsg->partCount == 3) {
       
   785 					conn->map->mazeSize = atoi(netmsg->parts[2]);
       
   786 					conn->onMapChangedCb(conn->onMapChangedCtx, conn->map, NETCONN_MAPCHANGE_MAZE_SIZE);
       
   787 				} else if(!strcmp(subcmd, "DRAWNMAP") && netmsg->partCount == 3) {
       
   788 					size_t drawnMapSize = 0;
       
   789 					uint8_t *drawnMapData = flib_netmsg_to_drawnmapdata(&drawnMapSize, netmsg->parts[2]);
       
   790 					if(drawnMapData) {
       
   791 						free(conn->map->drawData);
       
   792 						conn->map->drawData = drawnMapData;
       
   793 						conn->map->drawDataSize = drawnMapSize;
       
   794 						conn->onMapChangedCb(conn->onMapChangedCtx, conn->map, NETCONN_MAPCHANGE_DRAWNMAP);
       
   795 					} else {
       
   796 						flib_log_e("Error processing CFG DRAWNMAP message");
       
   797 					}
       
   798 				} else if(!strcmp(subcmd, "SCRIPT") && netmsg->partCount == 3) {
       
   799 					conn->onScriptChangedCb(conn->onScriptChangedCtx, netmsg->parts[2]);
       
   800 				} else if(!strcmp(subcmd, "AMMO") && netmsg->partCount == 4) {
       
   801 					flib_weaponset *weapons = flib_weaponset_from_ammostring(netmsg->parts[2], netmsg->parts[3]);
       
   802 					if(weapons) {
       
   803 						conn->onWeaponsetChangedCb(conn->onWeaponsetChangedCtx, weapons);
       
   804 					} else {
       
   805 						flib_log_e("Error processing CFG AMMO message");
       
   806 					}
       
   807 					flib_weaponset_release(weapons);
       
   808 				} else {
       
   809 					flib_log_w("Net: Unknown or malformed CFG subcommand: %s", subcmd);
       
   810 				}
   758 	        }
   811 	        }
   759 	    } else if (!strcmp(cmd, "HH_NUM")) {
   812 	    } else if (!strcmp(cmd, "HH_NUM")) {
   760 	        if (netmsg->partCount != 3) {
   813 	        if (netmsg->partCount != 3) {
   761 	            flib_log_w("Net: Bad TEAM_ACCEPTED message");
   814 	            flib_log_w("Net: Bad HH_NUM message");
   762 	        } else {
   815 	        } else {
   763 	        	// TODO
   816 	        	int hogs = atoi(netmsg->parts[2]);
   764 //				HWTeam tmptm(lst[1]);
   817 	        	if(hogs<=0 || hogs>HEDGEHOGS_PER_TEAM) {
   765 //				tmptm.setNumHedgehogs(lst[2].toUInt());
   818 	        		flib_log_w("Net: Bad HH_NUM message: %s hogs", netmsg->parts[2]);
   766 //				emit hhnumChanged(tmptm);
   819 	        	} else {
       
   820 	        		conn->onHogCountChangedCb(conn->onHogCountChangedCtx, netmsg->parts[1], hogs);
       
   821 	        	}
   767 	        }
   822 	        }
   768 	    } else if (!strcmp(cmd, "TEAM_COLOR")) {
   823 	    } else if (!strcmp(cmd, "TEAM_COLOR")) {
   769 	        if (netmsg->partCount != 3) {
   824 	        if (netmsg->partCount != 3) {
   770 	            flib_log_w("Net: Bad TEAM_COLOR message");
   825 	            flib_log_w("Net: Bad TEAM_COLOR message");
   771 	        } else {
   826 	        } else {
   772 	        	// TODO
   827 	        	long color;
   773 //				HWTeam tmptm(lst[1]);
   828 	        	if(sscanf(netmsg->parts[2], "#%lx", &color)) {
   774 //				tmptm.setColor(lst[2].toInt());
   829 	        		conn->onTeamColorChangedCb(conn->onTeamColorChangedCtx, netmsg->parts[1], (uint32_t)color);
   775 //				emit teamColorChanged(tmptm);
   830 	        	} else {
       
   831 	        		flib_log_w("Net: Bad TEAM_COLOR message: Color %s", netmsg->parts[2]);
       
   832 	        	}
   776 	        }
   833 	        }
   777 	    } else if (!strcmp(cmd, "EM")) {
   834 	    } else if (!strcmp(cmd, "EM")) {
   778 	        if(netmsg->partCount < 2) {
   835 	        if(netmsg->partCount < 2) {
   779 	            flib_log_w("Net: Bad EM message");
   836 	            flib_log_w("Net: Bad EM message");
   780 	        } else {
   837 	        } else {
   781 	        	// TODO
   838 	        	for(int i = 1; i < netmsg->partCount; ++i) {
   782 //				for(int i = 1; i < netmsg->partCount; ++i) {
   839 					char *out = NULL;
   783 //					QByteArray em = QByteArray::fromBase64(lst[i].toAscii());
   840 					size_t outlen;
   784 //					emit FromNet(em);
   841 					bool ok = base64_decode_alloc(netmsg->parts[i], strlen(netmsg->parts[i]), &out, &outlen);
   785 //				}
   842 					if(ok && outlen) {
       
   843 						conn->onEngineMessageCb(conn->onEngineMessageCtx, out, outlen);
       
   844 					} else {
       
   845 						flib_log_e("Net: Malformed engine message: %s", netmsg->parts[i]);
       
   846 					}
       
   847 					free(out);
       
   848 	        	}
   786 	        }
   849 	        }
   787 	    } else if (!strcmp(cmd, "BYE")) {
   850 	    } else if (!strcmp(cmd, "BYE")) {
   788 	        if (netmsg->partCount < 2) {
   851 	        if (netmsg->partCount < 2) {
   789 	            flib_log_w("Net: Bad BYE message");
   852 	            flib_log_w("Net: Bad BYE message");
   790 	        } else {
   853 	        } else {
   795 					conn->onDisconnectedCb(conn->onDisconnectedCtx, NETCONN_DISCONNECT_NORMAL, netmsg->parts[1]);
   858 					conn->onDisconnectedCb(conn->onDisconnectedCtx, NETCONN_DISCONNECT_NORMAL, netmsg->parts[1]);
   796 				}
   859 				}
   797 				exit = true;
   860 				exit = true;
   798 	        }
   861 	        }
   799 	    } else if (!strcmp(cmd, "ADMIN_ACCESS")) {
   862 	    } else if (!strcmp(cmd, "ADMIN_ACCESS")) {
   800 	    	// TODO callback?
   863 	    	conn->onAdminAccessCb(conn->onAdminAccessCtx);
   801 	    	conn->isAdmin = true;
   864 	    	conn->isAdmin = true;
   802 	    } else if (!strcmp(cmd, "ROOM_CONTROL_ACCESS")) {
   865 	    } else if (!strcmp(cmd, "ROOM_CONTROL_ACCESS")) {
   803 	        if (netmsg->partCount < 2) {
   866 	        if (netmsg->partCount < 2) {
   804 	            flib_log_w("Net: Bad ROOM_CONTROL_ACCESS message");
   867 	            flib_log_w("Net: Bad ROOM_CONTROL_ACCESS message");
   805 	        } else {
   868 	        } else {