project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/Netplay.java
changeset 7582 714310efad8f
parent 7568 75ba91f14ed5
child 7584 7831c84cc644
equal deleted inserted replaced
7580:c92596feac0d 7582:714310efad8f
     5 import java.io.IOException;
     5 import java.io.IOException;
     6 import java.util.Arrays;
     6 import java.util.Arrays;
     7 import java.util.Collections;
     7 import java.util.Collections;
     8 import java.util.LinkedList;
     8 import java.util.LinkedList;
     9 import java.util.List;
     9 import java.util.List;
    10 import java.util.Map;
       
    11 import java.util.TreeMap;
       
    12 
    10 
    13 import org.hedgewars.hedgeroid.RoomStateManager;
    11 import org.hedgewars.hedgeroid.RoomStateManager;
    14 import org.hedgewars.hedgeroid.Datastructures.GameConfig;
    12 import org.hedgewars.hedgeroid.Datastructures.GameConfig;
    15 import org.hedgewars.hedgeroid.Datastructures.MapRecipe;
    13 import org.hedgewars.hedgeroid.Datastructures.MapRecipe;
    16 import org.hedgewars.hedgeroid.Datastructures.Player;
    14 import org.hedgewars.hedgeroid.Datastructures.Player;
    17 import org.hedgewars.hedgeroid.Datastructures.PlayerInRoom;
    15 import org.hedgewars.hedgeroid.Datastructures.PlayerInRoom;
    18 import org.hedgewars.hedgeroid.Datastructures.Room;
    16 import org.hedgewars.hedgeroid.Datastructures.Room;
    19 import org.hedgewars.hedgeroid.Datastructures.Scheme;
    17 import org.hedgewars.hedgeroid.Datastructures.Scheme;
    20 import org.hedgewars.hedgeroid.Datastructures.Schemes;
    18 import org.hedgewars.hedgeroid.Datastructures.Schemes;
    21 import org.hedgewars.hedgeroid.Datastructures.Team;
       
    22 import org.hedgewars.hedgeroid.Datastructures.TeamInGame;
    19 import org.hedgewars.hedgeroid.Datastructures.TeamInGame;
    23 import org.hedgewars.hedgeroid.Datastructures.TeamIngameAttributes;
    20 import org.hedgewars.hedgeroid.Datastructures.TeamIngameAttributes;
    24 import org.hedgewars.hedgeroid.Datastructures.Weaponset;
    21 import org.hedgewars.hedgeroid.Datastructures.Weaponset;
    25 import org.hedgewars.hedgeroid.Datastructures.Weaponsets;
    22 import org.hedgewars.hedgeroid.Datastructures.Weaponsets;
    26 import org.hedgewars.hedgeroid.frontlib.Flib;
    23 import org.hedgewars.hedgeroid.frontlib.Flib;
    40 
    37 
    41 /**
    38 /**
    42  * This class manages the application's networking state.
    39  * This class manages the application's networking state.
    43  */
    40  */
    44 public class Netplay {
    41 public class Netplay {
    45 	public static enum State { NOT_CONNECTED, CONNECTING, LOBBY, ROOM, INGAME }
    42 	public static enum State { NOT_CONNECTED, CONNECTING, LOBBY, ROOM }
    46 	
    43 	
    47 	// Extras in broadcasts
    44 	// Extras in broadcasts
    48 	public static final String EXTRA_PLAYERNAME = "playerName";
    45 	public static final String EXTRA_PLAYERNAME = "playerName";
    49 	public static final String EXTRA_MESSAGE = "message";
    46 	public static final String EXTRA_MESSAGE = "message";
    50 	public static final String EXTRA_HAS_ERROR = "hasError";
    47 	public static final String EXTRA_HAS_ERROR = "hasError";
    62 	public static final int DEFAULT_PORT = 46631;
    59 	public static final int DEFAULT_PORT = 46631;
    63 		
    60 		
    64 	private final Context appContext;
    61 	private final Context appContext;
    65 	private final LocalBroadcastManager broadcastManager;
    62 	private final LocalBroadcastManager broadcastManager;
    66 	private final FromNetHandler fromNetHandler = new FromNetHandler();
    63 	private final FromNetHandler fromNetHandler = new FromNetHandler();
       
    64 	public final Scheme defaultScheme;
       
    65 	public final Weaponset defaultWeaponset;
    67 	
    66 	
    68 	private State state = State.NOT_CONNECTED;
    67 	private State state = State.NOT_CONNECTED;
    69 	private String playerName;
    68 	private String playerName;
    70 	
    69 	
    71 	// null or stale if not in room state
    70 	// null or stale if not in room state
    77 	public final ObservableTreeMap<String, Player> lobbyPlayerlist = new ObservableTreeMap<String, Player>();
    76 	public final ObservableTreeMap<String, Player> lobbyPlayerlist = new ObservableTreeMap<String, Player>();
    78 	public final ObservableTreeMap<String, PlayerInRoom> roomPlayerlist = new ObservableTreeMap<String, PlayerInRoom>();
    77 	public final ObservableTreeMap<String, PlayerInRoom> roomPlayerlist = new ObservableTreeMap<String, PlayerInRoom>();
    79 	public final Roomlist roomList = new Roomlist();
    78 	public final Roomlist roomList = new Roomlist();
    80 	public final MessageLog lobbyChatlog;
    79 	public final MessageLog lobbyChatlog;
    81 	public final MessageLog roomChatlog;
    80 	public final MessageLog roomChatlog;
    82 	public final ObservableTreeMap<String, TeamInGame> roomTeamlist = new ObservableTreeMap<String, TeamInGame>();
       
    83 	private final Map<String, TeamInGame> roomRequestedTeams = new TreeMap<String, TeamInGame>();
       
    84 	
    81 	
    85 	private final List<GameMessageListener> gameMessageListeners = new LinkedList<GameMessageListener>();
    82 	private final List<GameMessageListener> gameMessageListeners = new LinkedList<GameMessageListener>();
    86 	private final List<RunGameListener> runGameListeners = new LinkedList<RunGameListener>();
    83 	private final List<RunGameListener> runGameListeners = new LinkedList<RunGameListener>();
    87 	
    84 	
    88 	public Netplay(Context appContext) {
    85 	public Netplay(Context appContext, Scheme defaultScheme, Weaponset defaultWeaponset) {
    89 		this.appContext = appContext;
    86 		this.appContext = appContext;
    90 		broadcastManager = LocalBroadcastManager.getInstance(appContext);
    87 		broadcastManager = LocalBroadcastManager.getInstance(appContext);
    91 		lobbyChatlog = new MessageLog(appContext);
    88 		lobbyChatlog = new MessageLog(appContext);
    92 		roomChatlog = new MessageLog(appContext);
    89 		roomChatlog = new MessageLog(appContext);
       
    90 		this.defaultScheme = defaultScheme;
       
    91 		this.defaultWeaponset = defaultWeaponset;
    93 	}
    92 	}
    94 	
    93 	
    95 	public RoomStateManager getRoomStateManager() {
    94 	public RoomStateManager getRoomStateManager() {
    96 		return netRoomState;
    95 		return netRoomState;
    97 	}
    96 	}
   103 	}
   102 	}
   104 	
   103 	
   105 	private void initRoomState(boolean chief) {
   104 	private void initRoomState(boolean chief) {
   106 		roomChatlog.clear();
   105 		roomChatlog.clear();
   107 		roomPlayerlist.clear();
   106 		roomPlayerlist.clear();
   108 		roomTeamlist.clear();
   107 		netRoomState.initRoomState(chief);
   109 		roomRequestedTeams.clear();
       
   110 		
       
   111 		try {
       
   112 			netRoomState.setChief(chief);
       
   113 			netRoomState.setGameStyle(GameConfig.DEFAULT_STYLE);
       
   114 			List<Scheme> schemes = Schemes.loadBuiltinSchemes(appContext);
       
   115 			netRoomState.setScheme(schemes.get(Schemes.toNameList(schemes).indexOf(GameConfig.DEFAULT_SCHEME)));
       
   116 			netRoomState.setMapRecipe(MapRecipe.makeRandomMap(0, MapRecipe.makeRandomSeed(), GameConfig.DEFAULT_THEME));
       
   117 			List<Weaponset> weaponsets = Weaponsets.loadBuiltinWeaponsets(appContext);
       
   118 			netRoomState.setWeaponset(weaponsets.get(Weaponsets.toNameList(weaponsets).indexOf(GameConfig.DEFAULT_WEAPONSET)));
       
   119 			netRoomState.sendFullConfig();
       
   120 		} catch(IOException e) {
       
   121 			throw new RuntimeException(e);
       
   122 		}
       
   123 	}
   108 	}
   124 	
   109 	
   125 	public void registerGameMessageListener(GameMessageListener listener) {
   110 	public void registerGameMessageListener(GameMessageListener listener) {
   126 		gameMessageListeners.add(listener);
   111 		gameMessageListeners.add(listener);
   127 	}
   112 	}
   174 	public void sendFollowPlayer(String nick) { sendToNet(MSG_SEND_FOLLOW_PLAYER, nick); }
   159 	public void sendFollowPlayer(String nick) { sendToNet(MSG_SEND_FOLLOW_PLAYER, nick); }
   175 	public void sendJoinRoom(String name) { sendToNet(MSG_SEND_JOIN_ROOM, name); }
   160 	public void sendJoinRoom(String name) { sendToNet(MSG_SEND_JOIN_ROOM, name); }
   176 	public void sendCreateRoom(String name) { sendToNet(MSG_SEND_CREATE_ROOM, name); }
   161 	public void sendCreateRoom(String name) { sendToNet(MSG_SEND_CREATE_ROOM, name); }
   177 	public void sendLeaveRoom(String message) { sendToNet(MSG_SEND_LEAVE_ROOM, message); }
   162 	public void sendLeaveRoom(String message) { sendToNet(MSG_SEND_LEAVE_ROOM, message); }
   178 	public void sendKick(String player) { sendToNet(MSG_SEND_KICK, player); }
   163 	public void sendKick(String player) { sendToNet(MSG_SEND_KICK, player); }
   179 	public void sendAddTeam(Team newTeam, int colorIndex) {
       
   180 		TeamIngameAttributes tia = new TeamIngameAttributes(playerName, colorIndex, TeamIngameAttributes.DEFAULT_HOG_COUNT, false);
       
   181 		TeamInGame newTeamInGame = new TeamInGame(newTeam, tia);
       
   182 		roomRequestedTeams.put(newTeam.name, newTeamInGame);
       
   183 		sendToNet(MSG_SEND_ADD_TEAM, newTeamInGame);
       
   184 	}
       
   185 	public void sendRemoveTeam(String teamName) { sendToNet(MSG_SEND_REMOVE_TEAM, teamName); }
       
   186 	public void changeTeamColorIndex(String teamName, int colorIndex) {
       
   187 		if(isChief()) {
       
   188 			sendToNet(MSG_SEND_TEAM_COLOR_INDEX, colorIndex, teamName);
       
   189 			TeamInGame team = roomTeamlist.get(teamName);
       
   190 			roomTeamlist.put(teamName, team.withAttribs(team.ingameAttribs.withColorIndex(colorIndex)));
       
   191 		}
       
   192 	}
       
   193 	public void changeTeamHogCount(String teamName, int hogCount) {
       
   194 		if(isChief()) {
       
   195 			sendToNet(MSG_SEND_TEAM_HOG_COUNT, hogCount, teamName);
       
   196 			TeamInGame team = roomTeamlist.get(teamName);
       
   197 			roomTeamlist.put(teamName, team.withAttribs(team.ingameAttribs.withHogCount(hogCount)));
       
   198 		}
       
   199 	}
       
   200 	public void sendEngineMessage(byte[] engineMessage) { sendToNet(MSG_SEND_ENGINE_MESSAGE, engineMessage); }
   164 	public void sendEngineMessage(byte[] engineMessage) { sendToNet(MSG_SEND_ENGINE_MESSAGE, engineMessage); }
   201 	public void sendRoundFinished(boolean withoutError) { sendToNet(MSG_SEND_ROUND_FINISHED, Boolean.valueOf(withoutError)); }
   165 	public void sendRoundFinished(boolean withoutError) { sendToNet(MSG_SEND_ROUND_FINISHED, Boolean.valueOf(withoutError)); }
   202 	public void sendToggleReady() { sendToNet(MSG_SEND_TOGGLE_READY); }
   166 	public void sendToggleReady() { sendToNet(MSG_SEND_TOGGLE_READY); }
       
   167 	public void sendStartGame() { sendToNet(MSG_SEND_START_GAME); }
   203 	
   168 	
   204 	public void disconnect() { sendToNet(MSG_DISCONNECT, "User Quit"); }
   169 	public void disconnect() { sendToNet(MSG_DISCONNECT, "User Quit"); }
   205 	
   170 	
   206 	private static Netplay instance;
   171 	private static Netplay instance;
   207 	
   172 	
   216 		if(instance == null) {
   181 		if(instance == null) {
   217 			// We'll just do it here and never quit it again...
   182 			// We'll just do it here and never quit it again...
   218 			if(Flib.INSTANCE.flib_init() != 0) {
   183 			if(Flib.INSTANCE.flib_init() != 0) {
   219 				throw new RuntimeException("Unable to start frontlib");
   184 				throw new RuntimeException("Unable to start frontlib");
   220 			}
   185 			}
   221 			instance = new Netplay(applicationContext);
   186 			
       
   187 			// We will need some default values for rooms, best load them here
       
   188 			Scheme defaultScheme = null;
       
   189 			Weaponset defaultWeaponset = null;
       
   190 			try {
       
   191 				List<Scheme> schemes = Schemes.loadBuiltinSchemes(applicationContext);
       
   192 				for(Scheme scheme : schemes) {
       
   193 					if(scheme.name.equals(GameConfig.DEFAULT_SCHEME)) {
       
   194 						defaultScheme = scheme;
       
   195 					}
       
   196 				}
       
   197 				List<Weaponset> weaponsets = Weaponsets.loadBuiltinWeaponsets(applicationContext);
       
   198 				for(Weaponset weaponset : weaponsets) {
       
   199 					if(weaponset.name.equals(GameConfig.DEFAULT_WEAPONSET)) {
       
   200 						defaultWeaponset = weaponset;
       
   201 					}
       
   202 				}
       
   203 			} catch(IOException e) {
       
   204 				throw new RuntimeException(e);
       
   205 			}
       
   206 			
       
   207 			if(defaultScheme==null || defaultWeaponset==null) {
       
   208 				throw new RuntimeException("Unable to load default scheme or weaponset");
       
   209 			}
       
   210 			
       
   211 			instance = new Netplay(applicationContext, defaultScheme, defaultWeaponset);
   222 		}
   212 		}
   223 		return instance;
   213 		return instance;
   224 	}
   214 	}
   225 
   215 
   226 	public State getState() {
   216 	public State getState() {
   234 		}
   224 		}
   235 	}
   225 	}
   236 	
   226 	
   237 	public boolean isChief() {
   227 	public boolean isChief() {
   238 		if(netRoomState != null) {
   228 		if(netRoomState != null) {
   239 			return netRoomState.chief;
   229 			return netRoomState.getChiefStatus();
   240 		} else {
   230 		} else {
   241 			return false;
   231 			return false;
   242 		}
   232 		}
   243 	}
   233 	}
   244 	
   234 	
   262 			return false;
   252 			return false;
   263 		}
   253 		}
   264 	}
   254 	}
   265 	
   255 	
   266 	private MessageLog getCurrentLog() {
   256 	private MessageLog getCurrentLog() {
   267 		if(state == State.ROOM || state == State.INGAME) {
   257 		if(state == State.ROOM) {
   268 			return roomChatlog;
   258 			return roomChatlog;
   269 		} else {
   259 		} else {
   270 			return lobbyChatlog;
   260 			return lobbyChatlog;
   271 		}
   261 		}
   272 	}
   262 	}
   433 				break;
   423 				break;
   434 			}
   424 			}
   435 			case MSG_TEAM_ADDED: {
   425 			case MSG_TEAM_ADDED: {
   436 				TeamInGame newTeam = (TeamInGame)msg.obj;
   426 				TeamInGame newTeam = (TeamInGame)msg.obj;
   437 				if(isChief()) {
   427 				if(isChief()) {
   438 					int freeColor = TeamInGame.getUnusedOrRandomColorIndex(roomTeamlist.getMap().values());
   428 					int freeColor = TeamInGame.getUnusedOrRandomColorIndex(netRoomState.getTeams().values());
   439 					sendToNet(MSG_SEND_TEAM_HOG_COUNT, newTeam.ingameAttribs.hogCount, newTeam.team.name);
   429 					sendToNet(MSG_SEND_TEAM_HOG_COUNT, newTeam.ingameAttribs.hogCount, newTeam.team.name);
   440 					sendToNet(MSG_SEND_TEAM_COLOR_INDEX, freeColor, newTeam.team.name);
   430 					sendToNet(MSG_SEND_TEAM_COLOR_INDEX, freeColor, newTeam.team.name);
   441 					newTeam = newTeam.withAttribs(newTeam.ingameAttribs.withColorIndex(freeColor));
   431 					newTeam = newTeam.withAttribs(newTeam.ingameAttribs.withColorIndex(freeColor));
   442 				}
   432 				}
   443 				roomTeamlist.put(newTeam.team.name, newTeam);
   433 				netRoomState.putTeam(newTeam);
   444 				break;
   434 				break;
   445 			}
   435 			}
   446 			case MSG_TEAM_DELETED: {
   436 			case MSG_TEAM_DELETED: {
   447 				roomTeamlist.remove((String)msg.obj);
   437 				netRoomState.removeTeam((String)msg.obj);
   448 				break;
   438 				break;
   449 			}
   439 			}
   450 			case MSG_TEAM_ACCEPTED: {
   440 			case MSG_TEAM_ACCEPTED: {
   451 				TeamInGame requestedTeam = roomRequestedTeams.remove(msg.obj);
   441 				TeamInGame requestedTeam = netRoomState.requestedTeams.remove(msg.obj);
   452 				if(requestedTeam!=null) {
   442 				if(requestedTeam!=null) {
   453 					roomTeamlist.put(requestedTeam.team.name, requestedTeam);
   443 					netRoomState.putTeam(requestedTeam);
   454 					if(isChief()) {
   444 					if(isChief()) {
   455 						// Not strictly necessary, but QtFrontend does it...
   445 						// Not strictly necessary, but QtFrontend does it...
   456 						sendToNet(MSG_SEND_TEAM_HOG_COUNT, requestedTeam.ingameAttribs.hogCount, requestedTeam.team.name);
   446 						sendToNet(MSG_SEND_TEAM_HOG_COUNT, requestedTeam.ingameAttribs.hogCount, requestedTeam.team.name);
   457 					}
   447 					}
   458 				} else {
   448 				} else {
   459 					Log.e("Netplay", "Got accepted message for team that was never requested.");
   449 					Log.e("Netplay", "Got accepted message for team that was never requested.");
   460 				}
   450 				}
   461 				break;
   451 				break;
   462 			}
   452 			}
   463 			case MSG_TEAM_COLOR_CHANGED: {
   453 			case MSG_TEAM_COLOR_CHANGED: {
   464 				TeamInGame oldEntry = roomTeamlist.get((String)msg.obj);
   454 				TeamInGame oldEntry = netRoomState.getTeams().get((String)msg.obj);
   465 				if(oldEntry != null) {
   455 				if(oldEntry != null) {
   466 					/*
   456 					/*
   467 					 * If we are chief, we ignore colors from the outside. They only come from the server
   457 					 * If we are chief, we ignore colors from the outside. They only come from the server
   468 					 * when someone adds a team then, and we override that choice anyway.
   458 					 * when someone adds a team then, and we override that choice anyway.
   469 					 * Worse, that color message arrives *after* we have overridden the color, so it would
   459 					 * Worse, that color message arrives *after* we have overridden the color, so it would
   470 					 * re-override it right back.
   460 					 * re-override it right back.
   471 					 */
   461 					 */
   472 					if(!isChief()) {
   462 					if(!isChief()) {
   473 						TeamIngameAttributes newAttribs = oldEntry.ingameAttribs.withColorIndex(msg.arg1);
   463 						TeamIngameAttributes newAttribs = oldEntry.ingameAttribs.withColorIndex(msg.arg1);
   474 						roomTeamlist.put(oldEntry.team.name, oldEntry.withAttribs(newAttribs));
   464 						netRoomState.putTeam(oldEntry.withAttribs(newAttribs));
   475 					}
   465 					}
   476 				} else {
   466 				} else {
   477 					Log.e("Netplay", "Color update for unknown team "+msg.obj);
   467 					Log.e("Netplay", "Color update for unknown team "+msg.obj);
   478 				}
   468 				}
   479 				break;
   469 				break;
   480 			}
   470 			}
   481 			case MSG_HOG_COUNT_CHANGED: {
   471 			case MSG_HOG_COUNT_CHANGED: {
   482 				TeamInGame oldEntry = roomTeamlist.get((String)msg.obj);
   472 				TeamInGame oldEntry = netRoomState.getTeams().get((String)msg.obj);
   483 				if(oldEntry != null) {
   473 				if(oldEntry != null) {
   484 					TeamIngameAttributes newAttribs = oldEntry.ingameAttribs.withHogCount(msg.arg1);
   474 					TeamIngameAttributes newAttribs = oldEntry.ingameAttribs.withHogCount(msg.arg1);
   485 					roomTeamlist.put(oldEntry.team.name, oldEntry.withAttribs(newAttribs));
   475 					netRoomState.putTeam(oldEntry.withAttribs(newAttribs));
   486 				} else {
   476 				} else {
   487 					Log.e("Netplay", "Hog count update for unknown team "+msg.obj);
   477 					Log.e("Netplay", "Hog count update for unknown team "+msg.obj);
   488 				}
   478 				}
   489 				break;
   479 				break;
   490 			}
   480 			}