using System.Collections; using System.Collections.Generic; using System.Linq; using UnityEngine; using UnityEngine.Networking; namespace Networking.Server { [CreateAssetMenu(menuName = "Major Project/Networking/ClientList", order = 150)] public class ClientList : ScriptableObject, IEnumerable { #if UNITY_EDITOR [SerializeField] [Multiline] private string DevNotes; #endif [SerializeField] private Inventory StartInventory; #region Public Variables /// /// All Clients which are Currently Connected; /// public List ConnectedClients; /// /// Clients which connected at one point but no longer are /// Usefull if a client needs to reconnect /// public List DisconnectedClients; private List PotentialClients = new List(); /// /// Called when a client connects or disconnet /// public System.Action> OnClientsChange; #endregion Public Variables #region Private Variables public ServerObject server; #endregion Private Variables /// /// Clears the lists /// public void Reset() { ConnectedClients = new List(); DisconnectedClients = new List(); PotentialClients = new List(); } /// /// Resets the client list + registers handlers with the server /// /// Server to register handlers with public void SetUp(ServerObject serverObject) { this.server = serverObject; Reset(); server.RegisterHandler(MsgType.Connect, OnClientConnect); server.RegisterHandler(MsgType.Disconnect, OnClientDisconnect); server.RegisterHandler(LoginProtocols.SendingLoginDetails, OnClientLogin); } public void OnClientConnect(NetworkMessage msg) { //Debug.Log("newClient: " + msg.conn.address + ", " + msg.conn.Hash() + ", " + msg.conn.hostId); if (ConnectedClients.Any(p => p.ID == msg.channelId)) { Debug.LogError("Client[" + msg.channelId + "] already connected"); msg.conn.Send(LoginProtocols.LoginFail, new LoginProtocols.EmptyMsg()); return; } if (!PotentialClients.Contains(msg.conn)) PotentialClients.Add(msg.conn); msg.conn.Send(LoginProtocols.RequestLoginDetails, new LoginProtocols.EmptyMsg()); ConnectedClients.ForEach(p => msg.conn.Send(LoginProtocols.OtherClientConnected, new LoginProtocols.LoginMsg(p.Name, p.Color, p.characterAnimal))); } public void OnClientDisconnect(NetworkMessage msg) { PotentialClients.RemoveAll(p => p == msg.conn); if (!ConnectedClients.Any(p => p.ID == msg.conn.Hash())) { Debug.LogError("Unknown client disconnect [" + msg.conn.Hash() + "]"); return; } ClientData client = ConnectedClients.FirstOrDefault(p => p.ID == msg.conn.Hash()); client.Lives = 0; if (client.playerCharacter != null) { client.playerCharacter.lives = 0; } ConnectedClients.Remove(client); DisconnectedClients.Add(client); ConnectedClients.ForEach(p => p.conn.Send(LoginProtocols.OtherClientDisconnected, new LoginProtocols.LoginMsg(client.Name, client.Color, client.characterAnimal))); PotentialClients.ForEach(p => p.Send(LoginProtocols.OtherClientDisconnected, new LoginProtocols.LoginMsg(client.Name, client.Color, client.characterAnimal))); Debug.Log("Disconnected: " + client.Name); //somewhere here we need to remove the disconnected client from the client list and update such things OnClientsChange?.Invoke(ConnectedClients); } public void OnClientLogin(NetworkMessage msg) { LoginProtocols.LoginMsg loginMsg; if (!msg.TryRead(out loginMsg)) { msg.conn.Send(LoginProtocols.LoginFail, new LoginProtocols.EmptyMsg()); return; } if (ConnectedClients.Any(p => p.ID == msg.conn.Hash())) { Debug.LogError("Client[" + msg.channelId + "] already connected"); msg.conn.Send(LoginProtocols.LoginFail, new LoginProtocols.EmptyMsg()); return; } ClientData newClient = DisconnectedClients.FirstOrDefault(p => p.ID == msg.conn.Hash()); if (newClient != default) { Debug.Log("Reconnection: " + loginMsg.Name + ((loginMsg.Name != newClient.Name) ? (" (Prev. " + newClient.Name +")") : "")); DisconnectedClients.Remove(newClient); } else { //Debug.Log("new Connection: " + loginMsg.Name); newClient = new ClientData(); newClient.Inventory = Inventory.Clone(StartInventory); } newClient.Color = loginMsg.Color; newClient.characterAnimal = loginMsg.Animal; newClient.Name = loginMsg.Name; newClient.conn = msg.conn; ConnectedClients.Add(newClient); ConnectedClients.ForEach(p => p.conn.Send(LoginProtocols.OtherClientConnected, new LoginProtocols.LoginMsg(newClient.Name, newClient.Color, newClient.characterAnimal))); PotentialClients.RemoveAll(p => p == newClient.conn); PotentialClients.ForEach(p => p.Send(LoginProtocols.OtherClientConnected, new LoginProtocols.LoginMsg(newClient.Name, newClient.Color, newClient.characterAnimal))); newClient.conn.Send(LoginProtocols.LoginSuccess, new LoginProtocols.LoginMsg(newClient.Name, newClient.Color, newClient.characterAnimal)); OnClientsChange.Invoke(ConnectedClients); newClient.SendInventory(); newClient.SendLives(); newClient.ChangeScene("LobbyWait"); } #region IEnumerable Implementation public IEnumerator GetEnumerator() { return ((IEnumerable)ConnectedClients).GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)ConnectedClients).GetEnumerator(); } #endregion IEnumerable Implementation /// /// Returns client with id /// /// id to find client with /// client with provided id else null public ClientData this[int id] { get { return ConnectedClients.FirstOrDefault(p => p.ID == id); } } } [System.Serializable] public class ClientData { /// /// Client Name /// public string Name; /// /// Color which represents player, picked by player /// public Color Color; /// /// Clients Current Score /// public int Score; /// /// Clients Current Lives /// public int Lives = 3; /// /// Clients character animal - needed for instantiation /// public string characterAnimal = "Bear"; /// /// Network connection ID /// public int ID { get { return (conn != null) ? conn.Hash() : -1; }} /// /// Connection to Client /// public NetworkConnection conn; /// /// Current Logic elements held by player /// public Inventory Inventory; public Character playerCharacter; public void SendInventory() { //Debug.Log("Sending inventory"); conn.SendByChannel(LogicProtocols.SendInventory, new LogicProtocols.InventoryMsg(Inventory), TransportConfigure.ReliableFragmented); } public void SendLives() { conn.Send(LogicProtocols.SendLives, new LogicProtocols.FloatMsg(Lives)); } public void ChangeScene(string scene, bool Additive = false) { conn.Send(LoginProtocols.SceneChange, new LoginProtocols.SceneMsg(scene,Additive)); } } }