using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net.Http; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; using Edgegap.Editor.Api; using Edgegap.Editor.Api.Models; using Edgegap.Editor.Api.Models.Requests; using Edgegap.Editor.Api.Models.Results; using UnityEditor; using UnityEditor.Build.Reporting; using UnityEditor.UIElements; using UnityEngine; using UnityEngine.Assertions; using UnityEngine.UIElements; using Application = UnityEngine.Application; namespace Edgegap.Editor { /// /// Editor logic event handler for "UI Builder" EdgegapWindow.uxml, superceding` EdgegapWindow.cs`. /// public class EdgegapWindowV2 : EditorWindow { #region Vars public static bool IsLogLevelDebug => EdgegapWindowMetadata.LOG_LEVEL == EdgegapWindowMetadata.LogLevel.Debug; private bool IsInitd; private VisualTreeAsset _visualTree; private bool _isApiTokenVerified; // Toggles the rest of the UI private bool _isContainerRegistryReady; private Sprite _appIconSpriteObj; private string _appIconBase64Str; #pragma warning disable CS0414 // MIRROR CHANGE: hide unused warning private ApiEnvironment _apiEnvironment; // TODO: Swap out hard-coding with UI element? #pragma warning restore CS0414 // END MIRROR CHANGE private GetRegistryCredentialsResult _credentials; private static readonly Regex _appNameAllowedCharsRegex = new Regex(@"^[a-zA-Z0-9_\-+\.]*$"); // MIRROR CHANGE: 'new()' not supported in Unity 2020 private GetCreateAppResult _loadedApp; /// TODO: Make this a list private GetDeploymentStatusResult _lastKnownDeployment; private string _deploymentRequestId; private string _userExternalIp; private bool _isAwaitingDeploymentReadyStatus; #endregion // Vars #region Vars -> Interactable Elements private Button _debugBtn; /// (!) This is saved manually to EditorPrefs via Base64 instead of via UiBuilder private TextField _apiTokenInput; private Button _apiTokenVerifyBtn; private Button _apiTokenGetBtn; private VisualElement _postAuthContainer; private Foldout _appInfoFoldout; private Button _appLoadExistingBtn; private TextField _appNameInput; /// `Sprite` type private ObjectField _appIconSpriteObjInput; private Button _appCreateBtn; private Label _appCreateResultLabel; private Foldout _containerRegistryFoldout; private TextField _containerNewTagVersionInput; private TextField _containerPortNumInput; // MIRROR CHANGE: EnumField Port type fails to resolve unless in Assembly-CSharp-Editor.dll. replace with regular Dropdown instead. /// `ProtocolType` type // private EnumField _containerTransportTypeEnumInput; private PopupField _containerTransportTypeEnumInput; // END MIRROR CHANGE private Toggle _containerUseCustomRegistryToggle; private VisualElement _containerCustomRegistryWrapper; private TextField _containerRegistryUrlInput; private TextField _containerImageRepositoryInput; private TextField _containerUsernameInput; private TextField _containerTokenInput; private Button _containerBuildAndPushServerBtn; private Label _containerBuildAndPushResultLabel; private Foldout _deploymentsFoldout; private Button _deploymentsRefreshBtn; private Button _deploymentsCreateBtn; /// display:none (since it's on its own line), rather than !visible. private Label _deploymentsStatusLabel; private VisualElement _deploymentsServerDataContainer; private Button _deploymentConnectionCopyUrlBtn; private TextField _deploymentsConnectionUrlReadonlyInput; private Label _deploymentsConnectionStatusLabel; private Button _deploymentsConnectionStopBtn; private Button _footerDocumentationBtn; private Button _footerNeedMoreGameServersBtn; #endregion // Vars // MIRROR CHANGE // get the path of this .cs file so we don't need to hardcode paths to // the .uxml and .uss files: // https://forum.unity.com/threads/too-many-hard-coded-paths-in-the-templates-and-documentation.728138/ // this way users can move this folder without breaking UIToolkit paths. internal string StylesheetPath => Path.GetDirectoryName(AssetDatabase.GetAssetPath(MonoScript.FromScriptableObject(this))); // END MIRROR CHANGE // MIRROR CHANGE: images are dragged into the script in inspector and assigned to the UI at runtime. this way we don't need to hardcode it. public Texture2D LogoImage; public Texture2D ClipboardImage; // END MIRROR CHANGE [MenuItem("Edgegap/Edgegap Hosting")] // MIRROR CHANGE: more obvious title public static void ShowEdgegapToolWindow() { EdgegapWindowV2 window = GetWindow(); window.titleContent = new GUIContent("Edgegap Hosting"); // MIRROR CHANGE: 'Edgegap Server Management' is too long for the tab space window.maxSize = new Vector2(635, 900); window.minSize = window.maxSize; } #region Unity Funcs protected void OnEnable() { #if UNITY_2021_3_OR_NEWER // MIRROR CHANGE: only load stylesheet in supported Unity versions, otherwise it shows errors in U2020 // Set root VisualElement and style: V2 still uses EdgegapWindow.[uxml|uss] // BEGIN MIRROR CHANGE _visualTree = AssetDatabase.LoadAssetAtPath($"{StylesheetPath}/EdgegapWindow.uxml"); StyleSheet styleSheet = AssetDatabase.LoadAssetAtPath($"{StylesheetPath}/EdgegapWindow.uss"); // END MIRROR CHANGE rootVisualElement.styleSheets.Add(styleSheet); #endif } #pragma warning disable CS1998 // MIRROR CHANGE: disable async warning in U2020 public async void CreateGUI() #pragma warning restore CS1998 // END MIRROR CHANGE { // MIRROR CHANGE: the UI requires 'GroupBox', which is not available in Unity 2019/2020. // showing it will break all of Unity's Editor UIs, not just this one. // instead, show a warning that the Edgegap plugin only works on Unity 2021+ #if !UNITY_2021_3_OR_NEWER Debug.LogWarning("The Edgegap Hosting plugin requires UIToolkit in Unity 2021.3 or newer. Please upgrade your Unity version to use this."); #else // Get UI elements from UI Builder rootVisualElement.Clear(); _visualTree.CloneTree(rootVisualElement); // Register callbacks and sync UI builder elements to fields here InitUIElements(); syncFormWithObjectStatic(); await syncFormWithObjectDynamicAsync(); // API calls IsInitd = true; #endif } /// The user closed the window. Save the data. protected void OnDisable() { #if UNITY_2021_3_OR_NEWER // MIRROR CHANGE: only load stylesheet in supported Unity versions, otherwise it shows errors in U2020 // MIRROR CHANGE: sometimes this is called without having been registered, throwing NRE if (_debugBtn == null) return; // END MIRROR CHANGE unregisterClickEvents(); unregisterFieldCallbacks(); SyncObjectWithForm(); #endif } #endregion // Unity Funcs #region Init /// /// Binds the form inputs to the associated variables and initializes the inputs as required. /// Requires the VisualElements to be loaded before this call. Otherwise, the elements cannot be found. /// private void InitUIElements() { setVisualElementsToFields(); assertVisualElementKeys(); closeDisableGroups(); registerClickCallbacks(); registerFieldCallbacks(); initToggleDynamicUi(); AssignImages(); // MIRROR CHANGE } private void closeDisableGroups() { _appInfoFoldout.value = false; _containerRegistryFoldout.value = false; _deploymentsFoldout.value = false; _appInfoFoldout.SetEnabled(false); _containerRegistryFoldout.SetEnabled(false); _deploymentsFoldout.SetEnabled(false); } // MIRROR CHANGE: assign images to the UI at runtime instead of hardcoding it void AssignImages() { // header logo VisualElement logoElement = rootVisualElement.Q("header-logo-img"); logoElement.style.backgroundImage = LogoImage; // clipboard button VisualElement copyElement = rootVisualElement.Q("DeploymentConnectionCopyUrlBtn"); copyElement.style.backgroundImage = ClipboardImage; } // END MIRROR CHANGE /// Set fields referencing UI Builder's fields. In order of appearance from top-to-bottom. private void setVisualElementsToFields() { _debugBtn = rootVisualElement.Q