project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/NetplayService.java
changeset 7355 5673e95ef647
parent 7349 12fdfd2038d4
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/NetplayService.java	Mon Jul 23 00:17:06 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/NetplayService.java	Tue Jul 24 16:57:48 2012 +0200
@@ -1,66 +1,317 @@
 package org.hedgewars.hedgeroid.netplay;
 
-import java.io.IOException;
+import java.io.File;
+import java.io.FileNotFoundException;
+
+import org.hedgewars.hedgeroid.R;
+import org.hedgewars.hedgeroid.Utils;
+import org.hedgewars.hedgeroid.netplay.JnaFrontlib.IntStrCallback;
+import org.hedgewars.hedgeroid.netplay.JnaFrontlib.MetaschemePtr;
+import org.hedgewars.hedgeroid.netplay.JnaFrontlib.NetconnPtr;
+import org.hedgewars.hedgeroid.netplay.JnaFrontlib.RoomArrayPtr;
+import org.hedgewars.hedgeroid.netplay.JnaFrontlib.RoomCallback;
+import org.hedgewars.hedgeroid.netplay.JnaFrontlib.RoomListCallback;
+import org.hedgewars.hedgeroid.netplay.JnaFrontlib.RoomPtr;
+import org.hedgewars.hedgeroid.netplay.JnaFrontlib.StrCallback;
+import org.hedgewars.hedgeroid.netplay.JnaFrontlib.StrRoomCallback;
+import org.hedgewars.hedgeroid.netplay.JnaFrontlib.StrStrCallback;
+import org.hedgewars.hedgeroid.netplay.JnaFrontlib.VoidCallback;
+
+import com.sun.jna.Pointer;
 
 import android.app.Service;
 import android.content.Intent;
 import android.os.Binder;
-import android.os.CountDownTimer;
 import android.os.IBinder;
