#if UNITY_2021_3_OR_NEWER // MIRROR CHANGE
#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Web;
using Edgegap.Editor.Api;
using Edgegap.Editor.Api.Models;
using Edgegap.Editor.Api.Models.Requests;
using Edgegap.Editor.Api.Models.Results;
using Edgegap.Codice.Utils;
using UnityEditor;
using UnityEditor.Build.Reporting;
using UnityEngine;
using UnityEngine.UIElements;
using Application = UnityEngine.Application;
using HttpUtility = Edgegap.Codice.Utils.HttpUtility; // MIRROR CHANGE for Unity 2023 support
#if !EDGEGAP_PLUGIN_SERVERS
using UnityEditor.Build;
#endif
namespace Edgegap.Editor
{
///
/// Editor logic event handler for "UI Builder" EdgegapWindow.uxml, superceding` EdgegapWindow.cs`.
///
public class EdgegapWindowV2 : EditorWindow
{
#region Vars
#region Filepaths
internal string ProjectRootPath => Directory.GetCurrentDirectory();
internal string ThisScriptPath =>
Directory.GetFiles(
ProjectRootPath,
GetType().Name + ".cs",
SearchOption.AllDirectories
)[0];
#endregion
#region State Variables
public static bool IsLogLevelDebug =>
EdgegapWindowMetadata.LOG_LEVEL == EdgegapWindowMetadata.LogLevel.Debug;
private bool _isApiTokenVerified; // Toggles the rest of the UI
private GetRegistryCredentialsResult _credentials;
private string _userExternalIp;
private string _containerRegistryUrl;
private string _containerProject;
private string _containerUsername;
private string _containerToken;
private List _localImages = null;
private List _storedAppNames = null;
private List _storedAppVersions = null;
EdgegapDeploymentsApi _deployAPI;
#endregion
#region UI
private float ProgressCounter = 0;
#region UI / Containers
private VisualTreeAsset _visualTree;
internal string _stylesheetPath =>
Path.GetDirectoryName(
AssetDatabase.GetAssetPath(MonoScript.FromScriptableObject(this))
);
private Button _debugBtn;
private VisualElement _postAuthContainer;
#endregion
#region UI / Containers / Connect
private VisualElement _preAuthContainer;
private VisualElement _authContainer;
private Button _joinEdgegapDiscordBtn;
#endregion
#region UI / Connect / Pre-Auth
private Button _edgegapSignInBtn;
#endregion
#region UI / Connect / Auth
private Button _signOutBtn;
private TextField _apiTokenInput;
private string _apiToken => _apiTokenInput is null ? "" : _apiTokenInput.value.Trim();
private Button _apiTokenVerifyBtn;
private Button _apiTokenGetBtn;
#endregion
#region UI / Build
private Foldout _serverBuildFoldout;
private Button _infoLinuxRequirementsBtn;
private Button _installLinuxRequirementsBtn;
private Label _linuxRequirementsResultLabel;
private Button _buildParamsBtn;
private TextField _buildFolderNameInput;
internal string _buildFolderNameInputDefault => "EdgegapServer";
private Button _serverBuildBtn;
private Label _serverBuildResultLabel;
#endregion
#region UI / Containerize
private Foldout _containerizeFoldout;
private Button _infoDockerRequirementsBtn;
private Button _validateDockerRequirementsBtn;
private Label _dockerRequirementsResultLabel;
private TextField _buildPathInput;
internal string _buildPathInputDefault => $"Builds/{_buildFolderNameInput.value}";
private Button _buildPathResetBtn;
private TextField _containerizeImageNameInput;
public string _containerizeImageNameInputDefault =>
Tokenize(Application.productName.ToLowerInvariant());
private TextField _containerizeImageTagInput;
internal string _containerizeImageTagInputDefault =>
EdgegapWindowMetadata.DEFAULT_VERSION_TAG;
internal string nowUTC => $"{DateTime.UtcNow.ToString("yy.MM.dd-HH.mm.ss")}-UTC";
private TextField _dockerfilePathInput;
internal string _dockerfilePathInputDefault =>
$"{Directory.GetParent(ThisScriptPath).FullName}{Path.DirectorySeparatorChar}Dockerfile";
private Button _dockerfilePathResetBtn;
private TextField _optionalDockerParamsInput;
private Button _containerizeServerBtn;
private Label _containerizeServerResultLabel;
#endregion
#region UI / Test
private Foldout _localTestFoldout;
private TextField _localTestImageInput;
private Button _localTestImageShowDropdownBtn;
private TextField _localTestDockerRunInput;
internal string _localTestDockerRunInputDefault => "-p 7777/udp";
private Button _localTestDeployBtn;
private Button _localTestTerminateBtn;
private Button _localTestDiscordHelpBtn;
private Label _localTestResultLabel;
private Button _localTestInfoConnectBtn;
#endregion
#region UI / Upload App
private Foldout _createAppFoldout;
private TextField _createAppNameInput;
private static readonly Regex _appNameAllowedCharsRegex = new Regex(
@"^[a-zA-Z0-9_\-+\.]*$"
);
private TextField _serverImageNameInput;
private TextField _serverImageTagInput;
private Button _portMappingLabelLink;
private Button _uploadImageCreateAppBtn;
private Button _appInfoLabelLink;
private Button _createAppNameShowDropdownBtn;
#endregion
#region UI / Deploy
private Foldout _deployAppFoldout;
private TextField _deployAppNameInput;
private TextField _deployAppVersionInput;
private Button _deployLimitLabelLink;
private Button _deployAppBtn;
private Button _stopLastDeployBtn;
private Button _discordHelpBtn;
private Label _deployResultLabel;
private Button _deployAppNameShowDropdownBtn;
private Button _deployAppVersionShowDropdownBtn;
#endregion
#region UI / Next
private Foldout _nextStepsFoldout;
private Button _serverConnectLink;
private Button _gen2MatchmakerLabelLink;
private Button _lifecycleManageLabelLink;
#endregion
#endregion
#endregion
#region Unity Integration
[MenuItem("Tools/Edgegap Hosting")]
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(600, 900);
window.minSize = window.maxSize;
}
// Compiler symbols can be used by other plugin developers to detect presence of Edgegap plugin
[InitializeOnLoadMethod]
public static void AddDefineSymbols()
{
// check if defined first, otherwise adding the symbol causes an infinite loop of recompilation
#if !EDGEGAP_PLUGIN_SERVERS
// Get data about current target group
bool standaloneAndServer = false;
BuildTarget buildTarget = EditorUserBuildSettings.activeBuildTarget;
BuildTargetGroup buildTargetGroup = BuildPipeline.GetBuildTargetGroup(buildTarget);
if (buildTargetGroup == BuildTargetGroup.Standalone)
{
StandaloneBuildSubtarget standaloneSubTarget =
EditorUserBuildSettings.standaloneBuildSubtarget;
if (standaloneSubTarget == StandaloneBuildSubtarget.Server)
standaloneAndServer = true;
}
// Prepare named target, depending on above stuff
NamedBuildTarget namedBuildTarget;
if (standaloneAndServer)
namedBuildTarget = NamedBuildTarget.Server;
else
namedBuildTarget = NamedBuildTarget.FromBuildTargetGroup(buildTargetGroup);
// Set universal compiler macro
PlayerSettings.SetScriptingDefineSymbols(
namedBuildTarget,
$"{PlayerSettings.GetScriptingDefineSymbols(namedBuildTarget)};{EdgegapWindowMetadata.KEY_COMPILER_MACRO}"
);
#endif
}
protected void OnEnable()
{
#if UNITY_2021_3_OR_NEWER // 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}{Path.DirectorySeparatorChar}EdgegapWindow.uxml"
);
StyleSheet styleSheet = AssetDatabase.LoadAssetAtPath(
$"{_stylesheetPath}{Path.DirectorySeparatorChar}EdgegapWindow.uss"
);
// END MIRROR CHANGE
rootVisualElement.styleSheets.Add(styleSheet);
#endif
}
public async void CreateGUI()
{
// 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();
// TODO: Load persistent data?
// Only show the rest of the form if apiToken is verified
_postAuthContainer.SetEnabled(_isApiTokenVerified);
await InitializeState(); // API calls
#endif
}
/// The user closed the window. Save the data.
protected void OnDisable()
{
#if UNITY_2021_3_OR_NEWER // only load stylesheet in supported Unity versions, otherwise it shows errors in U2020
// sometimes this is called without having been registered, throwing NRE
unregisterUICallbacks();
#endif
}
#endregion // Unity Funcs
#region Init & Cleanup
///
/// 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();
closeDisableGroups();
registerUICallbacks();
initToggleDynamicUI();
}
/// Set fields referencing UI Builder's fields. In order of appearance from top-to-bottom.
private void setVisualElementsToFields()
{
_debugBtn = rootVisualElement.Q