diff --git a/Fishnet_v4.1.6R_WebGL/Assets/SpaceEdge/Scripts/Systems/MatchmakingSystem.cs b/Fishnet_v4.1.6R_WebGL/Assets/SpaceEdge/Scripts/Systems/MatchmakingSystem.cs index 69634d4..29671eb 100644 --- a/Fishnet_v4.1.6R_WebGL/Assets/SpaceEdge/Scripts/Systems/MatchmakingSystem.cs +++ b/Fishnet_v4.1.6R_WebGL/Assets/SpaceEdge/Scripts/Systems/MatchmakingSystem.cs @@ -1,13 +1,9 @@ -using System; -using System.Net; -using System.Net.Http; -using System.Net.Http.Headers; -using System.Net.Sockets; -using System.Text; -using System.Threading.Tasks; using FishNet; using Leguar.TotalJSON; +using System; +using System.Collections; using UnityEngine; +using UnityEngine.Networking; namespace SpaceEdge @@ -60,7 +56,7 @@ public class MatchmakingSystem : MonoBehaviour /// Refer to https://docs.edgegap.com/api/#operation/deployment-status-get for details. /// private const string AppListURL = "https://api.edgegap.com/v1/deployments"; - + /// /// The URL to get the public ip of the player. /// Refer to https://docs.edgegap.com/api/#operation/IP for details. @@ -68,46 +64,34 @@ public class MatchmakingSystem : MonoBehaviour private const string PublicIpURL = "https://api.edgegap.com/v1/ip"; /// - /// The HTTP client that will handle all the communication with the EdgeGap API. - /// - private readonly HttpClient _httpClient = new(); - - /// - /// The request object that sends the "_requestData" via the "_httpClient". + /// Stores the JSON request data that is properly formatted + /// for properly delivering the UnityWebRequest to the EdgeGap API. /// - private HttpResponseMessage _request; + private string _requestData; /// - /// Stores the JSON request data that is properly formatted and converted to a ByteArray - /// for properly delivering the HTTP request to the EdgeGap API. - /// - private StringContent _requestData; - - /// - /// Used by the "StartConnectionAttempt" method to communicate with and keep track of the deployment instance - /// it's attempting to connect to. This variable is either populated by the "FindDeployedServers" method if trying - /// to connect to an existing deployment , or by the "DeployNewServer" method if attempting a new deployment. + /// Used by the "StartConnectionAttempt" coroutine to communicate with and keep track of the deployment instance + /// it's attempting to connect to. This variable is either populated by the "RequestServers" coroutine if trying + /// to connect to an existing deployment , or by the "DeployNewServer" coroutine if attempting a new deployment. /// private string _requestId; /// - /// Acts as a buffer for HTTP request's response that is read asynchronously. + /// Store the response from UnityWebRequests. /// private string _response; private string _publicIP; - + public Action OnStartGameFailed; //These public events are used to communicate with the "MainMenuSystem". public Action OnStatusUpdate; - private void Awake() + public void FindDeployedServers() { - //Setting the proper Content-Type and Authorization header values to the _httpClient. - _httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(TypeHeaderValue)); - _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("token", AuthHeaderValue); + StartCoroutine(RequestServers()); } /// @@ -116,62 +100,66 @@ private void Awake() /// to the first one makes the most sense. But in case of paid tier account this method can be extended /// easily to allow players to select any preferred deployment from the list and connect to it. /// - public async void FindDeployedServers() + private IEnumerator RequestServers() { //Request the list of deployments by sending a HTTP GET request to the AppListURL on EdgeGap API - _request = await _httpClient.GetAsync(AppListURL); - //Store the response into the buffer. - _response = await _request.Content.ReadAsStringAsync(); - - //If the request is successful. - if (_request.IsSuccessStatusCode) + using (UnityWebRequest webRequest = UnityWebRequest.Get(AppListURL)) { - //Parse the response string into a JSON object - var responseJson = JSON.ParseString(_response); - //If total count of deployments is 0 then start new server deployment. - if (responseJson.GetInt("total_count") == 0) + SetCustomHeaders(webRequest); + yield return webRequest.SendWebRequest(); + + _response = webRequest.downloadHandler.text; + + //If the request is successful. + if (webRequest.result == UnityWebRequest.Result.Success) { - OnStatusUpdate?.Invoke("No Deployed Servers Found, Attempting To Deploy A New Instance ", false); + //Parse the response string into a JSON object + var responseJson = JSON.ParseString(_response); + //If total count of deployments is 0 then start new server deployment. + if (responseJson.GetInt("total_count") == 0) + { + OnStatusUpdate?.Invoke("No Deployed Servers Found, Attempting To Deploy A New Instance ", false); - //Attempt to deploy new server as no instances are found. - DeployNewServer(); + //Attempt to deploy new server as no instances are found. + StartCoroutine(DeployNewServer()); + } + //Else connect to the first instance of the deployment from the deployments list. + else + { + OnStatusUpdate?.Invoke("Found Deployed Servers, Attempting Connection..", false); + //Parse the deployments list into a JSON Array. + var allServers = responseJson.GetJArray("data"); + //Grab the entry from the first index of the JSON Array. + var firstServer = allServers.GetJSON(0); + //Populate the _requestID with the first server's "request_id" parameter. + _requestId = firstServer.GetString("request_id"); + //Attempt to connect to the server. + StartCoroutine(StartConnectionAttempt()); + } } - //Else connect to the first instance of the deployment from the deployments list. + //Invoked in case of an unexpected error. else { - OnStatusUpdate?.Invoke("Found Deployed Servers, Attempting Connection..", false); - //Parse the deployments list into a JSON Array. - var allServers = responseJson.GetJArray("data"); - //Grab the entry from the first index of the JSON Array. - var firstServer = allServers.GetJSON(0); - //Populate the _requestID with the first server's "request_id" parameter. - _requestId = firstServer.GetString("request_id"); - //Attempt to connect to the server. - StartConnectionAttempt(); + OnStatusUpdate?.Invoke( + $"Could Not Start Game, Error {(int)webRequest.responseCode} With Message: \n{_response}", + true); + OnStartGameFailed?.Invoke(); } } - //Invoked in case of an unexpected error. - else - { - OnStatusUpdate?.Invoke( - $"Could Not Start Game, Error {(int)_request.StatusCode} With Message: \n{_response}", - true); - OnStartGameFailed?.Invoke(); - } } /// /// This method configures and sends a POST request to the EdgeGap API for deploying a new server instance /// If the server deployment is successful, the "StartConnectionAttempt" method is called. /// - public async void DeployNewServer() + private IEnumerator DeployNewServer() { //Get the public ip of the player. If null or empty then return from the function - //GetLocalIPAddress method will take care of error handling and logging. - await GetLocalIPAddress(); - if(String.IsNullOrEmpty(_publicIP)) - return; - + //GetLocalIpAddress method will take care of error handling and logging. + yield return GetLocalIpAddress(); + if (String.IsNullOrEmpty(_publicIP)) + yield break; + //Create a new JSON object and add the minimum required fields for a deployment. var requestJson = new JSON(); //Please refer to https://docs.edgegap.com/api/#operation/deploy for more details. @@ -179,119 +167,141 @@ public async void DeployNewServer() requestJson.Add("version_name", AppVersion); requestJson.Add("ip_list", new[] { _publicIP }); //Convert the JSON object to a ByteArray and format it for "application/json" Content-Type. - _requestData = new StringContent(requestJson.CreateString(), Encoding.UTF8, TypeHeaderValue); + _requestData = requestJson.CreateString(); //Request new deployment by sending HTTP POST request to the AppDeployURL on EdgeGap API. - _request = await _httpClient.PostAsync(AppDeployURL, _requestData); - //Store the response inti the buffer. - _response = await _request.Content.ReadAsStringAsync(); - //If the request is successful. - if (_request.IsSuccessStatusCode) - { - //Parse the response string into a JSON object. - var responseJson = JSON.ParseString(_response); - //Fetch the response_id of the deployed server. - _requestId = responseJson.GetString("request_id"); - //Start connection attempt to the deployed server. - OnStatusUpdate?.Invoke("Server Deployment Successful, Attempting To Connect To Server...", false); - StartConnectionAttempt(); - } - //Invoked in case of an unexpected error. - else + using (UnityWebRequest webRequest = UnityWebRequest.Post(AppDeployURL, _requestData, TypeHeaderValue)) { - OnStatusUpdate?.Invoke( - $"Could Not Deploy Server, Error {(int)_request.StatusCode} With Message: \n{_response}", - true); - OnStartGameFailed?.Invoke(); + SetCustomHeaders(webRequest); + yield return webRequest.SendWebRequest(); + + _response = webRequest.downloadHandler.text; + + //If the request is successful. + if (webRequest.result == UnityWebRequest.Result.Success) + { + //Parse the response string into a JSON object. + var responseJson = JSON.ParseString(_response); + //Fetch the response_id of the deployed server. + _requestId = responseJson.GetString("request_id"); + //Start connection attempt to the deployed server. + OnStatusUpdate?.Invoke("Server Deployment Successful, Attempting To Connect To Server...", false); + StartCoroutine(StartConnectionAttempt()); + } + //Invoked in case of an unexpected error. + else + { + OnStatusUpdate?.Invoke( + $"Could Not Deploy Server, Error {(int)webRequest.responseCode} With Message: \n{_response}", + true); + OnStartGameFailed?.Invoke(); + } } } - - private async void StartConnectionAttempt() + private IEnumerator StartConnectionAttempt() { var isServerReady = false; while (!isServerReady) { //Send a GET HTTP request to the EdgeGap api to get the status of the deployment linked to the _requestId. - _request = await _httpClient.GetAsync(AppStatusURL + _requestId); - _response = await _request.Content.ReadAsStringAsync(); - - if (_request.IsSuccessStatusCode) + using (UnityWebRequest webRequest = UnityWebRequest.Get(AppStatusURL + _requestId)) { - var responseJson = JSON.ParseString(_response); + SetCustomHeaders(webRequest); + yield return webRequest.SendWebRequest(); + + _response = webRequest.downloadHandler.text; - isServerReady = responseJson.GetBool("running"); - //If the response has the "running" bool as "true" then the server is running and ready for connection. - if (isServerReady) + //If the request is successful. + if (webRequest.result == UnityWebRequest.Result.Success) { - OnStatusUpdate?.Invoke("Server Is Ready For Connection, Starting The Game...", false); - //Grab the port number of the port named "Game Port" from the "ports" object in the responseJson. - //For more information on how to configure ports please refer to the "FishNet Example Guide". - var serverPort = (ushort)responseJson.GetJSON("ports").GetJSON("Game Port").GetInt("external"); - //The "fqdn" string in the responseJson represents the server address to which we want the client to connect. - var serverAddress = responseJson.GetString("fqdn"); - - //Set the serverPort and serverAddress to the default transport (Tugboat) - InstanceFinder.TransportManager.Transport.SetPort(serverPort); - InstanceFinder.TransportManager.Transport.SetClientAddress(serverAddress); - Debug.Log($"Connecting To Server Using Port {serverPort} And IP {serverAddress}"); - //StartConnection method will connect to the server with the given port and address. - //Once the connection is complete the SceneManager will auto load the "OnlineScene". - //Please refer to the "FishNet Example Guide" for more details on SceneManager. - InstanceFinder.ClientManager.StartConnection(); + var responseJson = JSON.ParseString(_response); + + isServerReady = responseJson.GetBool("running"); + //If the response has the "running" bool as "true" then the server is running and ready for connection. + if (isServerReady) + { + OnStatusUpdate?.Invoke("Server Is Ready For Connection, Starting The Game...", false); + //Grab the port number of the port named "Game Port" from the "ports" object in the responseJson. + //For more information on how to configure ports please refer to the "FishNet Example Guide". + var serverPort = (ushort)responseJson.GetJSON("ports").GetJSON("Game Port").GetInt("external"); + //The "fqdn" string in the responseJson represents the server address to which we want the client to connect. + var serverAddress = responseJson.GetString("fqdn"); + + //Set the serverPort and serverAddress to the default transport (Tugboat) + InstanceFinder.TransportManager.Transport.SetPort(serverPort); + //InstanceFinder.TransportManager.Transport.SetPort(7770); + InstanceFinder.TransportManager.Transport.SetClientAddress(serverAddress); + Debug.Log($"Connecting To Server Using Port {serverPort} And IP {serverAddress}"); + //StartConnection method will connect to the server with the given port and address. + //Once the connection is complete the SceneManager will auto load the "OnlineScene". + //Please refer to the "FishNet Example Guide" for more details on SceneManager. + InstanceFinder.ClientManager.StartConnection(); + } + //If the server is not ready for connection, we delay the function execution for 10 seconds. + //After 10 seconds the loop will run again as the "isServerReady" bool is still set to false + else + { + OnStatusUpdate?.Invoke("Server Is Not Ready For Connection Retrying In 10 Seconds...", false); + } } - //If the server is not ready for connection, we delay the function execution for 10 seconds. - //After 10 seconds the loop will run again as the "isServerReady" bool is still set to false + //Invoked in case of an unexpected error. else { - OnStatusUpdate?.Invoke("Server Is Not Ready For Connection Retrying In 10 Seconds...", false); - await Task.Delay(10000); - } - } - //Invoked in case of an unexpected error - else - { - OnStatusUpdate?.Invoke( - $"Could Not Update Server Status, Error {(int)_request.StatusCode} With Message: \n{_response}", + OnStatusUpdate?.Invoke( + $"Could Not Update Server Status, Error {(int)webRequest.responseCode} With Message: \n{_response}", true); - OnStartGameFailed?.Invoke(); + OnStartGameFailed?.Invoke(); + } } + yield return new WaitForSeconds(10); } } - //Function to get the IP address of the client's device - private async Task GetLocalIPAddress() + //Coroutine to get the IP address of the client's device + private IEnumerator GetLocalIpAddress() { - //Request the public ip of the player using the EdgeGap API - _request = await _httpClient.GetAsync(PublicIpURL); - //Store the response into the buffer. - _response = await _request.Content.ReadAsStringAsync(); - - - if (_request.IsSuccessStatusCode) + using (UnityWebRequest webRequest = UnityWebRequest.Get(PublicIpURL)) { - OnStatusUpdate?.Invoke("Fetching The Public Ip Address",false); - var responseJson = JSON.ParseString(_response); - var ip = responseJson.GetString("public_ip"); - if (String.IsNullOrEmpty(ip)) + SetCustomHeaders(webRequest); + yield return webRequest.SendWebRequest(); + + _response = webRequest.downloadHandler.text; + + //If the request is successful. + if (webRequest.result == UnityWebRequest.Result.Success) { - OnStatusUpdate?.Invoke("Failed To Get Player Public Ip",true); - OnStartGameFailed?.Invoke(); + OnStatusUpdate?.Invoke("Fetching The Public Ip Address", false); + var responseJson = JSON.ParseString(_response); + var ip = responseJson.GetString("public_ip"); + if (String.IsNullOrEmpty(ip)) + { + OnStatusUpdate?.Invoke("Failed To Get Player Public Ip", true); + OnStartGameFailed?.Invoke(); + } + else + { + OnStatusUpdate?.Invoke($"Public Ip Address is {ip}", false); + _publicIP = ip; + } } + //Invoked in case of an unexpected error. else { - OnStatusUpdate?.Invoke($"Public Ip Address is {ip}",false); - _publicIP = ip; - } - } - else - { - OnStatusUpdate?.Invoke( - $"Could Not Get Player Ip, Error {(int)_request.StatusCode} With Message: \n{_response}", + OnStatusUpdate?.Invoke( + $"Could Not Get Player Ip, Error {(int)webRequest.responseCode} With Message: \n{_response}", true); - OnStartGameFailed?.Invoke(); - + OnStartGameFailed?.Invoke(); + } } } + + //Utility function to configure every UnityWebRequest + private void SetCustomHeaders(UnityWebRequest request) + { + //Setting the proper Content-Type and Authorization header values to the _httpClient. + request.SetRequestHeader("Accept", TypeHeaderValue); + request.SetRequestHeader("Authorization", "token " + AuthHeaderValue); + } } } \ No newline at end of file diff --git a/Fishnet_v4.1.6R_WebGL/Assets/SpaceEdge/Scripts/Systems/MatchmakingSystem.cs.meta b/Fishnet_v4.1.6R_WebGL/Assets/SpaceEdge/Scripts/Systems/MatchmakingSystem.cs.meta index dcbce9c..e691f28 100644 --- a/Fishnet_v4.1.6R_WebGL/Assets/SpaceEdge/Scripts/Systems/MatchmakingSystem.cs.meta +++ b/Fishnet_v4.1.6R_WebGL/Assets/SpaceEdge/Scripts/Systems/MatchmakingSystem.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: b259e353e158c456d85dce46edc50782 +guid: 5b0fc49f25a41e541a8b5171abe9af68 MonoImporter: externalObjects: {} serializedVersion: 2