/// <summary> /// This will receive UDP messages. This thread will /// only run if UseUDP is set to true. /// </summary> public void UDP_Recv() { if (UsingUDP) { try { while (this.NetSystem.IsConnected) { if (IsDisconnecting) { break; } EndPoint tempUdpEp2 = UDPSocketS.RemoteEndPoint; //EndPoint tempUdpEp2 = new IPEndPoint(TCPSocket. int bytesRead = -10; string tempMessage = ""; while (this.NetSystem.IsConnected && (tempMessage == "" || bytesRead == 0)) //|| tempMessage.EndsWith("\n") == false { bytesRead = UDPSocketR.ReceiveFrom(UDPbuffer, 1024, SocketFlags.None, ref tempUdpEp2); tempMessage += Encoding.ASCII.GetString(UDPbuffer); UDPbuffer = new byte[1024]; } UDPMessage.Append(tempMessage); //Thread.Sleep(ThreadTimer); } } catch (SocketException e) { //Do nothing we have disconnected GenericNetworkCore.Logger("Socket Exception: " + e.ToString()); } } }
/// <summary> /// DisconnectServer - This will disconnect the server and gracefully close down all Clients connections. /// </summary> /// <returns></returns> public IEnumerator DisconnectServer() { if (IsServer) { StartCoroutine(OnServerDisconnect()); List <int> bad = new List <int>(); foreach (KeyValuePair <int, Connector> x in Connections) { bad.Add(x.Key); } foreach (int b in bad) { yield return(StartCoroutine(Disconnect(b, true))); } NetSystem.Disconnect(); Connections.Clear(); NetSystem.ThreadListener.Abort(); while (NetSystem.ThreadListener.ThreadState == 0) { yield return(new WaitForSeconds(.2f)); } NetSystem.Listener.Close(); NetSystem.Listener.Dispose(); NetSystem.Listener = null; OnServerDisconnectCleanup(); LocalConnectionID = -10; GenericNetworkCore.Logger("Server on " + name + this.GetType().ToString() + " is shut down!"); } }
/// <summary> /// TCPHandleMessage - Will handle all TCP messages from the connection ID specified. /// </summary> /// <param name="ConID">The connection ID to handle messages from.</param> /// <returns>Will yield control with IEnumerators</returns> private IEnumerator TCPHandleMessages(int ConID) { while (IsConnected) { string msg = ""; while (IsConnected && Connections[ConID].TCPMessage.Count > 0) { msg += Connections[ConID].TCPMessage.Consume(); } if (!IsConnected || Connections[ConID] == null) //This should break...? { break; } string[] args = msg.Split('\n'); foreach (string x in args) { if (x.Trim() != null) { try { OnHandleMessages(x); } catch (System.Exception e) { GenericNetworkCore.Logger("TCP malformed a packet - or exception in handle message: " + e.ToString()); } } } yield return(new WaitForSeconds(MasterTimer)); } }
/// <summary> /// This function will deal with delays of setting up an agent. /// </summary> /// <returns>IEnumerator to allow for delays.</returns> public IEnumerator SlowAgentStart() { bool UsePublic = false; bool UseFlorida = false; //Ping Public Ip address to see if we are external.......... //GenericNetworkCore.Logger("Trying Public IP Address: " + PublicIP.ToString()); System.Net.NetworkInformation.Ping ping = new System.Net.NetworkInformation.Ping(); System.Net.NetworkInformation.PingOptions po = new System.Net.NetworkInformation.PingOptions(); po.DontFragment = true; string data = "HELLLLOOOOO!"; byte[] buffer = ASCIIEncoding.ASCII.GetBytes(data); int timeout = 500; System.Net.NetworkInformation.PingReply pr = ping.Send(PublicIP, timeout, buffer, po); yield return(new WaitForSeconds(1.5f)); Debug.Log("Ping Return: " + pr.Status.ToString()); if (pr.Status == System.Net.NetworkInformation.IPStatus.Success) { GenericNetworkCore.Logger("The public IP responded with a roundtrip time of: " + pr.RoundtripTime); UsePublic = true; IP = PublicIP; } else { GenericNetworkCore.Logger("The public IP failed to respond"); UsePublic = false; } //-------------------If not public, ping Florida Poly for internal access. if (!UsePublic) { GenericNetworkCore.Logger("Trying Florida Poly Address: " + FloridaPolyIP.ToString()); pr = ping.Send(FloridaPolyIP, timeout, buffer, po); yield return(new WaitForSeconds(1.5f)); Debug.Log("Ping Return: " + pr.Status.ToString()); if (pr.Status.ToString() == "Success") { GenericNetworkCore.Logger("The Florida Poly IP responded with a roundtrip time of: " + pr.RoundtripTime); UseFlorida = true; IP = FloridaPolyIP; } else { GenericNetworkCore.Logger("The Florida Poly IP failed to respond"); UseFlorida = false; } } //Otherwise use local host, assume testing. if (!UsePublic && !UseFlorida) { IP = "127.0.0.1"; GenericNetworkCore.Logger("Using Home Address!"); } this.StartAgent(); }
/// <summary> /// SocketCloser - Will close the sockets and stop thread fo the specified connection ID /// </summary> /// <param name="id">Teh specified connection ID.</param> /// <returns>True if successful, false if an exception occured.</returns> private IEnumerator SocketCloser(int id) { if (Connections[id] == null) { yield break; } try { Connections[id].TCPRecvThread.Abort(); StopCoroutine(Connections[id].MessageHandler); } catch { } while (Connections[id] != null && Connections[id].TCPRecvThread.ThreadState == 0) { GenericNetworkCore.Logger("Trying to close the TCPRecv thread on connections: " + id.ToString()); yield return(new WaitForSeconds(.2f)); } if (Connections[id] != null && Connections[id].UsingUDP) { lock (Connections[id].UPDSendLock) { Connections[id].UDPRecvThread.Abort(); StopCoroutine(Connections[id].UDPMessageHandler); while (Connections[id].UDPRecvThread.ThreadState == 0) { GenericNetworkCore.Logger("Trying to close the UDPRecv thread on connections: " + id.ToString()); yield return(new WaitForSeconds(.2f)); } try { Connections[id].UDPSocketR.Dispose(); Connections[id].UDPSocketS.Dispose(); Connections[id].UDPSocketS = null; Connections[id].UDPSocketR = null; } catch { } } } lock (Connections[id].TCPSendLock) { try { Connections[id].TCPSocket.Shutdown(SocketShutdown.Both); Connections[id].TCPSocket.Close(); //Connections[id].TCPSocket = null; //Connections[id].TCPSocket.Shutdown(SocketShutdown.Both); } catch { //This will throw an error if I disconnect. It won't matter. //I am disconnecting } } }
// Start is called before the first frame update /// <summary> /// IF command line args have passed in "MASTER" this code will create a Lobby Manager Server - referred to as Master /// If the command line arg involves a _Port this code will tell the NetCore to create a game server and connect as an Agent to the Lobby Manager /// Otherwise, this code will simply connect you as an agent to the Lobby manager. /// </summary> void Start() { UsingUDP = false; MyCore = GameObject.FindObjectOfType <NetworkCore>(); if (MyCore == null) { throw new System.Exception("Could not find core!"); } string[] args = System.Environment.GetCommandLineArgs(); //GenericNetworkCore.Logger(System.Environment.CommandLine); foreach (string a in args) { try { if (a.StartsWith("PORT_")) { string[] temp = a.Split('_'); int port = int.Parse(temp[1]); LocalGameID = int.Parse(temp[3]); GenericNetworkCore.Logger("The number of Max connections is: " + MyCore.MaxConnections); MyCore.PortNumber = port; IP = "127.0.0.1"; StartAgent(); //StartCoroutine(SlowAgentStart()); //MyCore.IP = this.IP; StartCoroutine(SlowStart()); } } catch (System.Exception e) { GenericNetworkCore.Logger("Exception caught starting the server: " + e.ToString()); } if (a.Contains("MASTER")) { this.StartMaster(); } } if (!IsConnected) { StartCoroutine(SlowAgentStart()); } //Maintained on clients with same ID as Lobby. GameRoomButtons = new ExclusiveDictionary <int, GameRoomButton>(); //Lobby only maintained on the server. Lobbies = new ExclusiveDictionary <int, GameRoom>(); if (Content == null) { throw new System.Exception("Could not find container for game buttons!"); } }
/// <summary> /// This is a coroutine that will kill any room that is given a non -1 time to live (ttl) /// Once the time is up the room is destroyed. /// </summary> /// <param name="t">Time in seconds for the game to lasts</param> /// <param name="g">Game ID</param> /// <returns></returns> public IEnumerator RoomKiller(float t, GameRoom g) { if (IsMaster) { yield return(new WaitForSecondsRealtime(t)); yield return(StartCoroutine(Disconnect(g.ServerC))); GenericNetworkCore.Logger(" -Trying to destroy the process!"); g.MyProcess.Kill(); GenericNetworkCore.Logger(" - Removing game from dictionary\n"); Lobbies.Remove(g.GameID); } }
/// <summary> /// This will gather all messages that need to be sent form the NetworkIdentities /// Then either send it to the server or to all of the clients. /// This will also send the UDP messages as well. /// </summary> public override void OnSlowUpdate() { List <string> UDPMasterStringList = new List <string>(); foreach (KeyValuePair <int, NetworkID> id in NetObjs) { //Add their message to the masterMessage (the one we send) MasterMessage += id.Value.GameObjectMessages.ReadAndClear() + "\n"; UDPMasterStringList.Add(id.Value.UDPGameObjectMessages.ReadAndClear() + "\n"); } //Send Master Message List <int> bad = new List <int>(); string msgToSend = MasterMessage.ReadAndClear(); //string UDPmsgToSend = UDPMasterMessage.ReadAndClear(); foreach (KeyValuePair <int, Connector> item in Connections) { try { //This will send all of the information to the client (or to the server if on a client). if (msgToSend.Trim() != "") { Send(msgToSend, item.Key); } foreach (string msg in UDPMasterStringList) { if (msg.Trim() != "") { Send(msg, item.Key, false); } } } catch (System.Exception e) { GenericNetworkCore.Logger("Exception occured in slow update: " + e.ToString()); bad.Add(item.Key); } } //MasterMessage.SetData("");//delete old values. foreach (int i in bad) { GenericNetworkCore.Logger("We are disconecting Connection " + i.ToString() + " from " + name + ":" + this.GetType().ToString()); this.Disconnect(i); } }
/// <summary> /// ServerStart - Will start a server from the NetSystem. /// Will wait until the server has started listening. /// </summary> /// <returns>IENumerator in order to surrender control.</returns> public IEnumerator ServerStart() { if (!IsConnected) { NetSystem.ThreadTimer = (int)(1000 * MasterTimer); NetSystem.GenCore = this; NetSystem.IP = IP; NetSystem.PortNumber = PortNumber; NetSystem.StartServer(); yield return(new WaitUntil(() => NetSystem.IsConnected)); LocalConnectionID = -1; StartCoroutine(SlowUpdate()); StartCoroutine(OnServerStart()); GenericNetworkCore.Logger("Server has started!"); } }
/// <summary> /// Called by a client to a server. /// The server will spawn a process with the next available port. /// This port will then be sent back to the client so they can join. /// </summary> /// <param name="g">String form of the command.</param> public void CreateNewGame(string g) { GenericNetworkCore.Logger("Creating room: " + g); //create new room instance.... GameRoom temp = new GameRoom(); temp.GameID = -1; int range = (HighPort - LowPort); for (int i = 0; i < range; i++) { if (Lobbies.ContainsKey(i) != true) { temp.GameID = i; temp.Port = i + LowPort; break; } } try { System.Diagnostics.Process proc = new System.Diagnostics.Process(); proc.StartInfo.UseShellExecute = true; string[] args = System.Environment.GetCommandLineArgs(); GenericNetworkCore.Logger("Starting new process " + args[0]); proc.StartInfo.FileName = args[0]; proc.StartInfo.Arguments = "PORT_" + temp.Port + "_GAMEID_" + temp.GameID + " -batchmode -nographics"; temp.MyProcess = proc; temp.ProcessTTL = MaxGameTime; GenericNetworkCore.Logger("PORT_" + temp.Port + "_GAMEID_" + temp.GameID + "\nTTL " + temp.ProcessTTL); proc.Start(); temp.Creator = int.Parse(g.Split('#')[2]); temp.GameName = g.Split('#')[1]; Lobbies.Add(temp.GameID, temp); GameCounter++; if (temp.ProcessTTL != -1) { StartCoroutine(RoomKiller(Lobbies[temp.GameID].ProcessTTL, Lobbies[temp.GameID])); } } catch (System.Exception e) { GenericNetworkCore.Logger("EXCEPTION - in creating a game!!! - " + e.ToString()); } }
/// <summary> /// ConnectClient - Will instantiate the net system and call connect client. /// </summary> /// <returns>IENumerator - This is a co-routine therefore it will delay until it connects.</returns> public IEnumerator ClientStart() { if (!IsConnected) { NetSystem.ThreadTimer = (int)(1000 * MasterTimer); NetSystem.GenCore = this; NetSystem.IP = IP; NetSystem.PortNumber = PortNumber; bool NoContact = false; try { NetSystem.StartClient(); } catch { NoContact = true; } if (NoContact) { //If you cannot connect as a client go back to the main menu scene. string message = "Could not Connect to Generic Server!"; GenericNetworkCore.Logger(message); yield return(new WaitForSeconds(10)); SceneManager.LoadScene(0); } while (LocalConnectionID < 0) { yield return(new WaitForSeconds(.2f)); LocalConnectionID = NetSystem.Connections[0].ConnectionID; } StartCoroutine(SlowUpdate()); Connections[0].MessageHandler = StartCoroutine(TCPHandleMessages(0)); if (Connections[0].UsingUDP) { Connections[0].UDPMessageHandler = StartCoroutine(UDPHandleMessages(0)); } yield return(new WaitUntil(() => IsClient)); StartCoroutine(OnClientConnect(LocalConnectionID)); GenericNetworkCore.Logger("Client connected to Generic Server!"); } }
/// <summary> /// This is the mmessagge a new game server will send to the lobby manager. /// It is specifying that it is a game server and it is ready to be played. /// </summary> /// <param name="g">String form of the command.</param> public void RegisterGame(string g) { GenericNetworkCore.Logger("We recieved ISServer: " + g); int GameID = int.Parse(g.Split('#')[1]); int ServerId = int.Parse(g.Split('#')[2]); if (IsMaster) { if (Lobbies.ContainsKey(GameID)) { Lobbies[GameID].ServerC = ServerId; Send("JOIN#" + Lobbies[GameID].Port + "\n", Lobbies[GameID].Creator); } foreach (KeyValuePair <int, Connector> con in Connections) { Send("NEWGAME#" + GameID + "#" + Lobbies[GameID].GameName + "\n", con.Key); } } }
/// <summary> /// Due to a strange co-routine problem. /// The client must first requests to join a game /// Have the Lobby Master return the value of the port /// and then the player can join. /// The client already has the port information but the coroutine to join /// must be called within this function. /// /// </summary> /// <param name="g">string form of the command.</param> public void JoinAGame(string g) { if (IsMaster) { GenericNetworkCore.Logger("Master received join: " + g); int gID = int.Parse(g.Split('#')[1]); int agentId = int.Parse(g.Split('#')[2]); if (Lobbies.ContainsKey(gID)) { Send("JOIN#" + Lobbies[gID].Port + "\n", agentId); } } if (IsAgent) { GenericNetworkCore.Logger("Agent received join: " + g); MyCore.PortNumber = int.Parse(g.Split('#')[1].Trim()); MyCore.UI_StartClient(); StartCoroutine(MenuManager()); } }
/// <summary> /// If an agent is being disconnected then this function cleans up the details. /// Such as disconnecting them from the game or closing the game if it is a game server. /// </summary> /// <param name="id">ID of the agent connection being closed.</param> /// <returns>IEnumerator to allow for delays.</returns> public override IEnumerator OnClientDisconnect(int id) { if (MyCore.IsConnected && IsGameServer) { Debug.Log("Trying to disconnect the game server!"); yield return(StartCoroutine(MyCore.DisconnectServer())); } else if (IsMaster) { int badGameID = -1; foreach (KeyValuePair <int, GameRoom> x in Lobbies) { if (x.Value.ServerC == id) { badGameID = x.Key; break; } } if (badGameID != -1) { GenericNetworkCore.Logger("Found game id that is being removed - " + badGameID); //Just in case the menu hasn't been cleared out. foreach (KeyValuePair <int, Connector> x in Connections) { if (x.Key != id) { Send("GAMESTART#" + badGameID.ToString() + "\n", x.Key); } } if (Lobbies.ContainsKey(badGameID)) { Lobbies.Remove(badGameID); } } } }
public void TCP_Recv() { //Must have IsConnected //Must have IsSever //Must have IsClient try { while (this.NetSystem.IsConnected) { if (IsDisconnecting) { break; } int byteRead = -10; string tempMessage = ""; while (this.NetSystem.IsConnected && (tempMessage == "" || byteRead == 0)) //|| tempMessage.EndsWith("\n") == false { byteRead += TCPSocket.Receive(TCPbuffer, 1024, SocketFlags.None); //TCPMessage.Append(Encoding.ASCII.GetString(TCPbuffer)); tempMessage += Encoding.ASCII.GetString(TCPbuffer); TCPbuffer = new byte[1024]; } TCPMessage.Append(tempMessage); string[] tempArgs = tempMessage.Split('\n'); foreach (string s in tempArgs) { if (s.StartsWith("DISCON")) { if (!IsDisconnecting) { TCP_Send("DISCON\n"); } IsDisconnecting = true; } if (s.StartsWith("IDPLEASE") && this.NetSystem.IsServer) { TCP_Send("ID#" + ConnectionID + "\n"); } if (s.StartsWith("ID#") && this.NetSystem.IsClient) { ConnectionID = int.Parse(s.Split('#')[1].Split('\n')[0]); GenericNetworkCore.Logger("The ID for the connection is: " + ConnectionID); DidClientRecvId = true; TCP_Send("ID#" + ConnectionID + "\n"); } if (s.StartsWith("ID#") && this.NetSystem.IsServer) { GenericNetworkCore.Logger("The Client Acknowledged their ID!"); DidClientRecvId = true; } if (s.StartsWith("LAT#")) { Latency = float.Parse(s.Split('#')[1].Split('\n')[0]); } if (s.StartsWith("OK")) { if (this.NetSystem.IsClient) { TCP_Send("OK\n"); } if (this.NetSystem.IsServer) { Latency = (float)(System.DateTime.Now - StartCall).TotalSeconds; TCP_Send("LAT#" + Latency.ToString("F2")); } } } Thread.Sleep(ThreadTimer); } } catch (SocketException e) { //Do nothing. We have disconnected. GenericNetworkCore.Logger("Socket Exception: " + e.ToString()); IsDisconnecting = true; } }
/// <summary> /// Disconnect - Will disconnect a specific connection from a server or a client. /// Will send DISCON if SendMsg is true telling the other party that disconection has began. /// </summary> /// <param name="id">The connection you wish to disconnect. (0 for clients)</param> /// <param name="sendMsg">If true will notify remote machine that disconection is about to take place</param> /// <returns></returns> public IEnumerator Disconnect(int id, bool sendMsg = false) { if (IsClient) { if (sendMsg) { Connections[0].TCP_Send("DISCON\n"); int TimeOut = 10; while (Connections[0] != null && Connections[0].IsDisconnecting == false) { yield return(new WaitForSeconds(.1f)); TimeOut--; if (TimeOut <= 0) { break; } } } if (Connections[0] == null) { yield break; } yield return(StartCoroutine(OnClientDisconnect(id))); yield return(StartCoroutine(SocketCloser(0))); NetSystem.Disconnect(); Connections.Clear(); LocalConnectionID = -10; NetSystem.IsConnected = false; GenericNetworkCore.Logger("Disconnected from " + name + ":" + this.GetType().ToString()); OnClientDisconnectCleanup(id); } if (IsServer) { if (sendMsg) { Connections[id].TCP_Send("DISCON\n"); int TimeOut = 10; while (Connections.ContainsKey(id) && Connections[id].IsDisconnecting == false) { GenericNetworkCore.Logger("Waiting to get approval for disconnect."); yield return(new WaitForSeconds(.1f)); TimeOut--; if (TimeOut <= 0) { break; } } } StartCoroutine(OnClientDisconnect(id)); yield return(StartCoroutine(SocketCloser(id))); Connections.Remove(id); GenericNetworkCore.Logger("Client " + id + " disconnected from " + name + ":" + this.GetType().ToString()); OnClientDisconnectCleanup(id); } }
/// <summary> /// This function get's called from TCP and UDP receive functions. /// Therefore, the game programmer does not have to worry about having two /// seperate handle messages. /// </summary> /// <param name="commands">The string containing the received message.</param> public override void OnHandleMessages(string commands) { try { if (commands.Trim(' ') == "OK" && IsClient) { //Heartbeat } else if (commands.Contains("CREATE#")) { if (IsClient) { string[] arg = commands.Split('#'); try { int o = int.Parse(arg[2]); int n = int.Parse(arg[3]); Vector3 pos = NetworkCore.Vector3FromString(arg[4]); Quaternion qtemp = Quaternion.Euler(NetworkCore.Vector3FromString(arg[5])); int type = int.Parse(arg[1]); GameObject Temp; if (type != -1) { Temp = GameObject.Instantiate(SpawnPrefab[int.Parse(arg[1])], pos, qtemp); } else { Temp = GameObject.Instantiate(NetworkPlayerManager, pos, qtemp); } Temp.GetComponent <NetworkID>().Owner = o; Temp.GetComponent <NetworkID>().NetId = n; Temp.GetComponent <NetworkID>().Type = type; NetObjs.Add(n, Temp.GetComponent <NetworkID>()); } catch (System.Exception e) { //Malformed packet. GenericNetworkCore.Logger("Exception occured inside create! " + e.ToString()); } } } else if (commands.Contains("DELETE#")) { if (IsClient) { try { string[] args = commands.Split('#'); if (NetObjs.ContainsKey(int.Parse(args[1]))) { GameObject.Destroy(NetObjs[int.Parse(args[1])].gameObject); NetObjs.Remove(int.Parse(args[1])); } } catch (System.Exception e) { GenericNetworkCore.Logger("ERROR OCCURED: " + e); } } } else if (commands.Contains("DIRTY#")) { if (IsServer) { int id = int.Parse(commands.Split('#')[1]); if (NetObjs.ContainsKey(id)) { foreach (NetworkComponent n in NetObjs[id].gameObject.GetComponents <NetworkComponent>()) { n.IsDirty = true; } } } } else if (commands.Contains("COMMAND#") || commands.Contains("UPDATE#")) { //We will assume it is Game Object specific message //string msg = "COMMAND#" + myId.netId + "#" + var + "#" + value; string[] args = commands.Split('#'); int n = int.Parse(args[1]); if (NetObjs.ContainsKey(n)) { NetObjs[n].Net_Update(args[0], args[2], args[3]); } } } catch (System.Exception e) { GenericNetworkCore.Logger("Exception in handle message: " + e.ToString()); } }