project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/GameConnection.java
changeset 7582 714310efad8f
parent 7558 983ff426f91e
child 7584 7831c84cc644
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/GameConnection.java	Mon Aug 20 20:16:37 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/GameConnection.java	Mon Aug 20 20:19:35 2012 +0200
@@ -1,5 +1,26 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
 package org.hedgewars.hedgeroid;
 
+import java.net.ConnectException;
+
 import org.hedgewars.hedgeroid.Datastructures.GameConfig;
 import org.hedgewars.hedgeroid.frontlib.Flib;
 import org.hedgewars.hedgeroid.frontlib.Frontlib;
@@ -23,106 +44,95 @@
 import com.sun.jna.Memory;
 import com.sun.jna.Pointer;
 
+/**
+ * This class handles both talking to the engine (IPC) for running a game, and
+ * coordinating with the netconn if it is a netgame, using the frontlib for the
+ * actual IPC networking communication.
+ * 
+ * After creating the GameConnection object, it will communicate with the engine
+ * on its own thread. It shuts itself down as soon as the connection to the engine
+ * is lost.
+ */
 public final class GameConnection {
 	private static final Handler mainHandler = new Handler(Looper.getMainLooper());
 	
+	public final int port;
 	private final HandlerThread thread;
 	private final Handler handler;
-	private final TickHandler tickHandler;
+	private TickHandler tickHandler;
 	private final Netplay netplay; // ==null if not a netgame
 	private GameconnPtr conn;
-
-	/**
-	 * The actual connection has to be set up on a separate thread because networking
-	 * is not allowed on the UI thread, so the port can't be queried immediately after
-	 * creating the GameConnection object. Instead, one of these interface methods is
-	 * called once we know which port we are listening on (or once we fail to set this up).
-	 * Methods will be called on the UI thread.
-	 */
-	public static interface Listener {
-		/**
-		 * We are listening for the engine at $port, go start the engine.
-		 */
-		void gameConnectionReady(int port);
-		
-		/**
-		 * The connection has stopped, either because the game has ended or was interrupted,
-		 * or maybe we failed to create the connection at all (in that case gameConnectionReady wasn't called).
-		 */
-		void gameConnectionDisconnected(int reason);
+	
+	private GameConnection(GameconnPtr conn, Netplay netplay) {
+		this.conn = conn;
+		this.port = Flib.INSTANCE.flib_gameconn_getport(conn);
+		this.netplay = netplay;
+		this.thread = new HandlerThread("IPCThread");
+		thread.start();
+		this.handler = new Handler(thread.getLooper());
 	}
 	
-	private GameConnection(Netplay netplay) {
-		this.netplay = netplay;
-		thread = new HandlerThread("IPCThread");
-		thread.start();
-		handler = new Handler(thread.getLooper());
-		tickHandler = new TickHandler(thread.getLooper(), 50, new Runnable() {
-			public void run() {
-				if(conn != null) {
-					Flib.INSTANCE.flib_gameconn_tick(conn);
+	private void setupConnection() {
+		tickHandler = new TickHandler(thread.getLooper(), 50, tickCb);
+		tickHandler.start();
+		
+		if(netplay != null) {
+			mainHandler.post(new Runnable() {
+				public void run() { 
+					netplay.registerGameMessageListener(gameMessageListener);
 				}
-			}
-		});
-		tickHandler.start();
+			});
+			Flib.INSTANCE.flib_gameconn_onChat(conn, chatCb, null);
+			Flib.INSTANCE.flib_gameconn_onEngineMessage(conn, engineMessageCb, null);
+		}
+		Flib.INSTANCE.flib_gameconn_onConnect(conn, connectCb, null);
+		Flib.INSTANCE.flib_gameconn_onDisconnect(conn, disconnectCb, null);
+		Flib.INSTANCE.flib_gameconn_onErrorMessage(conn, errorMessageCb, null);
 	}
 	
-	public static GameConnection forNetgame(final GameConfig config, Netplay netplay, final Listener listener) {
-		final GameConnection result = new GameConnection(netplay);
+	/**
+	 * Start a new IPC server to communicate with the engine.
+	 * Performs networking operations, don't run on the UI thread.
+	 * @throws ConnectException if we can't set up the IPC server
+	 */
+	public static GameConnection forNetgame(final GameConfig config, Netplay netplay) throws ConnectException {
 		final String playerName = netplay.getPlayerName();
-		result.handler.post(new Runnable() {
-			public void run() {
-				GameconnPtr conn = Flib.INSTANCE.flib_gameconn_create(playerName, GameSetupPtr.createJavaOwned(config), true);
-				result.setupConnection(conn, true, listener);
-			}
-		});
+		GameconnPtr conn = Flib.INSTANCE.flib_gameconn_create(playerName, GameSetupPtr.createJavaOwned(config), true);
+		if(conn == null) {
+			throw new ConnectException();
+		}
+		GameConnection result = new GameConnection(conn, netplay);
+		result.setupConnection();
 		return result;
 	}
 	
-	public static GameConnection forLocalGame(final GameConfig config, final Listener listener) {
-		final GameConnection result = new GameConnection(null);
-		result.handler.post(new Runnable() {
-			public void run() {
-				GameconnPtr conn = Flib.INSTANCE.flib_gameconn_create("Player", GameSetupPtr.createJavaOwned(config), false);
-				result.setupConnection(conn, false, listener);
-			}
-		});
+	/**
+	 * Start a new IPC server to communicate with the engine.
+	 * Performs networking operations, don't run on the UI thread.
+	 * @throws ConnectException if we can't set up the IPC server
+	 */
+	public static GameConnection forLocalGame(final GameConfig config) throws ConnectException {
+		GameconnPtr conn = Flib.INSTANCE.flib_gameconn_create("Player", GameSetupPtr.createJavaOwned(config), false);
+		if(conn == null) {
+			throw new ConnectException();
+		}
+		GameConnection result = new GameConnection(conn, null);
+		result.setupConnection();
 		return result;
 	}
 	
-	// runs on the IPCThread
-	private void setupConnection(GameconnPtr conn, final boolean netgame, final Listener listener) {
-		if(conn == null) {
-			mainHandler.post(new Runnable() {
-				public void run() { listener.gameConnectionDisconnected(Frontlib.GAME_END_ERROR); }
-			});
-			shutdown();
-		} else {
-			this.conn = conn;
-			final int port = Flib.INSTANCE.flib_gameconn_getport(conn);
-			mainHandler.post(new Runnable() {
-				public void run() { 
-					listener.gameConnectionReady(port);
-					if(netgame) {
-						netplay.registerGameMessageListener(gameMessageListener);
-					}
-				}
-			});
-			Flib.INSTANCE.flib_gameconn_onConnect(conn, connectCb, null);
-			Flib.INSTANCE.flib_gameconn_onDisconnect(conn, disconnectCb, null);
-			Flib.INSTANCE.flib_gameconn_onErrorMessage(conn, errorMessageCb, null);
-			if(netgame) {
-				Flib.INSTANCE.flib_gameconn_onChat(conn, chatCb, null);
-				Flib.INSTANCE.flib_gameconn_onEngineMessage(conn, engineMessageCb, null);
-			}
+	private final Runnable tickCb = new Runnable() {
+		public void run() {
+			Flib.INSTANCE.flib_gameconn_tick(conn);
 		}
-	}
+	};
 	
 	// runs on the IPCThread
 	private void shutdown() {
 		tickHandler.stop();
 		thread.quit();
 		Flib.INSTANCE.flib_gameconn_destroy(conn);
+		conn = null;
 		if(netplay != null) {
 			mainHandler.post(new Runnable() {
 				public void run() {
@@ -179,7 +189,7 @@
 		public void onNetDisconnected() {
 			handler.post(new Runnable() {
 				public void run() {
-					shutdown();
+					Flib.INSTANCE.flib_gameconn_send_quit(conn);
 				}
 			});
 		}