1 package org.hedgewars.hedgeroid.netplay; |
1 package org.hedgewars.hedgeroid.netplay; |
2 |
2 |
3 import java.io.File; |
3 import static org.hedgewars.hedgeroid.netplay.ThreadedNetConnection.ToNetMsgType.*; |
4 import java.io.FileNotFoundException; |
4 |
|
5 import java.io.IOException; |
|
6 import java.util.Arrays; |
|
7 import java.util.Collections; |
|
8 import java.util.LinkedList; |
|
9 import java.util.List; |
5 import java.util.Map; |
10 import java.util.Map; |
6 import java.util.TreeMap; |
11 import java.util.TreeMap; |
7 |
12 |
8 import org.hedgewars.hedgeroid.R; |
13 import org.hedgewars.hedgeroid.RoomStateManager; |
9 import org.hedgewars.hedgeroid.Utils; |
14 import org.hedgewars.hedgeroid.Datastructures.GameConfig; |
10 import org.hedgewars.hedgeroid.Datastructures.RoomlistRoom; |
15 import org.hedgewars.hedgeroid.Datastructures.MapRecipe; |
|
16 import org.hedgewars.hedgeroid.Datastructures.Player; |
|
17 import org.hedgewars.hedgeroid.Datastructures.PlayerInRoom; |
|
18 import org.hedgewars.hedgeroid.Datastructures.Room; |
|
19 import org.hedgewars.hedgeroid.Datastructures.Scheme; |
|
20 import org.hedgewars.hedgeroid.Datastructures.Schemes; |
11 import org.hedgewars.hedgeroid.Datastructures.Team; |
21 import org.hedgewars.hedgeroid.Datastructures.Team; |
12 import org.hedgewars.hedgeroid.Datastructures.TeamInGame; |
22 import org.hedgewars.hedgeroid.Datastructures.TeamInGame; |
13 import org.hedgewars.hedgeroid.Datastructures.TeamIngameAttributes; |
23 import org.hedgewars.hedgeroid.Datastructures.TeamIngameAttributes; |
|
24 import org.hedgewars.hedgeroid.Datastructures.Weaponset; |
|
25 import org.hedgewars.hedgeroid.Datastructures.Weaponsets; |
14 import org.hedgewars.hedgeroid.frontlib.Flib; |
26 import org.hedgewars.hedgeroid.frontlib.Flib; |
15 import org.hedgewars.hedgeroid.frontlib.Frontlib; |
27 import org.hedgewars.hedgeroid.netplay.ThreadedNetConnection.ToNetMsgType; |
16 import org.hedgewars.hedgeroid.frontlib.Frontlib.BoolCallback; |
28 import org.hedgewars.hedgeroid.util.ObservableTreeMap; |
17 import org.hedgewars.hedgeroid.frontlib.Frontlib.IntStrCallback; |
|
18 import org.hedgewars.hedgeroid.frontlib.Frontlib.MetaschemePtr; |
|
19 import org.hedgewars.hedgeroid.frontlib.Frontlib.NetconnPtr; |
|
20 import org.hedgewars.hedgeroid.frontlib.Frontlib.RoomArrayPtr; |
|
21 import org.hedgewars.hedgeroid.frontlib.Frontlib.RoomCallback; |
|
22 import org.hedgewars.hedgeroid.frontlib.Frontlib.RoomListCallback; |
|
23 import org.hedgewars.hedgeroid.frontlib.Frontlib.RoomPtr; |
|
24 import org.hedgewars.hedgeroid.frontlib.Frontlib.StrBoolCallback; |
|
25 import org.hedgewars.hedgeroid.frontlib.Frontlib.StrCallback; |
|
26 import org.hedgewars.hedgeroid.frontlib.Frontlib.StrIntCallback; |
|
27 import org.hedgewars.hedgeroid.frontlib.Frontlib.StrRoomCallback; |
|
28 import org.hedgewars.hedgeroid.frontlib.Frontlib.StrStrCallback; |
|
29 import org.hedgewars.hedgeroid.frontlib.Frontlib.TeamCallback; |
|
30 import org.hedgewars.hedgeroid.frontlib.Frontlib.TeamPtr; |
|
31 import org.hedgewars.hedgeroid.frontlib.Frontlib.VoidCallback; |
|
32 |
29 |
33 import android.annotation.SuppressLint; |
30 import android.annotation.SuppressLint; |
34 import android.content.Context; |
31 import android.content.Context; |
35 import android.content.Intent; |
32 import android.content.Intent; |
36 import android.content.res.Resources; |
|
37 import android.os.Handler; |
33 import android.os.Handler; |
38 import android.os.HandlerThread; |
|
39 import android.os.Looper; |
34 import android.os.Looper; |
40 import android.os.Message; |
35 import android.os.Message; |
41 import android.support.v4.content.LocalBroadcastManager; |
36 import android.support.v4.content.LocalBroadcastManager; |
42 import android.util.Log; |
37 import android.util.Log; |
43 import android.util.Pair; |
38 import android.util.Pair; |
44 |
39 |
45 import com.sun.jna.Pointer; |
|
46 |
40 |
47 /** |
41 /** |
48 * This class manages the application's networking state. |
42 * This class manages the application's networking state. |
49 */ |
43 */ |
50 public class Netplay { |
44 public class Netplay { |
70 private final Context appContext; |
64 private final Context appContext; |
71 private final LocalBroadcastManager broadcastManager; |
65 private final LocalBroadcastManager broadcastManager; |
72 private final FromNetHandler fromNetHandler = new FromNetHandler(); |
66 private final FromNetHandler fromNetHandler = new FromNetHandler(); |
73 |
67 |
74 private State state = State.NOT_CONNECTED; |
68 private State state = State.NOT_CONNECTED; |
75 private int foregroundUsers = 0; // Reference counter of clients requesting foreground tick speed (fast ticks) |
|
76 private boolean chief; // Do we control the current room? |
|
77 private String playerName; |
69 private String playerName; |
|
70 |
|
71 // null or stale if not in room state |
|
72 private final NetRoomState netRoomState = new NetRoomState(this); |
78 |
73 |
79 // null if there is no running connection (==state is NOT_CONNECTED) |
74 // null if there is no running connection (==state is NOT_CONNECTED) |
80 private ThreadedNetConnection connection; |
75 private ThreadedNetConnection connection; |
81 |
76 |
82 public final LobbyPlayerlist lobbyPlayerlist = new LobbyPlayerlist(); |
77 public final ObservableTreeMap<String, Player> lobbyPlayerlist = new ObservableTreeMap<String, Player>(); |
83 public final RoomPlayerlist roomPlayerlist = new RoomPlayerlist(); |
78 public final ObservableTreeMap<String, PlayerInRoom> roomPlayerlist = new ObservableTreeMap<String, PlayerInRoom>(); |
84 public final Roomlist roomList = new Roomlist(); |
79 public final Roomlist roomList = new Roomlist(); |
85 public final MessageLog lobbyChatlog; |
80 public final MessageLog lobbyChatlog; |
86 public final MessageLog roomChatlog; |
81 public final MessageLog roomChatlog; |
87 public final Teamlist roomTeamlist = new Teamlist(); |
82 public final ObservableTreeMap<String, TeamInGame> roomTeamlist = new ObservableTreeMap<String, TeamInGame>(); |
88 private final Map<String, Team> roomRequestedTeams = new TreeMap<String, Team>(); |
83 private final Map<String, Team> roomRequestedTeams = new TreeMap<String, Team>(); |
|
84 |
|
85 private final List<GameMessageListener> gameMessageListeners = new LinkedList<GameMessageListener>(); |
|
86 private final List<RunGameListener> runGameListeners = new LinkedList<RunGameListener>(); |
89 |
87 |
90 public Netplay(Context appContext) { |
88 public Netplay(Context appContext) { |
91 this.appContext = appContext; |
89 this.appContext = appContext; |
92 broadcastManager = LocalBroadcastManager.getInstance(appContext); |
90 broadcastManager = LocalBroadcastManager.getInstance(appContext); |
93 lobbyChatlog = new MessageLog(appContext); |
91 lobbyChatlog = new MessageLog(appContext); |
94 roomChatlog = new MessageLog(appContext); |
92 roomChatlog = new MessageLog(appContext); |
95 } |
93 } |
96 |
94 |
97 private void clearState() { |
95 public RoomStateManager getRoomStateManager() { |
|
96 return netRoomState; |
|
97 } |
|
98 |
|
99 private void clearLobbyState() { |
98 lobbyPlayerlist.clear(); |
100 lobbyPlayerlist.clear(); |
99 roomList.clear(); |
101 roomList.clear(); |
100 lobbyChatlog.clear(); |
102 lobbyChatlog.clear(); |
|
103 } |
|
104 |
|
105 private void initRoomState(boolean chief) { |
|
106 roomChatlog.clear(); |
|
107 roomPlayerlist.clear(); |
|
108 roomTeamlist.clear(); |
|
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 } |
|
124 |
|
125 public void registerGameMessageListener(GameMessageListener listener) { |
|
126 gameMessageListeners.add(listener); |
|
127 } |
|
128 |
|
129 public void unregisterGameMessageListener(GameMessageListener listener) { |
|
130 gameMessageListeners.remove(listener); |
|
131 } |
|
132 |
|
133 public void registerRunGameListener(RunGameListener listener) { |
|
134 runGameListeners.add(listener); |
|
135 } |
|
136 |
|
137 public void unregisterRunGameListener(RunGameListener listener) { |
|
138 runGameListeners.remove(listener); |
101 } |
139 } |
102 |
140 |
103 public void connectToDefaultServer(String playerName) { |
141 public void connectToDefaultServer(String playerName) { |
104 connect(playerName, DEFAULT_SERVER, DEFAULT_PORT); |
142 connect(playerName, DEFAULT_SERVER, DEFAULT_PORT); |
105 } |
143 } |
115 playerName = name; |
153 playerName = name; |
116 if(state != State.NOT_CONNECTED) { |
154 if(state != State.NOT_CONNECTED) { |
117 throw new IllegalStateException("Attempt to start a new connection while the old one was still running."); |
155 throw new IllegalStateException("Attempt to start a new connection while the old one was still running."); |
118 } |
156 } |
119 |
157 |
120 clearState(); |
158 clearLobbyState(); |
121 changeState(State.CONNECTING); |
159 changeState(State.CONNECTING); |
122 connection = ThreadedNetConnection.startConnection(appContext, fromNetHandler, name, host, port); |
160 connection = ThreadedNetConnection.startConnection(appContext, fromNetHandler, name, host, port); |
123 connection.setFastTickRate(foregroundUsers > 0); |
161 connection.setFastTickRate(true); |
124 } |
162 } |
125 |
163 |
126 public void sendNick(String nick) { |
164 public void sendNick(String nick) { |
127 playerName = nick; |
165 playerName = nick; |
128 sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_NICK, nick); |
166 sendToNet(MSG_SEND_NICK, nick); |
129 } |
167 } |
130 public void sendPassword(String password) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_PASSWORD, password); } |
168 public void sendPassword(String password) { sendToNet(MSG_SEND_PASSWORD, password); } |
131 public void sendQuit(String message) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_QUIT, message); } |
169 public void sendQuit(String message) { sendToNet(MSG_SEND_QUIT, message); } |
132 public void sendRoomlistRequest() { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_ROOMLIST_REQUEST); } |
170 public void sendRoomlistRequest() { sendToNet(MSG_SEND_ROOMLIST_REQUEST); } |
133 public void sendPlayerInfoQuery(String name) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_PLAYER_INFO_REQUEST, name); } |
171 public void sendPlayerInfoQuery(String name) { sendToNet(MSG_SEND_PLAYER_INFO_REQUEST, name); } |
134 public void sendChat(String s) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_CHAT, s); } |
172 public void sendChat(String s) { sendToNet(MSG_SEND_CHAT, s); } |
135 public void sendFollowPlayer(String nick) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_FOLLOW_PLAYER, nick); } |
173 public void sendTeamChat(String s) { sendToNet(MSG_SEND_TEAMCHAT, s); } |
136 public void sendJoinRoom(String name) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_JOIN_ROOM, name); } |
174 public void sendFollowPlayer(String nick) { sendToNet(MSG_SEND_FOLLOW_PLAYER, nick); } |
137 public void sendCreateRoom(String name) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_CREATE_ROOM, name); } |
175 public void sendJoinRoom(String name) { sendToNet(MSG_SEND_JOIN_ROOM, name); } |
138 public void sendLeaveRoom(String message) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_LEAVE_ROOM, message); } |
176 public void sendCreateRoom(String name) { sendToNet(MSG_SEND_CREATE_ROOM, name); } |
139 public void sendKick(String player) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_KICK, player); } |
177 public void sendLeaveRoom(String message) { sendToNet(MSG_SEND_LEAVE_ROOM, message); } |
|
178 public void sendKick(String player) { sendToNet(MSG_SEND_KICK, player); } |
140 public void sendAddTeam(Team newTeam) { |
179 public void sendAddTeam(Team newTeam) { |
141 roomRequestedTeams.put(newTeam.name, newTeam); |
180 roomRequestedTeams.put(newTeam.name, newTeam); |
142 sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_ADD_TEAM, newTeam); |
181 sendToNet(MSG_SEND_ADD_TEAM, newTeam); |
143 } |
182 } |
144 public void sendRemoveTeam(String teamName) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_REMOVE_TEAM, teamName); } |
183 public void sendRemoveTeam(String teamName) { sendToNet(MSG_SEND_REMOVE_TEAM, teamName); } |
145 public void sendTeamColorIndex(String teamName, int colorIndex) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_TEAM_COLOR_INDEX, colorIndex, teamName); } |
184 public void sendTeamColorIndex(String teamName, int colorIndex) { sendToNet(MSG_SEND_TEAM_COLOR_INDEX, colorIndex, teamName); } |
146 public void sendTeamHogCount(String teamName, int hogCount) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_TEAM_HOG_COUNT, hogCount, teamName); } |
185 public void sendTeamHogCount(String teamName, int hogCount) { sendToNet(MSG_SEND_TEAM_HOG_COUNT, hogCount, teamName); } |
147 |
186 public void sendEngineMessage(byte[] engineMessage) { sendToNet(MSG_SEND_ENGINE_MESSAGE, engineMessage); } |
148 public void disconnect() { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_DISCONNECT, "User Quit"); } |
187 public void sendRoundFinished(boolean withoutError) { sendToNet(MSG_SEND_ROUND_FINISHED, Boolean.valueOf(withoutError)); } |
|
188 public void sendToggleReady() { sendToNet(MSG_SEND_TOGGLE_READY); } |
|
189 |
|
190 public void disconnect() { sendToNet(MSG_DISCONNECT, "User Quit"); } |
149 |
191 |
150 private static Netplay instance; |
192 private static Netplay instance; |
151 |
193 |
152 /** |
194 /** |
153 * Retrieve the single app-wide instance of the netplay interface, creating it if it |
195 * Retrieve the single app-wide instance of the netplay interface, creating it if it |
384 break; |
435 break; |
385 } |
436 } |
386 case MSG_TEAM_ACCEPTED: { |
437 case MSG_TEAM_ACCEPTED: { |
387 Team requestedTeam = roomRequestedTeams.remove(msg.obj); |
438 Team requestedTeam = roomRequestedTeams.remove(msg.obj); |
388 if(requestedTeam!=null) { |
439 if(requestedTeam!=null) { |
389 TeamIngameAttributes attrs = new TeamIngameAttributes(playerName, roomTeamlist.getUnusedOrRandomColorIndex(), TeamIngameAttributes.DEFAULT_HOG_COUNT, false); |
440 int colorIndex = TeamInGame.getUnusedOrRandomColorIndex(roomTeamlist.getMap().values()); |
|
441 TeamIngameAttributes attrs = new TeamIngameAttributes(playerName, colorIndex, TeamIngameAttributes.DEFAULT_HOG_COUNT, false); |
390 TeamInGame tig = new TeamInGame(requestedTeam, attrs); |
442 TeamInGame tig = new TeamInGame(requestedTeam, attrs); |
391 roomTeamlist.addTeamWithNewId(tig); |
443 roomTeamlist.put(requestedTeam.name, tig); |
392 if(chief) { |
444 if(isChief()) { |
393 sendTeamColorIndex(requestedTeam.name, attrs.colorIndex); |
445 sendTeamColorIndex(requestedTeam.name, attrs.colorIndex); |
394 sendTeamHogCount(requestedTeam.name, attrs.hogCount); |
446 sendTeamHogCount(requestedTeam.name, attrs.hogCount); |
395 } |
447 } |
396 } else { |
448 } else { |
397 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."); |
398 } |
450 } |
399 break; |
451 break; |
400 } |
452 } |
401 case MSG_TEAM_COLOR_CHANGED: { |
453 case MSG_TEAM_COLOR_CHANGED: { |
402 Pair<TeamInGame, Long> oldEntry = roomTeamlist.get((String)msg.obj); |
454 TeamInGame oldEntry = roomTeamlist.get((String)msg.obj); |
403 if(oldEntry != null) { |
455 if(oldEntry != null) { |
404 TeamInGame tig = oldEntry.first; |
456 TeamIngameAttributes newAttribs = oldEntry.ingameAttribs.withColorIndex(msg.arg1); |
405 TeamIngameAttributes tiga = tig.ingameAttribs.withColorIndex(msg.arg1); |
457 roomTeamlist.put(oldEntry.team.name, oldEntry.withAttribs(newAttribs)); |
406 roomTeamlist.put(tig.team.name, Pair.create(tig.withAttribs(tiga), oldEntry.second)); |
|
407 } else { |
458 } else { |
408 Log.e("Netplay", "Color update for unknown team "+msg.obj); |
459 Log.e("Netplay", "Color update for unknown team "+msg.obj); |
409 } |
460 } |
410 break; |
461 break; |
411 } |
462 } |
412 case MSG_HOG_COUNT_CHANGED: { |
463 case MSG_HOG_COUNT_CHANGED: { |
413 Pair<TeamInGame, Long> oldEntry = roomTeamlist.get((String)msg.obj); |
464 TeamInGame oldEntry = roomTeamlist.get((String)msg.obj); |
414 if(oldEntry != null) { |
465 if(oldEntry != null) { |
415 TeamInGame tig = oldEntry.first; |
466 TeamIngameAttributes newAttribs = oldEntry.ingameAttribs.withHogCount(msg.arg1); |
416 TeamIngameAttributes tiga = tig.ingameAttribs.withHogCount(msg.arg1); |
467 roomTeamlist.put(oldEntry.team.name, oldEntry.withAttribs(newAttribs)); |
417 roomTeamlist.put(tig.team.name, Pair.create(tig.withAttribs(tiga), oldEntry.second)); |
|
418 } else { |
468 } else { |
419 Log.e("Netplay", "Hog count update for unknown team "+msg.obj); |
469 Log.e("Netplay", "Hog count update for unknown team "+msg.obj); |
420 } |
470 } |
421 break; |
471 break; |
422 } |
472 } |
|
473 case MSG_ENGINE_MESSAGE: { |
|
474 byte[] em = (byte[])msg.obj; |
|
475 for(GameMessageListener listener : gameMessageListeners) { |
|
476 listener.onEngineMessage(em); |
|
477 } |
|
478 break; |
|
479 } |
|
480 case MSG_RUN_GAME: { |
|
481 GameConfig config = (GameConfig)msg.obj; |
|
482 for(RunGameListener listener : runGameListeners) { |
|
483 listener.runGame(config); |
|
484 } |
|
485 break; |
|
486 } |
|
487 case MSG_MAP_CHANGED: { |
|
488 netRoomState.setMapRecipe((MapRecipe)msg.obj); |
|
489 break; |
|
490 } |
|
491 case MSG_ROOM_CHIEF_STATUS_CHANGED: { |
|
492 netRoomState.setChief((Boolean)msg.obj); |
|
493 break; |
|
494 } |
|
495 case MSG_SCHEME_CHANGED: { |
|
496 netRoomState.setScheme((Scheme)msg.obj); |
|
497 break; |
|
498 } |
|
499 case MSG_SCRIPT_CHANGED: { |
|
500 netRoomState.setGameStyle((String)msg.obj); |
|
501 break; |
|
502 } |
|
503 case MSG_WEAPONSET_CHANGED: { |
|
504 netRoomState.setWeaponset((Weaponset)msg.obj); |
|
505 break; |
|
506 } |
423 default: { |
507 default: { |
424 Log.e("FromNetHandler", "Unknown message type: "+msg.what); |
508 Log.e("FromNetHandler", "Unknown message type: "+msg.what); |
425 break; |
509 break; |
426 } |
510 } |
427 } |
511 } |
428 } |
512 } |
429 } |
513 } |
430 |
|
431 /** |
|
432 * This class handles the actual communication with the networking library, on a separate thread. |
|
433 */ |
|
434 private static class ThreadedNetConnection { |
|
435 private static final long TICK_INTERVAL_FAST = 100; |
|
436 private static final long TICK_INTERVAL_SLOW = 5000; |
|
437 private static final Frontlib FLIB = Flib.INSTANCE; |
|
438 |
|
439 public final ToNetHandler toNetHandler; |
|
440 |
|
441 private final Context appContext; |
|
442 private final FromNetHandler fromNetHandler; |
|
443 private final TickHandler tickHandler; |
|
444 |
|
445 /** |
|
446 * conn can only be null while connecting (the first thing in the thread), and directly after disconnecting, |
|
447 * in the same message (the looper is shut down on disconnect, so there will be no messages after that). |
|
448 */ |
|
449 private NetconnPtr conn; |
|
450 private String playerName; |
|
451 |
|
452 private ThreadedNetConnection(Context appContext, FromNetHandler fromNetHandler) { |
|
453 this.appContext = appContext; |
|
454 this.fromNetHandler = fromNetHandler; |
|
455 |
|
456 HandlerThread thread = new HandlerThread("NetThread"); |
|
457 thread.start(); |
|
458 toNetHandler = new ToNetHandler(thread.getLooper()); |
|
459 tickHandler = new TickHandler(thread.getLooper(), TICK_INTERVAL_FAST, tickCb); |
|
460 } |
|
461 |
|
462 private void connect(final String name, final String host, final int port) { |
|
463 toNetHandler.post(new Runnable() { |
|
464 public void run() { |
|
465 playerName = name == null ? "Player" : name; |
|
466 MetaschemePtr meta = null; |
|
467 File dataPath; |
|
468 try { |
|
469 dataPath = Utils.getDataPathFile(appContext); |
|
470 } catch (FileNotFoundException e) { |
|
471 shutdown(true, appContext.getString(R.string.sdcard_not_mounted)); |
|
472 return; |
|
473 } |
|
474 String metaschemePath = new File(dataPath, "metasettings.ini").getAbsolutePath(); |
|
475 meta = FLIB.flib_metascheme_from_ini(metaschemePath); |
|
476 if(meta == null) { |
|
477 shutdown(true, appContext.getString(R.string.error_unexpected, "Missing metasettings.ini")); |
|
478 return; |
|
479 } |
|
480 conn = FLIB.flib_netconn_create(playerName, meta, dataPath.getAbsolutePath(), host, port); |
|
481 if(conn == null) { |
|
482 shutdown(true, appContext.getString(R.string.error_connection_failed)); |
|
483 return; |
|
484 } |
|
485 FLIB.flib_netconn_onLobbyJoin(conn, lobbyJoinCb, null); |
|
486 FLIB.flib_netconn_onLobbyLeave(conn, lobbyLeaveCb, null); |
|
487 FLIB.flib_netconn_onRoomJoin(conn, roomJoinCb, null); |
|
488 FLIB.flib_netconn_onRoomLeave(conn, roomLeaveCb, null); |
|
489 FLIB.flib_netconn_onChat(conn, chatCb, null); |
|
490 FLIB.flib_netconn_onMessage(conn, messageCb, null); |
|
491 FLIB.flib_netconn_onRoomAdd(conn, roomAddCb, null); |
|
492 FLIB.flib_netconn_onRoomUpdate(conn, roomUpdateCb, null); |
|
493 FLIB.flib_netconn_onRoomDelete(conn, roomDeleteCb, null); |
|
494 FLIB.flib_netconn_onConnected(conn, connectedCb, null); |
|
495 FLIB.flib_netconn_onRoomlist(conn, roomlistCb, null); |
|
496 FLIB.flib_netconn_onDisconnected(conn, disconnectCb, null); |
|
497 FLIB.flib_netconn_onPasswordRequest(conn, passwordRequestCb, null); |
|
498 FLIB.flib_netconn_onEnterRoom(conn, enterRoomCb, null); |
|
499 FLIB.flib_netconn_onLeaveRoom(conn, leaveRoomCb, null); |
|
500 FLIB.flib_netconn_onReadyState(conn, readyStateCb, null); |
|
501 FLIB.flib_netconn_onTeamAdd(conn, teamAddedCb, null); |
|
502 FLIB.flib_netconn_onTeamDelete(conn, teamDeletedCb, null); |
|
503 FLIB.flib_netconn_onTeamAccepted(conn, teamAcceptedCb, null); |
|
504 FLIB.flib_netconn_onTeamColorChanged(conn, teamColorChangedCb, null); |
|
505 FLIB.flib_netconn_onHogCountChanged(conn, hogCountChangedCb, null); |
|
506 |
|
507 FLIB.flib_metascheme_release(meta); |
|
508 tickHandler.start(); |
|
509 } |
|
510 }); |
|
511 } |
|
512 |
|
513 public static ThreadedNetConnection startConnection(Context appContext, FromNetHandler fromNetHandler, String playerName, String host, int port) { |
|
514 ThreadedNetConnection result = new ThreadedNetConnection(appContext, fromNetHandler); |
|
515 result.connect(playerName, host, port); |
|
516 return result; |
|
517 } |
|
518 |
|
519 public void setFastTickRate(boolean fastTickRate) { |
|
520 tickHandler.setInterval(fastTickRate ? TICK_INTERVAL_FAST : TICK_INTERVAL_SLOW); |
|
521 } |
|
522 |
|
523 private final Runnable tickCb = new Runnable() { |
|
524 public void run() { |
|
525 FLIB.flib_netconn_tick(conn); |
|
526 } |
|
527 }; |
|
528 |
|
529 private final StrCallback lobbyJoinCb = new StrCallback() { |
|
530 public void callback(Pointer context, String name) { |
|
531 sendFromNet(FromNetHandler.MSG_LOBBY_JOIN, name); |
|
532 } |
|
533 }; |
|
534 |
|
535 private final StrStrCallback lobbyLeaveCb = new StrStrCallback() { |
|
536 public void callback(Pointer context, String name, String msg) { |
|
537 sendFromNet(FromNetHandler.MSG_LOBBY_LEAVE, Pair.create(name, msg)); |
|
538 } |
|
539 }; |
|
540 |
|
541 private final StrCallback roomJoinCb = new StrCallback() { |
|
542 public void callback(Pointer context, String name) { |
|
543 sendFromNet(FromNetHandler.MSG_ROOM_JOIN, name); |
|
544 } |
|
545 }; |
|
546 private final StrStrCallback roomLeaveCb = new StrStrCallback() { |
|
547 public void callback(Pointer context, String name, String message) { |
|
548 sendFromNet(FromNetHandler.MSG_ROOM_LEAVE, Pair.create(name, message)); |
|
549 } |
|
550 }; |
|
551 private final StrStrCallback chatCb = new StrStrCallback() { |
|
552 public void callback(Pointer context, String name, String msg) { |
|
553 sendFromNet(FromNetHandler.MSG_CHAT, Pair.create(name, msg)); |
|
554 } |
|
555 }; |
|
556 |
|
557 private final IntStrCallback messageCb = new IntStrCallback() { |
|
558 public void callback(Pointer context, int type, String msg) { |
|
559 sendFromNet(FromNetHandler.MSG_MESSAGE, type, msg); |
|
560 } |
|
561 }; |
|
562 |
|
563 private final RoomCallback roomAddCb = new RoomCallback() { |
|
564 public void callback(Pointer context, RoomPtr roomPtr) { |
|
565 sendFromNet(FromNetHandler.MSG_ROOM_ADD, roomPtr.deref()); |
|
566 } |
|
567 }; |
|
568 |
|
569 private final StrRoomCallback roomUpdateCb = new StrRoomCallback() { |
|
570 public void callback(Pointer context, String name, RoomPtr roomPtr) { |
|
571 sendFromNet(FromNetHandler.MSG_ROOM_UPDATE, Pair.create(name, roomPtr.deref())); |
|
572 } |
|
573 }; |
|
574 |
|
575 private final StrCallback roomDeleteCb = new StrCallback() { |
|
576 public void callback(Pointer context, final String name) { |
|
577 sendFromNet(FromNetHandler.MSG_ROOM_DELETE, name); |
|
578 } |
|
579 }; |
|
580 |
|
581 private final RoomListCallback roomlistCb = new RoomListCallback() { |
|
582 public void callback(Pointer context, RoomArrayPtr arg1, int count) { |
|
583 sendFromNet(FromNetHandler.MSG_ROOMLIST, arg1.getRooms(count)); |
|
584 } |
|
585 }; |
|
586 |
|
587 private final VoidCallback connectedCb = new VoidCallback() { |
|
588 public void callback(Pointer context) { |
|
589 FLIB.flib_netconn_send_request_roomlist(conn); |
|
590 playerName = FLIB.flib_netconn_get_playername(conn); |
|
591 sendFromNet(FromNetHandler.MSG_CONNECTED, playerName); |
|
592 } |
|
593 }; |
|
594 |
|
595 private final StrCallback passwordRequestCb = new StrCallback() { |
|
596 public void callback(Pointer context, String nickname) { |
|
597 sendFromNet(FromNetHandler.MSG_PASSWORD_REQUEST, playerName); |
|
598 } |
|
599 }; |
|
600 |
|
601 private final BoolCallback enterRoomCb = new BoolCallback() { |
|
602 public void callback(Pointer context, boolean isChief) { |
|
603 sendFromNet(FromNetHandler.MSG_ENTER_ROOM_FROM_LOBBY, isChief); |
|
604 } |
|
605 }; |
|
606 |
|
607 private final IntStrCallback leaveRoomCb = new IntStrCallback() { |
|
608 public void callback(Pointer context, int reason, String message) { |
|
609 sendFromNet(FromNetHandler.MSG_LEAVE_ROOM, reason, message); |
|
610 } |
|
611 }; |
|
612 |
|
613 private final StrBoolCallback readyStateCb = new StrBoolCallback() { |
|
614 public void callback(Pointer context, String player, boolean ready) { |
|
615 sendFromNet(FromNetHandler.MSG_READYSTATE, Pair.create(player, ready)); |
|
616 } |
|
617 }; |
|
618 |
|
619 private final TeamCallback teamAddedCb = new TeamCallback() { |
|
620 public void callback(Pointer context, TeamPtr team) { |
|
621 sendFromNet(FromNetHandler.MSG_TEAM_ADDED, team.deref().team); |
|
622 } |
|
623 }; |
|
624 |
|
625 private final StrCallback teamDeletedCb = new StrCallback() { |
|
626 public void callback(Pointer context, String teamName) { |
|
627 sendFromNet(FromNetHandler.MSG_TEAM_DELETED, teamName); |
|
628 } |
|
629 }; |
|
630 |
|
631 private final StrCallback teamAcceptedCb = new StrCallback() { |
|
632 public void callback(Pointer context, String teamName) { |
|
633 sendFromNet(FromNetHandler.MSG_TEAM_ACCEPTED, teamName); |
|
634 } |
|
635 }; |
|
636 |
|
637 private final StrIntCallback teamColorChangedCb = new StrIntCallback() { |
|
638 public void callback(Pointer context, String teamName, int colorIndex) { |
|
639 sendFromNet(FromNetHandler.MSG_TEAM_COLOR_CHANGED, colorIndex, teamName); |
|
640 } |
|
641 }; |
|
642 |
|
643 private final StrIntCallback hogCountChangedCb = new StrIntCallback() { |
|
644 public void callback(Pointer context, String teamName, int hogCount) { |
|
645 sendFromNet(FromNetHandler.MSG_HOG_COUNT_CHANGED, hogCount, teamName); |
|
646 } |
|
647 }; |
|
648 |
|
649 private void shutdown(boolean error, String message) { |
|
650 if(conn != null) { |
|
651 FLIB.flib_netconn_destroy(conn); |
|
652 conn = null; |
|
653 } |
|
654 tickHandler.stop(); |
|
655 toNetHandler.getLooper().quit(); |
|
656 sendFromNet(FromNetHandler.MSG_DISCONNECTED, Pair.create(error, message)); |
|
657 } |
|
658 |
|
659 private final IntStrCallback disconnectCb = new IntStrCallback() { |
|
660 public void callback(Pointer context, int reason, String message) { |
|
661 Boolean error = reason != Frontlib.NETCONN_DISCONNECT_NORMAL; |
|
662 String messageForUser = createDisconnectUserMessage(appContext.getResources(), reason, message); |
|
663 shutdown(error, messageForUser); |
|
664 } |
|
665 }; |
|
666 |
|
667 private static String createDisconnectUserMessage(Resources res, int reason, String message) { |
|
668 switch(reason) { |
|
669 case Frontlib.NETCONN_DISCONNECT_AUTH_FAILED: |
|
670 return res.getString(R.string.error_auth_failed); |
|
671 case Frontlib.NETCONN_DISCONNECT_CONNLOST: |
|
672 return res.getString(R.string.error_connection_lost); |
|
673 case Frontlib.NETCONN_DISCONNECT_INTERNAL_ERROR: |
|
674 return res.getString(R.string.error_unexpected, message); |
|
675 case Frontlib.NETCONN_DISCONNECT_SERVER_TOO_OLD: |
|
676 return res.getString(R.string.error_server_too_old); |
|
677 default: |
|
678 return message; |
|
679 } |
|
680 } |
|
681 |
|
682 private boolean sendFromNet(int what, Object obj) { |
|
683 return fromNetHandler.sendMessage(fromNetHandler.obtainMessage(what, obj)); |
|
684 } |
|
685 |
|
686 private boolean sendFromNet(int what, int arg1, Object obj) { |
|
687 return fromNetHandler.sendMessage(fromNetHandler.obtainMessage(what, arg1, 0, obj)); |
|
688 } |
|
689 |
|
690 /** |
|
691 * Processes messages to the networking system. Runs on a non-main thread. |
|
692 */ |
|
693 @SuppressLint("HandlerLeak") |
|
694 public final class ToNetHandler extends Handler { |
|
695 public static final int MSG_SEND_NICK = 0; |
|
696 public static final int MSG_SEND_PASSWORD = 1; |
|
697 public static final int MSG_SEND_QUIT = 2; |
|
698 public static final int MSG_SEND_ROOMLIST_REQUEST = 3; |
|
699 public static final int MSG_SEND_PLAYER_INFO_REQUEST = 4; |
|
700 public static final int MSG_SEND_CHAT = 5; |
|
701 public static final int MSG_SEND_FOLLOW_PLAYER = 6; |
|
702 public static final int MSG_SEND_JOIN_ROOM = 7; |
|
703 public static final int MSG_SEND_CREATE_ROOM = 8; |
|
704 public static final int MSG_SEND_LEAVE_ROOM = 9; |
|
705 public static final int MSG_SEND_KICK = 10; |
|
706 public static final int MSG_SEND_ADD_TEAM = 11; |
|
707 public static final int MSG_SEND_REMOVE_TEAM = 12; |
|
708 public static final int MSG_DISCONNECT = 13; |
|
709 public static final int MSG_SEND_TEAM_COLOR_INDEX = 14; |
|
710 public static final int MSG_SEND_TEAM_HOG_COUNT = 15; |
|
711 |
|
712 public ToNetHandler(Looper looper) { |
|
713 super(looper); |
|
714 } |
|
715 |
|
716 @Override |
|
717 public void handleMessage(Message msg) { |
|
718 switch(msg.what) { |
|
719 case MSG_SEND_NICK: { |
|
720 FLIB.flib_netconn_send_nick(conn, (String)msg.obj); |
|
721 break; |
|
722 } |
|
723 case MSG_SEND_PASSWORD: { |
|
724 FLIB.flib_netconn_send_password(conn, (String)msg.obj); |
|
725 break; |
|
726 } |
|
727 case MSG_SEND_QUIT: { |
|
728 FLIB.flib_netconn_send_quit(conn, (String)msg.obj); |
|
729 break; |
|
730 } |
|
731 case MSG_SEND_ROOMLIST_REQUEST: { |
|
732 FLIB.flib_netconn_send_request_roomlist(conn); |
|
733 break; |
|
734 } |
|
735 case MSG_SEND_PLAYER_INFO_REQUEST: { |
|
736 FLIB.flib_netconn_send_playerInfo(conn, (String)msg.obj); |
|
737 break; |
|
738 } |
|
739 case MSG_SEND_CHAT: { |
|
740 if(FLIB.flib_netconn_send_chat(conn, (String)msg.obj) == 0) { |
|
741 sendFromNet(FromNetHandler.MSG_CHAT, Pair.create(playerName, (String)msg.obj)); |
|
742 } |
|
743 break; |
|
744 } |
|
745 case MSG_SEND_FOLLOW_PLAYER: { |
|
746 FLIB.flib_netconn_send_playerFollow(conn, (String)msg.obj); |
|
747 break; |
|
748 } |
|
749 case MSG_SEND_JOIN_ROOM: { |
|
750 FLIB.flib_netconn_send_joinRoom(conn, (String)msg.obj); |
|
751 break; |
|
752 } |
|
753 case MSG_SEND_CREATE_ROOM: { |
|
754 FLIB.flib_netconn_send_createRoom(conn, (String)msg.obj); |
|
755 break; |
|
756 } |
|
757 case MSG_SEND_LEAVE_ROOM: { |
|
758 if(FLIB.flib_netconn_send_leaveRoom(conn, (String)msg.obj) == 0) { |
|
759 sendFromNet(FromNetHandler.MSG_LEAVE_ROOM, -1, ""); |
|
760 } |
|
761 break; |
|
762 } |
|
763 case MSG_SEND_KICK: { |
|
764 FLIB.flib_netconn_send_kick(conn, (String)msg.obj); |
|
765 break; |
|
766 } |
|
767 case MSG_SEND_ADD_TEAM: { |
|
768 FLIB.flib_netconn_send_addTeam(conn, TeamPtr.createJavaOwned((Team)msg.obj)); |
|
769 break; |
|
770 } |
|
771 case MSG_SEND_REMOVE_TEAM: { |
|
772 if(FLIB.flib_netconn_send_removeTeam(conn, (String)msg.obj)==0) { |
|
773 sendFromNet(FromNetHandler.MSG_TEAM_DELETED, msg.obj); |
|
774 } |
|
775 break; |
|
776 } |
|
777 case MSG_DISCONNECT: { |
|
778 FLIB.flib_netconn_send_quit(conn, (String)msg.obj); |
|
779 shutdown(false, "User quit"); |
|
780 break; |
|
781 } |
|
782 case MSG_SEND_TEAM_COLOR_INDEX: { |
|
783 if(FLIB.flib_netconn_send_teamColor(conn, (String)msg.obj, msg.arg1)==0) { |
|
784 sendFromNet(FromNetHandler.MSG_TEAM_COLOR_CHANGED, msg.arg1, msg.obj); |
|
785 } |
|
786 break; |
|
787 } |
|
788 case MSG_SEND_TEAM_HOG_COUNT: { |
|
789 if(FLIB.flib_netconn_send_teamHogCount(conn, (String)msg.obj, msg.arg1)==0) { |
|
790 sendFromNet(FromNetHandler.MSG_HOG_COUNT_CHANGED, msg.arg1, msg.obj); |
|
791 } |
|
792 break; |
|
793 } |
|
794 default: { |
|
795 Log.e("ToNetHandler", "Unknown message type: "+msg.what); |
|
796 break; |
|
797 } |
|
798 } |
|
799 } |
|
800 } |
|
801 } |
|
802 } |
514 } |