project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/Netplay.java
changeset 7476 2fb781bbdd51
parent 7461 38acbfdb484f
child 7485 0481bd74267c
equal deleted inserted replaced
7473:45b9f25ff611 7476:2fb781bbdd51
     3 import java.io.File;
     3 import java.io.File;
     4 import java.io.FileNotFoundException;
     4 import java.io.FileNotFoundException;
     5 
     5 
     6 import org.hedgewars.hedgeroid.R;
     6 import org.hedgewars.hedgeroid.R;
     7 import org.hedgewars.hedgeroid.Utils;
     7 import org.hedgewars.hedgeroid.Utils;
     8 import org.hedgewars.hedgeroid.netplay.JnaFrontlib.BoolCallback;
     8 import org.hedgewars.hedgeroid.Datastructures.RoomlistRoom;
     9 import org.hedgewars.hedgeroid.netplay.JnaFrontlib.IntStrCallback;
     9 import org.hedgewars.hedgeroid.Datastructures.TeamInGame;
    10 import org.hedgewars.hedgeroid.netplay.JnaFrontlib.MetaschemePtr;
    10 import org.hedgewars.hedgeroid.Datastructures.TeamIngameAttributes;
    11 import org.hedgewars.hedgeroid.netplay.JnaFrontlib.NetconnPtr;
    11 import org.hedgewars.hedgeroid.frontlib.Flib;
    12 import org.hedgewars.hedgeroid.netplay.JnaFrontlib.RoomArrayPtr;
    12 import org.hedgewars.hedgeroid.frontlib.Frontlib;
    13 import org.hedgewars.hedgeroid.netplay.JnaFrontlib.RoomCallback;
    13 import org.hedgewars.hedgeroid.frontlib.Frontlib.BoolCallback;
    14 import org.hedgewars.hedgeroid.netplay.JnaFrontlib.RoomListCallback;
    14 import org.hedgewars.hedgeroid.frontlib.Frontlib.IntStrCallback;
    15 import org.hedgewars.hedgeroid.netplay.JnaFrontlib.RoomPtr;
    15 import org.hedgewars.hedgeroid.frontlib.Frontlib.MetaschemePtr;
    16 import org.hedgewars.hedgeroid.netplay.JnaFrontlib.StrCallback;
    16 import org.hedgewars.hedgeroid.frontlib.Frontlib.NetconnPtr;
    17 import org.hedgewars.hedgeroid.netplay.JnaFrontlib.StrRoomCallback;
    17 import org.hedgewars.hedgeroid.frontlib.Frontlib.RoomArrayPtr;
    18 import org.hedgewars.hedgeroid.netplay.JnaFrontlib.StrStrCallback;
    18 import org.hedgewars.hedgeroid.frontlib.Frontlib.RoomCallback;
    19 import org.hedgewars.hedgeroid.netplay.JnaFrontlib.VoidCallback;
    19 import org.hedgewars.hedgeroid.frontlib.Frontlib.RoomListCallback;
       
    20 import org.hedgewars.hedgeroid.frontlib.Frontlib.RoomPtr;
       
    21 import org.hedgewars.hedgeroid.frontlib.Frontlib.StrBoolCallback;
       
    22 import org.hedgewars.hedgeroid.frontlib.Frontlib.StrCallback;
       
    23 import org.hedgewars.hedgeroid.frontlib.Frontlib.StrIntCallback;
       
    24 import org.hedgewars.hedgeroid.frontlib.Frontlib.StrRoomCallback;
       
    25 import org.hedgewars.hedgeroid.frontlib.Frontlib.StrStrCallback;
       
    26 import org.hedgewars.hedgeroid.frontlib.Frontlib.TeamCallback;
       
    27 import org.hedgewars.hedgeroid.frontlib.Frontlib.TeamPtr;
       
    28 import org.hedgewars.hedgeroid.frontlib.Frontlib.VoidCallback;
    20 
    29 
       
    30 import android.annotation.SuppressLint;
    21 import android.content.Context;
    31 import android.content.Context;
    22 import android.content.Intent;
    32 import android.content.Intent;
    23 import android.content.res.Resources;
    33 import android.content.res.Resources;
    24 import android.os.Handler;
    34 import android.os.Handler;
    25 import android.os.HandlerThread;
    35 import android.os.HandlerThread;
    56 		
    66 		
    57 	private final Context appContext;
    67 	private final Context appContext;
    58 	private final LocalBroadcastManager broadcastManager;
    68 	private final LocalBroadcastManager broadcastManager;
    59 	private final FromNetHandler fromNetHandler = new FromNetHandler();
    69 	private final FromNetHandler fromNetHandler = new FromNetHandler();
    60 	
    70 	
    61 	private State state;
    71 	private State state = State.NOT_CONNECTED;
    62 	private int foregroundUsers = 0;	// Reference counter of clients requesting foreground tick speed (fast ticks)
    72 	private int foregroundUsers = 0;	// Reference counter of clients requesting foreground tick speed (fast ticks)
    63 	private boolean chief;				// Do we control the current room?
    73 	private boolean chief;				// Do we control the current room?
       
    74 	private String playerName;
    64 	
    75 	
    65 	// null if there is no running connection (==state is NOT_CONNECTED)
    76 	// null if there is no running connection (==state is NOT_CONNECTED)
    66 	private ThreadedNetConnection connection;
    77 	private ThreadedNetConnection connection;
    67 	
    78 	
    68 	public final Playerlist playerList = new Playerlist();
    79 	public final LobbyPlayerlist lobbyPlayerlist = new LobbyPlayerlist();
       
    80 	public final RoomPlayerlist roomPlayerlist = new RoomPlayerlist();
    69 	public final Roomlist roomList = new Roomlist();
    81 	public final Roomlist roomList = new Roomlist();
    70 	public final MessageLog lobbyChatlog;
    82 	public final MessageLog lobbyChatlog;
    71 	public final MessageLog roomChatlog;
    83 	public final MessageLog roomChatlog;
       
    84 	public final Teamlist roomTeamlist = new Teamlist();
    72 	
    85 	
    73 	public Netplay(Context appContext) {
    86 	public Netplay(Context appContext) {
    74 		this.appContext = appContext;
    87 		this.appContext = appContext;
    75 		broadcastManager = LocalBroadcastManager.getInstance(appContext);
    88 		broadcastManager = LocalBroadcastManager.getInstance(appContext);
    76 		lobbyChatlog = new MessageLog(appContext);
    89 		lobbyChatlog = new MessageLog(appContext);
    77 		roomChatlog = new MessageLog(appContext);
    90 		roomChatlog = new MessageLog(appContext);
    78 		state = State.NOT_CONNECTED;
       
    79 	}
    91 	}
    80 	
    92 	
    81 	private void clearState() {
    93 	private void clearState() {
    82 		playerList.clear();
    94 		lobbyPlayerlist.clear();
    83 		roomList.clear();
    95 		roomList.clear();
    84 		lobbyChatlog.clear();
    96 		lobbyChatlog.clear();
    85 		roomChatlog.clear();
       
    86 	}
    97 	}
    87 	
    98 	
    88 	public void connectToDefaultServer(String playerName) {
    99 	public void connectToDefaultServer(String playerName) {
    89 		connect(playerName, DEFAULT_SERVER, DEFAULT_PORT);
   100 		connect(playerName, DEFAULT_SERVER, DEFAULT_PORT);
    90 	}
   101 	}
    95 	 * The state will switch to CONNECTING immediately. After that, it can asynchronously change to any other state.
   106 	 * The state will switch to CONNECTING immediately. After that, it can asynchronously change to any other state.
    96 	 * State changes are indicated by broadcasts. In particular, if an error occurs while trying to connect, the state
   107 	 * State changes are indicated by broadcasts. In particular, if an error occurs while trying to connect, the state
    97 	 * will change back to NOT_CONNECTED and an ACTION_DISCONNECTED broadcast is sent.
   108 	 * will change back to NOT_CONNECTED and an ACTION_DISCONNECTED broadcast is sent.
    98 	 */
   109 	 */
    99 	public void connect(String name, String host, int port) {
   110 	public void connect(String name, String host, int port) {
       
   111 		playerName = name;
   100 		if(state != State.NOT_CONNECTED) {
   112 		if(state != State.NOT_CONNECTED) {
   101 			throw new IllegalStateException("Attempt to start a new connection while the old one was still running.");
   113 			throw new IllegalStateException("Attempt to start a new connection while the old one was still running.");
   102 		}
   114 		}
   103 		
   115 		
   104 		clearState();
   116 		clearState();
   105 		changeState(State.CONNECTING);
   117 		changeState(State.CONNECTING);
   106 		connection = ThreadedNetConnection.startConnection(appContext, fromNetHandler, name, host, port);
   118 		connection = ThreadedNetConnection.startConnection(appContext, fromNetHandler, name, host, port);
   107 		connection.setFastTickRate(foregroundUsers > 0);
   119 		connection.setFastTickRate(foregroundUsers > 0);
   108 	}
   120 	}
   109 	
   121 	
   110 	public void sendNick(String nick) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_NICK, nick); }
   122 	public void sendNick(String nick) {
       
   123 		playerName = nick;
       
   124 		sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_NICK, nick);
       
   125 	}
   111 	public void sendPassword(String password) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_PASSWORD, password); }
   126 	public void sendPassword(String password) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_PASSWORD, password); }
   112 	public void sendQuit(String message) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_QUIT, message); }
   127 	public void sendQuit(String message) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_QUIT, message); }
   113 	public void sendRoomlistRequest() { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_ROOMLIST_REQUEST); }
   128 	public void sendRoomlistRequest() { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_ROOMLIST_REQUEST); }
   114 	public void sendPlayerInfoQuery(String name) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_PLAYER_INFO_REQUEST, name); }
   129 	public void sendPlayerInfoQuery(String name) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_PLAYER_INFO_REQUEST, name); }
   115 	public void sendChat(String s) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_CHAT, s); }
   130 	public void sendChat(String s) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_CHAT, s); }
   116 	public void sendFollowPlayer(String nick) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_FOLLOW_PLAYER, nick); }
   131 	public void sendFollowPlayer(String nick) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_FOLLOW_PLAYER, nick); }
   117 	public void sendJoinRoom(String name) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_JOIN_ROOM, name); }
   132 	public void sendJoinRoom(String name) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_JOIN_ROOM, name); }
   118 	public void sendCreateRoom(String name) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_CREATE_ROOM, name); }
   133 	public void sendCreateRoom(String name) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_CREATE_ROOM, name); }
   119 	public void sendLeaveRoom(String message) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_LEAVE_ROOM, message); }
   134 	public void sendLeaveRoom(String message) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_LEAVE_ROOM, message); }
       
   135 	public void sendKick(String player) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_KICK, player); }
   120 	
   136 	
   121 	public void disconnect() { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_DISCONNECT, "User Quit"); }
   137 	public void disconnect() { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_DISCONNECT, "User Quit"); }
   122 	
   138 	
   123 	private static Netplay instance;
   139 	private static Netplay instance;
   124 	
   140 	
   153 	
   169 	
   154 	public boolean isChief() {
   170 	public boolean isChief() {
   155 		return chief;
   171 		return chief;
   156 	}
   172 	}
   157 	
   173 	
       
   174 	public String getPlayerName() {
       
   175 		return playerName;
       
   176 	}
       
   177 	
   158 	/**
   178 	/**
   159 	 * Indicate that you want network messages to be checked regularly (several times per second).
   179 	 * Indicate that you want network messages to be checked regularly (several times per second).
   160 	 * As long as nobody requests fast ticks, the network is only checked once every few seconds
   180 	 * As long as nobody requests fast ticks, the network is only checked once every few seconds
   161 	 * to conserve battery power.
   181 	 * to conserve battery power.
   162 	 * Once you no longer need fast updates, call unrequestFastTicks.
   182 	 * Once you no longer need fast updates, call unrequestFastTicks.
   208 	}
   228 	}
   209 	
   229 	
   210 	/**
   230 	/**
   211 	 * Processes messages from the networking system. Always runs on the main thread.
   231 	 * Processes messages from the networking system. Always runs on the main thread.
   212 	 */
   232 	 */
       
   233 	@SuppressLint("HandlerLeak")
   213 	final class FromNetHandler extends Handler {
   234 	final class FromNetHandler extends Handler {
   214 		public static final int MSG_LOBBY_JOIN = 0;
   235 		public static final int MSG_LOBBY_JOIN = 0;
   215 		public static final int MSG_LOBBY_LEAVE = 1;
   236 		public static final int MSG_LOBBY_LEAVE = 1;
   216 		public static final int MSG_ROOM_JOIN = 2;
   237 		public static final int MSG_ROOM_JOIN = 2;
   217 		public static final int MSG_ROOM_LEAVE = 3;
   238 		public static final int MSG_ROOM_LEAVE = 3;
   224 		public static final int MSG_CONNECTED = 10;
   245 		public static final int MSG_CONNECTED = 10;
   225 		public static final int MSG_DISCONNECTED = 11;
   246 		public static final int MSG_DISCONNECTED = 11;
   226 		public static final int MSG_PASSWORD_REQUEST = 12;
   247 		public static final int MSG_PASSWORD_REQUEST = 12;
   227 		public static final int MSG_ENTER_ROOM_FROM_LOBBY = 13;
   248 		public static final int MSG_ENTER_ROOM_FROM_LOBBY = 13;
   228 		public static final int MSG_LEAVE_ROOM = 14;
   249 		public static final int MSG_LEAVE_ROOM = 14;
       
   250 		public static final int MSG_READYSTATE = 15;
       
   251 		public static final int MSG_TEAM_ADDED = 16;
       
   252 		public static final int MSG_TEAM_DELETED = 17;
       
   253 		public static final int MSG_TEAM_ACCEPTED = 18;
       
   254 		public static final int MSG_TEAM_COLOR_CHANGED = 19;
       
   255 		public static final int MSG_HOG_COUNT_CHANGED = 20;
   229 		
   256 		
   230 		public FromNetHandler() {
   257 		public FromNetHandler() {
   231 			super(Looper.getMainLooper());
   258 			super(Looper.getMainLooper());
   232 		}
   259 		}
   233 		
   260 		
   235 		@Override
   262 		@Override
   236 		public void handleMessage(Message msg) {
   263 		public void handleMessage(Message msg) {
   237 			switch(msg.what) {
   264 			switch(msg.what) {
   238 			case MSG_LOBBY_JOIN: {
   265 			case MSG_LOBBY_JOIN: {
   239 				String name = (String)msg.obj;
   266 				String name = (String)msg.obj;
   240 				playerList.addPlayerWithNewId(name);
   267 				lobbyPlayerlist.addPlayerWithNewId(name);
   241 				lobbyChatlog.appendPlayerJoin(name);
   268 				lobbyChatlog.appendPlayerJoin(name);
   242 				break;
   269 				break;
   243 			}
   270 			}
   244 			case MSG_LOBBY_LEAVE: {
   271 			case MSG_LOBBY_LEAVE: {
   245 				Pair<String, String> args = (Pair<String, String>)msg.obj;
   272 				Pair<String, String> args = (Pair<String, String>)msg.obj;
   246 				playerList.removePlayer(args.first);
   273 				lobbyPlayerlist.remove(args.first);
   247 				lobbyChatlog.appendPlayerLeave(args.first, args.second);
   274 				lobbyChatlog.appendPlayerLeave(args.first, args.second);
   248 				break;
   275 				break;
   249 			}
   276 			}
   250 			case MSG_ROOM_JOIN: {
   277 			case MSG_ROOM_JOIN: {
   251 				String name = (String)msg.obj;
   278 				String name = (String)msg.obj;
   252 				// TODO roomPlayerList.addPlayerWithNewId(name);
   279 				roomPlayerlist.addPlayerWithNewId(name);
   253 				roomChatlog.appendPlayerJoin(name);
   280 				roomChatlog.appendPlayerJoin(name);
   254 				break;
   281 				break;
   255 			}
   282 			}
   256 			case MSG_ROOM_LEAVE: {
   283 			case MSG_ROOM_LEAVE: {
   257 				Pair<String, String> args = (Pair<String, String>)msg.obj;
   284 				Pair<String, String> args = (Pair<String, String>)msg.obj;
   258 				// TODO roomPlayerList.removePlayer(args.first);
   285 				roomPlayerlist.remove(args.first);
   259 				roomChatlog.appendPlayerLeave(args.first, args.second);
   286 				roomChatlog.appendPlayerLeave(args.first, args.second);
   260 				break;
   287 				break;
   261 			}
   288 			}
   262 			case MSG_CHAT: {
   289 			case MSG_CHAT: {
   263 				Pair<String, String> args = (Pair<String, String>)msg.obj;
   290 				Pair<String, String> args = (Pair<String, String>)msg.obj;
   267 			case MSG_MESSAGE: {
   294 			case MSG_MESSAGE: {
   268 				getCurrentLog().appendMessage(msg.arg1, (String)msg.obj);
   295 				getCurrentLog().appendMessage(msg.arg1, (String)msg.obj);
   269 				break;
   296 				break;
   270 			}
   297 			}
   271 			case MSG_ROOM_ADD: {
   298 			case MSG_ROOM_ADD: {
   272 				roomList.addRoomWithNewId((Room)msg.obj);
   299 				roomList.addRoomWithNewId((RoomlistRoom)msg.obj);
   273 				break;
   300 				break;
   274 			}
   301 			}
   275 			case MSG_ROOM_UPDATE: {
   302 			case MSG_ROOM_UPDATE: {
   276 				Pair<String, Room> args = (Pair<String, Room>)msg.obj;
   303 				Pair<String, RoomlistRoom> args = (Pair<String, RoomlistRoom>)msg.obj;
   277 				roomList.updateRoom(args.first, args.second);
   304 				roomList.updateRoom(args.first, args.second);
   278 				break;
   305 				break;
   279 			}
   306 			}
   280 			case MSG_ROOM_DELETE: {
   307 			case MSG_ROOM_DELETE: {
   281 				roomList.removeRoom((String)msg.obj);
   308 				roomList.remove((String)msg.obj);
   282 				break;
   309 				break;
   283 			}
   310 			}
   284 			case MSG_ROOMLIST: {
   311 			case MSG_ROOMLIST: {
   285 				roomList.updateList((Room[])msg.obj);
   312 				roomList.updateList((RoomlistRoom[])msg.obj);
   286 				break;
   313 				break;
   287 			}
   314 			}
   288 			case MSG_CONNECTED: {
   315 			case MSG_CONNECTED: {
       
   316 				playerName = (String)msg.obj;
   289 				changeState(State.LOBBY);
   317 				changeState(State.LOBBY);
   290 				broadcastManager.sendBroadcast(new Intent(ACTION_CONNECTED));
   318 				broadcastManager.sendBroadcast(new Intent(ACTION_CONNECTED));
   291 				break;
   319 				break;
   292 			}
   320 			}
   293 			case MSG_DISCONNECTED: {
   321 			case MSG_DISCONNECTED: {
   306 				broadcastManager.sendBroadcast(intent);
   334 				broadcastManager.sendBroadcast(intent);
   307 				break;
   335 				break;
   308 			}
   336 			}
   309 			case MSG_ENTER_ROOM_FROM_LOBBY: {
   337 			case MSG_ENTER_ROOM_FROM_LOBBY: {
   310 				roomChatlog.clear();
   338 				roomChatlog.clear();
       
   339 				roomPlayerlist.clear();
       
   340 				roomTeamlist.clear();
   311 				changeState(State.ROOM);
   341 				changeState(State.ROOM);
   312 				chief = (Boolean)msg.obj;
   342 				chief = (Boolean)msg.obj;
   313 				Intent intent = new Intent(ACTION_ENTERED_ROOM_FROM_LOBBY);
   343 				Intent intent = new Intent(ACTION_ENTERED_ROOM_FROM_LOBBY);
   314 				broadcastManager.sendBroadcastSync(intent);
   344 				broadcastManager.sendBroadcastSync(intent);
   315 				break;
   345 				break;
   320 				intent.putExtra(EXTRA_MESSAGE, (String)msg.obj);
   350 				intent.putExtra(EXTRA_MESSAGE, (String)msg.obj);
   321 				intent.putExtra(EXTRA_REASON, msg.arg1);
   351 				intent.putExtra(EXTRA_REASON, msg.arg1);
   322 				broadcastManager.sendBroadcastSync(intent);
   352 				broadcastManager.sendBroadcastSync(intent);
   323 				break;
   353 				break;
   324 			}
   354 			}
       
   355 			case MSG_READYSTATE: {
       
   356 				Pair<String, Boolean> args = (Pair<String, Boolean>)msg.obj;
       
   357 				roomPlayerlist.setReady(args.first, args.second);
       
   358 				break;
       
   359 			}
       
   360 			case MSG_TEAM_ADDED: {
       
   361 				roomTeamlist.addTeamWithNewId((TeamInGame)msg.obj);
       
   362 				break;
       
   363 			}
       
   364 			case MSG_TEAM_DELETED: {
       
   365 				roomTeamlist.remove((String)msg.obj);
       
   366 				break;
       
   367 			}
       
   368 			case MSG_TEAM_ACCEPTED: {
       
   369 				// TODO depends: adding teams
       
   370 				break;
       
   371 			}
       
   372 			case MSG_TEAM_COLOR_CHANGED: {
       
   373 				Pair<TeamInGame, Long> oldEntry = roomTeamlist.get((String)msg.obj);
       
   374 				if(oldEntry != null) {
       
   375 					TeamInGame tig = oldEntry.first;
       
   376 					TeamIngameAttributes tiga = tig.ingameAttribs.withColorIndex(msg.arg1);
       
   377 					roomTeamlist.put(tig.team.name, Pair.create(tig.withAttribs(tiga), oldEntry.second));
       
   378 				} else {
       
   379 					Log.e("Netplay", "Color update for unknown team "+msg.obj);
       
   380 				}
       
   381 				break;
       
   382 			}
       
   383 			case MSG_HOG_COUNT_CHANGED: {
       
   384 				Pair<TeamInGame, Long> oldEntry = roomTeamlist.get((String)msg.obj);
       
   385 				if(oldEntry != null) {
       
   386 					TeamInGame tig = oldEntry.first;
       
   387 					TeamIngameAttributes tiga = tig.ingameAttribs.withHogCount(msg.arg1);
       
   388 					roomTeamlist.put(tig.team.name, Pair.create(tig.withAttribs(tiga), oldEntry.second));
       
   389 				} else {
       
   390 					Log.e("Netplay", "Hog count update for unknown team "+msg.obj);
       
   391 				}
       
   392 				break;
       
   393 			}
   325 			default: {
   394 			default: {
   326 				Log.e("FromNetHandler", "Unknown message type: "+msg.what);
   395 				Log.e("FromNetHandler", "Unknown message type: "+msg.what);
   327 				break;
   396 				break;
   328 			}
   397 			}
   329 			}
   398 			}
   334 	 * This class handles the actual communication with the networking library, on a separate thread.
   403 	 * This class handles the actual communication with the networking library, on a separate thread.
   335 	 */
   404 	 */
   336 	private static class ThreadedNetConnection {
   405 	private static class ThreadedNetConnection {
   337 		private static final long TICK_INTERVAL_FAST = 100;
   406 		private static final long TICK_INTERVAL_FAST = 100;
   338 		private static final long TICK_INTERVAL_SLOW = 5000;
   407 		private static final long TICK_INTERVAL_SLOW = 5000;
   339 		private static final JnaFrontlib FLIB = Flib.INSTANCE;
   408 		private static final Frontlib FLIB = Flib.INSTANCE;
   340 		
   409 		
   341 		public final ToNetHandler toNetHandler;
   410 		public final ToNetHandler toNetHandler;
   342 		
   411 		
   343 		private final Context appContext;
   412 		private final Context appContext;
   344 		private final FromNetHandler fromNetHandler;
   413 		private final FromNetHandler fromNetHandler;
   397 					FLIB.flib_netconn_onRoomlist(conn, roomlistCb, null);
   466 					FLIB.flib_netconn_onRoomlist(conn, roomlistCb, null);
   398 					FLIB.flib_netconn_onDisconnected(conn, disconnectCb, null);
   467 					FLIB.flib_netconn_onDisconnected(conn, disconnectCb, null);
   399 					FLIB.flib_netconn_onPasswordRequest(conn, passwordRequestCb, null);
   468 					FLIB.flib_netconn_onPasswordRequest(conn, passwordRequestCb, null);
   400 					FLIB.flib_netconn_onEnterRoom(conn, enterRoomCb, null);
   469 					FLIB.flib_netconn_onEnterRoom(conn, enterRoomCb, null);
   401 					FLIB.flib_netconn_onLeaveRoom(conn, leaveRoomCb, null);
   470 					FLIB.flib_netconn_onLeaveRoom(conn, leaveRoomCb, null);
       
   471 					FLIB.flib_netconn_onReadyState(conn, readyStateCb, null);
       
   472 					FLIB.flib_netconn_onTeamAdd(conn, teamAddedCb, null);
       
   473 					FLIB.flib_netconn_onTeamDelete(conn, teamDeletedCb, null);
       
   474 					FLIB.flib_netconn_onTeamAccepted(conn, teamAcceptedCb, null);
       
   475 					FLIB.flib_netconn_onTeamColorChanged(conn, teamColorChangedCb, null);
       
   476 					FLIB.flib_netconn_onHogCountChanged(conn, hogCountChangedCb, null);
   402 					
   477 					
   403 					FLIB.flib_metascheme_release(meta);
   478 					FLIB.flib_metascheme_release(meta);
   404 					tickHandler.start();
   479 					tickHandler.start();
   405 				}
   480 				}
   406 			});
   481 			});
   494 			}
   569 			}
   495 		};
   570 		};
   496 		
   571 		
   497 		private final BoolCallback enterRoomCb = new BoolCallback() {
   572 		private final BoolCallback enterRoomCb = new BoolCallback() {
   498 			public void callback(Pointer context, boolean isChief) {
   573 			public void callback(Pointer context, boolean isChief) {
   499 				sendFromNet(FromNetHandler.MSG_ENTER_ROOM_FROM_LOBBY, Boolean.TRUE);
   574 				sendFromNet(FromNetHandler.MSG_ENTER_ROOM_FROM_LOBBY, isChief);
   500 			}
   575 			}
   501 		};
   576 		};
   502 		
   577 		
   503 		private final IntStrCallback leaveRoomCb = new IntStrCallback() {
   578 		private final IntStrCallback leaveRoomCb = new IntStrCallback() {
   504 			public void callback(Pointer context, int reason, String message) {
   579 			public void callback(Pointer context, int reason, String message) {
   505 				sendFromNet(FromNetHandler.MSG_LEAVE_ROOM, reason, message);
   580 				sendFromNet(FromNetHandler.MSG_LEAVE_ROOM, reason, message);
       
   581 			}
       
   582 		};
       
   583 		
       
   584 		private final StrBoolCallback readyStateCb = new StrBoolCallback() {
       
   585 			public void callback(Pointer context, String player, boolean ready) {
       
   586 				sendFromNet(FromNetHandler.MSG_READYSTATE, Pair.create(player, ready));
       
   587 			}
       
   588 		};
       
   589 		
       
   590 		private final TeamCallback teamAddedCb = new TeamCallback() {
       
   591 			public void callback(Pointer context, TeamPtr team) {
       
   592 				sendFromNet(FromNetHandler.MSG_TEAM_ADDED, team.deref());
       
   593 			}
       
   594 		};
       
   595 		
       
   596 		private final StrCallback teamDeletedCb = new StrCallback() {
       
   597 			public void callback(Pointer context, String teamName) {
       
   598 				sendFromNet(FromNetHandler.MSG_TEAM_DELETED, teamName);
       
   599 			}
       
   600 		};
       
   601 		
       
   602 		private final StrCallback teamAcceptedCb = new StrCallback() {
       
   603 			public void callback(Pointer context, String teamName) {
       
   604 				sendFromNet(FromNetHandler.MSG_TEAM_ACCEPTED, teamName);
       
   605 			}
       
   606 		};
       
   607 		
       
   608 		private final StrIntCallback teamColorChangedCb = new StrIntCallback() {
       
   609 			public void callback(Pointer context, String teamName, int colorIndex) {
       
   610 				sendFromNet(FromNetHandler.MSG_TEAM_COLOR_CHANGED, colorIndex, teamName);
       
   611 			}
       
   612 		};
       
   613 		
       
   614 		private final StrIntCallback hogCountChangedCb = new StrIntCallback() {
       
   615 			public void callback(Pointer context, String teamName, int hogCount) {
       
   616 				sendFromNet(FromNetHandler.MSG_HOG_COUNT_CHANGED, hogCount, teamName);
   506 			}
   617 			}
   507 		};
   618 		};
   508 		
   619 		
   509 		private void shutdown(boolean error, String message) {
   620 		private void shutdown(boolean error, String message) {
   510 			if(conn != null) {
   621 			if(conn != null) {
   516 			sendFromNet(FromNetHandler.MSG_DISCONNECTED, Pair.create(error, message));
   627 			sendFromNet(FromNetHandler.MSG_DISCONNECTED, Pair.create(error, message));
   517 		}
   628 		}
   518 		
   629 		
   519 		private final IntStrCallback disconnectCb = new IntStrCallback() {
   630 		private final IntStrCallback disconnectCb = new IntStrCallback() {
   520 			public void callback(Pointer context, int reason, String message) {
   631 			public void callback(Pointer context, int reason, String message) {
   521 				Boolean error = reason != JnaFrontlib.NETCONN_DISCONNECT_NORMAL;
   632 				Boolean error = reason != Frontlib.NETCONN_DISCONNECT_NORMAL;
   522 				String messageForUser = createDisconnectUserMessage(appContext.getResources(), reason, message);
   633 				String messageForUser = createDisconnectUserMessage(appContext.getResources(), reason, message);
   523 				shutdown(error, messageForUser);
   634 				shutdown(error, messageForUser);
   524 			}
   635 			}
   525 		};
   636 		};
   526 		
   637 		
   527 		private static String createDisconnectUserMessage(Resources res, int reason, String message) {
   638 		private static String createDisconnectUserMessage(Resources res, int reason, String message) {
   528 			switch(reason) {
   639 			switch(reason) {
   529 			case JnaFrontlib.NETCONN_DISCONNECT_AUTH_FAILED:
   640 			case Frontlib.NETCONN_DISCONNECT_AUTH_FAILED:
   530 				return res.getString(R.string.error_auth_failed);
   641 				return res.getString(R.string.error_auth_failed);
   531 			case JnaFrontlib.NETCONN_DISCONNECT_CONNLOST:
   642 			case Frontlib.NETCONN_DISCONNECT_CONNLOST:
   532 				return res.getString(R.string.error_connection_lost);
   643 				return res.getString(R.string.error_connection_lost);
   533 			case JnaFrontlib.NETCONN_DISCONNECT_INTERNAL_ERROR:
   644 			case Frontlib.NETCONN_DISCONNECT_INTERNAL_ERROR:
   534 				return res.getString(R.string.error_unexpected, message);
   645 				return res.getString(R.string.error_unexpected, message);
   535 			case JnaFrontlib.NETCONN_DISCONNECT_SERVER_TOO_OLD:
   646 			case Frontlib.NETCONN_DISCONNECT_SERVER_TOO_OLD:
   536 				return res.getString(R.string.error_server_too_old);
   647 				return res.getString(R.string.error_server_too_old);
   537 			default:
   648 			default:
   538 				return message;
   649 				return message;
   539 			}
   650 			}
   540 		}
   651 		}
   548 		}
   659 		}
   549 		
   660 		
   550 		/**
   661 		/**
   551 		 * Processes messages to the networking system. Runs on a non-main thread.
   662 		 * Processes messages to the networking system. Runs on a non-main thread.
   552 		 */
   663 		 */
       
   664 		@SuppressLint("HandlerLeak")
   553 		public final class ToNetHandler extends Handler {
   665 		public final class ToNetHandler extends Handler {
   554 			public static final int MSG_SEND_NICK = 0;
   666 			public static final int MSG_SEND_NICK = 0;
   555 			public static final int MSG_SEND_PASSWORD = 1;
   667 			public static final int MSG_SEND_PASSWORD = 1;
   556 			public static final int MSG_SEND_QUIT = 2;
   668 			public static final int MSG_SEND_QUIT = 2;
   557 			public static final int MSG_SEND_ROOMLIST_REQUEST = 3;
   669 			public static final int MSG_SEND_ROOMLIST_REQUEST = 3;
   559 			public static final int MSG_SEND_CHAT = 5;
   671 			public static final int MSG_SEND_CHAT = 5;
   560 			public static final int MSG_SEND_FOLLOW_PLAYER = 6;
   672 			public static final int MSG_SEND_FOLLOW_PLAYER = 6;
   561 			public static final int MSG_SEND_JOIN_ROOM = 7;
   673 			public static final int MSG_SEND_JOIN_ROOM = 7;
   562 			public static final int MSG_SEND_CREATE_ROOM = 8;
   674 			public static final int MSG_SEND_CREATE_ROOM = 8;
   563 			public static final int MSG_SEND_LEAVE_ROOM = 9;
   675 			public static final int MSG_SEND_LEAVE_ROOM = 9;
       
   676 			public static final int MSG_SEND_KICK = 10;
   564 			
   677 			
   565 			public static final int MSG_DISCONNECT = 10;
   678 			public static final int MSG_DISCONNECT = 11;
   566 			
   679 			
   567 			public ToNetHandler(Looper looper) {
   680 			public ToNetHandler(Looper looper) {
   568 				super(looper);
   681 				super(looper);
   569 			}
   682 			}
   570 			
   683 			
   613 					if(FLIB.flib_netconn_send_leaveRoom(conn, (String)msg.obj) == 0) {
   726 					if(FLIB.flib_netconn_send_leaveRoom(conn, (String)msg.obj) == 0) {
   614 						sendFromNet(FromNetHandler.MSG_LEAVE_ROOM, -1, "");
   727 						sendFromNet(FromNetHandler.MSG_LEAVE_ROOM, -1, "");
   615 					}
   728 					}
   616 					break;
   729 					break;
   617 				}
   730 				}
       
   731 				case MSG_SEND_KICK: {
       
   732 					FLIB.flib_netconn_send_kick(conn, (String)msg.obj);
       
   733 					break;
       
   734 				}
   618 				case MSG_DISCONNECT: {
   735 				case MSG_DISCONNECT: {
   619 					FLIB.flib_netconn_send_quit(conn, (String)msg.obj);
   736 					FLIB.flib_netconn_send_quit(conn, (String)msg.obj);
   620 					shutdown(false, "User quit");
   737 					shutdown(false, "User quit");
   621 					break;
   738 					break;
   622 				}
   739 				}