diff --git a/Assets/Mirror/Hosting/Edgegap/.config/dotnet-tools.json b/Assets/Mirror/Hosting/Edgegap/.config/dotnet-tools.json new file mode 100644 index 00000000000..03efa87b749 --- /dev/null +++ b/Assets/Mirror/Hosting/Edgegap/.config/dotnet-tools.json @@ -0,0 +1,12 @@ +{ + "version": 1, + "isRoot": true, + "tools": { + "csharpier": { + "version": "0.29.2", + "commands": [ + "dotnet-csharpier" + ] + } + } +} \ No newline at end of file diff --git a/Assets/Mirror/Hosting/Edgegap/Edgegap.asmdef b/Assets/Mirror/Hosting/Edgegap/Edgegap.asmdef index e9811e2c269..c37c2b8edee 100644 --- a/Assets/Mirror/Hosting/Edgegap/Edgegap.asmdef +++ b/Assets/Mirror/Hosting/Edgegap/Edgegap.asmdef @@ -2,7 +2,9 @@ "name": "Edgegap", "rootNamespace": "", "references": [], - "includePlatforms": [], + "includePlatforms": [ + "Editor" + ], "excludePlatforms": [], "allowUnsafeCode": false, "overrideReferences": false, diff --git a/Assets/Mirror/Hosting/Edgegap/Editor/Api/EdgegapApiBase.cs b/Assets/Mirror/Hosting/Edgegap/Editor/Api/EdgegapApiBase.cs index fd04901899c..f85f2904f7e 100755 --- a/Assets/Mirror/Hosting/Edgegap/Editor/Api/EdgegapApiBase.cs +++ b/Assets/Mirror/Hosting/Edgegap/Editor/Api/EdgegapApiBase.cs @@ -38,22 +38,25 @@ private string GetBaseUrl() => protected EdgegapApiBase( ApiEnvironment apiEnvironment, string apiToken, - EdgegapWindowMetadata.LogLevel logLevel = EdgegapWindowMetadata.LogLevel.Error) + EdgegapWindowMetadata.LogLevel logLevel = EdgegapWindowMetadata.LogLevel.Error + ) { this.SelectedApiEnvironment = apiEnvironment; this._httpClient.BaseAddress = new Uri($"{GetBaseUrl()}/"); this._httpClient.DefaultRequestHeaders.Accept.Add( - new MediaTypeWithQualityHeaderValue("application/json")); + new MediaTypeWithQualityHeaderValue("application/json") + ); string cleanedApiToken = apiToken.Replace("token ", ""); // We already prefixed token below - this._httpClient.DefaultRequestHeaders.Authorization = - new AuthenticationHeaderValue("token", cleanedApiToken); + this._httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue( + "token", + cleanedApiToken + ); this.LogLevel = logLevel; } - #region HTTP Requests /// /// POST | We already added "https://api.edgegap.com/" (or similar) BaseAddress via constructor. @@ -64,7 +67,10 @@ protected EdgegapApiBase( /// - Success => returns HttpResponseMessage result /// - Error => Catches errs => returns null (no rethrow) /// - protected async Task PostAsync(string relativePath = "", string json = "{}") + protected async Task PostAsync( + string relativePath = "", + string json = "{}" + ) { StringContent stringContent = CreateStringContent(json); Uri uri = new Uri(_httpClient.BaseAddress, relativePath); // Normalize POST uri: Can't end with `/`. @@ -92,7 +98,10 @@ protected async Task PostAsync(string relativePath = "", st /// - Success => returns HttpResponseMessage result /// - Error => Catches errs => returns null (no rethrow) /// - protected async Task PatchAsync(string relativePath = "", string json = "{}") + protected async Task PatchAsync( + string relativePath = "", + string json = "{}" + ) { StringContent stringContent = CreateStringContent(json); Uri uri = new Uri(_httpClient.BaseAddress, relativePath); // Normalize PATCH uri: Can't end with `/`. @@ -129,11 +138,12 @@ protected async Task PatchAsync(string relativePath = "", s /// - Success => returns HttpResponseMessage result /// - Error => Catches errs => returns null (no rethrow) /// - protected async Task GetAsync(string relativePath = "", string customQuery = "") + protected async Task GetAsync( + string relativePath = "", + string customQuery = "" + ) { - string completeRelativeUri = prepareEdgegapUriWithQuery( - relativePath, - customQuery); + string completeRelativeUri = prepareEdgegapUriWithQuery(relativePath, customQuery); if (IsLogLevelDebug) Debug.Log($"GetAsync to: `{completeRelativeUri} with customQuery: `{customQuery}`"); @@ -160,18 +170,23 @@ protected async Task GetAsync(string relativePath = "", str /// - Success => returns HttpResponseMessage result /// - Error => Catches errs => returns null (no rethrow) /// - protected async Task DeleteAsync(string relativePath = "", string customQuery = "") + protected async Task DeleteAsync( + string relativePath = "", + string customQuery = "" + ) { - string completeRelativeUri = prepareEdgegapUriWithQuery( - relativePath, - customQuery); + string completeRelativeUri = prepareEdgegapUriWithQuery(relativePath, customQuery); if (IsLogLevelDebug) - Debug.Log($"DeleteAsync to: `{completeRelativeUri} with customQuery: `{customQuery}`"); + Debug.Log( + $"DeleteAsync to: `{completeRelativeUri} with customQuery: `{customQuery}`" + ); try { - return await ExecuteRequestAsync(() => _httpClient.DeleteAsync(completeRelativeUri)); + return await ExecuteRequestAsync( + () => _httpClient.DeleteAsync(completeRelativeUri) + ); } catch (Exception e) { @@ -186,7 +201,8 @@ protected async Task DeleteAsync(string relativePath = "", /// private static async Task ExecuteRequestAsync( Func> requestFunc, - CancellationToken cancellationToken = default) + CancellationToken cancellationToken = default + ) { HttpResponseMessage response = null; try @@ -215,16 +231,18 @@ private static async Task ExecuteRequestAsync( // Check for a successful status code if (response == null) { - Debug.Log("!Success (null response) - returning 500"); + Debug.Log("Error: (null response) - returning 500"); return CreateUnknown500Err(); } if (!response.IsSuccessStatusCode) { HttpMethod httpMethod = response.RequestMessage.Method; - Debug.Log($"!Success: {(short)response.StatusCode} {response.ReasonPhrase} - " + - $"{httpMethod} | {response.RequestMessage.RequestUri}` - " + - $"{response.Content?.ReadAsStringAsync().Result}"); + Debug.Log( + $"Error: {(short)response.StatusCode} {response.ReasonPhrase} - " + + $"{httpMethod} | {response.RequestMessage.RequestUri}` - " + + $"{response.Content?.ReadAsStringAsync().Result}" + ); } return response; diff --git a/Assets/Mirror/Hosting/Edgegap/Editor/Api/EdgegapAppApi.cs b/Assets/Mirror/Hosting/Edgegap/Editor/Api/EdgegapAppApi.cs index 4e7eab14e89..b95153ea73c 100755 --- a/Assets/Mirror/Hosting/Edgegap/Editor/Api/EdgegapAppApi.cs +++ b/Assets/Mirror/Hosting/Edgegap/Editor/Api/EdgegapAppApi.cs @@ -110,9 +110,49 @@ public async Task> CreateAppVersion(Cr return result; } + + /// + /// GET to v1/apps + /// - Get all applications. + /// - API Doc | https://docs.edgegap.com/api/#tag/Applications/operation/applications-get + /// + /// + /// Http info with GetAppsResult data model + /// - Success: 200 + /// + public async Task> GetApps() + { + HttpResponseMessage response = await GetAsync($"v1/apps"); + EdgegapHttpResult result = new EdgegapHttpResult(response); // MIRROR CHANGE: 'new()' not supported in Unity 2020 + + bool isSuccess = response.StatusCode == HttpStatusCode.OK; // 200 + if (!isSuccess) + return result; + + return result; + } + + /// + /// GET to v1/app/{appName}/versions + /// + /// + /// Http info with GetAppVersionsResult data model + /// - Success: 200 + /// + public async Task> GetAppVersions(string appName) + { + HttpResponseMessage response = await GetAsync($"v1/app/{appName}/versions"); + EdgegapHttpResult result = new EdgegapHttpResult(response); + + bool isSuccess = response.StatusCode == HttpStatusCode.OK; // 200 + if (!isSuccess) + return result; + + return result; + } #endregion // API Methods - - + + #region Chained API Methods /// /// PATCH and/or POST to v1/app/: Upsert an *existing* application version with new specifications. diff --git a/Assets/Mirror/Hosting/Edgegap/Editor/Api/EdgegapDeploymentsApi.cs b/Assets/Mirror/Hosting/Edgegap/Editor/Api/EdgegapDeploymentsApi.cs index dda3ce24af9..837eb4f9834 100755 --- a/Assets/Mirror/Hosting/Edgegap/Editor/Api/EdgegapDeploymentsApi.cs +++ b/Assets/Mirror/Hosting/Edgegap/Editor/Api/EdgegapDeploymentsApi.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics; using System.Net; using System.Net.Http; using System.Threading; @@ -11,18 +12,16 @@ namespace Edgegap.Editor.Api { /// /// Wraps the v1/[deploy | status | stop] API endpoints: Deployments Control API. - /// - API Doc | https://docs.edgegap.com/api/#tag/Deployments + /// - API Doc | https://docs.edgegap.com/api/#tag/Deployments /// public class EdgegapDeploymentsApi : EdgegapApiBase { public EdgegapDeploymentsApi( - ApiEnvironment apiEnvironment, - string apiToken, - EdgegapWindowMetadata.LogLevel logLevel = EdgegapWindowMetadata.LogLevel.Error) - : base(apiEnvironment, apiToken, logLevel) - { - } - + ApiEnvironment apiEnvironment, + string apiToken, + EdgegapWindowMetadata.LogLevel logLevel = EdgegapWindowMetadata.LogLevel.Error + ) + : base(apiEnvironment, apiToken, logLevel) { } #region API Methods /// @@ -35,18 +34,16 @@ public EdgegapDeploymentsApi( /// - Success: 200 /// public async Task> CreateDeploymentAsync( - CreateDeploymentRequest request) + CreateDeploymentRequest request + ) { HttpResponseMessage response = await PostAsync("v1/deploy", request.ToString()); - EdgegapHttpResult result = new EdgegapHttpResult(response); // MIRROR CHANGE: 'new()' not supported in Unity 2020 + EdgegapHttpResult result = + new EdgegapHttpResult(response); // MIRROR CHANGE: 'new()' not supported in Unity 2020 - bool isSuccess = response.StatusCode == HttpStatusCode.OK; // 200 - if (!isSuccess) - return result; - return result; } - + /// /// GET v1/status/{requestId} /// - Retrieve the information for a deployment. @@ -59,18 +56,34 @@ public async Task> CreateDeploymentAsy /// Http info with GetDeploymentStatusResult data model /// - Success: 200 /// - public async Task> GetDeploymentStatusAsync(string requestId) + public async Task> GetDeploymentStatusAsync( + string requestId + ) { HttpResponseMessage response = await GetAsync($"v1/status/{requestId}"); - EdgegapHttpResult result = new EdgegapHttpResult(response); // MIRROR CHANGE: 'new()' not supported in Unity 2020 + EdgegapHttpResult result = + new EdgegapHttpResult(response); // MIRROR CHANGE: 'new()' not supported in Unity 2020 bool isSuccess = response.StatusCode == HttpStatusCode.OK; // 200 if (!isSuccess) return result; - + return result; } - + + public async Task> GetDeploymentsAsync() + { + HttpResponseMessage response = await GetAsync($"v1/deployments"); + EdgegapHttpResult result = + new EdgegapHttpResult(response); + + bool isSuccess = response.StatusCode == HttpStatusCode.OK; // 200 + if (!isSuccess) + return result; + + return result; + } + /// /// DELETE v1/stop/{requestId} /// - Delete an instance of deployment. It will stop the running container and all its games. @@ -83,20 +96,23 @@ public async Task> GetDeploymentSta /// Http info with GetDeploymentStatusResult data model /// - Success: 200 /// - public async Task> StopActiveDeploymentAsync(string requestId) + public async Task> StopActiveDeploymentAsync( + string requestId + ) { HttpResponseMessage response = await DeleteAsync($"v1/stop/{requestId}"); - EdgegapHttpResult result = new EdgegapHttpResult(response); // MIRROR CHANGE: 'new()' not supported in Unity 2020 + EdgegapHttpResult result = + new EdgegapHttpResult(response); // MIRROR CHANGE: 'new()' not supported in Unity 2020 bool isSuccess = response.StatusCode == HttpStatusCode.OK; // 200 if (!isSuccess) return result; - + return result; } #endregion // API Methods - - + + #region Chained API Methods /// /// POST v1/deploy => GET v1/status/{requestId} @@ -109,16 +125,22 @@ public async Task> StopActiveDeplo /// - Success: 200 /// - Error: If createResult.HasErr, returns createResult /// - public async Task> CreateDeploymentAwaitReadyStatusAsync( - CreateDeploymentRequest request, TimeSpan pollInterval) + public async Task< + EdgegapHttpResult + > CreateDeploymentAwaitReadyStatusAsync( + CreateDeploymentRequest request, + TimeSpan pollInterval + ) { - EdgegapHttpResult createResponse = await CreateDeploymentAsync(request); + EdgegapHttpResult createResponse = await CreateDeploymentAsync( + request + ); // Create => bool isCreateSuccess = createResponse.StatusCode == HttpStatusCode.OK; // 200 if (!isCreateSuccess) return createResponse; - + // Await Status READY => string requestId = createResponse.Data.RequestId; _ = await AwaitReadyStatusAsync(requestId, pollInterval); @@ -126,21 +148,25 @@ public async Task> CreateDeploymentAwa // Return no matter what the result; no need to validate return createResponse; } - + /// If you recently deployed but want to await READY status. /// /// public async Task> AwaitReadyStatusAsync( - string requestId, - TimeSpan pollInterval) + string requestId, + TimeSpan pollInterval + ) { Assert.IsTrue(!string.IsNullOrEmpty(requestId)); // Validate - + EdgegapHttpResult statusResponse = null; - CancellationTokenSource cts = new CancellationTokenSource (TimeSpan.FromMinutes( // MIRROR CHANGE: 'new()' not supported in Unity 2020 - EdgegapWindowMetadata.DEPLOYMENT_AWAIT_READY_STATUS_TIMEOUT_MINS)); + CancellationTokenSource cts = new CancellationTokenSource( + TimeSpan.FromMinutes( // MIRROR CHANGE: 'new()' not supported in Unity 2020 + EdgegapWindowMetadata.DEPLOYMENT_AWAIT_READY_STATUS_TIMEOUT_MINS + ) + ); bool isReady = false; - + while (!isReady && !cts.Token.IsCancellationRequested) { await Task.Delay(pollInterval, cts.Token); @@ -150,19 +176,22 @@ public async Task> AwaitReadyStatus return statusResponse; } - + /// If you recently stopped a deployment, but want to await TERMINATED (410) status. /// /// - public async Task> AwaitTerminatedDeleteStatusAsync( - string requestId, - TimeSpan pollInterval) + public async Task< + EdgegapHttpResult + > AwaitTerminatedDeleteStatusAsync(string requestId, TimeSpan pollInterval) { EdgegapHttpResult deleteResponse = null; - CancellationTokenSource cts = new CancellationTokenSource(TimeSpan.FromMinutes( // MIRROR CHANGE: 'new()' not supported in Unity 2020 - EdgegapWindowMetadata.DEPLOYMENT_AWAIT_READY_STATUS_TIMEOUT_MINS)); + CancellationTokenSource cts = new CancellationTokenSource( + TimeSpan.FromMinutes( // MIRROR CHANGE: 'new()' not supported in Unity 2020 + EdgegapWindowMetadata.DEPLOYMENT_AWAIT_READY_STATUS_TIMEOUT_MINS + ) + ); bool isStopped = false; - + while (!isStopped && !cts.Token.IsCancellationRequested) { await Task.Delay(pollInterval, cts.Token); diff --git a/Assets/Mirror/Hosting/Edgegap/Editor/Api/EdgegapIpApi.cs b/Assets/Mirror/Hosting/Edgegap/Editor/Api/EdgegapIpApi.cs index 86e4b26a310..5f05e11087b 100755 --- a/Assets/Mirror/Hosting/Edgegap/Editor/Api/EdgegapIpApi.cs +++ b/Assets/Mirror/Hosting/Edgegap/Editor/Api/EdgegapIpApi.cs @@ -7,18 +7,16 @@ namespace Edgegap.Editor.Api { /// /// Wraps the v1/ip API endpoint: "IP Lookup" API. - /// - API Doc | https://docs.edgegap.com/api/#tag/IP-Lookup + /// - API Doc | https://docs.edgegap.com/api/#tag/IP-Lookup /// public class EdgegapIpApi : EdgegapApiBase { public EdgegapIpApi( - ApiEnvironment apiEnvironment, - string apiToken, - EdgegapWindowMetadata.LogLevel logLevel = EdgegapWindowMetadata.LogLevel.Error) - : base(apiEnvironment, apiToken, logLevel) - { - } - + ApiEnvironment apiEnvironment, + string apiToken, + EdgegapWindowMetadata.LogLevel logLevel = EdgegapWindowMetadata.LogLevel.Error + ) + : base(apiEnvironment, apiToken, logLevel) { } #region API Methods /// @@ -34,12 +32,9 @@ public EdgegapIpApi( public async Task> GetYourPublicIp() { HttpResponseMessage response = await GetAsync("v1/ip"); - EdgegapHttpResult result = new EdgegapHttpResult(response); // MIRROR CHANGE: 'new()' not supported in Unity 2020 + EdgegapHttpResult result = + new EdgegapHttpResult(response); // MIRROR CHANGE: 'new()' not supported in Unity 2020 - bool isSuccess = response.StatusCode == HttpStatusCode.OK; // 200 - if (!isSuccess) - return result; - return result; } #endregion // API Methods diff --git a/Assets/Mirror/Hosting/Edgegap/Editor/Api/Models/Requests/CreateDeploymentRequest.cs b/Assets/Mirror/Hosting/Edgegap/Editor/Api/Models/Requests/CreateDeploymentRequest.cs index 968821cc93f..eae55f7b392 100755 --- a/Assets/Mirror/Hosting/Edgegap/Editor/Api/Models/Requests/CreateDeploymentRequest.cs +++ b/Assets/Mirror/Hosting/Edgegap/Editor/Api/Models/Requests/CreateDeploymentRequest.cs @@ -12,14 +12,14 @@ public class CreateDeploymentRequest /// *Required: The name of the App you want to deploy. [JsonProperty("app_name")] public string AppName { get; set; } - + /// /// *Required: The name of the App Version you want to deploy; /// if not present, the last version created is picked. /// [JsonProperty("version_name")] public string VersionName { get; set; } - + /// /// *Required: The List of IP of your user. /// @@ -30,14 +30,17 @@ public class CreateDeploymentRequest /// *Required: The list of IP of your user with their location (latitude, longitude). /// [JsonProperty("geo_ip_list")] - public string[] GeoIpList { get; set; } = {}; + public string[] GeoIpList { get; set; } = { }; #endregion // Required - - + + /// + /// The list of tags assigned to the deployment + /// + [JsonProperty("tags")] + public string[] Tags { get; set; } = { EdgegapWindowMetadata.DEFAULT_DEPLOYMENT_TAG }; + /// Used by Newtonsoft - public CreateDeploymentRequest() - { - } + public CreateDeploymentRequest() { } /// Init with required info; used for a single external IP address. /// The name of the application. @@ -46,18 +49,14 @@ public CreateDeploymentRequest() /// the last version created is picked. /// /// Obtain from IpApi. - public CreateDeploymentRequest( - string appName, - string versionName, - string externalIp) + public CreateDeploymentRequest(string appName, string versionName, string externalIp) { this.AppName = appName; this.VersionName = versionName; this.IpList = new[] { externalIp }; } - + /// Parse to json str - public override string ToString() => - JsonConvert.SerializeObject(this); + public override string ToString() => JsonConvert.SerializeObject(this); } } diff --git a/Assets/Mirror/Hosting/Edgegap/Editor/Api/Models/Results/EdgegapErrorResult.cs b/Assets/Mirror/Hosting/Edgegap/Editor/Api/Models/Results/EdgegapErrorResult.cs index 55b03f2c2f2..6cdc37a31a0 100755 --- a/Assets/Mirror/Hosting/Edgegap/Editor/Api/Models/Results/EdgegapErrorResult.cs +++ b/Assets/Mirror/Hosting/Edgegap/Editor/Api/Models/Results/EdgegapErrorResult.cs @@ -3,7 +3,7 @@ namespace Edgegap.Editor.Api.Models.Results { /// Edgegap error, generally just containing `message` - public class EdgegapErrorResult + public class EdgegapErrorResult { /// Friendly, UI-facing error message from Edgegap; can be lengthy. [JsonProperty("message")] diff --git a/Assets/Mirror/Hosting/Edgegap/Editor/Api/Models/Results/EdgegapHttpResult.cs b/Assets/Mirror/Hosting/Edgegap/Editor/Api/Models/Results/EdgegapHttpResult.cs index 55b4892b85b..54713a2a304 100755 --- a/Assets/Mirror/Hosting/Edgegap/Editor/Api/Models/Results/EdgegapHttpResult.cs +++ b/Assets/Mirror/Hosting/Edgegap/Editor/Api/Models/Results/EdgegapHttpResult.cs @@ -15,13 +15,13 @@ public class EdgegapHttpResult { /// HTTP Status code for the request. public HttpStatusCode StatusCode { get; } - + /// This could be err, success, or null. public string Json { get; } - + /// eg: "POST" public HttpMethod HttpMethod; - + /// /// Typically is sent by servers together with the status code. /// Useful for fallback err descriptions, often based on the status code. @@ -30,29 +30,32 @@ public class EdgegapHttpResult /// Contains `message` with friendly info. public bool HasErr => Error != null; - public EdgegapErrorResult Error { get; set; } - + public EdgegapErrorResult Error + { + get { return JsonConvert.DeserializeObject(Json); } + } + #region Common Shortcuts /// OK public bool IsResultCode200 => StatusCode == HttpStatusCode.OK; - + /// NoContent public bool IsResultCode204 => StatusCode == HttpStatusCode.NoContent; - + /// Forbidden public bool IsResultCode403 => StatusCode == HttpStatusCode.Forbidden; - + /// Conflict public bool IsResultCode409 => StatusCode == HttpStatusCode.Conflict; /// BadRequest public bool IsResultCode400 => StatusCode == HttpStatusCode.BadRequest; - + /// Gone public bool IsResultCode410 => StatusCode == HttpStatusCode.Gone; #endregion // Common Shortcuts - - + + /// /// Constructor that initializes the class based on an HttpResponseMessage. /// @@ -65,16 +68,13 @@ public EdgegapHttpResult(HttpResponseMessage httpResponse) { // TODO: This can be read async with `await`, but can't do this in a Constructor. // Instead, make a factory builder Task => - this.Json = httpResponse.Content.ReadAsStringAsync().Result; - - this.Error = JsonConvert.DeserializeObject(Json); - if (Error != null && string.IsNullOrEmpty(Error.ErrorMessage)) - Error = null; + Json = httpResponse.Content.ReadAsStringAsync().Result; } catch (Exception e) { - Debug.LogError("Error (reading httpResponse.Content): Client expected json, " + - $"but server returned !json: {e} - "); + Debug.LogError( + $"Couldn't parse error response. HTTP {httpResponse.StatusCode}.\n{e}" + ); } } } @@ -87,16 +87,16 @@ public class EdgegapHttpResult : EdgegapHttpResult { /// The actual result model from Json. Could be null! public TResult Data { get; set; } - - - public EdgegapHttpResult(HttpResponseMessage httpResponse, bool isLogLevelDebug = false) + + public EdgegapHttpResult(HttpResponseMessage httpResponse, bool isLogLevelDebug = false) : base(httpResponse) { this.HttpMethod = httpResponse.RequestMessage.Method; - + // Assuming JSON content and using Newtonsoft.Json for deserialization - bool isDeserializable = httpResponse.Content != null && - httpResponse.Content.Headers.ContentType.MediaType == "application/json"; + bool isDeserializable = + httpResponse.Content != null + && httpResponse.Content.Headers.ContentType.MediaType == "application/json"; if (isDeserializable) { @@ -106,7 +106,9 @@ public EdgegapHttpResult(HttpResponseMessage httpResponse, bool isLogLevelDebug } catch (Exception e) { - Debug.LogError($"Error (deserializing EdgegapHttpResult.Data): {e} - json: {Json}"); + Debug.LogError( + $"Error (deserializing EdgegapHttpResult.Data): {e} - json: {Json}" + ); throw; } } diff --git a/Assets/Mirror/Hosting/Edgegap/Editor/Api/Models/Results/GetAppVersionsResult.cs b/Assets/Mirror/Hosting/Edgegap/Editor/Api/Models/Results/GetAppVersionsResult.cs new file mode 100644 index 00000000000..f82c6de250f --- /dev/null +++ b/Assets/Mirror/Hosting/Edgegap/Editor/Api/Models/Results/GetAppVersionsResult.cs @@ -0,0 +1,15 @@ +using Newtonsoft.Json; +using System.Collections.Generic; + +namespace Edgegap.Editor.Api.Models.Results +{ + /// + /// Result model for `[GET] v1/app/{app_name}/versions`. + /// GET API Doc | https://docs.edgegap.com/api/#tag/Applications/operation/app-versions-get + /// + public class GetAppVersionsResult + { + [JsonProperty("versions")] + public List Versions { get; set; } + } +} diff --git a/Assets/Mirror/Hosting/Edgegap/Editor/Api/Models/Results/GetAppVersionsResult.cs.meta b/Assets/Mirror/Hosting/Edgegap/Editor/Api/Models/Results/GetAppVersionsResult.cs.meta new file mode 100644 index 00000000000..b29dd7a2b5e --- /dev/null +++ b/Assets/Mirror/Hosting/Edgegap/Editor/Api/Models/Results/GetAppVersionsResult.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 39dff8eb2a96db14581701965c2663c2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Hosting/Edgegap/Editor/Api/Models/Results/GetAppsResult.cs b/Assets/Mirror/Hosting/Edgegap/Editor/Api/Models/Results/GetAppsResult.cs new file mode 100644 index 00000000000..462bce97740 --- /dev/null +++ b/Assets/Mirror/Hosting/Edgegap/Editor/Api/Models/Results/GetAppsResult.cs @@ -0,0 +1,15 @@ +using Newtonsoft.Json; +using System.Collections.Generic; + +namespace Edgegap.Editor.Api.Models.Results +{ + /// + /// Result model for `[GET] v1/apps`. + /// GET API Doc | https://docs.edgegap.com/api/#tag/Applications/operation/applications-get + /// + public class GetAppsResult + { + [JsonProperty("applications")] + public List Applications { get; set; } + } +} diff --git a/Assets/Mirror/Hosting/Edgegap/Editor/Api/Models/Results/GetAppsResult.cs.meta b/Assets/Mirror/Hosting/Edgegap/Editor/Api/Models/Results/GetAppsResult.cs.meta new file mode 100644 index 00000000000..bbf3927e6a5 --- /dev/null +++ b/Assets/Mirror/Hosting/Edgegap/Editor/Api/Models/Results/GetAppsResult.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 88f306b7c80ebad4eada4c62e875d2a6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Hosting/Edgegap/Editor/Api/Models/Results/GetDeploymentResult.cs b/Assets/Mirror/Hosting/Edgegap/Editor/Api/Models/Results/GetDeploymentResult.cs new file mode 100644 index 00000000000..2a64bf96d28 --- /dev/null +++ b/Assets/Mirror/Hosting/Edgegap/Editor/Api/Models/Results/GetDeploymentResult.cs @@ -0,0 +1,20 @@ +using Newtonsoft.Json; + +namespace Edgegap.Editor.Api.Models.Results +{ + /// + /// Result model of a deployment for `GET v1/deployments`. + /// API Doc | https://docs.edgegap.com/api/#tag/Deployments/operation/deployments-get + /// + public class GetDeploymentResult + { + [JsonProperty("request_id")] + public string RequestId { get; set; } + + [JsonProperty("ready")] + public bool Ready { get; set; } + + [JsonProperty("tags")] + public string[] Tags { get; set; } + } +} diff --git a/Assets/Mirror/Hosting/Edgegap/Editor/Api/Models/Results/GetDeploymentResult.cs.meta b/Assets/Mirror/Hosting/Edgegap/Editor/Api/Models/Results/GetDeploymentResult.cs.meta new file mode 100644 index 00000000000..db84279a62c --- /dev/null +++ b/Assets/Mirror/Hosting/Edgegap/Editor/Api/Models/Results/GetDeploymentResult.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 87df84c48a738aa48b66d0c42aacb3db +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Hosting/Edgegap/Editor/Api/Models/Results/GetDeploymentsResult.cs b/Assets/Mirror/Hosting/Edgegap/Editor/Api/Models/Results/GetDeploymentsResult.cs new file mode 100644 index 00000000000..1028eff5ca9 --- /dev/null +++ b/Assets/Mirror/Hosting/Edgegap/Editor/Api/Models/Results/GetDeploymentsResult.cs @@ -0,0 +1,14 @@ +using Newtonsoft.Json; + +namespace Edgegap.Editor.Api.Models.Results +{ + /// + /// Result model for `GET v1/deployments`. + /// API Doc | https://docs.edgegap.com/api/#tag/Deployments/operation/deployments-get + /// + public class GetDeploymentsResult + { + [JsonProperty("data")] + public GetDeploymentResult[] Data { get; set; } + } +} diff --git a/Assets/Mirror/Hosting/Edgegap/Editor/Api/Models/Results/GetDeploymentsResult.cs.meta b/Assets/Mirror/Hosting/Edgegap/Editor/Api/Models/Results/GetDeploymentsResult.cs.meta new file mode 100644 index 00000000000..12a913d0d70 --- /dev/null +++ b/Assets/Mirror/Hosting/Edgegap/Editor/Api/Models/Results/GetDeploymentsResult.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d39faf2ee20bb0647b63b9b72910d2c8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Hosting/Edgegap/Editor/Api/Models/Results/StopActiveDeploymentResult.cs b/Assets/Mirror/Hosting/Edgegap/Editor/Api/Models/Results/StopActiveDeploymentResult.cs index 5d4d6a0b44a..65ee1a14fff 100755 --- a/Assets/Mirror/Hosting/Edgegap/Editor/Api/Models/Results/StopActiveDeploymentResult.cs +++ b/Assets/Mirror/Hosting/Edgegap/Editor/Api/Models/Results/StopActiveDeploymentResult.cs @@ -6,84 +6,79 @@ public class StopActiveDeploymentResult { [JsonProperty("message")] public string Message { get; set; } - + [JsonProperty("deployment_summary")] public DeploymentSummaryData DeploymentSummary { get; set; } - public class DeploymentSummaryData { [JsonProperty("request_id")] public string RequestId { get; set; } - + [JsonProperty("fqdn")] public string Fqdn { get; set; } - + [JsonProperty("app_name")] public string AppName { get; set; } - + [JsonProperty("app_version")] public string AppVersion { get; set; } - + [JsonProperty("current_status")] public string CurrentStatus { get; set; } - + [JsonProperty("running")] public bool Running { get; set; } - + [JsonProperty("whitelisting_active")] public bool WhitelistingActive { get; set; } - + [JsonProperty("start_time")] public string StartTime { get; set; } - + [JsonProperty("removal_time")] public string RemovalTime { get; set; } - + [JsonProperty("elapsed_time")] - public int ElapsedTime { get; set; } - + public int? ElapsedTime { get; set; } + [JsonProperty("last_status")] public string LastStatus { get; set; } - + [JsonProperty("error")] public bool Error { get; set; } - + [JsonProperty("error_detail")] public string ErrorDetail { get; set; } - + [JsonProperty("ports")] public PortsData Ports { get; set; } - + [JsonProperty("public_ip")] public string PublicIp { get; set; } - + [JsonProperty("sessions")] public SessionData[] Sessions { get; set; } - + [JsonProperty("location")] public LocationData Location { get; set; } - + [JsonProperty("tags")] public string[] Tags { get; set; } - + [JsonProperty("sockets")] public string Sockets { get; set; } - + [JsonProperty("sockets_usage")] public string SocketsUsage { get; set; } - + [JsonProperty("command")] public string Command { get; set; } - + [JsonProperty("arguments")] public string Arguments { get; set; } - } - - public class PortsData - { - } + public class PortsData { } } } diff --git a/Assets/Mirror/Hosting/Edgegap/Editor/Api/Models/VersionData.cs b/Assets/Mirror/Hosting/Edgegap/Editor/Api/Models/VersionData.cs new file mode 100644 index 00000000000..4a7a49bd024 --- /dev/null +++ b/Assets/Mirror/Hosting/Edgegap/Editor/Api/Models/VersionData.cs @@ -0,0 +1,13 @@ +using Newtonsoft.Json; + +namespace Edgegap.Editor.Api.Models +{ + public class VersionData + { + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("is_active")] + public bool IsActive { get; set; } + } +} diff --git a/Assets/Mirror/Hosting/Edgegap/Editor/Api/Models/VersionData.cs.meta b/Assets/Mirror/Hosting/Edgegap/Editor/Api/Models/VersionData.cs.meta new file mode 100644 index 00000000000..6cd9419121f --- /dev/null +++ b/Assets/Mirror/Hosting/Edgegap/Editor/Api/Models/VersionData.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7dfe72e265bef484ea78bd7e105e0e77 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Hosting/Edgegap/Editor/CustomPopupContent.cs b/Assets/Mirror/Hosting/Edgegap/Editor/CustomPopupContent.cs new file mode 100644 index 00000000000..cc899635145 --- /dev/null +++ b/Assets/Mirror/Hosting/Edgegap/Editor/CustomPopupContent.cs @@ -0,0 +1,70 @@ +#if UNITY_EDITOR +using System; +using System.Collections.Generic; +using UnityEditor; +using UnityEngine; + +namespace Edgegap.Editor +{ + public class CustomPopupContent : PopupWindowContent + { + private Vector2 scrollPos; + private List _btnNames; + private Action _onBtnClick; + private string _defaultValue = ""; + + private float _minHeight = 25; + private float _maxHeight = 100; + private float _width; + + public CustomPopupContent( + List btnNames, + Action btnCallback, + string defaultValue, + float width = 400 + ) + { + _btnNames = btnNames; + _onBtnClick = btnCallback; + _width = width; + _defaultValue = defaultValue; + } + + public override Vector2 GetWindowSize() + { + float height = _minHeight; + + if (_btnNames.Count > 0) + { + height *= _btnNames.Count; + } + + return new Vector2(_width, height <= _maxHeight ? height : _maxHeight); + } + + public override void OnGUI(Rect rect) + { + scrollPos = EditorGUILayout.BeginScrollView(scrollPos); + + foreach (string name in _btnNames) + { + if (GUILayout.Button(name, GUILayout.Width(_width - 25))) + { + if (name == "Create New Application") + { + _onBtnClick(_defaultValue); + } + else + { + _onBtnClick(name); + } + + editorWindow.Close(); + } + } + + EditorGUILayout.EndScrollView(); + } + } +} +#endif diff --git a/Assets/Mirror/Hosting/Edgegap/Editor/CustomPopupContent.cs.meta b/Assets/Mirror/Hosting/Edgegap/Editor/CustomPopupContent.cs.meta new file mode 100644 index 00000000000..9d520111bfa --- /dev/null +++ b/Assets/Mirror/Hosting/Edgegap/Editor/CustomPopupContent.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6a68367cec4322b4d8c9ac1545bf430a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Hosting/Edgegap/Editor/EdgegapBuildUtils.cs b/Assets/Mirror/Hosting/Edgegap/Editor/EdgegapBuildUtils.cs index e5e9e9aa520..0ae77b8c114 100755 --- a/Assets/Mirror/Hosting/Edgegap/Editor/EdgegapBuildUtils.cs +++ b/Assets/Mirror/Hosting/Edgegap/Editor/EdgegapBuildUtils.cs @@ -1,4 +1,5 @@ -#if UNITY_EDITOR +#if UNITY_EDITOR +using Edgegap.Editor; using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -10,18 +11,20 @@ using System.Threading.Tasks; using UnityEditor; using UnityEditor.Build.Reporting; - +using UnityEngine; using Debug = UnityEngine.Debug; namespace Edgegap { internal static class EdgegapBuildUtils { + public static bool IsLogLevelDebug => + EdgegapWindowMetadata.LOG_LEVEL == EdgegapWindowMetadata.LogLevel.Debug; public static bool IsArmCPU() => RuntimeInformation.ProcessArchitecture == Architecture.Arm || RuntimeInformation.ProcessArchitecture == Architecture.Arm64; - public static BuildReport BuildServer() + public static BuildReport BuildServer(string folderName) { IEnumerable scenes = EditorBuildSettings.scenes .Where(s => s.enabled) @@ -37,13 +40,13 @@ public static BuildReport BuildServer() options = BuildOptions.EnableHeadlessMode, // obsolete and missing UNITY_SERVER define #endif // END MIRROR CHANGE - locationPathName = "Builds/EdgegapServer/ServerBuild" + locationPathName = $"Builds/{folderName}/ServerBuild" }; return BuildPipeline.BuildPlayer(options); } - public static async Task DockerSetupAndInstallationCheck(string path) + public static async Task DockerSetupAndInstallationCheck(string path) { if (!File.Exists(path)) { @@ -52,14 +55,58 @@ public static async Task DockerSetupAndInstallationCheck(string path) string output = null; string error = null; - await RunCommand_DockerVersion(msg => output = msg, msg => error = msg); // MIRROR CHANGE + await RunCommand_DockerVersion(msg => output = msg, + (msg) => + { + if (msg.ToLowerInvariant().Contains("error") || msg.ToLowerInvariant().Contains("invalid")) + { + error = msg; + } + }); + if (!string.IsNullOrEmpty(error)) { Debug.LogError(error); - return false; + return error; } + Debug.Log($"[Edgegap] Docker version detected: {output}"); // MIRROR CHANGE - return true; + + await RunCommand_DockerPS(null, + (msg) => + { + if (msg.ToLowerInvariant().Contains("error") || msg.ToLowerInvariant().Contains("invalid")) + { + error = msg; + } + }); + + if (!string.IsNullOrEmpty(error)) + { + Debug.LogError(error); + return error; + } + + return null; + } + + public static async Task InstallLinuxModules(string unityVersion, Action outputReciever = null, Action errorReciever = null) + { + await RunCommand_InstallLinuxRequirements("linux-mono", unityVersion, outputReciever); + await RunCommand_InstallLinuxRequirements("linux-il2cpp", unityVersion, outputReciever); + } + + static async Task RunCommand_DockerPS(Action outputReciever = null, Action errorReciever = null) + { +#if UNITY_EDITOR_WIN + await RunCommand("cmd.exe", "/c docker ps -q", outputReciever, errorReciever); +#elif UNITY_EDITOR_OSX + await RunCommand("/bin/bash", "-c \"docker ps -q\"", outputReciever, errorReciever); +#elif UNITY_EDITOR_LINUX + await RunCommand("/bin/bash", "-c \"docker ps -q\"", outputReciever, errorReciever); +#else + Debug.LogError("The platform is not supported yet."); +#endif } // MIRROR CHANGE @@ -76,8 +123,116 @@ static async Task RunCommand_DockerVersion(Action outputReciever = null, #endif } - // MIRROR CHANGE - public static async Task RunCommand_DockerBuild(string dockerfilePath, string registry, string imageRepo, string tag, string projectPath, Action onStatusUpdate) + public static async Task RunCommand_DockerImage(Action outputReciever, Action errorReciever) + { +#if UNITY_EDITOR_WIN + await RunCommand("cmd.exe", "/c docker image ls --format \"{{.Repository}}:{{.Tag}}\"", outputReciever, + +#elif UNITY_EDITOR_OSX + await RunCommand("/bin/bash", "-c \"docker image ls --format \"{{.Repository}}:{{.Tag}}\"\"", outputReciever, +#elif UNITY_EDITOR_LINUX + await RunCommand("/bin/bash", "-c \"docker image ls --format \"{{.Repository}}:{{.Tag}}\"\"", outputReciever, +#endif + (msg) => + { + if (msg.ToLowerInvariant().Contains("error") || msg.ToLowerInvariant().Contains("invalid")) + { + errorReciever(msg); + } + }); + } + + public static async Task RunCommand_DockerRun(string image, string extraParams) + { + // ARM -> x86 support: + string runCommand = IsArmCPU() ? "run --platform linux/amd64" : "run"; + +#if UNITY_EDITOR_WIN + await RunCommand("docker.exe", $"{runCommand} --name edgegap-server-test -d {extraParams} {image}", +#elif UNITY_EDITOR_OSX + await RunCommand("/bin/bash", $"-c \"docker {runCommand} --name edgegap-server-test -d {extraParams} {image}\"", +#elif UNITY_EDITOR_LINUX + await RunCommand("/bin/bash", $"-c \"docker {runCommand} --name edgegap-server-test -d {extraParams} {image}\"", +#endif + null, + (msg) => + { + if (msg.ToLowerInvariant().Contains("error") || msg.ToLowerInvariant().Contains("invalid")) + { + throw new Exception(msg); + } + }); + } + + public static async Task RunCommand_DockerStop() + { + //Stopping running container +#if UNITY_EDITOR_WIN + await RunCommand("docker.exe", $"stop edgegap-server-test", +#elif UNITY_EDITOR_OSX + await RunCommand("/bin/bash", $"-c \"docker stop edgegap-server-test\"", +#elif UNITY_EDITOR_LINUX + await RunCommand("/bin/bash", $"-c \"docker stop edgegap-server-test\"", +#endif + null, + (msg) => + { + if (msg.ToLowerInvariant().Contains("error") || msg.ToLowerInvariant().Contains("invalid")) + { + throw new Exception(msg); + } + }); + + //Deleting the stopped container +#if UNITY_EDITOR_WIN + await RunCommand("docker.exe", $"rm edgegap-server-test", +#elif UNITY_EDITOR_OSX + await RunCommand("/bin/bash", $"-c \"docker rm edgegap-server-test\"", +#elif UNITY_EDITOR_LINUX + await RunCommand("/bin/bash", $"-c \"docker rm edgegap-server-test\"", +#endif + null, + (msg) => + { + if (msg.ToLowerInvariant().Contains("error") || msg.ToLowerInvariant().Contains("invalid")) + { + throw new Exception(msg); + } + }); + } + + static async Task RunCommand_InstallLinuxRequirements(string module, string unityVersion, Action outputReciever = null, Action errorReciever = null) + { + string error = null; +#if UNITY_EDITOR_WIN + await RunCommand("cmd.exe", + $"\"C:\\Program Files\\Unity Hub\\Unity Hub.exe\" -- --headless install-modules --version {unityVersion} -m {module}", + outputReciever, +#elif UNITY_EDITOR_OSX + await RunCommand("/bin/bash", + $"/Applications/Unity/Hub.app/Contents/MacOS/Unity/Hub -- --headless install-modules --version {unityVersion} -m linux-mono linux-il2cpp", + outputReciever, +#elif UNITY_EDITOR_LINUX + await RunCommand("/bin/bash", + $"~/Applications/Unity/Hub.AppImage --headless install-modules --version {unityVersion} -m linux-mono linux-il2cpp", + outputReciever, +#endif + (msg) => + { + if (msg.ToLowerInvariant().Contains("error") || msg.ToLowerInvariant().Contains("invalid")) + { + error = msg; + } + outputReciever(msg); + }); + + if (error != null) + { + errorReciever(error); + } + } + + public static async Task RunCommand_DockerBuild(string dockerfilePath, string registry, string imageRepo, string tag, string projectPath, Action onStatusUpdate, string extraParams = null) { string realErrorMessage = null; @@ -88,6 +243,13 @@ public static async Task RunCommand_DockerBuild(string dockerfilePath, string re // would show an error in a linux .go file with 'not found'. string buildCommand = IsArmCPU() ? "buildx build --platform linux/amd64" : "build"; + if (!string.IsNullOrEmpty(extraParams)) + { + buildCommand += $" {extraParams}"; + } + + bool done = false; + #if UNITY_EDITOR_WIN await RunCommand("docker.exe", $"{buildCommand} -f \"{dockerfilePath}\" -t \"{registry}/{imageRepo}:{tag}\" \"{projectPath}\"", onStatusUpdate, #elif UNITY_EDITOR_OSX @@ -97,37 +259,42 @@ await RunCommand("/bin/bash", $"-c \"docker {buildCommand} -f {dockerfilePath} - #endif (msg) => { - if (msg.Contains("ERROR")) + if (msg.ToLowerInvariant().Contains("error") || msg.ToLowerInvariant().Contains("invalid")) { realErrorMessage = msg; } + if (msg.ToLowerInvariant().Contains("done")) + { + done = true; + } + Debug.Log(msg); onStatusUpdate(msg); }); - if(realErrorMessage != null) + if (realErrorMessage != null) { throw new Exception(realErrorMessage); } + else if (!done) + { + throw new Exception("Couldn't complete containerization, see console log for details."); + } } - public static async Task<(bool, string)> RunCommand_DockerPush(string registry, string imageRepo, string tag, Action onStatusUpdate) + public static async Task RunCommand_DockerPush(string registry, string imageRepo, string tag, Action onStatusUpdate) { - string error = string.Empty; + string error = null; #if UNITY_EDITOR_WIN - await RunCommand("docker.exe", $"push {registry}/{imageRepo}:{tag}", onStatusUpdate, (msg) => error += msg + "\n"); + await RunCommand("docker.exe", $"push {registry}/{imageRepo}:{tag}", onStatusUpdate, #elif UNITY_EDITOR_OSX - await RunCommand("/bin/bash", $"-c \"docker push {registry}/{imageRepo}:{tag}\"", onStatusUpdate, (msg) => error += msg + "\n"); + await RunCommand("/bin/bash", $"-c \"docker push {registry}/{imageRepo}:{tag}\"", onStatusUpdate, #elif UNITY_EDITOR_LINUX - await RunCommand("/bin/bash", $"-c \"docker push {registry}/{imageRepo}:{tag}\"", onStatusUpdate, (msg) => error += msg + "\n"); + await RunCommand("/bin/bash", $"-c \"docker push {registry}/{imageRepo}:{tag}\"", onStatusUpdate, #endif - if (!string.IsNullOrEmpty(error)) - { - Debug.LogError(error); - return (false, error); - } - return (true, null); + (msg) => error += msg + "\n"); + + return error ?? ""; } - // END MIRROR CHANGE static async Task RunCommand(string command, string arguments, Action outputReciever = null, Action errorReciever = null) { @@ -141,7 +308,6 @@ static async Task RunCommand(string command, string arguments, Action ou CreateNoWindow = true, }; - // MIRROR CHANGE #if !UNITY_EDITOR_WIN // on mac, commands like 'docker' aren't found because it's not in the application's PATH // even if it runs on mac's terminal. @@ -154,7 +320,6 @@ static async Task RunCommand(string command, string arguments, Action ou startInfo.EnvironmentVariables["PATH"] = customPath; // Debug.Log("PATH: " + customPath); #endif - // END MIRROR CHANGE Process proc = new Process() { StartInfo = startInfo, }; proc.EnableRaisingEvents = true; @@ -217,7 +382,7 @@ public static string IncrementTag(string tag) public static void UpdateEdgegapAppTag(string tag) { - // throw new NotImplementedException(); + // throw new NotImplementedException(); } /// Run a Docker cmd with streaming log response. TODO: Plugin to other Docker cmds @@ -240,13 +405,13 @@ static async Task RunCommand_DockerLogin( try { #if UNITY_EDITOR_WIN - await RunCommand("cmd.exe", $"/c docker login -u \"{repoUsername}\" --password \"{repoPasswordToken}\" \"{registryUrl}\"", outputReciever, errorReciever); + await RunCommand("cmd.exe", $"/c docker login -u \"{repoUsername}\" --password \"{repoPasswordToken}\" \"{registryUrl}\"", outputReciever, errorReciever); #elif UNITY_EDITOR_OSX - await RunCommand("/bin/bash", $"-c \"docker login -u \"{repoUsername}\" --password \"{repoPasswordToken}\" \"{registryUrl}\"\"", outputReciever, errorReciever); + await RunCommand("/bin/bash", $"-c \"docker login -u '{repoUsername}' --password '{repoPasswordToken}' '{registryUrl}'\"", outputReciever, errorReciever); #elif UNITY_EDITOR_LINUX - await RunCommand("/bin/bash", $"-c \"docker login -u \"{repoUsername}\" --password \"{repoPasswordToken}\" \"{registryUrl}\"\"", outputReciever, errorReciever); + await RunCommand("/bin/bash", $"-c \"docker login -u '{repoUsername}' --password '{repoPasswordToken}' '{registryUrl}'\"", outputReciever, errorReciever); #else - Debug.LogError("The platform is not supported yet."); + Debug.LogError("The platform is not supported yet."); #endif } catch (Exception e) @@ -275,10 +440,9 @@ public static async Task LoginContainerRegistry( { string error = null; await RunCommand_DockerLogin(registryUrl, repoUsername, repoPasswordToken, onStatusUpdate, msg => error = msg); // MIRROR CHANGE - if (error.Contains("ERROR")) + if (error.ToLowerInvariant().Contains("error") || error.ToLowerInvariant().Contains("invalid")) { - Debug.LogError(error); - return false; + throw new Exception(error); } return true; } diff --git a/Assets/Mirror/Hosting/Edgegap/Editor/EdgegapWindow.uss b/Assets/Mirror/Hosting/Edgegap/Editor/EdgegapWindow.uss index 6228fed92db..697fb45b081 100755 --- a/Assets/Mirror/Hosting/Edgegap/Editor/EdgegapWindow.uss +++ b/Assets/Mirror/Hosting/Edgegap/Editor/EdgegapWindow.uss @@ -38,6 +38,14 @@ color: rgb(144, 190, 109); } +.text--link { + color: rgb(26, 142, 173); +} + +.text--link:hover { + color: rgb(23, 190, 235); +} + .container { padding: 4px 4px; } @@ -90,15 +98,26 @@ border-bottom-color: rgba(0, 0, 0, 0.35); height: 27px; -unity-font-style: bold; - background-color: rgb(36, 76, 87); min-width: 170px; max-width: 200px; } -.button-edgegap:hover { +.button-blue { + background-color: rgb(36, 76, 87); +} + +.button-blue:hover { background-color: rgb(56, 96, 107); } +.button-red { + background-color: rgb(135, 36, 23); +} + +.button-red:hover { + background-color: rgb(156, 58, 45); +} + .button-purple-hover:hover { background-color: rgb(44, 30, 210); } @@ -119,6 +138,7 @@ .text-edgegap { font-size: 11px; color: rgb(222, 222, 222); + overflow: hidden; /* MIRROR CHANGE: disable hardcoded font path -unity-font-definition: url('./Fonts/Spartan-Regular%20SDF.asset?fileID=11400000&guid=8b0fb2c68be09174f8ea5057b27a545c&type=2#Spartan-Regular SDF'); */ @@ -149,26 +169,32 @@ Toggle > #unity-checkmark { padding-bottom: 10px; } -.unity-text-field { - min-width: auto; - width: 400px; +.container-row { + background-color: rgb(37, 37, 37); + padding-top: 0; + padding-bottom: 0; + padding-left: 10px; padding-right: 5px; + margin-bottom: 3px; + justify-content: flex-start; + overflow: hidden; +} + +.unity-text-field { + min-width: 400px; + padding: 5px; white-space: normal; -unity-text-align: middle-left; opacity: 1; - padding-bottom: 5px; - padding-top: 5px; align-items: center; + align-content: stretch; + flex-grow: 10; + flex-shrink: 1; + overflow: hidden; } -.unity-text-field > Label { -} - -.container-row { - background-color: rgb(37, 37, 37); - padding-top: 0; - padding-bottom: 0; - margin-bottom: 3px; +#ApiTokenMaskedTxt { + flex-shrink: 0; } .checkmark-edgegap { @@ -179,6 +205,10 @@ Toggle > #unity-checkmark { margin-right: 15px; } +.unity-text-field__input { + text-overflow: clip; +} + .unity-text-field__input > TextElement { color: rgb(255, 255, 255); /* MIRROR CHANGE: disable hardcoded font path @@ -211,3 +241,58 @@ Toggle > #unity-checkmark { .button-purple-hover { } + +.label-right-padding Label { + min-width: 220px; +} + +.btn-text-link-blue { + background-color: rgba(0, 0, 0, 0); + border-top-width: 0px; + border-right-width: 0px; + border-bottom-width: 0px; + border-left-width: 0px; + color: rgb(26, 142, 173); + height: 27px; + -unity-font-style: bold; + min-width: 170px; + max-width: 500px; +} + +.btn-text-link-blue:hover { + color: rgb(23, 190, 235); +} + +.btn-text-link { + background-color: rgba(0, 0, 0, 0); + border-top-width: 0px; + border-right-width: 0px; + border-bottom-width: 0px; + border-left-width: 0px; + color: rgb(200, 200, 200); + height: 27px; + -unity-font-style: bold; + min-width: 170px; + max-width: 500px; + left: -5px; +} + +.btn-text-link:hover { + color: rgb(250, 250, 250); +} + +.btn-show-dropdown { + margin-left: 15px; + margin-right: 15px; + padding: 0; + background-color: rgba(0, 0, 0, 0); + color: rgb(200, 200, 200); + border-top-width: 0px; + border-right-width: 0px; + border-bottom-width: 0px; + border-left-width: 0px; +} + +.btn-show-dropdown:hover { + color: rgb(250, 250, 250); +} diff --git a/Assets/Mirror/Hosting/Edgegap/Editor/EdgegapWindow.uxml b/Assets/Mirror/Hosting/Edgegap/Editor/EdgegapWindow.uxml index f927c5481dd..4df0318cf47 100755 --- a/Assets/Mirror/Hosting/Edgegap/Editor/EdgegapWindow.uxml +++ b/Assets/Mirror/Hosting/Edgegap/Editor/EdgegapWindow.uxml @@ -1,102 +1,163 @@ -