Fix engine crashes on game end
authorMedo <smaxein@googlemail.com>
Sun, 19 Aug 2012 15:05:23 +0200
changeset 7576 65d29988fd3d
parent 7574 b9ec869e624a
child 7578 d5c880e6d772
Fix engine crashes on game end - frontlib now waits until the engine actually disconnects - engine does not write round-end stats to stdout anymore on mobile (caused crash) - Removed HWTerminate function binding for android (not threadsafe, better send "eforcequit" over IPC for the same effect)
hedgewars/hwLibrary.pas
hedgewars/uStats.pas
project_files/frontlib/ipc/gameconn.c
project_files/frontlib/ipc/gameconn.h
project_files/frontlib/ipc/ipcbase.c
project_files/frontlib/ipc/mapconn.c
--- a/hedgewars/hwLibrary.pas	Sat Aug 18 21:05:30 2012 +0200
+++ b/hedgewars/hwLibrary.pas	Sun Aug 19 15:05:23 2012 +0200
@@ -104,11 +104,6 @@
 	GenLandPreview(port);
 end;
 
-procedure JNI_HW_Terminate(env: PJNIEnv; c: JClass); cdecl;
-begin
-	HW_terminate(false);
-end;
-
 exports
     JNI_HW_versionInfoNet name Java_Prefix+'HWversionInfoNetProto', 
     JNI_HW_versionInfoVersion name Java_Prefix+'HWversionInfoVersion', 
@@ -116,7 +111,6 @@
     HW_getNumberOfweapons name Java_Prefix + 'HWgetNumberOfWeapons',
     HW_getMaxNumberOfHogs name Java_Prefix + 'HWgetMaxNumberOfHogs',
     HW_getMaxNumberOfTeams name Java_Prefix + 'HWgetMaxNumberOfTeams',
-    JNI_HW_Terminate name Java_Prefix + 'HWterminate',
     Game;
 {$ELSE}
 exports
--- a/hedgewars/uStats.pas	Sat Aug 18 21:05:30 2012 +0200
+++ b/hedgewars/uStats.pas	Sun Aug 19 15:05:23 2012 +0200
@@ -185,10 +185,12 @@
 begin
 if time > 4000 then
     begin
+	{$IFNDEF MOBILE}
     writeln(stdout, 'FLIGHT');
     writeln(stdout, Gear^.Hedgehog^.Team^.TeamName);
     writeln(stdout, inttostr(time));
     writeln(stdout, '');
+	{$ENDIF}
     end
 end;
 
@@ -290,6 +292,7 @@
 if KilledHHs > 0 then
     SendStat(siKilledHHs, IntToStr(KilledHHs));
 
+{$IFNDEF MOBILE}
 // now to console
 if winnersClan <> nil then 
     begin
@@ -301,6 +304,7 @@
     writeln(stdout, 'DRAW');
 
 writeln(stdout, '');
+{$ENDIF}
 end;
 
 procedure initModule;
--- a/project_files/frontlib/ipc/gameconn.c	Sat Aug 18 21:05:30 2012 +0200
+++ b/project_files/frontlib/ipc/gameconn.c	Sun Aug 19 15:05:23 2012 +0200
@@ -41,6 +41,7 @@
 
 	gameconn_state state;
 	bool netgame;
+	int disconnectReason;
 
 	void (*onConnectCb)(void* context);
 	void *onConnectCtx;
@@ -90,6 +91,7 @@
 			}
 			tempConn->state = AWAIT_CONNECTION;
 			tempConn->netgame = netGame;
+			tempConn->disconnectReason = GAME_END_ERROR;
 			clearCallbacks(tempConn);
 			result = tempConn;
 			tempConn = NULL;
