diff -r c92596feac0d -r 714310efad8f project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/GameConnection.java --- 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); } }); }