diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/Netplay.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/Netplay.java Sat Aug 18 00:22:33 2012 +0200 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/Netplay.java Sat Aug 18 00:47:51 2012 +0200 @@ -1,48 +1,42 @@ package org.hedgewars.hedgeroid.netplay; -import java.io.File; -import java.io.FileNotFoundException; +import static org.hedgewars.hedgeroid.netplay.ThreadedNetConnection.ToNetMsgType.*; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; import java.util.Map; import java.util.TreeMap; -import org.hedgewars.hedgeroid.R; -import org.hedgewars.hedgeroid.Utils; -import org.hedgewars.hedgeroid.Datastructures.RoomlistRoom; +import org.hedgewars.hedgeroid.RoomStateManager; +import org.hedgewars.hedgeroid.Datastructures.GameConfig; +import org.hedgewars.hedgeroid.Datastructures.MapRecipe; +import org.hedgewars.hedgeroid.Datastructures.Player; +import org.hedgewars.hedgeroid.Datastructures.PlayerInRoom; +import org.hedgewars.hedgeroid.Datastructures.Room; +import org.hedgewars.hedgeroid.Datastructures.Scheme; +import org.hedgewars.hedgeroid.Datastructures.Schemes; import org.hedgewars.hedgeroid.Datastructures.Team; import org.hedgewars.hedgeroid.Datastructures.TeamInGame; import org.hedgewars.hedgeroid.Datastructures.TeamIngameAttributes; +import org.hedgewars.hedgeroid.Datastructures.Weaponset; +import org.hedgewars.hedgeroid.Datastructures.Weaponsets; import org.hedgewars.hedgeroid.frontlib.Flib; -import org.hedgewars.hedgeroid.frontlib.Frontlib; -import org.hedgewars.hedgeroid.frontlib.Frontlib.BoolCallback; -import org.hedgewars.hedgeroid.frontlib.Frontlib.IntStrCallback; -import org.hedgewars.hedgeroid.frontlib.Frontlib.MetaschemePtr; -import org.hedgewars.hedgeroid.frontlib.Frontlib.NetconnPtr; -import org.hedgewars.hedgeroid.frontlib.Frontlib.RoomArrayPtr; -import org.hedgewars.hedgeroid.frontlib.Frontlib.RoomCallback; -import org.hedgewars.hedgeroid.frontlib.Frontlib.RoomListCallback; -import org.hedgewars.hedgeroid.frontlib.Frontlib.RoomPtr; -import org.hedgewars.hedgeroid.frontlib.Frontlib.StrBoolCallback; -import org.hedgewars.hedgeroid.frontlib.Frontlib.StrCallback; -import org.hedgewars.hedgeroid.frontlib.Frontlib.StrIntCallback; -import org.hedgewars.hedgeroid.frontlib.Frontlib.StrRoomCallback; -import org.hedgewars.hedgeroid.frontlib.Frontlib.StrStrCallback; -import org.hedgewars.hedgeroid.frontlib.Frontlib.TeamCallback; -import org.hedgewars.hedgeroid.frontlib.Frontlib.TeamPtr; -import org.hedgewars.hedgeroid.frontlib.Frontlib.VoidCallback; +import org.hedgewars.hedgeroid.netplay.ThreadedNetConnection.ToNetMsgType; +import org.hedgewars.hedgeroid.util.ObservableTreeMap; import android.annotation.SuppressLint; import android.content.Context; import android.content.Intent; -import android.content.res.Resources; import android.os.Handler; -import android.os.HandlerThread; import android.os.Looper; import android.os.Message; import android.support.v4.content.LocalBroadcastManager; import android.util.Log; import android.util.Pair; -import com.sun.jna.Pointer; /** * This class manages the application's networking state. @@ -72,21 +66,25 @@ private final FromNetHandler fromNetHandler = new FromNetHandler(); private State state = State.NOT_CONNECTED; - private int foregroundUsers = 0; // Reference counter of clients requesting foreground tick speed (fast ticks) - private boolean chief; // Do we control the current room? private String playerName; + // null or stale if not in room state + private final NetRoomState netRoomState = new NetRoomState(this); + // null if there is no running connection (==state is NOT_CONNECTED) private ThreadedNetConnection connection; - public final LobbyPlayerlist lobbyPlayerlist = new LobbyPlayerlist(); - public final RoomPlayerlist roomPlayerlist = new RoomPlayerlist(); + public final ObservableTreeMap lobbyPlayerlist = new ObservableTreeMap(); + public final ObservableTreeMap roomPlayerlist = new ObservableTreeMap(); public final Roomlist roomList = new Roomlist(); public final MessageLog lobbyChatlog; public final MessageLog roomChatlog; - public final Teamlist roomTeamlist = new Teamlist(); + public final ObservableTreeMap roomTeamlist = new ObservableTreeMap(); private final Map roomRequestedTeams = new TreeMap(); + private final List gameMessageListeners = new LinkedList(); + private final List runGameListeners = new LinkedList(); + public Netplay(Context appContext) { this.appContext = appContext; broadcastManager = LocalBroadcastManager.getInstance(appContext); @@ -94,12 +92,52 @@ roomChatlog = new MessageLog(appContext); } - private void clearState() { + public RoomStateManager getRoomStateManager() { + return netRoomState; + } + + private void clearLobbyState() { lobbyPlayerlist.clear(); roomList.clear(); lobbyChatlog.clear(); } + private void initRoomState(boolean chief) { + roomChatlog.clear(); + roomPlayerlist.clear(); + roomTeamlist.clear(); + roomRequestedTeams.clear(); + + try { + netRoomState.setChief(chief); + netRoomState.setGameStyle(GameConfig.DEFAULT_STYLE); + List schemes = Schemes.loadBuiltinSchemes(appContext); + netRoomState.setScheme(schemes.get(Schemes.toNameList(schemes).indexOf(GameConfig.DEFAULT_SCHEME))); + netRoomState.setMapRecipe(MapRecipe.makeRandomMap(0, MapRecipe.makeRandomSeed(), GameConfig.DEFAULT_THEME)); + List weaponsets = Weaponsets.loadBuiltinWeaponsets(appContext); + netRoomState.setWeaponset(weaponsets.get(Weaponsets.toNameList(weaponsets).indexOf(GameConfig.DEFAULT_WEAPONSET))); + netRoomState.sendFullConfig(); + } catch(IOException e) { + throw new RuntimeException(e); + } + } + + public void registerGameMessageListener(GameMessageListener listener) { + gameMessageListeners.add(listener); + } + + public void unregisterGameMessageListener(GameMessageListener listener) { + gameMessageListeners.remove(listener); + } + + public void registerRunGameListener(RunGameListener listener) { + runGameListeners.add(listener); + } + + public void unregisterRunGameListener(RunGameListener listener) { + runGameListeners.remove(listener); + } + public void connectToDefaultServer(String playerName) { connect(playerName, DEFAULT_SERVER, DEFAULT_PORT); } @@ -117,35 +155,39 @@ throw new IllegalStateException("Attempt to start a new connection while the old one was still running."); } - clearState(); + clearLobbyState(); changeState(State.CONNECTING); connection = ThreadedNetConnection.startConnection(appContext, fromNetHandler, name, host, port); - connection.setFastTickRate(foregroundUsers > 0); + connection.setFastTickRate(true); } public void sendNick(String nick) { playerName = nick; - sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_NICK, nick); + sendToNet(MSG_SEND_NICK, nick); } - public void sendPassword(String password) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_PASSWORD, password); } - public void sendQuit(String message) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_QUIT, message); } - public void sendRoomlistRequest() { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_ROOMLIST_REQUEST); } - public void sendPlayerInfoQuery(String name) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_PLAYER_INFO_REQUEST, name); } - public void sendChat(String s) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_CHAT, s); } - public void sendFollowPlayer(String nick) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_FOLLOW_PLAYER, nick); } - public void sendJoinRoom(String name) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_JOIN_ROOM, name); } - public void sendCreateRoom(String name) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_CREATE_ROOM, name); } - public void sendLeaveRoom(String message) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_LEAVE_ROOM, message); } - public void sendKick(String player) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_KICK, player); } + public void sendPassword(String password) { sendToNet(MSG_SEND_PASSWORD, password); } + public void sendQuit(String message) { sendToNet(MSG_SEND_QUIT, message); } + public void sendRoomlistRequest() { sendToNet(MSG_SEND_ROOMLIST_REQUEST); } + public void sendPlayerInfoQuery(String name) { sendToNet(MSG_SEND_PLAYER_INFO_REQUEST, name); } + public void sendChat(String s) { sendToNet(MSG_SEND_CHAT, s); } + public void sendTeamChat(String s) { sendToNet(MSG_SEND_TEAMCHAT, s); } + public void sendFollowPlayer(String nick) { sendToNet(MSG_SEND_FOLLOW_PLAYER, nick); } + public void sendJoinRoom(String name) { sendToNet(MSG_SEND_JOIN_ROOM, name); } + public void sendCreateRoom(String name) { sendToNet(MSG_SEND_CREATE_ROOM, name); } + public void sendLeaveRoom(String message) { sendToNet(MSG_SEND_LEAVE_ROOM, message); } + public void sendKick(String player) { sendToNet(MSG_SEND_KICK, player); } public void sendAddTeam(Team newTeam) { roomRequestedTeams.put(newTeam.name, newTeam); - sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_ADD_TEAM, newTeam); + sendToNet(MSG_SEND_ADD_TEAM, newTeam); } - public void sendRemoveTeam(String teamName) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_REMOVE_TEAM, teamName); } - public void sendTeamColorIndex(String teamName, int colorIndex) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_TEAM_COLOR_INDEX, colorIndex, teamName); } - public void sendTeamHogCount(String teamName, int hogCount) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_TEAM_HOG_COUNT, hogCount, teamName); } + public void sendRemoveTeam(String teamName) { sendToNet(MSG_SEND_REMOVE_TEAM, teamName); } + public void sendTeamColorIndex(String teamName, int colorIndex) { sendToNet(MSG_SEND_TEAM_COLOR_INDEX, colorIndex, teamName); } + public void sendTeamHogCount(String teamName, int hogCount) { sendToNet(MSG_SEND_TEAM_HOG_COUNT, hogCount, teamName); } + public void sendEngineMessage(byte[] engineMessage) { sendToNet(MSG_SEND_ENGINE_MESSAGE, engineMessage); } + public void sendRoundFinished(boolean withoutError) { sendToNet(MSG_SEND_ROUND_FINISHED, Boolean.valueOf(withoutError)); } + public void sendToggleReady() { sendToNet(MSG_SEND_TOGGLE_READY); } - public void disconnect() { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_DISCONNECT, "User Quit"); } + public void disconnect() { sendToNet(MSG_DISCONNECT, "User Quit"); } private static Netplay instance; @@ -179,51 +221,29 @@ } public boolean isChief() { - return chief; + if(netRoomState != null) { + return netRoomState.chief; + } else { + return false; + } } public String getPlayerName() { return playerName; } - /** - * Indicate that you want network messages to be checked regularly (several times per second). - * As long as nobody requests fast ticks, the network is only checked once every few seconds - * to conserve battery power. - * Once you no longer need fast updates, call unrequestFastTicks. - */ - public void requestFastTicks() { - if(foregroundUsers == Integer.MAX_VALUE) { - throw new RuntimeException("Reference counter overflow"); - } - if(foregroundUsers == 0 && connection != null) { - connection.setFastTickRate(true); - } - foregroundUsers++; - } - - public void unrequestFastTicks() { - if(foregroundUsers == 0) { - throw new RuntimeException("Reference counter underflow"); - } - foregroundUsers--; - if(foregroundUsers == 0 && connection != null) { - connection.setFastTickRate(false); - } - } - - private boolean sendToNet(int what) { + boolean sendToNet(ToNetMsgType what) { return sendToNet(what, 0, null); } - private boolean sendToNet(int what, Object obj) { + boolean sendToNet(ToNetMsgType what, Object obj) { return sendToNet(what, 0, obj); } - private boolean sendToNet(int what, int arg1, Object obj) { + boolean sendToNet(ToNetMsgType what, int arg1, Object obj) { if(connection != null) { Handler handler = connection.toNetHandler; - return handler.sendMessage(handler.obtainMessage(what, arg1, 0, obj)); + return handler.sendMessage(handler.obtainMessage(what.ordinal(), arg1, 0, obj)); } else { return false; } @@ -237,33 +257,44 @@ } } + public static enum FromNetMsgType { + MSG_LOBBY_JOIN, + MSG_LOBBY_LEAVE, + MSG_ROOM_JOIN, + MSG_ROOM_LEAVE, + MSG_CHAT, + MSG_MESSAGE, + MSG_ROOM_ADD, + MSG_ROOM_UPDATE, + MSG_ROOM_DELETE, + MSG_ROOMLIST, + MSG_CONNECTED, + MSG_DISCONNECTED, + MSG_PASSWORD_REQUEST, + MSG_ENTER_ROOM_FROM_LOBBY, + MSG_LEAVE_ROOM, + MSG_READYSTATE, + MSG_TEAM_ADDED, + MSG_TEAM_DELETED, + MSG_TEAM_ACCEPTED, + MSG_TEAM_COLOR_CHANGED, + MSG_HOG_COUNT_CHANGED, + MSG_ENGINE_MESSAGE, + MSG_RUN_GAME, + MSG_SCHEME_CHANGED, + MSG_MAP_CHANGED, + MSG_ROOM_CHIEF_STATUS_CHANGED, + MSG_SCRIPT_CHANGED, + MSG_WEAPONSET_CHANGED; + + static final List values = Collections.unmodifiableList(Arrays.asList(FromNetMsgType.values())); + } + /** * Processes messages from the networking system. Always runs on the main thread. */ @SuppressLint("HandlerLeak") final class FromNetHandler extends Handler { - public static final int MSG_LOBBY_JOIN = 0; - public static final int MSG_LOBBY_LEAVE = 1; - public static final int MSG_ROOM_JOIN = 2; - public static final int MSG_ROOM_LEAVE = 3; - public static final int MSG_CHAT = 4; - public static final int MSG_MESSAGE = 5; - public static final int MSG_ROOM_ADD = 6; - public static final int MSG_ROOM_UPDATE = 7; - public static final int MSG_ROOM_DELETE = 8; - public static final int MSG_ROOMLIST = 9; - public static final int MSG_CONNECTED = 10; - public static final int MSG_DISCONNECTED = 11; - public static final int MSG_PASSWORD_REQUEST = 12; - public static final int MSG_ENTER_ROOM_FROM_LOBBY = 13; - public static final int MSG_LEAVE_ROOM = 14; - public static final int MSG_READYSTATE = 15; - public static final int MSG_TEAM_ADDED = 16; - public static final int MSG_TEAM_DELETED = 17; - public static final int MSG_TEAM_ACCEPTED = 18; - public static final int MSG_TEAM_COLOR_CHANGED = 19; - public static final int MSG_HOG_COUNT_CHANGED = 20; - public FromNetHandler() { super(Looper.getMainLooper()); } @@ -271,10 +302,10 @@ @SuppressWarnings("unchecked") @Override public void handleMessage(Message msg) { - switch(msg.what) { + switch(FromNetMsgType.values.get(msg.what)) { case MSG_LOBBY_JOIN: { String name = (String)msg.obj; - lobbyPlayerlist.addPlayerWithNewId(name); + lobbyPlayerlist.put(name, new Player(name, false, false)); lobbyChatlog.appendPlayerJoin(name); break; } @@ -286,7 +317,12 @@ } case MSG_ROOM_JOIN: { String name = (String)msg.obj; - roomPlayerlist.addPlayerWithNewId(name); + Player p = lobbyPlayerlist.get(name); + if(p==null) { + Log.w("Netplay", "Unknown player joined room: "+name); + p = new Player(name, false, false); + } + roomPlayerlist.put(name, new PlayerInRoom(p, false)); roomChatlog.appendPlayerJoin(name); break; } @@ -299,18 +335,25 @@ case MSG_CHAT: { Pair args = (Pair)msg.obj; getCurrentLog().appendChat(args.first, args.second); + for(GameMessageListener listener : gameMessageListeners) { + listener.onChatMessage(args.first, args.second); + } break; } case MSG_MESSAGE: { getCurrentLog().appendMessage(msg.arg1, (String)msg.obj); + for(GameMessageListener listener : gameMessageListeners) { + listener.onMessage(1, (String)msg.obj); + } break; } case MSG_ROOM_ADD: { - roomList.addRoomWithNewId((RoomlistRoom)msg.obj); + Room room = (Room)msg.obj; + roomList.addRoomWithNewId(room); break; } case MSG_ROOM_UPDATE: { - Pair args = (Pair)msg.obj; + Pair args = (Pair)msg.obj; roomList.updateRoom(args.first, args.second); break; } @@ -319,7 +362,8 @@ break; } case MSG_ROOMLIST: { - roomList.updateList((RoomlistRoom[])msg.obj); + Room[] rooms = (Room[])msg.obj; + roomList.updateList(rooms); break; } case MSG_CONNECTED: { @@ -330,6 +374,9 @@ } case MSG_DISCONNECTED: { Pair args = (Pair)msg.obj; + for(GameMessageListener listener : gameMessageListeners) { + listener.onNetDisconnected(); + } changeState(State.NOT_CONNECTED); connection = null; Intent intent = new Intent(ACTION_DISCONNECTED); @@ -345,12 +392,8 @@ break; } case MSG_ENTER_ROOM_FROM_LOBBY: { - roomChatlog.clear(); - roomPlayerlist.clear(); - roomTeamlist.clear(); - roomRequestedTeams.clear(); + initRoomState((Boolean)msg.obj); changeState(State.ROOM); - chief = (Boolean)msg.obj; Intent intent = new Intent(ACTION_ENTERED_ROOM_FROM_LOBBY); broadcastManager.sendBroadcastSync(intent); break; @@ -365,15 +408,23 @@ } case MSG_READYSTATE: { Pair args = (Pair)msg.obj; - roomPlayerlist.setReady(args.first, args.second); + String name = args.first; + Boolean newReadyState = args.second; + PlayerInRoom oldEntry = roomPlayerlist.get(name); + if(oldEntry==null) { + Log.e("Netplay", "Setting readystate for unknown player "+name); + } else { + roomPlayerlist.put(name, new PlayerInRoom(oldEntry.player, newReadyState)); + } break; } case MSG_TEAM_ADDED: { Team newTeam = (Team)msg.obj; - TeamIngameAttributes attrs = new TeamIngameAttributes(playerName, roomTeamlist.getUnusedOrRandomColorIndex(), TeamIngameAttributes.DEFAULT_HOG_COUNT, false); + int colorIndex = TeamInGame.getUnusedOrRandomColorIndex(roomTeamlist.getMap().values()); + TeamIngameAttributes attrs = new TeamIngameAttributes(playerName, colorIndex, TeamIngameAttributes.DEFAULT_HOG_COUNT, true); TeamInGame tig = new TeamInGame(newTeam, attrs); - roomTeamlist.addTeamWithNewId(tig); - if(chief) { + roomTeamlist.put(newTeam.name, tig); + if(isChief()) { sendTeamColorIndex(newTeam.name, attrs.colorIndex); sendTeamHogCount(newTeam.name, attrs.hogCount); } @@ -386,10 +437,11 @@ case MSG_TEAM_ACCEPTED: { Team requestedTeam = roomRequestedTeams.remove(msg.obj); if(requestedTeam!=null) { - TeamIngameAttributes attrs = new TeamIngameAttributes(playerName, roomTeamlist.getUnusedOrRandomColorIndex(), TeamIngameAttributes.DEFAULT_HOG_COUNT, false); + int colorIndex = TeamInGame.getUnusedOrRandomColorIndex(roomTeamlist.getMap().values()); + TeamIngameAttributes attrs = new TeamIngameAttributes(playerName, colorIndex, TeamIngameAttributes.DEFAULT_HOG_COUNT, false); TeamInGame tig = new TeamInGame(requestedTeam, attrs); - roomTeamlist.addTeamWithNewId(tig); - if(chief) { + roomTeamlist.put(requestedTeam.name, tig); + if(isChief()) { sendTeamColorIndex(requestedTeam.name, attrs.colorIndex); sendTeamHogCount(requestedTeam.name, attrs.hogCount); } @@ -399,27 +451,59 @@ break; } case MSG_TEAM_COLOR_CHANGED: { - Pair oldEntry = roomTeamlist.get((String)msg.obj); + TeamInGame oldEntry = roomTeamlist.get((String)msg.obj); if(oldEntry != null) { - TeamInGame tig = oldEntry.first; - TeamIngameAttributes tiga = tig.ingameAttribs.withColorIndex(msg.arg1); - roomTeamlist.put(tig.team.name, Pair.create(tig.withAttribs(tiga), oldEntry.second)); + TeamIngameAttributes newAttribs = oldEntry.ingameAttribs.withColorIndex(msg.arg1); + roomTeamlist.put(oldEntry.team.name, oldEntry.withAttribs(newAttribs)); } else { Log.e("Netplay", "Color update for unknown team "+msg.obj); } break; } case MSG_HOG_COUNT_CHANGED: { - Pair oldEntry = roomTeamlist.get((String)msg.obj); + TeamInGame oldEntry = roomTeamlist.get((String)msg.obj); if(oldEntry != null) { - TeamInGame tig = oldEntry.first; - TeamIngameAttributes tiga = tig.ingameAttribs.withHogCount(msg.arg1); - roomTeamlist.put(tig.team.name, Pair.create(tig.withAttribs(tiga), oldEntry.second)); + TeamIngameAttributes newAttribs = oldEntry.ingameAttribs.withHogCount(msg.arg1); + roomTeamlist.put(oldEntry.team.name, oldEntry.withAttribs(newAttribs)); } else { Log.e("Netplay", "Hog count update for unknown team "+msg.obj); } break; } + case MSG_ENGINE_MESSAGE: { + byte[] em = (byte[])msg.obj; + for(GameMessageListener listener : gameMessageListeners) { + listener.onEngineMessage(em); + } + break; + } + case MSG_RUN_GAME: { + GameConfig config = (GameConfig)msg.obj; + for(RunGameListener listener : runGameListeners) { + listener.runGame(config); + } + break; + } + case MSG_MAP_CHANGED: { + netRoomState.setMapRecipe((MapRecipe)msg.obj); + break; + } + case MSG_ROOM_CHIEF_STATUS_CHANGED: { + netRoomState.setChief((Boolean)msg.obj); + break; + } + case MSG_SCHEME_CHANGED: { + netRoomState.setScheme((Scheme)msg.obj); + break; + } + case MSG_SCRIPT_CHANGED: { + netRoomState.setGameStyle((String)msg.obj); + break; + } + case MSG_WEAPONSET_CHANGED: { + netRoomState.setWeaponset((Weaponset)msg.obj); + break; + } default: { Log.e("FromNetHandler", "Unknown message type: "+msg.what); break; @@ -427,376 +511,4 @@ } } } - - /** - * This class handles the actual communication with the networking library, on a separate thread. - */ - private static class ThreadedNetConnection { - private static final long TICK_INTERVAL_FAST = 100; - private static final long TICK_INTERVAL_SLOW = 5000; - private static final Frontlib FLIB = Flib.INSTANCE; - - public final ToNetHandler toNetHandler; - - private final Context appContext; - private final FromNetHandler fromNetHandler; - private final TickHandler tickHandler; - - /** - * conn can only be null while connecting (the first thing in the thread), and directly after disconnecting, - * in the same message (the looper is shut down on disconnect, so there will be no messages after that). - */ - private NetconnPtr conn; - private String playerName; - - private ThreadedNetConnection(Context appContext, FromNetHandler fromNetHandler) { - this.appContext = appContext; - this.fromNetHandler = fromNetHandler; - - HandlerThread thread = new HandlerThread("NetThread"); - thread.start(); - toNetHandler = new ToNetHandler(thread.getLooper()); - tickHandler = new TickHandler(thread.getLooper(), TICK_INTERVAL_FAST, tickCb); - } - - private void connect(final String name, final String host, final int port) { - toNetHandler.post(new Runnable() { - public void run() { - playerName = name == null ? "Player" : name; - MetaschemePtr meta = null; - File dataPath; - try { - dataPath = Utils.getDataPathFile(appContext); - } catch (FileNotFoundException e) { - shutdown(true, appContext.getString(R.string.sdcard_not_mounted)); - return; - } - String metaschemePath = new File(dataPath, "metasettings.ini").getAbsolutePath(); - meta = FLIB.flib_metascheme_from_ini(metaschemePath); - if(meta == null) { - shutdown(true, appContext.getString(R.string.error_unexpected, "Missing metasettings.ini")); - return; - } - conn = FLIB.flib_netconn_create(playerName, meta, dataPath.getAbsolutePath(), host, port); - if(conn == null) { - shutdown(true, appContext.getString(R.string.error_connection_failed)); - return; - } - FLIB.flib_netconn_onLobbyJoin(conn, lobbyJoinCb, null); - FLIB.flib_netconn_onLobbyLeave(conn, lobbyLeaveCb, null); - FLIB.flib_netconn_onRoomJoin(conn, roomJoinCb, null); - FLIB.flib_netconn_onRoomLeave(conn, roomLeaveCb, null); - FLIB.flib_netconn_onChat(conn, chatCb, null); - FLIB.flib_netconn_onMessage(conn, messageCb, null); - FLIB.flib_netconn_onRoomAdd(conn, roomAddCb, null); - FLIB.flib_netconn_onRoomUpdate(conn, roomUpdateCb, null); - FLIB.flib_netconn_onRoomDelete(conn, roomDeleteCb, null); - FLIB.flib_netconn_onConnected(conn, connectedCb, null); - FLIB.flib_netconn_onRoomlist(conn, roomlistCb, null); - FLIB.flib_netconn_onDisconnected(conn, disconnectCb, null); - FLIB.flib_netconn_onPasswordRequest(conn, passwordRequestCb, null); - FLIB.flib_netconn_onEnterRoom(conn, enterRoomCb, null); - FLIB.flib_netconn_onLeaveRoom(conn, leaveRoomCb, null); - FLIB.flib_netconn_onReadyState(conn, readyStateCb, null); - FLIB.flib_netconn_onTeamAdd(conn, teamAddedCb, null); - FLIB.flib_netconn_onTeamDelete(conn, teamDeletedCb, null); - FLIB.flib_netconn_onTeamAccepted(conn, teamAcceptedCb, null); - FLIB.flib_netconn_onTeamColorChanged(conn, teamColorChangedCb, null); - FLIB.flib_netconn_onHogCountChanged(conn, hogCountChangedCb, null); - - FLIB.flib_metascheme_release(meta); - tickHandler.start(); - } - }); - } - - public static ThreadedNetConnection startConnection(Context appContext, FromNetHandler fromNetHandler, String playerName, String host, int port) { - ThreadedNetConnection result = new ThreadedNetConnection(appContext, fromNetHandler); - result.connect(playerName, host, port); - return result; - } - - public void setFastTickRate(boolean fastTickRate) { - tickHandler.setInterval(fastTickRate ? TICK_INTERVAL_FAST : TICK_INTERVAL_SLOW); - } - - private final Runnable tickCb = new Runnable() { - public void run() { - FLIB.flib_netconn_tick(conn); - } - }; - - private final StrCallback lobbyJoinCb = new StrCallback() { - public void callback(Pointer context, String name) { - sendFromNet(FromNetHandler.MSG_LOBBY_JOIN, name); - } - }; - - private final StrStrCallback lobbyLeaveCb = new StrStrCallback() { - public void callback(Pointer context, String name, String msg) { - sendFromNet(FromNetHandler.MSG_LOBBY_LEAVE, Pair.create(name, msg)); - } - }; - - private final StrCallback roomJoinCb = new StrCallback() { - public void callback(Pointer context, String name) { - sendFromNet(FromNetHandler.MSG_ROOM_JOIN, name); - } - }; - private final StrStrCallback roomLeaveCb = new StrStrCallback() { - public void callback(Pointer context, String name, String message) { - sendFromNet(FromNetHandler.MSG_ROOM_LEAVE, Pair.create(name, message)); - } - }; - private final StrStrCallback chatCb = new StrStrCallback() { - public void callback(Pointer context, String name, String msg) { - sendFromNet(FromNetHandler.MSG_CHAT, Pair.create(name, msg)); - } - }; - - private final IntStrCallback messageCb = new IntStrCallback() { - public void callback(Pointer context, int type, String msg) { - sendFromNet(FromNetHandler.MSG_MESSAGE, type, msg); - } - }; - - private final RoomCallback roomAddCb = new RoomCallback() { - public void callback(Pointer context, RoomPtr roomPtr) { - sendFromNet(FromNetHandler.MSG_ROOM_ADD, roomPtr.deref()); - } - }; - - private final StrRoomCallback roomUpdateCb = new StrRoomCallback() { - public void callback(Pointer context, String name, RoomPtr roomPtr) { - sendFromNet(FromNetHandler.MSG_ROOM_UPDATE, Pair.create(name, roomPtr.deref())); - } - }; - - private final StrCallback roomDeleteCb = new StrCallback() { - public void callback(Pointer context, final String name) { - sendFromNet(FromNetHandler.MSG_ROOM_DELETE, name); - } - }; - - private final RoomListCallback roomlistCb = new RoomListCallback() { - public void callback(Pointer context, RoomArrayPtr arg1, int count) { - sendFromNet(FromNetHandler.MSG_ROOMLIST, arg1.getRooms(count)); - } - }; - - private final VoidCallback connectedCb = new VoidCallback() { - public void callback(Pointer context) { - FLIB.flib_netconn_send_request_roomlist(conn); - playerName = FLIB.flib_netconn_get_playername(conn); - sendFromNet(FromNetHandler.MSG_CONNECTED, playerName); - } - }; - - private final StrCallback passwordRequestCb = new StrCallback() { - public void callback(Pointer context, String nickname) { - sendFromNet(FromNetHandler.MSG_PASSWORD_REQUEST, playerName); - } - }; - - private final BoolCallback enterRoomCb = new BoolCallback() { - public void callback(Pointer context, boolean isChief) { - sendFromNet(FromNetHandler.MSG_ENTER_ROOM_FROM_LOBBY, isChief); - } - }; - - private final IntStrCallback leaveRoomCb = new IntStrCallback() { - public void callback(Pointer context, int reason, String message) { - sendFromNet(FromNetHandler.MSG_LEAVE_ROOM, reason, message); - } - }; - - private final StrBoolCallback readyStateCb = new StrBoolCallback() { - public void callback(Pointer context, String player, boolean ready) { - sendFromNet(FromNetHandler.MSG_READYSTATE, Pair.create(player, ready)); - } - }; - - private final TeamCallback teamAddedCb = new TeamCallback() { - public void callback(Pointer context, TeamPtr team) { - sendFromNet(FromNetHandler.MSG_TEAM_ADDED, team.deref().team); - } - }; - - private final StrCallback teamDeletedCb = new StrCallback() { - public void callback(Pointer context, String teamName) { - sendFromNet(FromNetHandler.MSG_TEAM_DELETED, teamName); - } - }; - - private final StrCallback teamAcceptedCb = new StrCallback() { - public void callback(Pointer context, String teamName) { - sendFromNet(FromNetHandler.MSG_TEAM_ACCEPTED, teamName); - } - }; - - private final StrIntCallback teamColorChangedCb = new StrIntCallback() { - public void callback(Pointer context, String teamName, int colorIndex) { - sendFromNet(FromNetHandler.MSG_TEAM_COLOR_CHANGED, colorIndex, teamName); - } - }; - - private final StrIntCallback hogCountChangedCb = new StrIntCallback() { - public void callback(Pointer context, String teamName, int hogCount) { - sendFromNet(FromNetHandler.MSG_HOG_COUNT_CHANGED, hogCount, teamName); - } - }; - - private void shutdown(boolean error, String message) { - if(conn != null) { - FLIB.flib_netconn_destroy(conn); - conn = null; - } - tickHandler.stop(); - toNetHandler.getLooper().quit(); - sendFromNet(FromNetHandler.MSG_DISCONNECTED, Pair.create(error, message)); - } - - private final IntStrCallback disconnectCb = new IntStrCallback() { - public void callback(Pointer context, int reason, String message) { - Boolean error = reason != Frontlib.NETCONN_DISCONNECT_NORMAL; - String messageForUser = createDisconnectUserMessage(appContext.getResources(), reason, message); - shutdown(error, messageForUser); - } - }; - - private static String createDisconnectUserMessage(Resources res, int reason, String message) { - switch(reason) { - case Frontlib.NETCONN_DISCONNECT_AUTH_FAILED: - return res.getString(R.string.error_auth_failed); - case Frontlib.NETCONN_DISCONNECT_CONNLOST: - return res.getString(R.string.error_connection_lost); - case Frontlib.NETCONN_DISCONNECT_INTERNAL_ERROR: - return res.getString(R.string.error_unexpected, message); - case Frontlib.NETCONN_DISCONNECT_SERVER_TOO_OLD: - return res.getString(R.string.error_server_too_old); - default: - return message; - } - } - - private boolean sendFromNet(int what, Object obj) { - return fromNetHandler.sendMessage(fromNetHandler.obtainMessage(what, obj)); - } - - private boolean sendFromNet(int what, int arg1, Object obj) { - return fromNetHandler.sendMessage(fromNetHandler.obtainMessage(what, arg1, 0, obj)); - } - - /** - * Processes messages to the networking system. Runs on a non-main thread. - */ - @SuppressLint("HandlerLeak") - public final class ToNetHandler extends Handler { - public static final int MSG_SEND_NICK = 0; - public static final int MSG_SEND_PASSWORD = 1; - public static final int MSG_SEND_QUIT = 2; - public static final int MSG_SEND_ROOMLIST_REQUEST = 3; - public static final int MSG_SEND_PLAYER_INFO_REQUEST = 4; - public static final int MSG_SEND_CHAT = 5; - public static final int MSG_SEND_FOLLOW_PLAYER = 6; - public static final int MSG_SEND_JOIN_ROOM = 7; - public static final int MSG_SEND_CREATE_ROOM = 8; - public static final int MSG_SEND_LEAVE_ROOM = 9; - public static final int MSG_SEND_KICK = 10; - public static final int MSG_SEND_ADD_TEAM = 11; - public static final int MSG_SEND_REMOVE_TEAM = 12; - public static final int MSG_DISCONNECT = 13; - public static final int MSG_SEND_TEAM_COLOR_INDEX = 14; - public static final int MSG_SEND_TEAM_HOG_COUNT = 15; - - public ToNetHandler(Looper looper) { - super(looper); - } - - @Override - public void handleMessage(Message msg) { - switch(msg.what) { - case MSG_SEND_NICK: { - FLIB.flib_netconn_send_nick(conn, (String)msg.obj); - break; - } - case MSG_SEND_PASSWORD: { - FLIB.flib_netconn_send_password(conn, (String)msg.obj); - break; - } - case MSG_SEND_QUIT: { - FLIB.flib_netconn_send_quit(conn, (String)msg.obj); - break; - } - case MSG_SEND_ROOMLIST_REQUEST: { - FLIB.flib_netconn_send_request_roomlist(conn); - break; - } - case MSG_SEND_PLAYER_INFO_REQUEST: { - FLIB.flib_netconn_send_playerInfo(conn, (String)msg.obj); - break; - } - case MSG_SEND_CHAT: { - if(FLIB.flib_netconn_send_chat(conn, (String)msg.obj) == 0) { - sendFromNet(FromNetHandler.MSG_CHAT, Pair.create(playerName, (String)msg.obj)); - } - break; - } - case MSG_SEND_FOLLOW_PLAYER: { - FLIB.flib_netconn_send_playerFollow(conn, (String)msg.obj); - break; - } - case MSG_SEND_JOIN_ROOM: { - FLIB.flib_netconn_send_joinRoom(conn, (String)msg.obj); - break; - } - case MSG_SEND_CREATE_ROOM: { - FLIB.flib_netconn_send_createRoom(conn, (String)msg.obj); - break; - } - case MSG_SEND_LEAVE_ROOM: { - if(FLIB.flib_netconn_send_leaveRoom(conn, (String)msg.obj) == 0) { - sendFromNet(FromNetHandler.MSG_LEAVE_ROOM, -1, ""); - } - break; - } - case MSG_SEND_KICK: { - FLIB.flib_netconn_send_kick(conn, (String)msg.obj); - break; - } - case MSG_SEND_ADD_TEAM: { - FLIB.flib_netconn_send_addTeam(conn, TeamPtr.createJavaOwned((Team)msg.obj)); - break; - } - case MSG_SEND_REMOVE_TEAM: { - if(FLIB.flib_netconn_send_removeTeam(conn, (String)msg.obj)==0) { - sendFromNet(FromNetHandler.MSG_TEAM_DELETED, msg.obj); - } - break; - } - case MSG_DISCONNECT: { - FLIB.flib_netconn_send_quit(conn, (String)msg.obj); - shutdown(false, "User quit"); - break; - } - case MSG_SEND_TEAM_COLOR_INDEX: { - if(FLIB.flib_netconn_send_teamColor(conn, (String)msg.obj, msg.arg1)==0) { - sendFromNet(FromNetHandler.MSG_TEAM_COLOR_CHANGED, msg.arg1, msg.obj); - } - break; - } - case MSG_SEND_TEAM_HOG_COUNT: { - if(FLIB.flib_netconn_send_teamHogCount(conn, (String)msg.obj, msg.arg1)==0) { - sendFromNet(FromNetHandler.MSG_HOG_COUNT_CHANGED, msg.arg1, msg.obj); - } - break; - } - default: { - Log.e("ToNetHandler", "Unknown message type: "+msg.what); - break; - } - } - } - } - } }