project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/Netplay.java
changeset 10017 de822cd3df3a
parent 7691 55c0a856ecd0
child 15511 fb23e49b2d4e
equal deleted inserted replaced
10015:4feced261c68 10017:de822cd3df3a
    55 
    55 
    56 /**
    56 /**
    57  * This class manages the application's networking state.
    57  * This class manages the application's networking state.
    58  */
    58  */
    59 public class Netplay {
    59 public class Netplay {
    60 	public static enum State { NOT_CONNECTED, CONNECTING, LOBBY, ROOM }
    60     public static enum State { NOT_CONNECTED, CONNECTING, LOBBY, ROOM }
    61 	
    61 
    62 	// Extras in broadcasts
    62     // Extras in broadcasts
    63 	public static final String EXTRA_PLAYERNAME = "playerName";
    63     public static final String EXTRA_PLAYERNAME = "playerName";
    64 	public static final String EXTRA_MESSAGE = "message";
    64     public static final String EXTRA_MESSAGE = "message";
    65 	public static final String EXTRA_HAS_ERROR = "hasError";
    65     public static final String EXTRA_HAS_ERROR = "hasError";
    66 	public static final String EXTRA_REASON = "reason";
    66     public static final String EXTRA_REASON = "reason";
    67 	
    67 
    68 	private static final String ACTIONPREFIX = "org.hedgewars.hedgeroid.netconn.";
    68     private static final String ACTIONPREFIX = "org.hedgewars.hedgeroid.netconn.";
    69 	public static final String ACTION_DISCONNECTED = ACTIONPREFIX+"DISCONNECTED";
    69     public static final String ACTION_DISCONNECTED = ACTIONPREFIX+"DISCONNECTED";
    70 	public static final String ACTION_CONNECTED = ACTIONPREFIX+"CONNECTED";
    70     public static final String ACTION_CONNECTED = ACTIONPREFIX+"CONNECTED";
    71 	public static final String ACTION_PASSWORD_REQUESTED = ACTIONPREFIX+"PASSWORD_REQUESTED";
    71     public static final String ACTION_PASSWORD_REQUESTED = ACTIONPREFIX+"PASSWORD_REQUESTED";
    72 	public static final String ACTION_ENTERED_ROOM_FROM_LOBBY = ACTIONPREFIX+"ENTERED_ROOM";
    72     public static final String ACTION_ENTERED_ROOM_FROM_LOBBY = ACTIONPREFIX+"ENTERED_ROOM";
    73 	public static final String ACTION_LEFT_ROOM = ACTIONPREFIX+"LEFT_ROOM";
    73     public static final String ACTION_LEFT_ROOM = ACTIONPREFIX+"LEFT_ROOM";
    74 	public static final String ACTION_STATE_CHANGED = ACTIONPREFIX+"STATE_CHANGED";
    74     public static final String ACTION_STATE_CHANGED = ACTIONPREFIX+"STATE_CHANGED";
    75 	
    75 
    76 	public static final String DEFAULT_SERVER = "netserver.hedgewars.org";
    76     public static final String DEFAULT_SERVER = "netserver.hedgewars.org";
    77 	public static final int DEFAULT_PORT = 46631;
    77     public static final int DEFAULT_PORT = 46631;
    78 		
    78 
    79 	private final Context appContext;
    79     private final Context appContext;
    80 	private final LocalBroadcastManager broadcastManager;
    80     private final LocalBroadcastManager broadcastManager;
    81 	private final FromNetHandler fromNetHandler = new FromNetHandler();
    81     private final FromNetHandler fromNetHandler = new FromNetHandler();
    82 	public final Scheme defaultScheme;
    82     public final Scheme defaultScheme;
    83 	public final Weaponset defaultWeaponset;
    83     public final Weaponset defaultWeaponset;
    84 	
    84 
    85 	private State state = State.NOT_CONNECTED;
    85     private State state = State.NOT_CONNECTED;
    86 	private String playerName;
    86     private String playerName;
    87 	
    87 
    88 	// null or stale if not in room state
    88     // null or stale if not in room state
    89 	private final NetRoomState netRoomState = new NetRoomState(this);
    89     private final NetRoomState netRoomState = new NetRoomState(this);
    90 	
    90 
    91 	// null if there is no running connection (==state is NOT_CONNECTED)
    91     // null if there is no running connection (==state is NOT_CONNECTED)
    92 	private ThreadedNetConnection connection;
    92     private ThreadedNetConnection connection;
    93 	
    93 
    94 	public final ObservableTreeMap<String, Player> lobbyPlayerlist = new ObservableTreeMap<String, Player>();
    94     public final ObservableTreeMap<String, Player> lobbyPlayerlist = new ObservableTreeMap<String, Player>();
    95 	public final ObservableTreeMap<String, PlayerInRoom> roomPlayerlist = new ObservableTreeMap<String, PlayerInRoom>();
    95     public final ObservableTreeMap<String, PlayerInRoom> roomPlayerlist = new ObservableTreeMap<String, PlayerInRoom>();
    96 	public final Roomlist roomList = new Roomlist();
    96     public final Roomlist roomList = new Roomlist();
    97 	public final MessageLog lobbyChatlog;
    97     public final MessageLog lobbyChatlog;
    98 	public final MessageLog roomChatlog;
    98     public final MessageLog roomChatlog;
    99 	
    99 
   100 	private final List<GameMessageListener> gameMessageListeners = new LinkedList<GameMessageListener>();
   100     private final List<GameMessageListener> gameMessageListeners = new LinkedList<GameMessageListener>();
   101 	private final List<RunGameListener> runGameListeners = new LinkedList<RunGameListener>();
   101     private final List<RunGameListener> runGameListeners = new LinkedList<RunGameListener>();
   102 	
   102 
   103 	public Netplay(Context appContext, Scheme defaultScheme, Weaponset defaultWeaponset) {
   103     public Netplay(Context appContext, Scheme defaultScheme, Weaponset defaultWeaponset) {
   104 		this.appContext = appContext;
   104         this.appContext = appContext;
   105 		broadcastManager = LocalBroadcastManager.getInstance(appContext);
   105         broadcastManager = LocalBroadcastManager.getInstance(appContext);
   106 		lobbyChatlog = new MessageLog(appContext);
   106         lobbyChatlog = new MessageLog(appContext);
   107 		roomChatlog = new MessageLog(appContext);
   107         roomChatlog = new MessageLog(appContext);
   108 		this.defaultScheme = defaultScheme;
   108         this.defaultScheme = defaultScheme;
   109 		this.defaultWeaponset = defaultWeaponset;
   109         this.defaultWeaponset = defaultWeaponset;
   110 	}
   110     }
   111 	
   111 
   112 	public RoomStateManager getRoomStateManager() {
   112     public RoomStateManager getRoomStateManager() {
   113 		return netRoomState;
   113         return netRoomState;
   114 	}
   114     }
   115 	
   115 
   116 	private void clearLobbyState() {
   116     private void clearLobbyState() {
   117 		lobbyPlayerlist.clear();
   117         lobbyPlayerlist.clear();
   118 		roomList.clear();
   118         roomList.clear();
   119 		lobbyChatlog.clear();
   119         lobbyChatlog.clear();
   120 	}
   120     }
   121 	
   121 
   122 	private void initRoomState(boolean chief) {
   122     private void initRoomState(boolean chief) {
   123 		roomChatlog.clear();
   123         roomChatlog.clear();
   124 		roomPlayerlist.clear();
   124         roomPlayerlist.clear();
   125 		netRoomState.initRoomState(chief);
   125         netRoomState.initRoomState(chief);
   126 	}
   126     }
   127 	
   127 
   128 	public void registerGameMessageListener(GameMessageListener listener) {
   128     public void registerGameMessageListener(GameMessageListener listener) {
   129 		gameMessageListeners.add(listener);
   129         gameMessageListeners.add(listener);
   130 	}
   130     }
   131 	
   131 
   132 	public void unregisterGameMessageListener(GameMessageListener listener) {
   132     public void unregisterGameMessageListener(GameMessageListener listener) {
   133 		gameMessageListeners.remove(listener);
   133         gameMessageListeners.remove(listener);
   134 	}
   134     }
   135 	
   135 
   136 	public void registerRunGameListener(RunGameListener listener) {
   136     public void registerRunGameListener(RunGameListener listener) {
   137 		runGameListeners.add(listener);
   137         runGameListeners.add(listener);
   138 	}
   138     }
   139 	
   139 
   140 	public void unregisterRunGameListener(RunGameListener listener) {
   140     public void unregisterRunGameListener(RunGameListener listener) {
   141 		runGameListeners.remove(listener);
   141         runGameListeners.remove(listener);
   142 	}
   142     }
   143 	
   143 
   144 	public void connectToDefaultServer(String playerName) {
   144     public void connectToDefaultServer(String playerName) {
   145 		connect(playerName, DEFAULT_SERVER, DEFAULT_PORT);
   145         connect(playerName, DEFAULT_SERVER, DEFAULT_PORT);
   146 	}
   146     }
   147 	
   147 
   148 	/**
   148     /**
   149 	 * Establish a new connection. Only call if the current state is NOT_CONNECTED.
   149      * Establish a new connection. Only call if the current state is NOT_CONNECTED.
   150 	 * 
   150      *
   151 	 * The state will switch to CONNECTING immediately. After that, it can asynchronously change to any other state.
   151      * The state will switch to CONNECTING immediately. After that, it can asynchronously change to any other state.
   152 	 * State changes are indicated by broadcasts. In particular, if an error occurs while trying to connect, the state
   152      * State changes are indicated by broadcasts. In particular, if an error occurs while trying to connect, the state
   153 	 * will change back to NOT_CONNECTED and an ACTION_DISCONNECTED broadcast is sent.
   153      * will change back to NOT_CONNECTED and an ACTION_DISCONNECTED broadcast is sent.
   154 	 */
   154      */
   155 	public void connect(String name, String host, int port) {
   155     public void connect(String name, String host, int port) {
   156 		playerName = name;
   156         playerName = name;
   157 		if(state != State.NOT_CONNECTED) {
   157         if(state != State.NOT_CONNECTED) {
   158 			throw new IllegalStateException("Attempt to start a new connection while the old one was still running.");
   158             throw new IllegalStateException("Attempt to start a new connection while the old one was still running.");
   159 		}
   159         }
   160 		
   160 
   161 		clearLobbyState();
   161         clearLobbyState();
   162 		changeState(State.CONNECTING);
   162         changeState(State.CONNECTING);
   163 		connection = ThreadedNetConnection.startConnection(appContext, fromNetHandler, name, host, port);
   163         connection = ThreadedNetConnection.startConnection(appContext, fromNetHandler, name, host, port);
   164 		connection.setFastTickRate(true);
   164         connection.setFastTickRate(true);
   165 	}
   165     }
   166 	
   166 
   167 	public void sendNick(String nick) {
   167     public void sendNick(String nick) {
   168 		playerName = nick;
   168         playerName = nick;
   169 		sendToNet(MSG_SEND_NICK, nick);
   169         sendToNet(MSG_SEND_NICK, nick);
   170 	}
   170     }
   171 	public void sendPassword(String password) { sendToNet(MSG_SEND_PASSWORD, password); }
   171     public void sendPassword(String password) { sendToNet(MSG_SEND_PASSWORD, password); }
   172 	public void sendQuit(String message) { sendToNet(MSG_SEND_QUIT, message); }
   172     public void sendQuit(String message) { sendToNet(MSG_SEND_QUIT, message); }
   173 	public void sendRoomlistRequest() { sendToNet(MSG_SEND_ROOMLIST_REQUEST); }
   173     public void sendRoomlistRequest() { sendToNet(MSG_SEND_ROOMLIST_REQUEST); }
   174 	public void sendPlayerInfoQuery(String name) { sendToNet(MSG_SEND_PLAYER_INFO_REQUEST, name); }
   174     public void sendPlayerInfoQuery(String name) { sendToNet(MSG_SEND_PLAYER_INFO_REQUEST, name); }
   175 	public void sendChat(String s) { sendToNet(MSG_SEND_CHAT, s); }
   175     public void sendChat(String s) { sendToNet(MSG_SEND_CHAT, s); }
   176 	public void sendTeamChat(String s) { sendToNet(MSG_SEND_TEAMCHAT, s); }
   176     public void sendTeamChat(String s) { sendToNet(MSG_SEND_TEAMCHAT, s); }
   177 	public void sendFollowPlayer(String nick) { sendToNet(MSG_SEND_FOLLOW_PLAYER, nick); }
   177     public void sendFollowPlayer(String nick) { sendToNet(MSG_SEND_FOLLOW_PLAYER, nick); }
   178 	public void sendJoinRoom(String name) { sendToNet(MSG_SEND_JOIN_ROOM, name); }
   178     public void sendJoinRoom(String name) { sendToNet(MSG_SEND_JOIN_ROOM, name); }
   179 	public void sendCreateRoom(String name) { sendToNet(MSG_SEND_CREATE_ROOM, name); }
   179     public void sendCreateRoom(String name) { sendToNet(MSG_SEND_CREATE_ROOM, name); }
   180 	public void sendLeaveRoom(String message) { sendToNet(MSG_SEND_LEAVE_ROOM, message); }
   180     public void sendLeaveRoom(String message) { sendToNet(MSG_SEND_LEAVE_ROOM, message); }
   181 	public void sendKick(String player) { sendToNet(MSG_SEND_KICK, player); }
   181     public void sendKick(String player) { sendToNet(MSG_SEND_KICK, player); }
   182 	public void sendEngineMessage(byte[] engineMessage) { sendToNet(MSG_SEND_ENGINE_MESSAGE, engineMessage); }
   182     public void sendEngineMessage(byte[] engineMessage) { sendToNet(MSG_SEND_ENGINE_MESSAGE, engineMessage); }
   183 	public void sendRoundFinished(boolean withoutError) { sendToNet(MSG_SEND_ROUND_FINISHED, Boolean.valueOf(withoutError)); }
   183     public void sendRoundFinished(boolean withoutError) { sendToNet(MSG_SEND_ROUND_FINISHED, Boolean.valueOf(withoutError)); }
   184 	public void sendToggleReady() { sendToNet(MSG_SEND_TOGGLE_READY); }
   184     public void sendToggleReady() { sendToNet(MSG_SEND_TOGGLE_READY); }
   185 	public void sendStartGame() { sendToNet(MSG_SEND_START_GAME); }
   185     public void sendStartGame() { sendToNet(MSG_SEND_START_GAME); }
   186 	
   186 
   187 	public void disconnect() { sendToNet(MSG_DISCONNECT, "User Quit"); }
   187     public void disconnect() { sendToNet(MSG_DISCONNECT, "User Quit"); }
   188 	
   188 
   189 	private static Netplay instance;
   189     private static Netplay instance;
   190 	
   190 
   191 	/**
   191     /**
   192 	 * Retrieve the single app-wide instance of the netplay interface, creating it if it
   192      * Retrieve the single app-wide instance of the netplay interface, creating it if it
   193 	 * does not exist yet.
   193      * does not exist yet.
   194 	 * 
   194      *
   195 	 * @param applicationContext
   195      * @param applicationContext
   196 	 * @return
   196      * @return
   197 	 */
   197      */
   198 	public static Netplay getAppInstance(Context applicationContext) {
   198     public static Netplay getAppInstance(Context applicationContext) {
   199 		if(instance == null) {
   199         if(instance == null) {
   200 			// We will need some default values for rooms, best load them here
   200             // We will need some default values for rooms, best load them here
   201 			Scheme defaultScheme = null;
   201             Scheme defaultScheme = null;
   202 			Weaponset defaultWeaponset = null;
   202             Weaponset defaultWeaponset = null;
   203 			try {
   203             try {
   204 				List<Scheme> schemes = Schemes.loadBuiltinSchemes(applicationContext);
   204                 List<Scheme> schemes = Schemes.loadBuiltinSchemes(applicationContext);
   205 				for(Scheme scheme : schemes) {
   205                 for(Scheme scheme : schemes) {
   206 					if(scheme.name.equals(GameConfig.DEFAULT_SCHEME)) {
   206                     if(scheme.name.equals(GameConfig.DEFAULT_SCHEME)) {
   207 						defaultScheme = scheme;
   207                         defaultScheme = scheme;
   208 					}
   208                     }
   209 				}
   209                 }
   210 				List<Weaponset> weaponsets = Weaponsets.loadBuiltinWeaponsets(applicationContext);
   210                 List<Weaponset> weaponsets = Weaponsets.loadBuiltinWeaponsets(applicationContext);
   211 				for(Weaponset weaponset : weaponsets) {
   211                 for(Weaponset weaponset : weaponsets) {
   212 					if(weaponset.name.equals(GameConfig.DEFAULT_WEAPONSET)) {
   212                     if(weaponset.name.equals(GameConfig.DEFAULT_WEAPONSET)) {
   213 						defaultWeaponset = weaponset;
   213                         defaultWeaponset = weaponset;
   214 					}
   214                     }
   215 				}
   215                 }
   216 			} catch(IOException e) {
   216             } catch(IOException e) {
   217 				throw new RuntimeException(e);
   217                 throw new RuntimeException(e);
   218 			}
   218             }
   219 			
   219 
   220 			if(defaultScheme==null || defaultWeaponset==null) {
   220             if(defaultScheme==null || defaultWeaponset==null) {
   221 				throw new RuntimeException("Unable to load default scheme or weaponset");
   221                 throw new RuntimeException("Unable to load default scheme or weaponset");
   222 			}
   222             }
   223 			
   223 
   224 			instance = new Netplay(applicationContext, defaultScheme, defaultWeaponset);
   224             instance = new Netplay(applicationContext, defaultScheme, defaultWeaponset);
   225 		}
   225         }
   226 		return instance;
   226         return instance;
   227 	}
   227     }
   228 
   228 
   229 	public State getState() {
   229     public State getState() {
   230 		return state;
   230         return state;
   231 	}
   231     }
   232 	
   232 
   233 	private void changeState(State newState) {
   233     private void changeState(State newState) {
   234 		if(newState != state) {
   234         if(newState != state) {
   235 			state = newState;
   235             state = newState;
   236 			broadcastManager.sendBroadcastSync(new Intent(ACTION_STATE_CHANGED));
   236             broadcastManager.sendBroadcastSync(new Intent(ACTION_STATE_CHANGED));
   237 		}
   237         }
   238 	}
   238     }
   239 	
   239 
   240 	public boolean isChief() {
   240     public boolean isChief() {
   241 		if(netRoomState != null) {
   241         if(netRoomState != null) {
   242 			return netRoomState.getChiefStatus();
   242             return netRoomState.getChiefStatus();
   243 		} else {
   243         } else {
   244 			return false;
   244             return false;
   245 		}
   245         }
   246 	}
   246     }
   247 	
   247 
   248 	public String getPlayerName() {
   248     public String getPlayerName() {
   249 		return playerName;
   249         return playerName;
   250 	}
   250     }
   251 	
   251 
   252 	boolean sendToNet(ToNetMsgType what) {
   252     boolean sendToNet(ToNetMsgType what) {
   253 		return sendToNet(what, 0, null);
   253         return sendToNet(what, 0, null);
   254 	}
   254     }
   255 	
   255 
   256 	boolean sendToNet(ToNetMsgType what, Object obj) {
   256     boolean sendToNet(ToNetMsgType what, Object obj) {
   257 		return sendToNet(what, 0, obj);
   257         return sendToNet(what, 0, obj);
   258 	}
   258     }
   259 	
   259 
   260 	boolean sendToNet(ToNetMsgType what, int arg1, Object obj) {
   260     boolean sendToNet(ToNetMsgType what, int arg1, Object obj) {
   261 		if(connection != null) {
   261         if(connection != null) {
   262 			Handler handler = connection.toNetHandler;
   262             Handler handler = connection.toNetHandler;
   263 			return handler.sendMessage(handler.obtainMessage(what.ordinal(), arg1, 0, obj));
   263             return handler.sendMessage(handler.obtainMessage(what.ordinal(), arg1, 0, obj));
   264 		} else {
   264         } else {
   265 			return false;
   265             return false;
   266 		}
   266         }
   267 	}
   267     }
   268 	
   268 
   269 	private MessageLog getCurrentLog() {
   269     private MessageLog getCurrentLog() {
   270 		if(state == State.ROOM) {
   270         if(state == State.ROOM) {
   271 			return roomChatlog;
   271             return roomChatlog;
   272 		} else {
   272         } else {
   273 			return lobbyChatlog;
   273             return lobbyChatlog;
   274 		}
   274         }
   275 	}
   275     }
   276 	
   276 
   277 	public static enum FromNetMsgType {
   277     public static enum FromNetMsgType {
   278 		MSG_LOBBY_JOIN,
   278         MSG_LOBBY_JOIN,
   279 		MSG_LOBBY_LEAVE,
   279         MSG_LOBBY_LEAVE,
   280 		MSG_ROOM_JOIN,
   280         MSG_ROOM_JOIN,
   281 		MSG_ROOM_LEAVE,
   281         MSG_ROOM_LEAVE,
   282 		MSG_CLIENT_FLAGS,
   282         MSG_CLIENT_FLAGS,
   283 		MSG_CHAT,
   283         MSG_CHAT,
   284 		MSG_MESSAGE,
   284         MSG_MESSAGE,
   285 		MSG_ROOM_ADD,
   285         MSG_ROOM_ADD,
   286 		MSG_ROOM_UPDATE,
   286         MSG_ROOM_UPDATE,
   287 		MSG_ROOM_DELETE,
   287         MSG_ROOM_DELETE,
   288 		MSG_ROOMLIST,
   288         MSG_ROOMLIST,
   289 		MSG_CONNECTED,
   289         MSG_CONNECTED,
   290 		MSG_DISCONNECTED,
   290         MSG_DISCONNECTED,
   291 		MSG_PASSWORD_REQUEST,
   291         MSG_PASSWORD_REQUEST,
   292 		MSG_ENTER_ROOM_FROM_LOBBY,
   292         MSG_ENTER_ROOM_FROM_LOBBY,
   293 		MSG_LEAVE_ROOM,
   293         MSG_LEAVE_ROOM,
   294 		MSG_TEAM_ADDED,
   294         MSG_TEAM_ADDED,
   295 		MSG_TEAM_DELETED,
   295         MSG_TEAM_DELETED,
   296 		MSG_TEAM_ACCEPTED,
   296         MSG_TEAM_ACCEPTED,
   297 		MSG_TEAM_COLOR_CHANGED,
   297         MSG_TEAM_COLOR_CHANGED,
   298 		MSG_HOG_COUNT_CHANGED,
   298         MSG_HOG_COUNT_CHANGED,
   299 		MSG_ENGINE_MESSAGE,
   299         MSG_ENGINE_MESSAGE,
   300 		MSG_RUN_GAME,
   300         MSG_RUN_GAME,
   301 		MSG_SCHEME_CHANGED,
   301         MSG_SCHEME_CHANGED,
   302 		MSG_MAP_CHANGED,
   302         MSG_MAP_CHANGED,
   303 		MSG_SCRIPT_CHANGED,
   303         MSG_SCRIPT_CHANGED,
   304 		MSG_WEAPONSET_CHANGED;
   304         MSG_WEAPONSET_CHANGED;
   305 		
   305 
   306 		static final List<FromNetMsgType> values = Collections.unmodifiableList(Arrays.asList(FromNetMsgType.values()));
   306         static final List<FromNetMsgType> values = Collections.unmodifiableList(Arrays.asList(FromNetMsgType.values()));
   307 	}
   307     }
   308 	
   308 
   309 	/**
   309     /**
   310 	 * Processes messages from the networking system. Always runs on the main thread.
   310      * Processes messages from the networking system. Always runs on the main thread.
   311 	 */
   311      */
   312 	@SuppressLint("HandlerLeak")
   312     @SuppressLint("HandlerLeak")
   313 	final class FromNetHandler extends Handler {
   313     final class FromNetHandler extends Handler {
   314 		public FromNetHandler() {
   314         public FromNetHandler() {
   315 			super(Looper.getMainLooper());
   315             super(Looper.getMainLooper());
   316 		}
   316         }
   317 		
   317 
   318 		@SuppressWarnings("unchecked")
   318         @SuppressWarnings("unchecked")
   319 		@Override
   319         @Override
   320 		public void handleMessage(Message msg) {
   320         public void handleMessage(Message msg) {
   321 			switch(FromNetMsgType.values.get(msg.what)) {
   321             switch(FromNetMsgType.values.get(msg.what)) {
   322 			case MSG_LOBBY_JOIN: {
   322             case MSG_LOBBY_JOIN: {
   323 				String name = (String)msg.obj;
   323                 String name = (String)msg.obj;
   324 				lobbyPlayerlist.put(name, new Player(name, false, false));
   324                 lobbyPlayerlist.put(name, new Player(name, false, false));
   325 				lobbyChatlog.appendPlayerJoin(name);
   325                 lobbyChatlog.appendPlayerJoin(name);
   326 				break;
   326                 break;
   327 			}
   327             }
   328 			case MSG_LOBBY_LEAVE: {
   328             case MSG_LOBBY_LEAVE: {
   329 				Pair<String, String> args = (Pair<String, String>)msg.obj;
   329                 Pair<String, String> args = (Pair<String, String>)msg.obj;
   330 				lobbyPlayerlist.remove(args.first);
   330                 lobbyPlayerlist.remove(args.first);
   331 				lobbyChatlog.appendPlayerLeave(args.first, args.second);
   331                 lobbyChatlog.appendPlayerLeave(args.first, args.second);
   332 				break;
   332                 break;
   333 			}
   333             }
   334 			case MSG_ROOM_JOIN: {
   334             case MSG_ROOM_JOIN: {
   335 				String name = (String)msg.obj;
   335                 String name = (String)msg.obj;
   336 				Player p = lobbyPlayerlist.get(name);
   336                 Player p = lobbyPlayerlist.get(name);
   337 				if(p==null) {
   337                 if(p==null) {
   338 					Log.w("Netplay", "Unknown player joined room: "+name);
   338                     Log.w("Netplay", "Unknown player joined room: "+name);
   339 					p = new Player(name, false, false);
   339                     p = new Player(name, false, false);
   340 				}
   340                 }
   341 				roomPlayerlist.put(name, new PlayerInRoom(p, false, false));
   341                 roomPlayerlist.put(name, new PlayerInRoom(p, false, false));
   342 				roomChatlog.appendPlayerJoin(name);
   342                 roomChatlog.appendPlayerJoin(name);
   343 				break;
   343                 break;
   344 			}
   344             }
   345 			case MSG_ROOM_LEAVE: {
   345             case MSG_ROOM_LEAVE: {
   346 				Pair<String, String> args = (Pair<String, String>)msg.obj;
   346                 Pair<String, String> args = (Pair<String, String>)msg.obj;
   347 				roomPlayerlist.remove(args.first);
   347                 roomPlayerlist.remove(args.first);
   348 				roomChatlog.appendPlayerLeave(args.first, args.second);
   348                 roomChatlog.appendPlayerLeave(args.first, args.second);
   349 				break;
   349                 break;
   350 			}
   350             }
   351 			case MSG_CLIENT_FLAGS: {
   351             case MSG_CLIENT_FLAGS: {
   352 				ClientFlagsUpdate upd = (ClientFlagsUpdate)msg.obj;
   352                 ClientFlagsUpdate upd = (ClientFlagsUpdate)msg.obj;
   353 				PlayerInRoom pir = roomPlayerlist.get(upd.nick);
   353                 PlayerInRoom pir = roomPlayerlist.get(upd.nick);
   354 				if(pir != null) {
   354                 if(pir != null) {
   355 					roomPlayerlist.put(upd.nick, upd.applyTo(pir));
   355                     roomPlayerlist.put(upd.nick, upd.applyTo(pir));
   356 				}
   356                 }
   357 				Player p = lobbyPlayerlist.get(upd.nick);
   357                 Player p = lobbyPlayerlist.get(upd.nick);
   358 				if(p != null) {
   358                 if(p != null) {
   359 					lobbyPlayerlist.put(upd.nick, upd.applyTo(p));
   359                     lobbyPlayerlist.put(upd.nick, upd.applyTo(p));
   360 				} else {
   360                 } else {
   361 					Log.w("Netplay", "Received client flags for unknown player "+upd.nick);
   361                     Log.w("Netplay", "Received client flags for unknown player "+upd.nick);
   362 				}
   362                 }
   363 				if(playerName.equals(upd.nick) && upd.appliesTo(ClientFlagsUpdate.FLAG_CHIEF)) {
   363                 if(playerName.equals(upd.nick) && upd.appliesTo(ClientFlagsUpdate.FLAG_CHIEF)) {
   364 					netRoomState.setChief(upd.newFlagState);
   364                     netRoomState.setChief(upd.newFlagState);
   365 				}
   365                 }
   366 				break;
   366                 break;
   367 			}
   367             }
   368 			case MSG_CHAT: {
   368             case MSG_CHAT: {
   369 				Pair<String, String> args = (Pair<String, String>)msg.obj;
   369                 Pair<String, String> args = (Pair<String, String>)msg.obj;
   370 				getCurrentLog().appendChat(args.first, args.second);
   370                 getCurrentLog().appendChat(args.first, args.second);
   371 				for(GameMessageListener listener : gameMessageListeners) {
   371                 for(GameMessageListener listener : gameMessageListeners) {
   372 					listener.onChatMessage(args.first, args.second);
   372                     listener.onChatMessage(args.first, args.second);
   373 				}
   373                 }
   374 				break;
   374                 break;
   375 			}
   375             }
   376 			case MSG_MESSAGE: {
   376             case MSG_MESSAGE: {
   377 				getCurrentLog().appendMessage(msg.arg1, (String)msg.obj);
   377                 getCurrentLog().appendMessage(msg.arg1, (String)msg.obj);
   378 				for(GameMessageListener listener : gameMessageListeners) {
   378                 for(GameMessageListener listener : gameMessageListeners) {
   379 					listener.onMessage(1, (String)msg.obj);
   379                     listener.onMessage(1, (String)msg.obj);
   380 				}
   380                 }
   381 				break;
   381                 break;
   382 			}
   382             }
   383 			case MSG_ROOM_ADD: {
   383             case MSG_ROOM_ADD: {
   384 				Room room = (Room)msg.obj;
   384                 Room room = (Room)msg.obj;
   385 				roomList.addRoomWithNewId(room);
   385                 roomList.addRoomWithNewId(room);
   386 				break;
   386                 break;
   387 			}
   387             }
   388 			case MSG_ROOM_UPDATE: {
   388             case MSG_ROOM_UPDATE: {
   389 				Pair<String, Room> args = (Pair<String, Room>)msg.obj;
   389                 Pair<String, Room> args = (Pair<String, Room>)msg.obj;
   390 				roomList.updateRoom(args.first, args.second);
   390                 roomList.updateRoom(args.first, args.second);
   391 				break;
   391                 break;
   392 			}
   392             }
   393 			case MSG_ROOM_DELETE: {
   393             case MSG_ROOM_DELETE: {
   394 				roomList.remove((String)msg.obj);
   394                 roomList.remove((String)msg.obj);
   395 				break;
   395                 break;
   396 			}
   396             }
   397 			case MSG_ROOMLIST: {
   397             case MSG_ROOMLIST: {
   398 				Room[] rooms = (Room[])msg.obj;
   398                 Room[] rooms = (Room[])msg.obj;
   399 				roomList.updateList(rooms);
   399                 roomList.updateList(rooms);
   400 				break;
   400                 break;
   401 			}
   401             }
   402 			case MSG_CONNECTED: {
   402             case MSG_CONNECTED: {
   403 				playerName = (String)msg.obj;
   403                 playerName = (String)msg.obj;
   404 				changeState(State.LOBBY);
   404                 changeState(State.LOBBY);
   405 				broadcastManager.sendBroadcast(new Intent(ACTION_CONNECTED));
   405                 broadcastManager.sendBroadcast(new Intent(ACTION_CONNECTED));
   406 				break;
   406                 break;
   407 			}
   407             }
   408 			case MSG_DISCONNECTED: {
   408             case MSG_DISCONNECTED: {
   409 				Pair<Boolean, String> args = (Pair<Boolean, String>)msg.obj;
   409                 Pair<Boolean, String> args = (Pair<Boolean, String>)msg.obj;
   410 				for(GameMessageListener listener : gameMessageListeners) {
   410                 for(GameMessageListener listener : gameMessageListeners) {
   411 					listener.onNetDisconnected();
   411                     listener.onNetDisconnected();
   412 				}
   412                 }
   413 				changeState(State.NOT_CONNECTED);
   413                 changeState(State.NOT_CONNECTED);
   414 				connection = null;
   414                 connection = null;
   415 				Intent intent = new Intent(ACTION_DISCONNECTED);
   415                 Intent intent = new Intent(ACTION_DISCONNECTED);
   416 				intent.putExtra(EXTRA_HAS_ERROR, args.first);
   416                 intent.putExtra(EXTRA_HAS_ERROR, args.first);
   417 				intent.putExtra(EXTRA_MESSAGE, args.second);
   417                 intent.putExtra(EXTRA_MESSAGE, args.second);
   418 				broadcastManager.sendBroadcastSync(intent);
   418                 broadcastManager.sendBroadcastSync(intent);
   419 				break;
   419                 break;
   420 			}
   420             }
   421 			case MSG_PASSWORD_REQUEST: {
   421             case MSG_PASSWORD_REQUEST: {
   422 				Intent intent = new Intent(ACTION_PASSWORD_REQUESTED);
   422                 Intent intent = new Intent(ACTION_PASSWORD_REQUESTED);
   423 				intent.putExtra(EXTRA_PLAYERNAME, (String)msg.obj);
   423                 intent.putExtra(EXTRA_PLAYERNAME, (String)msg.obj);
   424 				broadcastManager.sendBroadcast(intent);
   424                 broadcastManager.sendBroadcast(intent);
   425 				break;
   425                 break;
   426 			}
   426             }
   427 			case MSG_ENTER_ROOM_FROM_LOBBY: {
   427             case MSG_ENTER_ROOM_FROM_LOBBY: {
   428 				initRoomState((Boolean)msg.obj);
   428                 initRoomState((Boolean)msg.obj);
   429 				changeState(State.ROOM);
   429                 changeState(State.ROOM);
   430 				Intent intent = new Intent(ACTION_ENTERED_ROOM_FROM_LOBBY);
   430                 Intent intent = new Intent(ACTION_ENTERED_ROOM_FROM_LOBBY);
   431 				broadcastManager.sendBroadcastSync(intent);
   431                 broadcastManager.sendBroadcastSync(intent);
   432 				break;
   432                 break;
   433 			}
   433             }
   434 			case MSG_LEAVE_ROOM: {
   434             case MSG_LEAVE_ROOM: {
   435 				changeState(State.LOBBY);
   435                 changeState(State.LOBBY);
   436 				Intent intent = new Intent(ACTION_LEFT_ROOM);
   436                 Intent intent = new Intent(ACTION_LEFT_ROOM);
   437 				intent.putExtra(EXTRA_MESSAGE, (String)msg.obj);
   437                 intent.putExtra(EXTRA_MESSAGE, (String)msg.obj);
   438 				intent.putExtra(EXTRA_REASON, msg.arg1);
   438                 intent.putExtra(EXTRA_REASON, msg.arg1);
   439 				broadcastManager.sendBroadcastSync(intent);
   439                 broadcastManager.sendBroadcastSync(intent);
   440 				break;
   440                 break;
   441 			}
   441             }
   442 			case MSG_TEAM_ADDED: {
   442             case MSG_TEAM_ADDED: {
   443 				TeamInGame newTeam = (TeamInGame)msg.obj;
   443                 TeamInGame newTeam = (TeamInGame)msg.obj;
   444 				if(isChief()) {
   444                 if(isChief()) {
   445 					int freeColor = TeamInGame.getUnusedOrRandomColorIndex(netRoomState.getTeams().values());
   445                     int freeColor = TeamInGame.getUnusedOrRandomColorIndex(netRoomState.getTeams().values());
   446 					sendToNet(MSG_SEND_TEAM_HOG_COUNT, newTeam.ingameAttribs.hogCount, newTeam.team.name);
   446                     sendToNet(MSG_SEND_TEAM_HOG_COUNT, newTeam.ingameAttribs.hogCount, newTeam.team.name);
   447 					sendToNet(MSG_SEND_TEAM_COLOR_INDEX, freeColor, newTeam.team.name);
   447                     sendToNet(MSG_SEND_TEAM_COLOR_INDEX, freeColor, newTeam.team.name);
   448 					newTeam = newTeam.withAttribs(newTeam.ingameAttribs.withColorIndex(freeColor));
   448                     newTeam = newTeam.withAttribs(newTeam.ingameAttribs.withColorIndex(freeColor));
   449 				}
   449                 }
   450 				netRoomState.putTeam(newTeam);
   450                 netRoomState.putTeam(newTeam);
   451 				break;
   451                 break;
   452 			}
   452             }
   453 			case MSG_TEAM_DELETED: {
   453             case MSG_TEAM_DELETED: {
   454 				netRoomState.removeTeam((String)msg.obj);
   454                 netRoomState.removeTeam((String)msg.obj);
   455 				break;
   455                 break;
   456 			}
   456             }
   457 			case MSG_TEAM_ACCEPTED: {
   457             case MSG_TEAM_ACCEPTED: {
   458 				TeamInGame requestedTeam = netRoomState.requestedTeams.remove(msg.obj);
   458                 TeamInGame requestedTeam = netRoomState.requestedTeams.remove(msg.obj);
   459 				if(requestedTeam!=null) {
   459                 if(requestedTeam!=null) {
   460 					netRoomState.putTeam(requestedTeam);
   460                     netRoomState.putTeam(requestedTeam);
   461 					if(isChief()) {
   461                     if(isChief()) {
   462 						// Not strictly necessary, but QtFrontend does it...
   462                         // Not strictly necessary, but QtFrontend does it...
   463 						sendToNet(MSG_SEND_TEAM_HOG_COUNT, requestedTeam.ingameAttribs.hogCount, requestedTeam.team.name);
   463                         sendToNet(MSG_SEND_TEAM_HOG_COUNT, requestedTeam.ingameAttribs.hogCount, requestedTeam.team.name);
   464 					}
   464                     }
   465 				} else {
   465                 } else {
   466 					Log.e("Netplay", "Got accepted message for team that was never requested.");
   466                     Log.e("Netplay", "Got accepted message for team that was never requested.");
   467 				}
   467                 }
   468 				break;
   468                 break;
   469 			}
   469             }
   470 			case MSG_TEAM_COLOR_CHANGED: {
   470             case MSG_TEAM_COLOR_CHANGED: {
   471 				TeamInGame oldEntry = netRoomState.getTeams().get((String)msg.obj);
   471                 TeamInGame oldEntry = netRoomState.getTeams().get((String)msg.obj);
   472 				if(oldEntry != null) {
   472                 if(oldEntry != null) {
   473 					/*
   473                     /*
   474 					 * If we are chief, we ignore colors from the outside. They only come from the server
   474                      * If we are chief, we ignore colors from the outside. They only come from the server
   475 					 * when someone adds a team then, and we override that choice anyway.
   475                      * when someone adds a team then, and we override that choice anyway.
   476 					 * Worse, that color message arrives *after* we have overridden the color, so it would
   476                      * Worse, that color message arrives *after* we have overridden the color, so it would
   477 					 * re-override it right back.
   477                      * re-override it right back.
   478 					 */
   478                      */
   479 					if(!isChief()) {
   479                     if(!isChief()) {
   480 						TeamIngameAttributes newAttribs = oldEntry.ingameAttribs.withColorIndex(msg.arg1);
   480                         TeamIngameAttributes newAttribs = oldEntry.ingameAttribs.withColorIndex(msg.arg1);
   481 						netRoomState.putTeam(oldEntry.withAttribs(newAttribs));
   481                         netRoomState.putTeam(oldEntry.withAttribs(newAttribs));
   482 					}
   482                     }
   483 				} else {
   483                 } else {
   484 					Log.e("Netplay", "Color update for unknown team "+msg.obj);
   484                     Log.e("Netplay", "Color update for unknown team "+msg.obj);
   485 				}
   485                 }
   486 				break;
   486                 break;
   487 			}
   487             }
   488 			case MSG_HOG_COUNT_CHANGED: {
   488             case MSG_HOG_COUNT_CHANGED: {
   489 				TeamInGame oldEntry = netRoomState.getTeams().get((String)msg.obj);
   489                 TeamInGame oldEntry = netRoomState.getTeams().get((String)msg.obj);
   490 				if(oldEntry != null) {
   490                 if(oldEntry != null) {
   491 					TeamIngameAttributes newAttribs = oldEntry.ingameAttribs.withHogCount(msg.arg1);
   491                     TeamIngameAttributes newAttribs = oldEntry.ingameAttribs.withHogCount(msg.arg1);
   492 					netRoomState.putTeam(oldEntry.withAttribs(newAttribs));
   492                     netRoomState.putTeam(oldEntry.withAttribs(newAttribs));
   493 				} else {
   493                 } else {
   494 					Log.e("Netplay", "Hog count update for unknown team "+msg.obj);
   494                     Log.e("Netplay", "Hog count update for unknown team "+msg.obj);
   495 				}
   495                 }
   496 				break;
   496                 break;
   497 			}
   497             }
   498 			case MSG_ENGINE_MESSAGE: {
   498             case MSG_ENGINE_MESSAGE: {
   499 				byte[] em = (byte[])msg.obj;
   499                 byte[] em = (byte[])msg.obj;
   500 				for(GameMessageListener listener : gameMessageListeners) {
   500                 for(GameMessageListener listener : gameMessageListeners) {
   501 					listener.onEngineMessage(em);
   501                     listener.onEngineMessage(em);
   502 				}
   502                 }
   503 				break;
   503                 break;
   504 			}
   504             }
   505 			case MSG_RUN_GAME: {
   505             case MSG_RUN_GAME: {
   506 				GameConfig config = (GameConfig)msg.obj;
   506                 GameConfig config = (GameConfig)msg.obj;
   507 				for(RunGameListener listener : runGameListeners) {
   507                 for(RunGameListener listener : runGameListeners) {
   508 					listener.runGame(config);
   508                     listener.runGame(config);
   509 				}
   509                 }
   510 				break;
   510                 break;
   511 			}
   511             }
   512 			case MSG_MAP_CHANGED: {
   512             case MSG_MAP_CHANGED: {
   513 				netRoomState.setMapRecipe((MapRecipe)msg.obj);
   513                 netRoomState.setMapRecipe((MapRecipe)msg.obj);
   514 				break;
   514                 break;
   515 			}
   515             }
   516 			case MSG_SCHEME_CHANGED: {
   516             case MSG_SCHEME_CHANGED: {
   517 				netRoomState.setScheme((Scheme)msg.obj);
   517                 netRoomState.setScheme((Scheme)msg.obj);
   518 				break;
   518                 break;
   519 			}
   519             }
   520 			case MSG_SCRIPT_CHANGED: {
   520             case MSG_SCRIPT_CHANGED: {
   521 				netRoomState.setGameStyle((String)msg.obj);
   521                 netRoomState.setGameStyle((String)msg.obj);
   522 				break;
   522                 break;
   523 			}
   523             }
   524 			case MSG_WEAPONSET_CHANGED: {
   524             case MSG_WEAPONSET_CHANGED: {
   525 				netRoomState.setWeaponset((Weaponset)msg.obj);
   525                 netRoomState.setWeaponset((Weaponset)msg.obj);
   526 				break;
   526                 break;
   527 			}
   527             }
   528 			default: {
   528             default: {
   529 				Log.e("FromNetHandler", "Unknown message type: "+msg.what);
   529                 Log.e("FromNetHandler", "Unknown message type: "+msg.what);
   530 				break;
   530                 break;
   531 			}
   531             }
   532 			}
   532             }
   533 		}
   533         }
   534 	}
   534     }
   535 }
   535 }