project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/GameConnection.java
changeset 7582 714310efad8f
parent 7558 983ff426f91e
child 7584 7831c84cc644
equal deleted inserted replaced
7580:c92596feac0d 7582:714310efad8f
       
     1 /*
       
     2  * Hedgewars, a free turn based strategy game
       
     3  * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
       
     4  *
       
     5  * This program is free software; you can redistribute it and/or
       
     6  * modify it under the terms of the GNU General Public License
       
     7  * as published by the Free Software Foundation; either version 2
       
     8  * of the License, or (at your option) any later version.
       
     9  *
       
    10  * This program is distributed in the hope that it will be useful,
       
    11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
       
    13  * GNU General Public License for more details.
       
    14  *
       
    15  * You should have received a copy of the GNU General Public License
       
    16  * along with this program; if not, write to the Free Software
       
    17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
       
    18  */
       
    19 
     1 package org.hedgewars.hedgeroid;
    20 package org.hedgewars.hedgeroid;
       
    21 
       
    22 import java.net.ConnectException;
     2 
    23 
     3 import org.hedgewars.hedgeroid.Datastructures.GameConfig;
    24 import org.hedgewars.hedgeroid.Datastructures.GameConfig;
     4 import org.hedgewars.hedgeroid.frontlib.Flib;
    25 import org.hedgewars.hedgeroid.frontlib.Flib;
     5 import org.hedgewars.hedgeroid.frontlib.Frontlib;
    26 import org.hedgewars.hedgeroid.frontlib.Frontlib;
     6 import org.hedgewars.hedgeroid.frontlib.Frontlib.BytesCallback;
    27 import org.hedgewars.hedgeroid.frontlib.Frontlib.BytesCallback;
    21 import android.util.Log;
    42 import android.util.Log;
    22 
    43 
    23 import com.sun.jna.Memory;
    44 import com.sun.jna.Memory;
    24 import com.sun.jna.Pointer;
    45 import com.sun.jna.Pointer;
    25 
    46 
       
    47 /**
       
    48  * This class handles both talking to the engine (IPC) for running a game, and
       
    49  * coordinating with the netconn if it is a netgame, using the frontlib for the
       
    50  * actual IPC networking communication.
       
    51  * 
       
    52  * After creating the GameConnection object, it will communicate with the engine
       
    53  * on its own thread. It shuts itself down as soon as the connection to the engine
       
    54  * is lost.
       
    55  */
    26 public final class GameConnection {
    56 public final class GameConnection {
    27 	private static final Handler mainHandler = new Handler(Looper.getMainLooper());
    57 	private static final Handler mainHandler = new Handler(Looper.getMainLooper());
    28 	
    58 	
       
    59 	public final int port;
    29 	private final HandlerThread thread;
    60 	private final HandlerThread thread;
    30 	private final Handler handler;
    61 	private final Handler handler;
    31 	private final TickHandler tickHandler;
    62 	private TickHandler tickHandler;
    32 	private final Netplay netplay; // ==null if not a netgame
    63 	private final Netplay netplay; // ==null if not a netgame
    33 	private GameconnPtr conn;
    64 	private GameconnPtr conn;
    34 
    65 	
    35 	/**
    66 	private GameConnection(GameconnPtr conn, Netplay netplay) {
    36 	 * The actual connection has to be set up on a separate thread because networking
    67 		this.conn = conn;
    37 	 * is not allowed on the UI thread, so the port can't be queried immediately after
    68 		this.port = Flib.INSTANCE.flib_gameconn_getport(conn);
    38 	 * creating the GameConnection object. Instead, one of these interface methods is
       
    39 	 * called once we know which port we are listening on (or once we fail to set this up).
       
    40 	 * Methods will be called on the UI thread.
       
    41 	 */
       
    42 	public static interface Listener {
       
    43 		/**
       
    44 		 * We are listening for the engine at $port, go start the engine.
       
    45 		 */
       
    46 		void gameConnectionReady(int port);
       
    47 		
       
    48 		/**
       
    49 		 * The connection has stopped, either because the game has ended or was interrupted,
       
    50 		 * or maybe we failed to create the connection at all (in that case gameConnectionReady wasn't called).
       
    51 		 */
       
    52 		void gameConnectionDisconnected(int reason);
       
    53 	}
       
    54 	
       
    55 	private GameConnection(Netplay netplay) {
       
    56 		this.netplay = netplay;
    69 		this.netplay = netplay;
    57 		thread = new HandlerThread("IPCThread");
    70 		this.thread = new HandlerThread("IPCThread");
    58 		thread.start();
    71 		thread.start();
    59 		handler = new Handler(thread.getLooper());
    72 		this.handler = new Handler(thread.getLooper());
    60 		tickHandler = new TickHandler(thread.getLooper(), 50, new Runnable() {
    73 	}
    61 			public void run() {
    74 	
    62 				if(conn != null) {
    75 	private void setupConnection() {
    63 					Flib.INSTANCE.flib_gameconn_tick(conn);
    76 		tickHandler = new TickHandler(thread.getLooper(), 50, tickCb);
    64 				}
       
    65 			}
       
    66 		});
       
    67 		tickHandler.start();
    77 		tickHandler.start();
    68 	}
    78 		
    69 	
    79 		if(netplay != null) {
    70 	public static GameConnection forNetgame(final GameConfig config, Netplay netplay, final Listener listener) {
       
    71 		final GameConnection result = new GameConnection(netplay);
       
    72 		final String playerName = netplay.getPlayerName();
       
    73 		result.handler.post(new Runnable() {
       
    74 			public void run() {
       
    75 				GameconnPtr conn = Flib.INSTANCE.flib_gameconn_create(playerName, GameSetupPtr.createJavaOwned(config), true);
       
    76 				result.setupConnection(conn, true, listener);
       
    77 			}
       
    78 		});
       
    79 		return result;
       
    80 	}
       
    81 	
       
    82 	public static GameConnection forLocalGame(final GameConfig config, final Listener listener) {
       
    83 		final GameConnection result = new GameConnection(null);
       
    84 		result.handler.post(new Runnable() {
       
    85 			public void run() {
       
    86 				GameconnPtr conn = Flib.INSTANCE.flib_gameconn_create("Player", GameSetupPtr.createJavaOwned(config), false);
       
    87 				result.setupConnection(conn, false, listener);
       
    88 			}
       
    89 		});
       
    90 		return result;
       
    91 	}
       
    92 	
       
    93 	// runs on the IPCThread
       
    94 	private void setupConnection(GameconnPtr conn, final boolean netgame, final Listener listener) {
       
    95 		if(conn == null) {
       
    96 			mainHandler.post(new Runnable() {
       
    97 				public void run() { listener.gameConnectionDisconnected(Frontlib.GAME_END_ERROR); }
       
    98 			});
       
    99 			shutdown();
       
   100 		} else {
       
   101 			this.conn = conn;
       
   102 			final int port = Flib.INSTANCE.flib_gameconn_getport(conn);
       
   103 			mainHandler.post(new Runnable() {
    80 			mainHandler.post(new Runnable() {
   104 				public void run() { 
    81 				public void run() { 
   105 					listener.gameConnectionReady(port);
    82 					netplay.registerGameMessageListener(gameMessageListener);
   106 					if(netgame) {
    83 				}
   107 						netplay.registerGameMessageListener(gameMessageListener);
    84 			});
   108 					}
    85 			Flib.INSTANCE.flib_gameconn_onChat(conn, chatCb, null);
   109 				}
    86 			Flib.INSTANCE.flib_gameconn_onEngineMessage(conn, engineMessageCb, null);
   110 			});
    87 		}
   111 			Flib.INSTANCE.flib_gameconn_onConnect(conn, connectCb, null);
    88 		Flib.INSTANCE.flib_gameconn_onConnect(conn, connectCb, null);
   112 			Flib.INSTANCE.flib_gameconn_onDisconnect(conn, disconnectCb, null);
    89 		Flib.INSTANCE.flib_gameconn_onDisconnect(conn, disconnectCb, null);
   113 			Flib.INSTANCE.flib_gameconn_onErrorMessage(conn, errorMessageCb, null);
    90 		Flib.INSTANCE.flib_gameconn_onErrorMessage(conn, errorMessageCb, null);
   114 			if(netgame) {
    91 	}
   115 				Flib.INSTANCE.flib_gameconn_onChat(conn, chatCb, null);
    92 	
   116 				Flib.INSTANCE.flib_gameconn_onEngineMessage(conn, engineMessageCb, null);
    93 	/**
   117 			}
    94 	 * Start a new IPC server to communicate with the engine.
   118 		}
    95 	 * Performs networking operations, don't run on the UI thread.
   119 	}
    96 	 * @throws ConnectException if we can't set up the IPC server
       
    97 	 */
       
    98 	public static GameConnection forNetgame(final GameConfig config, Netplay netplay) throws ConnectException {
       
    99 		final String playerName = netplay.getPlayerName();
       
   100 		GameconnPtr conn = Flib.INSTANCE.flib_gameconn_create(playerName, GameSetupPtr.createJavaOwned(config), true);
       
   101 		if(conn == null) {
       
   102 			throw new ConnectException();
       
   103 		}
       
   104 		GameConnection result = new GameConnection(conn, netplay);
       
   105 		result.setupConnection();
       
   106 		return result;
       
   107 	}
       
   108 	
       
   109 	/**
       
   110 	 * Start a new IPC server to communicate with the engine.
       
   111 	 * Performs networking operations, don't run on the UI thread.
       
   112 	 * @throws ConnectException if we can't set up the IPC server
       
   113 	 */
       
   114 	public static GameConnection forLocalGame(final GameConfig config) throws ConnectException {
       
   115 		GameconnPtr conn = Flib.INSTANCE.flib_gameconn_create("Player", GameSetupPtr.createJavaOwned(config), false);
       
   116 		if(conn == null) {
       
   117 			throw new ConnectException();
       
   118 		}
       
   119 		GameConnection result = new GameConnection(conn, null);
       
   120 		result.setupConnection();
       
   121 		return result;
       
   122 	}
       
   123 	
       
   124 	private final Runnable tickCb = new Runnable() {
       
   125 		public void run() {
       
   126 			Flib.INSTANCE.flib_gameconn_tick(conn);
       
   127 		}
       
   128 	};
   120 	
   129 	
   121 	// runs on the IPCThread
   130 	// runs on the IPCThread
   122 	private void shutdown() {
   131 	private void shutdown() {
   123 		tickHandler.stop();
   132 		tickHandler.stop();
   124 		thread.quit();
   133 		thread.quit();
   125 		Flib.INSTANCE.flib_gameconn_destroy(conn);
   134 		Flib.INSTANCE.flib_gameconn_destroy(conn);
       
   135 		conn = null;
   126 		if(netplay != null) {
   136 		if(netplay != null) {
   127 			mainHandler.post(new Runnable() {
   137 			mainHandler.post(new Runnable() {
   128 				public void run() {
   138 				public void run() {
   129 					netplay.unregisterGameMessageListener(gameMessageListener);
   139 					netplay.unregisterGameMessageListener(gameMessageListener);
   130 				}
   140 				}
   177 	// runs on any thread
   187 	// runs on any thread
   178 	private final GameMessageListener gameMessageListener = new GameMessageListener() {
   188 	private final GameMessageListener gameMessageListener = new GameMessageListener() {
   179 		public void onNetDisconnected() {
   189 		public void onNetDisconnected() {
   180 			handler.post(new Runnable() {
   190 			handler.post(new Runnable() {
   181 				public void run() {
   191 				public void run() {
   182 					shutdown();
   192 					Flib.INSTANCE.flib_gameconn_send_quit(conn);
   183 				}
   193 				}
   184 			});
   194 			});
   185 		}
   195 		}
   186 		
   196 		
   187 		public void onMessage(final int type, final String message) {
   197 		public void onMessage(final int type, final String message) {