project_files/frontlib/net/netconn.c
changeset 7234 613998625a3c
child 7269 5b0aeef8ba2a
equal deleted inserted replaced
7230:240620f46dd7 7234:613998625a3c
       
     1 /*
       
     2  * Hedgewars, a free turn based strategy game
       
     3  * Copyright (c) 2006-2008 Igor Ulyanov <iulyanov@gmail.com>
       
     4  * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
       
     5  * Copyright (c) 2012 Simeon Maxein <smaxein@googlemail.com>
       
     6  *
       
     7  * This program is free software; you can redistribute it and/or modify
       
     8  * it under the terms of the GNU General Public License as published by
       
     9  * the Free Software Foundation; version 2 of the License
       
    10  *
       
    11  * This program is distributed in the hope that it will be useful,
       
    12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
       
    14  * GNU General Public License for more details.
       
    15  *
       
    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
       
    18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
       
    19  */
       
    20 
       
    21 #include "netconn.h"
       
    22 #include "netbase.h"
       
    23 #include "netprotocol.h"
       
    24 #include "../util/logging.h"
       
    25 #include "../util/util.h"
       
    26 
       
    27 #include <stdbool.h>
       
    28 #include <stdlib.h>
       
    29 #include <string.h>
       
    30 #include <errno.h>
       
    31 
       
    32 struct _flib_netconn {
       
    33 	flib_netbase *netBase;
       
    34 	char *playerName;
       
    35 
       
    36 	int netconnState;	// One of the NETCONN_STATE constants
       
    37 
       
    38 	void (*onErrorCb)(void* context, int errorCode, const char *errorMsg);
       
    39 	void *onErrorCtx;
       
    40 
       
    41 	void (*onConnectedCb)(void *context, const char *serverMessage);
       
    42 	void *onConnectedCtx;
       
    43 
       
    44 	bool running;
       
    45 	bool destroyRequested;
       
    46 };
       
    47 
       
    48 static void defaultCallback_onError(void* context, int errorCode, const char *errormsg) {}
       
    49 static void defaultCallback_onConnected(void *context, const char *serverMessage) {}
       
    50 
       
    51 static void clearCallbacks(flib_netconn *conn) {
       
    52 	conn->onErrorCb = &defaultCallback_onError;
       
    53 	conn->onConnectedCb = &defaultCallback_onConnected;
       
    54 }
       
    55 
       
    56 
       
    57 flib_netconn *flib_netconn_create(const char *playerName, const char *host, uint16_t port) {
       
    58 	flib_netconn *result = NULL;
       
    59 	flib_netconn *newConn = flib_calloc(1, sizeof(flib_netconn));
       
    60 	if(newConn) {
       
    61 		newConn->netconnState = NETCONN_STATE_AWAIT_CONNECTED;
       
    62 		newConn->running = false;
       
    63 		newConn->destroyRequested = false;
       
    64 		clearCallbacks(newConn);
       
    65 		newConn->netBase = flib_netbase_create(host, port);
       
    66 		newConn->playerName = flib_strdupnull(playerName);
       
    67 		if(newConn->netBase && newConn->playerName) {
       
    68 			result = newConn;
       
    69 			newConn = NULL;
       
    70 		}
       
    71 	}
       
    72 	flib_netconn_destroy(newConn);
       
    73 	return result;
       
    74 }
       
    75 
       
    76 void flib_netconn_destroy(flib_netconn *conn) {
       
    77 	if(conn) {
       
    78 		if(conn->running) {
       
    79 			/*
       
    80 			 * The function was called from a callback, so the tick function is still running
       
    81 			 * and we delay the actual destruction. We ensure no further callbacks will be
       
    82 			 * sent to prevent surprises.
       
    83 			 */
       
    84 			clearCallbacks(conn);
       
    85 			conn->destroyRequested = true;
       
    86 		} else {
       
    87 			flib_netbase_destroy(conn->netBase);
       
    88 			free(conn->playerName);
       
    89 			free(conn);
       
    90 		}
       
    91 	}
       
    92 }
       
    93 
       
    94 void flib_netconn_onError(flib_netconn *conn, void (*callback)(void *context, int errorCode, const char *errorMsg), void* context) {
       
    95 	if(!conn) {
       
    96 		flib_log_e("null parameter in flib_netconn_onError");
       
    97 	} else {
       
    98 		conn->onErrorCb = callback ? callback : &defaultCallback_onError;
       
    99 		conn->onErrorCtx = context;
       
   100 	}
       
   101 }
       
   102 
       
   103 void flib_netconn_onConnected(flib_netconn *conn, void (*callback)(void *context, const char *serverMessage), void* context) {
       
   104 	if(!conn) {
       
   105 		flib_log_e("null parameter in flib_netconn_onConnected");
       
   106 	} else {
       
   107 		conn->onConnectedCb = callback ? callback : &defaultCallback_onConnected;
       
   108 		conn->onConnectedCtx = context;
       
   109 	}
       
   110 }
       
   111 
       
   112 static void flib_netconn_wrappedtick(flib_netconn *conn) {
       
   113 	flib_netmsg *netmsg;
       
   114 	flib_netbase *net = conn->netBase;
       
   115 	bool exit = false;
       
   116 
       
   117 	while(!exit && !conn->destroyRequested && (netmsg=flib_netbase_recv_message(conn->netBase))) {
       
   118 		if(netmsg->partCount==0) {
       
   119 			flib_log_w("Empty server message");
       
   120 			continue;
       
   121 		}
       
   122 
       
   123 		const char *cmd = netmsg->parts[0];
       
   124 
       
   125 	    if (!strcmp(cmd, "NICK") && netmsg->partCount>=2) {
       
   126 	    	free(conn->playerName);
       
   127 	    	conn->playerName = flib_strdupnull(netmsg->parts[1]);
       
   128 	    	if(!conn->playerName) {
       
   129 	    		// TODO handle error
       
   130 	    	}
       
   131 	    	// TODO callback?
       
   132 	    } else if (!strcmp(cmd, "PROTO")) {
       
   133 	        // The server just echoes this back apparently
       
   134 		} else if (!strcmp(cmd, "ERROR")) {
       
   135 			// TODO: onErrorMessage?
       
   136 			if (netmsg->partCount == 2) {
       
   137 				conn->onErrorCb(conn->onErrorCtx, NETCONN_ERROR_FROM_SERVER, netmsg->parts[1]);
       
   138 			} else {
       
   139 				conn->onErrorCb(conn->onErrorCtx, NETCONN_ERROR_FROM_SERVER, "Unknown Error");
       
   140 			}
       
   141 		} else if(!strcmp(cmd, "WARNING")) {
       
   142 			// TODO: onWarnMessage?
       
   143 			if (netmsg->partCount == 2) {
       
   144 				conn->onErrorCb(conn->onErrorCtx, NETCONN_ERROR_FROM_SERVER, netmsg->parts[1]);
       
   145 			} else {
       
   146 				conn->onErrorCb(conn->onErrorCtx, NETCONN_ERROR_FROM_SERVER, "Unknown Warning");
       
   147 			}
       
   148 	    } else if(!strcmp(cmd, "CONNECTED")) {
       
   149 			if(netmsg->partCount<3 || atol(netmsg->parts[2])<MIN_SERVER_VERSION) {
       
   150 				flib_log_w("Server too old");
       
   151 				flib_netbase_sendf(net, "%s\n%s\n\n", "QUIT", "Server too old");
       
   152 				// TODO actually disconnect?
       
   153 				conn->netconnState = NETCONN_STATE_DISCONNECTED;
       
   154 				conn->onErrorCb(conn->onErrorCtx, NETCONN_ERROR_SERVER_TOO_OLD, "Server too old");
       
   155 				exit = true;
       
   156 			} else {
       
   157 				flib_netbase_sendf(net, "%s\n%s\n\n", "NICK", conn->playerName);
       
   158 				flib_netbase_sendf(net, "%s\n%i\n\n", "PROTO", (int)PROTOCOL_VERSION);
       
   159 				conn->netconnState = NETCONN_STATE_LOBBY;
       
   160 			}
       
   161 		} else if(!strcmp(cmd, "PING")) {
       
   162 	        if (netmsg->partCount > 1) {
       
   163 	        	flib_netbase_sendf(net, "%s\n%s\n\n", "PONG", netmsg->parts[1]);
       
   164 	        } else {
       
   165 	        	flib_netbase_sendf(net, "%s\n\n", "PONG");
       
   166 	        }
       
   167 	    } else if(!strcmp(cmd, "ROOMS")) {
       
   168 	        if(netmsg->partCount % 8 != 1) {
       
   169 	        	flib_log_w("Net: Malformed ROOMS message");
       
   170 	        } else {
       
   171 	        	// TODO
       
   172 				//QStringList tmp = lst;
       
   173 				//tmp.removeFirst();
       
   174 				//m_roomsListModel->setRoomsList(tmp);
       
   175 	        }
       
   176 	    } else if (!strcmp(cmd, "SERVER_MESSAGE")) {
       
   177 	        if(netmsg->partCount < 2) {
       
   178 	        	flib_log_w("Net: Empty SERVERMESSAGE message");
       
   179 	        } else {
       
   180 	        	// TODO
       
   181 	        	// emit serverMessage(lst[1]);
       
   182 	        }
       
   183 	    } else if (!strcmp(cmd, "CHAT")) {
       
   184 	        if(netmsg->partCount < 3) {
       
   185 	        	flib_log_w("Net: Empty CHAT message");
       
   186 	        } else {
       
   187 	        	// TODO
       
   188 				// if (netClientState == InLobby)
       
   189 				// 	emit chatStringLobby(lst[1], HWProto::formatChatMsgForFrontend(lst[2]));
       
   190 				// else
       
   191 				//	emit chatStringFromNet(HWProto::formatChatMsg(lst[1], lst[2]));
       
   192 	        }
       
   193 	    } else if (!strcmp(cmd, "INFO")) {
       
   194 	        if(netmsg->partCount < 5) {
       
   195 	        	flib_log_w("Net: Malformed INFO message");
       
   196 	        } else {
       
   197 	        	// TODO
       
   198 //				QStringList tmp = lst;
       
   199 //				tmp.removeFirst();
       
   200 //				if (netClientState == InLobby)
       
   201 //					emit chatStringLobby(tmp.join("\n").prepend('\x01'));
       
   202 //				else
       
   203 //					emit chatStringFromNet(tmp.join("\n").prepend('\x01'));
       
   204 	        }
       
   205 	    } else if(!strcmp(cmd, "SERVER_VARS")) {
       
   206 	    	// TODO
       
   207 //	        QStringList tmp = lst;
       
   208 //	        tmp.removeFirst();
       
   209 //	        while (tmp.size() >= 2)
       
   210 //	        {
       
   211 //	            if(tmp[0] == "MOTD_NEW") emit serverMessageNew(tmp[1]);
       
   212 //	            else if(tmp[0] == "MOTD_OLD") emit serverMessageOld(tmp[1]);
       
   213 //	            else if(tmp[0] == "LATEST_PROTO") emit latestProtocolVar(tmp[1].toInt());
       
   214 //
       
   215 //	            tmp.removeFirst();
       
   216 //	            tmp.removeFirst();
       
   217 //	        }
       
   218 	    } else if (!strcmp(cmd, "CLIENT_FLAGS")) {
       
   219 	        if(netmsg->partCount < 3 || strlen(netmsg->parts[1]) < 2) {
       
   220 	        	flib_log_w("Net: Malformed CLIENT_FLAGS message");
       
   221 	        } else {
       
   222 				const char *flags = netmsg->parts[1];
       
   223 				bool setFlag = flags[0] == '+';
       
   224 
       
   225 				for(int i=1; flags[i]; i++) {
       
   226 					switch(flags[i]) {
       
   227 					case 'r':
       
   228 						for(int j = 2; j < netmsg->partCount; ++j) {
       
   229 							if (!strcmp(conn->playerName, netmsg->parts[i])) {
       
   230 								// TODO
       
   231 								// if (isChief && !setFlag) ToggleReady();
       
   232 								// else emit setMyReadyStatus(setFlag);
       
   233 							}
       
   234 							// TODO
       
   235 							// emit setReadyStatus(lst[i], setFlag);
       
   236 						}
       
   237 						break;
       
   238 					default:
       
   239 						flib_log_w("Net: Unknown flag %c in CLIENT_FLAGS message", flags[i]);
       
   240 						break;
       
   241 					}
       
   242 				}
       
   243 	        }
       
   244 	    } else if (!strcmp(cmd, "ADD_TEAM")) {
       
   245 	        if(netmsg->partCount != 24) {
       
   246 	            flib_log_w("Net: Bad ADD_TEAM message");
       
   247 	        } else {
       
   248 	        	// TODO
       
   249 //				QStringList tmp = lst;
       
   250 //				tmp.removeFirst();
       
   251 //				emit AddNetTeam(tmp);
       
   252 	        }
       
   253 	    } else if (!strcmp(cmd, "REMOVE_TEAM")) {
       
   254 	        if(netmsg->partCount != 2) {
       
   255 	            flib_log_w("Net: Bad REMOVETEAM message");
       
   256 	        } else {
       
   257 	        	// TODO
       
   258 	        	// emit RemoveNetTeam(HWTeam(lst[1]));
       
   259 	        }
       
   260 	    } else if(!strcmp(cmd, "ROOMABANDONED")) {
       
   261 	        conn->netconnState = NETCONN_STATE_LOBBY;
       
   262 //	        TODO
       
   263 //	        askRoomsList();
       
   264 //	        emit LeftRoom(tr("Room destroyed"));
       
   265 	    } else if(!strcmp(cmd, "KICKED")) {
       
   266 	    	conn->netconnState = NETCONN_STATE_LOBBY;
       
   267 //	    	TODO
       
   268 //	        askRoomsList();
       
   269 //	        emit LeftRoom(tr("You got kicked"));
       
   270 	    } else if(!strcmp(cmd, "JOINED")) {
       
   271 	        if(netmsg->partCount < 2) {
       
   272 	            flib_log_w("Net: Bad JOINED message");
       
   273 	        } else {
       
   274 				for(int i = 1; i < netmsg->partCount; ++i)
       
   275 				{
       
   276 					bool isMe = !strcmp(conn->playerName, netmsg->parts[i]);
       
   277 					if (isMe) {
       
   278 						conn->netconnState = NETCONN_STATE_ROOM;
       
   279 //						TODO
       
   280 //						emit EnteredGame();
       
   281 //						emit roomMaster(isChief);
       
   282 //						if (isChief)
       
   283 //							emit configAsked();
       
   284 					}
       
   285 
       
   286 //					TODO
       
   287 //					emit nickAdded(lst[i], isChief && !isMe));
       
   288 //					emit chatStringFromNet(tr("%1 *** %2 has joined the room").arg('\x03').arg(lst[i]));
       
   289 				}
       
   290 	        }
       
   291 	    } else if(!strcmp(cmd, "LOBBY:JOINED")) {
       
   292 	        if(netmsg->partCount < 2) {
       
   293 	            flib_log_w("Net: Bad JOINED message");
       
   294 	        } else {
       
   295 				for(int i = 1; i < netmsg->partCount; ++i)
       
   296 				{
       
   297 					bool isMe = !strcmp(conn->playerName, netmsg->parts[i]);
       
   298 					if (isMe) {
       
   299 						conn->netconnState = NETCONN_STATE_LOBBY;
       
   300 						// TODO
       
   301 //						RawSendNet(QString("LIST"));
       
   302 //						emit connected();
       
   303 					}
       
   304 					// TODO
       
   305 //					emit nickAddedLobby(lst[i], false);
       
   306 //					emit chatStringLobby(lst[i], tr("%1 *** %2 has joined").arg('\x03').arg("|nick|"));
       
   307 				}
       
   308 	        }
       
   309 	    } else if(!strcmp(cmd, "LEFT")) {
       
   310 	        if(netmsg->partCount < 2) {
       
   311 	            flib_log_w("Net: Bad LEFT message");
       
   312 	        } else {
       
   313 	        	// TODO
       
   314 //				emit nickRemoved(lst[1]);
       
   315 //				if (netmsg->partCount < 3)
       
   316 //					emit chatStringFromNet(tr("%1 *** %2 has left").arg('\x03').arg(lst[1]));
       
   317 //				else
       
   318 //					emit chatStringFromNet(tr("%1 *** %2 has left (%3)").arg('\x03').arg(lst[1], lst[2]));
       
   319 	        }
       
   320 	    } else if(!strcmp(cmd, "ROOM") && netmsg->partCount >= 2) {
       
   321 	    	const char *subcmd = netmsg->parts[1];
       
   322 	    	if(!strcmp(subcmd, "ADD") && netmsg->partCount == 10) {
       
   323 	    		// TODO
       
   324 //				QStringList tmp = lst;
       
   325 //				tmp.removeFirst();
       
   326 //				tmp.removeFirst();
       
   327 //
       
   328 //				m_roomsListModel->addRoom(tmp);
       
   329 			} else if(!strcmp(subcmd, "UPD") && netmsg->partCount == 11) {
       
   330 				// TODO
       
   331 //				QStringList tmp = lst;
       
   332 //				tmp.removeFirst();
       
   333 //				tmp.removeFirst();
       
   334 //
       
   335 //				QString roomName = tmp.takeFirst();
       
   336 //				m_roomsListModel->updateRoom(roomName, tmp);
       
   337 			} else if(!strcmp(subcmd, "DEL") && netmsg->partCount == 3) {
       
   338 				// TODO
       
   339 				// m_roomsListModel->removeRoom(lst[2]);
       
   340 			} else {
       
   341 				flib_log_w("Net: Unknown or malformed ROOM subcommand: %s", subcmd);
       
   342 			}
       
   343 	    } else if(!strcmp(cmd, "LOBBY:LEFT")) {
       
   344 	        if(netmsg->partCount < 2) {
       
   345 	            flib_log_w("Net: Bad LOBBY:LEFT message");
       
   346 	        } else {
       
   347 	        	// TODO
       
   348 //				emit nickRemovedLobby(lst[1]);
       
   349 //				if (netmsg->partCount < 3)
       
   350 //					emit chatStringLobby(tr("%1 *** %2 has left").arg('\x03').arg(lst[1]));
       
   351 //				else
       
   352 //					emit chatStringLobby(lst[1], tr("%1 *** %2 has left (%3)").arg('\x03').arg("|nick|", lst[2]));
       
   353 	        }
       
   354 	    } else if (!strcmp(cmd, "RUN_GAME")) {
       
   355 	        conn->netconnState = NETCONN_STATE_INGAME;
       
   356 	        // TODO
       
   357 	        // emit AskForRunGame();
       
   358 	    } else if (!strcmp(cmd, "ASKPASSWORD")) {
       
   359 	    	// TODO
       
   360 	        // emit AskForPassword(mynick);
       
   361 	    } else if (!strcmp(cmd, "NOTICE")) {
       
   362 	        if(netmsg->partCount < 2) {
       
   363 	            flib_log_w("Net: Bad NOTICE message");
       
   364 	        } else {
       
   365 				errno = 0;
       
   366 				long n = strtol(netmsg->parts[1], NULL, 10);
       
   367 				if(errno) {
       
   368 					flib_log_w("Net: Bad NOTICE message");
       
   369 				} else {
       
   370 					// TODO
       
   371 					// handleNotice(n);
       
   372 				}
       
   373 	        }
       
   374 	    } else if (!strcmp(cmd, "TEAM_ACCEPTED")) {
       
   375 	        if (netmsg->partCount != 2) {
       
   376 	            flib_log_w("Net: Bad TEAM_ACCEPTED message");
       
   377 	        } else {
       
   378 	        	// TODO
       
   379 	        	// emit TeamAccepted(lst[1]);
       
   380 	        }
       
   381 	    } else if (!strcmp(cmd, "CFG")) {
       
   382 	        if(netmsg->partCount < 3) {
       
   383 	            flib_log_w("Net: Bad CFG message");
       
   384 	        } else {
       
   385 	        	// TODO
       
   386 //				QStringList tmp = lst;
       
   387 //				tmp.removeFirst();
       
   388 //				tmp.removeFirst();
       
   389 //				if (lst[1] == "SCHEME")
       
   390 //					emit netSchemeConfig(tmp);
       
   391 //				else
       
   392 //					emit paramChanged(lst[1], tmp);
       
   393 	        }
       
   394 	    } else if (!strcmp(cmd, "HH_NUM")) {
       
   395 	        if (netmsg->partCount != 3) {
       
   396 	            flib_log_w("Net: Bad TEAM_ACCEPTED message");
       
   397 	        } else {
       
   398 	        	// TODO
       
   399 //				HWTeam tmptm(lst[1]);
       
   400 //				tmptm.setNumHedgehogs(lst[2].toUInt());
       
   401 //				emit hhnumChanged(tmptm);
       
   402 	        }
       
   403 	    } else if (!strcmp(cmd, "TEAM_COLOR")) {
       
   404 	        if (netmsg->partCount != 3) {
       
   405 	            flib_log_w("Net: Bad TEAM_COLOR message");
       
   406 	        } else {
       
   407 	        	// TODO
       
   408 //				HWTeam tmptm(lst[1]);
       
   409 //				tmptm.setColor(lst[2].toInt());
       
   410 //				emit teamColorChanged(tmptm);
       
   411 	        }
       
   412 	    } else if (!strcmp(cmd, "EM")) {
       
   413 	        if(netmsg->partCount < 2) {
       
   414 	            flib_log_w("Net: Bad EM message");
       
   415 	        } else {
       
   416 	        	// TODO
       
   417 //				for(int i = 1; i < netmsg->partCount; ++i) {
       
   418 //					QByteArray em = QByteArray::fromBase64(lst[i].toAscii());
       
   419 //					emit FromNet(em);
       
   420 //				}
       
   421 	        }
       
   422 	    } else if (!strcmp(cmd, "BYE")) {
       
   423 	        if (netmsg->partCount < 2) {
       
   424 	            flib_log_w("Net: Bad BYE message");
       
   425 	        } else {
       
   426 				if (!strcmp(netmsg->parts[1], "Authentication failed")) {
       
   427 					// TODO
       
   428 					//emit AuthFailed();
       
   429 				}
       
   430 				// TODO
       
   431 //				m_game_connected = false;
       
   432 //				Disconnect();
       
   433 //				emit disconnected(lst[1]);
       
   434 	        }
       
   435 	    } else if (!strcmp(cmd, "ADMIN_ACCESS")) {
       
   436 	    	// TODO
       
   437 	        // emit adminAccess(true);
       
   438 	    } else if (!strcmp(cmd, "ROOM_CONTROL_ACCESS")) {
       
   439 	        if (netmsg->partCount < 2) {
       
   440 	            flib_log_w("Net: Bad ROOM_CONTROL_ACCESS message");
       
   441 	        } else {
       
   442 	        	// TODO
       
   443 //				isChief = (lst[1] != "0");
       
   444 //				emit roomMaster(isChief);
       
   445 	        }
       
   446 	    } else {
       
   447 	    	flib_log_w("Unknown server command: %s", cmd);
       
   448 	    }
       
   449 		flib_netmsg_destroy(netmsg);
       
   450 	}
       
   451 }
       
   452 
       
   453 void flib_netconn_tick(flib_netconn *conn) {
       
   454 	if(!conn) {
       
   455 		flib_log_e("null parameter in flib_netconn_tick");
       
   456 	} else if(conn->running) {
       
   457 		flib_log_w("Call to flib_netconn_tick from a callback");
       
   458 	} else if(conn->netconnState == NETCONN_STATE_DISCONNECTED) {
       
   459 		flib_log_w("Call to flib_netconn_tick, but we are already done.");
       
   460 	} else {
       
   461 		conn->running = true;
       
   462 		flib_netconn_wrappedtick(conn);
       
   463 		conn->running = false;
       
   464 
       
   465 		if(conn->destroyRequested) {
       
   466 			flib_netconn_destroy(conn);
       
   467 		}
       
   468 	}
       
   469 }