@@ -281,15 +283,25 @@
 }
 
 int flib_gameconn_send_quit(flib_gameconn *conn) {
-	if(log_badargs_if(conn==NULL)) {
+	return flib_gameconn_send_cmd(conn, "efinish");
+}
+
+int flib_gameconn_send_cmd(flib_gameconn *conn, const char *cmdString) {
+	if(log_badargs_if2(conn==NULL, cmdString==NULL)) {
 		return -1;
 	}
-	const char *msg = "\x07""efinish";
-	if(!flib_ipcbase_send_raw(conn->ipcBase, msg, msg[0]+1)) {
-		demo_append(conn, msg, msg[0]+1);
-		return 0;
+	int result = -1;
+	uint8_t converted[256];
+	size_t msglen = strlen(cmdString);
+	if(!log_e_if(msglen>255, "Message too long: %s", cmdString)) {
+		strcpy(converted+1, cmdString);
+		converted[0] = msglen;
+		if(!flib_ipcbase_send_raw(conn->ipcBase, converted, msglen+1)) {
+			demo_append(conn, converted, msglen+1);
+			result = 0;
+		}
 	}
-	return -1;
+	return result;
 }
 
 /**
@@ -384,6 +396,7 @@
 			case 'q':	// game finished
 				{
 					int reason = msgbuffer[1]=='Q' ? GAME_END_INTERRUPTED : msgbuffer[1]=='H' ? GAME_END_HALTED : GAME_END_FINISHED;
+					conn->disconnectReason = reason;
 					bool savegame = (reason != GAME_END_FINISHED) && !conn->netgame;
 					if(conn->demoBuffer) {
 						flib_buffer demoBuffer = flib_vector_as_buffer(conn->demoBuffer);
@@ -393,8 +406,6 @@
 							return;
 						}
 					}
-					conn->state = FINISHED;
-					conn->onDisconnectCb(conn->onDisconnectCtx, reason);
 					return;
 				}
 			case 's':	// Chat message
@@ -422,7 +433,7 @@
 
 	if(flib_ipcbase_state(conn->ipcBase) == IPC_NOT_CONNECTED) {
 		conn->state = FINISHED;
-		conn->onDisconnectCb(conn->onDisconnectCtx, GAME_END_ERROR);
+		conn->onDisconnectCb(conn->onDisconnectCtx, conn->disconnectReason);
 	}
 }
 
--- a/project_files/frontlib/ipc/gameconn.h	Sat Aug 18 21:05:30 2012 +0200
+++ b/project_files/frontlib/ipc/gameconn.h	Sun Aug 19 15:05:23 2012 +0200
@@ -123,12 +123,19 @@
 int flib_gameconn_send_chatmsg(flib_gameconn *conn, const char *playername, const char *msg);
 
 /**
- * Request the engine to stop the game.
+ * Request the engine to stop the game (efinish).
  * You can use this to shut down a game early without directly killing the engine process.
  */
 int flib_gameconn_send_quit(flib_gameconn *conn);
 
 /**
+ * Send an arbitrary command to the engine, e.g. "eforcequit" to shut down the engine
+ * quickly. Commands prefixed with "e" will be processed by the engine's ProcessCommand
+ * method (with the e removed, so e.g. efinish will be parsed as finish).
+ */
+int flib_gameconn_send_cmd(flib_gameconn *conn, const char *cmdString);
+
+/**
  * Expected callback signature: void handleConnect(void *context)
  * The engine has successfully connected. You don't have to react to this in any way.
  */
--- a/project_files/frontlib/ipc/ipcbase.c	Sat Aug 18 21:05:30 2012 +0200
+++ b/project_files/frontlib/ipc/ipcbase.c	Sun Aug 19 15:05:23 2012 +0200
@@ -74,6 +74,9 @@
 	if(ipc) {
 		flib_acceptor_close(ipc->acceptor);
 		flib_socket_close(ipc->sock);
+		if(ipc->sock) {
+			flib_log_d("IPC connection closed.");
+		}
 		free(ipc);
 	}
 }
@@ -96,6 +99,7 @@
 		if(size>=0) {
 			ipc->readBufferSize += size;
 		} else {
+			flib_log_d("IPC connection lost.");
 			flib_socket_close(ipc->sock);
 			ipc->sock = NULL;
 		}
@@ -106,6 +110,35 @@
 	return ipc->readBufferSize >= ipc->readBuffer[0]+1;
 }
 
