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 } |