project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/GameConnection.java
changeset 7508 763d3961400b
child 7558 983ff426f91e
equal deleted inserted replaced
7504:ed1d52c5aa94 7508:763d3961400b
       
     1 package org.hedgewars.hedgeroid;
       
     2 
       
     3 import org.hedgewars.hedgeroid.Datastructures.GameConfig;
       
     4 import org.hedgewars.hedgeroid.frontlib.Flib;
       
     5 import org.hedgewars.hedgeroid.frontlib.Frontlib;
       
     6 import org.hedgewars.hedgeroid.frontlib.Frontlib.BytesCallback;
       
     7 import org.hedgewars.hedgeroid.frontlib.Frontlib.GameSetupPtr;
       
     8 import org.hedgewars.hedgeroid.frontlib.Frontlib.GameconnPtr;
       
     9 import org.hedgewars.hedgeroid.frontlib.Frontlib.IntCallback;
       
    10 import org.hedgewars.hedgeroid.frontlib.Frontlib.StrBoolCallback;
       
    11 import org.hedgewars.hedgeroid.frontlib.Frontlib.StrCallback;
       
    12 import org.hedgewars.hedgeroid.frontlib.Frontlib.VoidCallback;
       
    13 import org.hedgewars.hedgeroid.netplay.GameMessageListener;
       
    14 import org.hedgewars.hedgeroid.netplay.Netplay;
       
    15 import org.hedgewars.hedgeroid.util.TickHandler;
       
    16 
       
    17 import android.os.Handler;
       
    18 import android.os.HandlerThread;
       
    19 import android.os.Looper;
       
    20 import android.util.Log;
       
    21 
       
    22 import com.sun.jna.Memory;
       
    23 import com.sun.jna.NativeLong;
       
    24 import com.sun.jna.Pointer;
       
    25 
       
    26 public final class GameConnection {
       
    27 	private static final Handler mainHandler = new Handler(Looper.getMainLooper());
       
    28 	
       
    29 	private final HandlerThread thread;
       
    30 	private final Handler handler;
       
    31 	private final TickHandler tickHandler;
       
    32 	private final Netplay netplay; // ==null if not a netgame
       
    33 	private GameconnPtr conn;
       
    34 
       
    35 	/**
       
    36 	 * The actual connection has to be set up on a separate thread because networking
       
    37 	 * is not allowed on the UI thread, so the port can't be queried immediately after
       
    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;
       
    57 		thread = new HandlerThread("IPCThread");
       
    58 		thread.start();
       
    59 		handler = new Handler(thread.getLooper());
       
    60 		tickHandler = new TickHandler(thread.getLooper(), 50, new Runnable() {
       
    61 			public void run() {
       
    62 				if(conn != null) {
       
    63 					Flib.INSTANCE.flib_gameconn_tick(conn);
       
    64 				}
       
    65 			}
       
    66 		});
       
    67 		tickHandler.start();
       
    68 	}
       
    69 	
       
    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() {
       
   104 				public void run() { 
       
   105 					listener.gameConnectionReady(port);
       
   106 					if(netgame) {
       
   107 						netplay.registerGameMessageListener(gameMessageListener);
       
   108 					}
       
   109 				}
       
   110 			});
       
   111 			Flib.INSTANCE.flib_gameconn_onConnect(conn, connectCb, null);
       
   112 			Flib.INSTANCE.flib_gameconn_onDisconnect(conn, disconnectCb, null);
       
   113 			Flib.INSTANCE.flib_gameconn_onErrorMessage(conn, errorMessageCb, null);
       
   114 			if(netgame) {
       
   115 				Flib.INSTANCE.flib_gameconn_onChat(conn, chatCb, null);
       
   116 				Flib.INSTANCE.flib_gameconn_onEngineMessage(conn, engineMessageCb, null);
       
   117 			}
       
   118 		}
       
   119 	}
       
   120 	
       
   121 	// runs on the IPCThread
       
   122 	private void shutdown() {
       
   123 		tickHandler.stop();
       
   124 		thread.quit();
       
   125 		Flib.INSTANCE.flib_gameconn_destroy(conn);
       
   126 		if(netplay != null) {
       
   127 			mainHandler.post(new Runnable() {
       
   128 				public void run() {
       
   129 					netplay.unregisterGameMessageListener(gameMessageListener);
       
   130 				}
       
   131 			});
       
   132 		}
       
   133 	}
       
   134 	
       
   135 	// runs on the IPCThread
       
   136 	private final StrBoolCallback chatCb = new StrBoolCallback() {
       
   137 		public void callback(Pointer context, String message, boolean teamChat) {
       
   138 			if(teamChat) {
       
   139 				netplay.sendTeamChat(message);
       
   140 			} else {
       
   141 				netplay.sendChat(message);
       
   142 			}
       
   143 		}
       
   144 	};
       
   145 	
       
   146 	// runs on the IPCThread
       
   147 	private final VoidCallback connectCb = new VoidCallback() {
       
   148 		public void callback(Pointer context) {
       
   149 			Log.i("GameConnection", "Connected");
       
   150 		}
       
   151 	};
       
   152 	
       
   153 	// runs on the IPCThread
       
   154 	private final IntCallback disconnectCb = new IntCallback() {
       
   155 		public void callback(Pointer context, int reason) {
       
   156 			if(netplay != null) {
       
   157 				netplay.sendRoundFinished(reason==Frontlib.GAME_END_FINISHED);
       
   158 			}
       
   159 			shutdown();
       
   160 		}
       
   161 	};
       
   162 	
       
   163 	// runs on the IPCThread
       
   164 	private final BytesCallback engineMessageCb = new BytesCallback() {
       
   165 		public void callback(Pointer context, Pointer buffer, NativeLong size) {
       
   166 			netplay.sendEngineMessage(buffer.getByteArray(0, size.intValue()));
       
   167 		}
       
   168 	};
       
   169 	
       
   170 	// runs on the IPCThread
       
   171 	private final StrCallback errorMessageCb = new StrCallback() {
       
   172 		public void callback(Pointer context, String message) {
       
   173 			Log.e("GameConnection", message);
       
   174 		}
       
   175 	};
       
   176 	
       
   177 	// runs on any thread
       
   178 	private final GameMessageListener gameMessageListener = new GameMessageListener() {
       
   179 		public void onNetDisconnected() {
       
   180 			handler.post(new Runnable() {
       
   181 				public void run() {
       
   182 					shutdown();
       
   183 				}
       
   184 			});
       
   185 		}
       
   186 		
       
   187 		public void onMessage(final int type, final String message) {
       
   188 			handler.post(new Runnable() {
       
   189 				public void run() {
       
   190 					Flib.INSTANCE.flib_gameconn_send_textmsg(conn, type, message);
       
   191 				}
       
   192 			});
       
   193 		}
       
   194 		
       
   195 		public void onEngineMessage(final byte[] em) {
       
   196 			handler.post(new Runnable() {
       
   197 				public void run() {
       
   198 					Memory mem = new Memory(em.length);
       
   199 					mem.write(0, em, 0, em.length);
       
   200 					Flib.INSTANCE.flib_gameconn_send_enginemsg(conn, mem, new NativeLong(em.length));
       
   201 				}
       
   202 			});
       
   203 			
       
   204 		}
       
   205 		
       
   206 		public void onChatMessage(final String nick, final String message) {
       
   207 			handler.post(new Runnable() {
       
   208 				public void run() {
       
   209 					Flib.INSTANCE.flib_gameconn_send_chatmsg(conn, nick, message);
       
   210 				}
       
   211 			});
       
   212 		}
       
   213 	};
       
   214 }