+static void logSentMsg(const uint8_t *data, size_t len) {
+	if(flib_log_isActive(FLIB_LOGLEVEL_DEBUG)) {
+		size_t msgStart = 0;
+		while(msgStart < len) {
+			uint8_t msglen = data[msgStart];
+			if(msgStart+msglen < len) {
+				flib_log_d("[IPC OUT][%03u]%*.*s",(unsigned)msglen, (unsigned)msglen, (unsigned)msglen, data+msgStart+1);
+			} else {
+				uint8_t msglen2 = len-msgStart-1;
+				flib_log_d("[IPC OUT][%03u/%03u]%*.*s",(unsigned)msglen2, (unsigned)msglen, (unsigned)msglen2, (unsigned)msglen2, data+msgStart+1);
+			}
+			msgStart += (uint8_t)data[msgStart]+1;
+		}
+	}
+}
+
+static void logRecvMsg(const uint8_t *data) {
+	if(flib_log_isActive(FLIB_LOGLEVEL_DEBUG)) {
+		uint8_t msglen = data[0];
+		flib_log_d("[IPC IN][%03u]%*.*s",(unsigned)msglen, (unsigned)msglen, (unsigned)msglen, data+1);
+	}
+}
+
+static void popFromReadBuffer(flib_ipcbase *ipc, uint8_t *outbuf, size_t size) {
+	memcpy(outbuf, ipc->readBuffer, size);
+	memmove(ipc->readBuffer, ipc->readBuffer+size, ipc->readBufferSize-size);
+	ipc->readBufferSize -= size;
+}
+
 int flib_ipcbase_recv_message(flib_ipcbase *ipc, void *data) {
 	if(log_badargs_if2(ipc==NULL, data==NULL)) {
 		return -1;
@@ -117,9 +150,8 @@
 
 	if(isMessageReady(ipc)) {
 		int msgsize = ipc->readBuffer[0]+1;
-		memcpy(data, ipc->readBuffer, msgsize);
-		memmove(ipc->readBuffer, ipc->readBuffer+msgsize, ipc->readBufferSize-msgsize);
-		ipc->readBufferSize -= msgsize;
+		popFromReadBuffer(ipc, data, msgsize);
+		logRecvMsg(data);
 		return msgsize;
 	} else if(!ipc->sock && ipc->readBufferSize>0) {
 		flib_log_w("Last message from engine data stream is incomplete (received %u of %u bytes)", (unsigned)ipc->readBufferSize, (unsigned)(ipc->readBuffer[0])+1);
@@ -138,30 +170,13 @@
 	receiveToBuffer(ipc);
 
 	if(ipc->readBufferSize >= IPCBASE_MAPMSG_BYTES) {
-		memcpy(data, ipc->readBuffer, IPCBASE_MAPMSG_BYTES);
-		memmove(ipc->readBuffer, ipc->readBuffer+IPCBASE_MAPMSG_BYTES, ipc->readBufferSize-IPCBASE_MAPMSG_BYTES);
+		popFromReadBuffer(ipc, data, IPCBASE_MAPMSG_BYTES);
 		return IPCBASE_MAPMSG_BYTES;
 	} else {
 		return -1;
 	}
 }
 
-static void logSentMsg(const uint8_t *data, size_t len) {
-	if(flib_log_isActive(FLIB_LOGLEVEL_DEBUG)) {
-		size_t msgStart = 0;
-		while(msgStart < len) {
-			uint8_t msglen = data[msgStart];
-			if(msgStart+msglen < len) {
-				flib_log_d("[IPC OUT][%03u]%*.*s",(unsigned)msglen, (unsigned)msglen, (unsigned)msglen, data+msgStart+1);
-			} else {
-				uint8_t msglen2 = len-msgStart-1;
-				flib_log_d("[IPC OUT][%03u/%03u]%*.*s",(unsigned)msglen2, (unsigned)msglen, (unsigned)msglen2, (unsigned)msglen2, data+msgStart+1);
-			}
-			msgStart += (uint8_t)data[msgStart]+1;
-		}
-	}
-}
-
 int flib_ipcbase_send_raw(flib_ipcbase *ipc, const void *data, size_t len) {
 	if(log_badargs_if2(ipc==NULL, data==NULL && len>0)
 			|| log_w_if(!ipc->sock, "flib_ipcbase_send_raw: Not connected.")) {
@@ -193,6 +208,7 @@
 	if(!log_badargs_if(ipc==NULL) && !ipc->sock && ipc->acceptor) {
 		ipc->sock = flib_socket_accept(ipc->acceptor, true);
 		if(ipc->sock) {
+			flib_log_d("IPC connection accepted.");
 			flib_acceptor_close(ipc->acceptor);
 			ipc->acceptor = NULL;
 		}
--- a/project_files/frontlib/ipc/mapconn.c	Sat Aug 18 21:05:30 2012 +0200
+++ b/project_files/frontlib/ipc/mapconn.c	Sun Aug 19 15:05:23 2012 +0200
@@ -30,6 +30,7 @@
 typedef enum {
 	AWAIT_CONNECTION,
 	AWAIT_REPLY,
+	AWAIT_CLOSE,
 	FINISHED
 } mapconn_state;
 
@@ -160,15 +161,24 @@
 
 	if(conn->progress == AWAIT_REPLY) {
 		if(flib_ipcbase_recv_map(conn->ipcBase, conn->mapBuffer) >= 0) {
-			conn->progress = FINISHED;
-			conn->onSuccessCb(conn->onSuccessCtx, conn->mapBuffer, conn->mapBuffer[IPCBASE_MAPMSG_BYTES-1]);
-			return;
+			conn->progress = AWAIT_CLOSE;
 		} else if(flib_ipcbase_state(conn->ipcBase) != IPC_CONNECTED) {
 			conn->progress = FINISHED;
 			conn->onFailureCb(conn->onSuccessCtx, "Engine connection closed unexpectedly.");
 			return;
 		}
 	}
+
+	if(conn->progress == AWAIT_CLOSE) {
+		// Just do throwaway reads so we find out when the engine disconnects
+		uint8_t buf[256];
+		flib_ipcbase_recv_message(conn->ipcBase, buf);
+		if(flib_ipcbase_state(conn->ipcBase) != IPC_CONNECTED) {
+			conn->progress = FINISHED;
+			conn->onSuccessCb(conn->onSuccessCtx, conn->mapBuffer, conn->mapBuffer[IPCBASE_MAPMSG_BYTES-1]);
+			return;
+		}
+	}
 }
 
 void flib_mapconn_tick(flib_mapconn *conn) {