diff --git a/src/main/java/fr/insa/clavardator/network/ConnectionListener.java b/src/main/java/fr/insa/clavardator/network/ConnectionListener.java index ce96473..bae3fd2 100644 --- a/src/main/java/fr/insa/clavardator/network/ConnectionListener.java +++ b/src/main/java/fr/insa/clavardator/network/ConnectionListener.java @@ -40,9 +40,9 @@ public class ConnectionListener { private static class Acceptor extends Thread { private boolean shouldStop = false; - NewConnectionCallback callback; - ErrorCallback errorCallback; - ServerSocket server; + private final NewConnectionCallback callback; + private final ErrorCallback errorCallback; + private ServerSocket server; public Acceptor(NewConnectionCallback callback, ErrorCallback errorCallback) { this.callback = callback; diff --git a/src/main/java/fr/insa/clavardator/users/UserList.java b/src/main/java/fr/insa/clavardator/users/UserList.java index b8a6407..2b65ac9 100644 --- a/src/main/java/fr/insa/clavardator/users/UserList.java +++ b/src/main/java/fr/insa/clavardator/users/UserList.java @@ -1,10 +1,12 @@ package fr.insa.clavardator.users; import fr.insa.clavardator.db.DatabaseController; +import fr.insa.clavardator.network.ConnectionListener; import fr.insa.clavardator.network.NetDiscoverer; import fr.insa.clavardator.util.ErrorCallback; import fr.insa.clavardator.util.Log; +import java.beans.PropertyChangeEvent; import java.net.InetAddress; import java.util.ArrayList; import java.util.HashMap; @@ -21,6 +23,7 @@ public class UserList { private final DatabaseController db = new DatabaseController(); private final NetDiscoverer netDiscoverer = new NetDiscoverer(); + private final ConnectionListener connectionListener = new ConnectionListener(); public UserList() { } @@ -35,17 +38,24 @@ public class UserList { netDiscoverer.discoverActiveUsers("CLAVARDATOR_BROADCAST", (ipAddr, data) -> { int id = getIdFromIp(ipAddr); Log.v(this.getClass().getSimpleName(), "Discovered new user: " + id); + // If already connected, do not modify if (activeUsers.containsKey(id)) { return; } + PeerUser user = inactiveUsers.get(id); if (user == null) { // Username is set on TCP connection start user = new PeerUser(id, ""); + inactiveUsers.put(id, user); } PeerUser finalUser = user; - user.connect(ipAddr, () -> notifyConnectionObservers(finalUser), errorCallback); + user.connect(ipAddr, () -> { + notifyConnectionObservers(finalUser); + finalUser.addObserver(evt -> userChangeObserver(finalUser, evt)); + }, errorCallback); + }, errorCallback); } @@ -56,10 +66,89 @@ public class UserList { Throwable::printStackTrace); } - private int getIdFromIp(InetAddress ipAddr) { - return ipAddr.hashCode(); + public void startUserListening(ErrorCallback errorCallback) { + connectionListener.acceptConnection(clientSocket -> { + int id = getIdFromIp(clientSocket.getInetAddress()); + Log.v(this.getClass().getSimpleName(), "new connection from user: " + id); + + // If already connected, warn and return + if (activeUsers.containsKey(id)) { + Log.w(getClass().getSimpleName(), "An already connected user tried to initiate a new connection: user id " + id); + return; + } + + // Get the user if already existing + PeerUser user = inactiveUsers.get(id); + // else create it + if (user == null) { + // Username is set on TCP connection start + user = new PeerUser(id, ""); + inactiveUsers.put(id, user); + } + + PeerUser finalUser = user; + user.connect(clientSocket, () -> { + notifyConnectionObservers(finalUser); + finalUser.addObserver(evt -> userChangeObserver(finalUser, evt)); + }, errorCallback); + + }, errorCallback); } + private void userChangeObserver(PeerUser user, PropertyChangeEvent evt) { + int id = user.id; + + if (evt.getPropertyName().equals("state")) { + PeerUser.State oldState = (PeerUser.State) evt.getOldValue(); + PeerUser.State newState = (PeerUser.State) evt.getNewValue(); + + if ((oldState == PeerUser.State.DISCONNECTED || oldState == PeerUser.State.CONNECTING) && + newState == PeerUser.State.CONNECTED) { + // Connection handling + if (!inactiveUsers.containsKey(id)) { + if (activeUsers.containsKey(id)) { + Log.e(getClass().getSimpleName(), "Tried to set state CONNECTED on an already connected user: user id " + id); + return; + } else { + Log.e(getClass().getSimpleName(), "Tried to set state CONNECTED on an unknown user: user id " + id); + return; + } + } + + inactiveUsers.remove(id); + activeUsers.put(id, user); + notifyConnectionObservers(user); + + } else if (oldState == PeerUser.State.CONNECTED && + (newState == PeerUser.State.DISCONNECTED || newState == PeerUser.State.CONNECTING)) { + // Disconnection + if (!activeUsers.containsKey(id)) { + if (inactiveUsers.containsKey(id)) { + Log.e(getClass().getSimpleName(), "Tried to set state DISCONNECTED on an already disconnected user: user id " + id); + return; + } else { + Log.e(getClass().getSimpleName(), "Tried to set state DISCONNECTED on an unknown user: user id " + id); + return; + } + } + + activeUsers.remove(id); + inactiveUsers.put(id, user); + notifyDisconnectionObservers(user); + } + } + } + + private int getIdFromIp(InetAddress ipAddr) { + byte[] addr = ipAddr.getAddress(); + int id = 0; + for (byte b : addr) { + id = (id << 8) + b; + } + return id; + } + + public void addActiveUsersObserver(UserConnectionCallback connectionCallback, UserDisconnectionCallback disconnectionCallback) { userConnectionObservers.add(connectionCallback); userDisconnectionObservers.add(disconnectionCallback);