+import android.support.v4.content.LocalBroadcastManager;
 import android.util.Log;
 
 public class NetplayService extends Service {
+	// Parameter extras for starting the service
+	public static final String EXTRA_PLAYERNAME = "playername";
+	public static final String EXTRA_PORT = "port";
+	public static final String EXTRA_HOST = "host";
+	
+	// Extras in broadcasts
+	public static final String EXTRA_MESSAGE = "message";
+	public static final String EXTRA_HAS_ERROR = "hasError";
+	
+	
+	private static final String ACTIONPREFIX = "org.hedgewars.hedgeroid.netconn.";
+	public static final String ACTION_DISCONNECTED = ACTIONPREFIX+"DISCONNECTED";
+	public static final String ACTION_CONNECTED = ACTIONPREFIX+"CONNECTED";
+	
+	private static final JnaFrontlib FLIB = Flib.INSTANCE;
+	public static final String DEFAULT_SERVER = "netserver.hedgewars.org";
+	public static final int DEFAULT_PORT = 46631;
+	
+	private static final long TICK_INTERVAL_MS_BOUND = 100;
+	private static final long TICK_INTERVAL_MS_UNBOUND = 2000;
+	
+	// null if the service is not active. Only updated from the main thread.
+	public static NetplayService instance;
+	
 	private final NetplayBinder binder = new NetplayBinder();
-	public Netconn netconn;
-	private CountDownTimer timer;
+	private TickHandler tickHandler;
+	private LocalBroadcastManager broadcastManager;
+	
+	private String playerName;
+	private NetconnPtr conn;
+	private boolean joined; // True once we have been admitted to the lobby
+	
+	public final PlayerList playerList = new PlayerList();
+	public final RoomList roomList = new RoomList();
+	public MessageLog lobbyChatlog;
+	public MessageLog roomChatlog;
 	
 	@Override
 	public IBinder onBind(Intent intent) {
+		Log.d("NetplayService", "onBind");
+		tickHandler.setInterval(TICK_INTERVAL_MS_BOUND);
 		return binder;
 	}
 	
 	@Override
+	public void onRebind(Intent intent) {
+		Log.d("NetplayService", "onRebind");
+		tickHandler.setInterval(TICK_INTERVAL_MS_BOUND);
+	}
+	
+	@Override
+	public boolean onUnbind(Intent intent) {
+		Log.d("NetplayService", "onUnbind");
+		tickHandler.setInterval(TICK_INTERVAL_MS_UNBOUND);
+		return true;
+	}
+	
+	@Override
+	public int onStartCommand(Intent intent, int flags, int startId) {
+		Log.d("NetplayService", "onStartCommand");
+		if(conn != null) {
+			Log.e("NetplayService", "Attempt to start while running");
+			return START_NOT_STICKY;
+		}
+		joined = false;
+		playerList.clear();
+		roomList.clear();
+		lobbyChatlog.clear();
+		roomChatlog.clear();
+		
+		playerName = intent.getStringExtra(EXTRA_PLAYERNAME);
+		if(playerName == null) playerName = "Player";
+		int port = intent.getIntExtra(EXTRA_PORT, DEFAULT_PORT);
+		String host = intent.getStringExtra(EXTRA_HOST);
+		if(host==null) host = DEFAULT_SERVER;
+		
+		MetaschemePtr meta = null;
+		File dataPath;
+		try {
+			dataPath = Utils.getDataPathFile(getApplicationContext());
+		} catch (FileNotFoundException e) {
+			stopWithError(getString(R.string.sdcard_not_mounted));
+			return START_NOT_STICKY;
+		}
+		String metaschemePath = new File(dataPath, "metasettings.ini").getAbsolutePath();
+		meta = FLIB.flib_metascheme_from_ini(metaschemePath);
+		if(meta == null) {
+			stopWithError(getString(R.string.error_unexpected, "Missing metasettings.ini"));
+			return START_NOT_STICKY;
+		}
+		conn = FLIB.flib_netconn_create(playerName, meta, dataPath.getAbsolutePath(), host, port);
+		if(conn == null) {
+			stopWithError(getString(R.string.error_connection_failed));
+			return START_NOT_STICKY;
+		}
+		FLIB.flib_netconn_onLobbyJoin(conn, lobbyJoinCb, null);
+		FLIB.flib_netconn_onLobbyLeave(conn, lobbyLeaveCb, null);
+		FLIB.flib_netconn_onChat(conn, chatCb, null);
+		FLIB.flib_netconn_onMessage(conn, messageCb, null);
+		FLIB.flib_netconn_onRoomAdd(conn, roomAddCb, null);
+		FLIB.flib_netconn_onRoomUpdate(conn, roomUpdateCb, null);
+		FLIB.flib_netconn_onRoomDelete(conn, roomDeleteCb, null);
+		FLIB.flib_netconn_onConnected(conn, connectedCb, null);
+		FLIB.flib_netconn_onRoomlist(conn, roomlistCb, null);
+		FLIB.flib_netconn_onDisconnected(conn, disconnectCb, null);
+		FLIB.flib_metascheme_release(meta);
+		tickHandler.start();
+		instance = this;
+		return START_NOT_STICKY;
+	}
+	
+	private void stopWithoutError() {
+		Intent intent = new Intent(ACTION_DISCONNECTED);
+		intent.putExtra(EXTRA_HAS_ERROR, false);
+		broadcastManager.sendBroadcast(intent);
+		stopSelf();
+	}
+	
+	private void stopWithError(String userMessage) {
+		Intent intent = new Intent(ACTION_DISCONNECTED);
+		intent.putExtra(EXTRA_MESSAGE, userMessage);
+		intent.putExtra(EXTRA_HAS_ERROR, true);
+		broadcastManager.sendBroadcast(intent);
+		stopSelf();
+	}
+	
+	@Override
 	public void onCreate() {
-		Log.d("NetplayService", "Creating");
-		if(Flib.INSTANCE.flib_init() != 0) {
-			throw new RuntimeException("Unable to start frontlib");
-		}
-		try {
-			netconn = new Netconn(getApplicationContext(), "AndroidTester");
-		} catch (IOException e) {
-			// TODO better handling
-			throw new RuntimeException("Unable to start frontlib", e);
-		}
-    	timer = new CountDownTimer(Long.MAX_VALUE, 50) {
-			@Override
-			public void onTick(long millisUntilFinished) {
-				if(netconn != null && netconn.isConnected()) {
-					netconn.tick();
+		Log.d("NetplayService", "onCreate");
+		broadcastManager = LocalBroadcastManager.getInstance(getApplicationContext());
+		lobbyChatlog = new MessageLog(getApplicationContext());
+		roomChatlog = new MessageLog(getApplicationContext());
+		tickHandler = new TickHandler(getMainLooper(), TICK_INTERVAL_MS_UNBOUND, new Runnable() {
+			public void run() {
+				if(conn != null) {
+					FLIB.flib_netconn_tick(conn);
 				}
 			}
-			
-			@Override
-			public void onFinish() {
-			}
-		};
-		timer.start();
+		});
+		if(Flib.INSTANCE.flib_init() != 0) {
+			stopWithError(getString(R.string.error_unexpected, "Unable to start frontlib"));
+		}
 	}
 	
 	@Override
 	public void onDestroy() {
-		Log.d("NetplayService", "Destroying");
-		timer.cancel();
-		netconn.disconnect();
+		instance = null;
+		Log.d("NetplayService", "onDestroy");
+		tickHandler.stop();
+		if(conn != null) {
+			FLIB.flib_netconn_destroy(conn);
+			conn = null;
+		}
 		Flib.INSTANCE.flib_quit();
 	}
 
 	public class NetplayBinder extends Binder {
-		Netconn getNetconn() {
-            return netconn;
+		NetplayService getService() {
+            return NetplayService.this;
         }
 	}
+	
+	private StrCallback lobbyJoinCb = new StrCallback() {
+		public void callback(Pointer context, String arg1) {
+			playerList.addPlayerWithNewId(arg1);
+			lobbyChatlog.appendPlayerJoin(arg1);
+		}
+	};
+	
+	private StrStrCallback lobbyLeaveCb = new StrStrCallback() {
+		public void callback(Pointer context, String name, String msg) {
+			playerList.removePlayer(name);
+			lobbyChatlog.appendPlayerLeave(name, msg);
+		}
+	};
+	
+	private StrStrCallback chatCb = new StrStrCallback() {
+		public void callback(Pointer context, String name, String msg) {
+			getCurrentLog().appendChat(name, msg);
+		}
+	};
+	
+	private IntStrCallback messageCb = new IntStrCallback() {
+		public void callback(Pointer context, int type, String msg) {
+			getCurrentLog().appendMessage(type, msg);
+		}
+	};
+	
+	private RoomCallback roomAddCb = new RoomCallback() {
+		public void callback(Pointer context, RoomPtr roomPtr) {
+			roomList.addRoomWithNewId(roomPtr);
+		}
+	};
+	
+	private StrRoomCallback roomUpdateCb = new StrRoomCallback() {
+		public void callback(Pointer context, String name, RoomPtr roomPtr) {
+			roomList.updateRoom(name, roomPtr);
+		}
+	};
+	
+	private StrCallback roomDeleteCb = new StrCallback() {
+		public void callback(Pointer context, String name) {
+			roomList.removeRoom(name);
+		}
+	};
+	
+	private VoidCallback connectedCb = new VoidCallback() {
+		public void callback(Pointer context) {
+			broadcastManager.sendBroadcast(new Intent(ACTION_CONNECTED));
+			joined = true;
+			FLIB.flib_netconn_send_request_roomlist(conn);
+		}
+	};
+	
+	private RoomListCallback roomlistCb = new RoomListCallback() {
+		public void callback(Pointer context, RoomArrayPtr arg1, int count) {
+			roomList.updateList(arg1.getRooms(count));
+		}
+	};
+	
+	private IntStrCallback disconnectCb = new IntStrCallback() {
+		public void callback(Pointer context, int reason, String arg2) {
+			switch(reason) {
+			case JnaFrontlib.NETCONN_DISCONNECT_AUTH_FAILED:
+				stopWithError(getString(R.string.error_auth_failed));
+				break;
+			case JnaFrontlib.NETCONN_DISCONNECT_CONNLOST:
+				stopWithError(getString(R.string.error_connection_lost));
+				break;
+			case JnaFrontlib.NETCONN_DISCONNECT_INTERNAL_ERROR:
+				stopWithError(getString(R.string.error_unexpected, arg2));
+				break;
+			case JnaFrontlib.NETCONN_DISCONNECT_NORMAL:
+				stopWithoutError();
+				break;
+			case JnaFrontlib.NETCONN_DISCONNECT_SERVER_TOO_OLD:
+				stopWithError(getString(R.string.error_server_too_old));
+				break;
+			default:
+				stopWithError(arg2);
+				break;
+			}
+			FLIB.flib_netconn_destroy(conn);
+			conn = null;
+		}
+	};
+	
+	public void disconnect() {
+		if(conn != null) {
+			FLIB.flib_netconn_send_quit(conn, "User quit");
+			FLIB.flib_netconn_destroy(conn);
+			conn = null;
+		}
+		stopWithoutError();
+	}
+	
+	public void sendChat(String s) {
+		FLIB.flib_netconn_send_chat(conn, s);
+		if(FLIB.flib_netconn_is_in_room_context(conn)) {
+			roomChatlog.appendChat(playerName, s);
+		} else {
+			lobbyChatlog.appendChat(playerName, s);
+		}
+	}
+	
+	private MessageLog getCurrentLog() {
+		if(FLIB.flib_netconn_is_in_room_context(conn)) {
+			return roomChatlog;
+		} else {
+			return lobbyChatlog;
+		}
+	}
+	
+	public void sendNick(String nick) { FLIB.flib_netconn_send_nick(conn, nick); }
+	public void sendPassword(String password) { FLIB.flib_netconn_send_password(conn, password); }
+	public void sendQuit(String message) { FLIB.flib_netconn_send_quit(conn, message); }
+	public void sendRoomlistRequest() { if(joined) FLIB.flib_netconn_send_request_roomlist(conn); }
+	public void sendPlayerInfoQuery(String name) { FLIB.flib_netconn_send_playerInfo(conn, name); }
+	
+	public boolean isConnected() {
+		return conn != null;
+	}
 
-	public boolean isConnected() {
-		return netconn!=null;
+	public static boolean isActive() {
+		return instance!=null;
 	}
 }
+