using System; using System.Collections.Generic; using System.IO; using System.IO.Compression; using UnityEditor; using COSXML; using COSXML.Model.Object; using UnityEngine; using System.Threading.Tasks; using System.Net.Http.Headers; using System.Text; using COSXML.Auth; using COSXML.Model.Tag; using COSXML.Transfer; using Newtonsoft.Json; namespace Metacity.Publish.Editor { public enum ServerType { None, // no need server Official, // use official server Customize // use customize server } public delegate void FailedCallBack(); public class MetacityBuildWindow : EditorWindow { private const string configPath = "config/config.json"; private FailedCallBack _failedCallBack; private Config _config = new Config(); private string _lastAccessToken = null; private bool _isTokenValid = false; private SerializedObject _portObject; private SerializedProperty _portsProperty; private SerializedProperty _envVerbsProperty; private readonly GUIStyle _errorStyle = new GUIStyle(); private readonly GUIStyle _passStyle = new GUIStyle(); public List gameServerPorts = new List(); public List environmentVariables = new List(); private float _process = 0.0f; private string _processNote = ""; private bool _showProcessBar = false; private bool _isPublishClick = false; private PutObjectRequest _serverRequest; private PutObjectRequest _clientRequest; private COSXMLUploadTask _uploadTask; private void OnEnable() { _failedCallBack = CloseProcessBar; _portObject = new SerializedObject(this); _envVerbsProperty = _portObject.FindProperty("environmentVariables"); _portsProperty = _portObject.FindProperty("gameServerPorts"); _errorStyle.normal.textColor = Color.red; _passStyle.normal.textColor = Color.green; _isPublishClick = false; LoadConfig(); } private void OnDestroy() { SaveConfig(); } private void LoadConfig() { string path = Path.Combine(Application.dataPath, configPath); if (!string.IsNullOrEmpty(path)) { string dir = Path.GetDirectoryName(path); if (!Directory.Exists(dir)) { Directory.CreateDirectory(dir); } if (!System.IO.File.Exists(path)) { _config = new Config(); var fs = System.IO.File.Create(path); var json = JsonConvert.SerializeObject(_config); byte[] array = Encoding.UTF8.GetBytes(json); fs.Write(array); fs.Close(); fs.Dispose(); return; } Config localConfig = JsonTools.FromFile(path); if (localConfig != null) { _config = localConfig; gameServerPorts = _config.gameServerPorts; environmentVariables = _config.environmentVariables; } else { Debug.LogError("failed to deserialize config data"); } } } private void SaveConfig() { _config.gameServerPorts = gameServerPorts; _config.environmentVariables = environmentVariables; string path = Path.Combine(Application.dataPath, configPath); if (!string.IsNullOrEmpty(path)) { string dir = Path.GetDirectoryName(path); if (!Directory.Exists(dir)) { Directory.CreateDirectory(dir); } if (!System.IO.File.Exists(path)) { var fs = System.IO.File.Create(path); var json = JsonConvert.SerializeObject(_config); byte[] array = Encoding.UTF8.GetBytes(json); fs.Write(array); fs.Close(); fs.Dispose(); return; } JsonTools.ToFile(_config, path); } } #region render UI private void OnGUI() { EditorGUILayout.BeginVertical(); RenderHelper(); EditorGUILayout.Space(10); RenderPublishInfo(); EditorGUILayout.Space(10); RenderActions(); EditorGUILayout.EndVertical(); Validate(); } private async void Validate() { if (!string.IsNullOrEmpty(_config.accessToken) && _config.accessToken != _lastAccessToken) { MetacityInterface.client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _config.accessToken); bool isValid = await MetacityInterface.CheckAccessToken(); if (isValid) { _isTokenValid = true; _lastAccessToken = _config.accessToken; UpdateConfig(); } else { _isTokenValid = false; MetacityInterface.client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _lastAccessToken); } } } private void RenderHelper() { EditorGUILayout.HelpBox("\n" + "Publish your cloudrendering build zip to metacity platform.\n" + "\n" , MessageType.Info); } private void RenderPublishInfo() { EditorGUILayout.HelpBox("\n" + "Get your access token from metacity website.\n" + "\n" , MessageType.Warning); EditorGUILayout.Space(10); _config.accessToken = EditorGUILayout.TextField(new GUIContent("Access Token"), _config.accessToken); if (!string.IsNullOrEmpty(_config.accessToken)) { if (_isTokenValid) { EditorGUILayout.LabelField("Valid", _passStyle); } else { EditorGUILayout.LabelField(new GUIContent("Invalid access token"), _errorStyle); } } else { EditorGUILayout.LabelField("Access token can't be null", _errorStyle); } _config.templateName = EditorGUILayout.TextField(new GUIContent("Template Name"), _config.templateName); _config.thumbnail = EditorGUILayout.TextField(new GUIContent("Template Thumbnail"), _config.thumbnail); _config.memberLimit = EditorGUILayout.IntSlider(new GUIContent("Template Member Limit"), _config.memberLimit, 5, 8); EditorGUILayout.Space(10); EditorGUILayout.LabelField("Template Description"); _config.description = EditorGUILayout.TextArea(_config.description, EditorStyles.textArea, GUILayout.Height(50)); EditorGUILayout.Space(10); EditorGUILayout.HelpBox("\n" + "If you need to add/update build content, please export the build file in advance and compress it into zip.\n" + "\n" , MessageType.Warning); _config.buildNeeded = EditorGUILayout.Toggle(new GUIContent("create/update Build"), _config.buildNeeded); EditorGUI.indentLevel++; if (_config.buildNeeded) { EditorGUILayout.Space(10); EditorGUILayout.BeginHorizontal(); _config.clientPath = EditorGUILayout.TextField(new GUIContent("client(zip) path"), _config.clientPath); if (GUILayout.Button(new GUIContent("Browser"), GUILayout.Width(80))) { _config.clientPath = EditorUtility.OpenFilePanel("select client zip file", Application.dataPath, "zip"); EditorUtility.SetDirty(this); } EditorGUILayout.EndHorizontal(); _config.executableClientPath = EditorGUILayout.TextField(new GUIContent("executable path"), _config.executableClientPath); EditorGUILayout.Space(10); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Server Type"); _config.serverType = (ServerType) EditorGUILayout.EnumPopup(_config.serverType); EditorGUILayout.EndHorizontal(); EditorGUILayout.Space(10); EditorGUI.indentLevel++; if (_config.serverType == ServerType.Customize) { EditorGUILayout.BeginHorizontal(); _config.serverPath = EditorGUILayout.TextField(new GUIContent("server(zip) path"), _config.serverPath); if (GUILayout.Button(new GUIContent("Browser"), GUILayout.Width(80))) { _config.serverPath = EditorUtility.OpenFilePanel("select server zip file", Application.dataPath, "zip"); EditorUtility.SetDirty(this); } EditorGUILayout.EndHorizontal(); _config.executableServerPath = EditorGUILayout.TextField(new GUIContent("executable path"), _config.executableServerPath); EditorGUILayout.Space(10); _portObject.Update(); EditorGUI.BeginChangeCheck(); EditorGUILayout.PropertyField(_portsProperty, new GUIContent("Game Server Ports"), true); EditorGUILayout.Space(10); EditorGUILayout.PropertyField(_envVerbsProperty, new GUIContent("Environment Variables"), true); if (EditorGUI.EndChangeCheck()) { _portObject.ApplyModifiedProperties(); } } EditorGUI.indentLevel--; EditorGUILayout.Space(10); EditorGUI.indentLevel--; } } private void RenderActions() { if (_isPublishClick) { _isPublishClick = false; _showProcessBar = true; Publish(); } if (GUILayout.Button($"Publish")) { _isPublishClick = true; EditorUtility.SetDirty(this); } if (_showProcessBar) { Repaint(); if (EditorUtility.DisplayCancelableProgressBar("Upload File to COS", _processNote, _process)) { _clientRequest?.Cancel(); _serverRequest?.Cancel(); _uploadTask?.Cancel(); CloseProcessBar(); Debug.Log("Publish canceled"); } } } private async void Publish() { bool firstPublish = false; string crUrl = ""; string uosUrl = ""; bool isValid = await MetacityInterface.CheckAccessToken(); if (!isValid) { _isTokenValid = false; EditorUtility.SetDirty(this); CloseProcessBar(); return; } if (string.IsNullOrEmpty(_config.templateId)) { var result = await CreateNewTemplateByConfig(_failedCallBack); if(!result) return; firstPublish = true; } else { var template = await MetacityInterface.GetTemplateById(_config.templateId); if (template == null) { var result = await CreateNewTemplateByConfig(_failedCallBack); if(!result) return; firstPublish = true; } var templateResponse = await MetacityInterface.UpdateTemplateById(template, _config.templateId, _config.templateName, _config.description, _config.thumbnail, _config.buildId); if (templateResponse == null) { CloseProcessBar(); } } SaveConfig(); Debug.Log("Template message has been updated."); if (_config.buildNeeded) { // BuildByMultipleCatalog.DoBuild(); // var rootPath = Path.GetDirectoryName(Application.dataPath); // var buildPath = Path.Combine(rootPath, BuildByMultipleCatalog.ProjectCacheFolderName); // var zipPath = Path.Combine(rootPath, "build.zip"); // ProcessBuildZip(buildPath, zipPath); // // this._config.clientPath = zipPath; // this._config.executableClientPath = Path.Combine(EditorUserBuildSettings.activeBuildTarget.ToString(), // BuildByMultipleCatalog.ProjectCacheProjectName, BuildByMultipleCatalog.ProjectCacheName); // this._config.serverType = ServerType.Official; if (string.IsNullOrEmpty(_config.buildId)) { var result = await CreateNewCloudrenderingByConfig(_failedCallBack); if (!result) return; } else { var cr = await MetacityInterface.GetCloudrenderingById(_config.buildId); if (cr == null) { var result = await CreateNewCloudrenderingByConfig(_failedCallBack); if (!result) return; } } _processNote = "Start upload ..."; if (!string.IsNullOrEmpty(_config.clientPath)) { crUrl = await UploadFile(_config.clientPath, _failedCallBack, false); if (string.IsNullOrEmpty(crUrl)) { return; } } if (_config.serverType == ServerType.Customize) { uosUrl = await UploadFile(_config.serverPath, _failedCallBack, true); if (string.IsNullOrEmpty(uosUrl)) { return; } } _processNote = "Upload Finished"; bool isPublish = await MetacityInterface.PublishPrepare( _config.userId, _config.templateId, _config.buildId, _config.serverType.ToString(), crUrl, _config.executableClientPath, Path.GetFileName(_config.clientPath), clientSize, uosUrl, _config.executableServerPath, _config.environmentVariables, _config.gameServerPorts, firstPublish); if (isPublish) { Debug.Log("Publish successful. Please go to metacity website to continue the follow-up operation"); } else { Debug.LogError("Publish Failed"); } } CloseProcessBar(); } private void CloseProcessBar() { EditorUtility.ClearProgressBar(); _showProcessBar = false; EditorUtility.SetDirty(this); } private async Task CreateNewTemplateByConfig(FailedCallBack callBack) { var template = await MetacityInterface.CreateTemplate(_config.templateName, _config.description, _config.thumbnail); if (template == null) { callBack.Invoke(); return false; } _config.templateId = template.id; _config.templateName = template.name; _config.description = template.description; _config.thumbnail = template.banner; return true; } private async Task CreateNewCloudrenderingByConfig(FailedCallBack callBack) { var cr = await MetacityInterface.CreateCloudrendering(_config.templateName, _config.description, _config.templateName); if (cr == null) { callBack.Invoke(); return false; } _config.buildId = cr.id; var template = await MetacityInterface.GetTemplateById(_config.templateId); if (template == null) { callBack.Invoke(); return false; } var templateResponse = await MetacityInterface.UpdateTemplateById(template, _config.templateId, _config.templateName, _config.description, _config.thumbnail, _config.buildId); if (templateResponse == null) { callBack.Invoke(); return false; } return true; } private async void UpdateConfig() { _config.userId = await MetacityInterface.GetUserId(); if(string.IsNullOrEmpty(_config.templateId)) return; var template = await MetacityInterface.GetTemplateById(_config.templateId); _config.templateName = template.name; _config.thumbnail = template.banner; _config.description = template.description; } #endregion #region process zip private string ProcessBuildZip(string buildPath, string zipPath) { if (File.Exists(zipPath)) { File.Delete(zipPath); } ZipFile.CreateFromDirectory(buildPath, zipPath); return zipPath; } #endregion #region Upload cos private long clientSize; private long serverSize; private async Task UploadFile(string uploadFile, FailedCallBack onFailed, bool isServer) { if (!File.Exists(uploadFile)) { onFailed.Invoke(); return null; } FileInfo fileInfo = new FileInfo(uploadFile); if (isServer) { serverSize = fileInfo.Length; } else { clientSize = fileInfo.Length; } string uploadUrl = await MetacityInterface.GetUploadUrlAsync(fileInfo.Name); string downloadUrl = await MetacityInterface.GetDownloadUrlAsync(fileInfo.Name); var result = await Task.Run(() => { return UploadFileCos(uploadFile, uploadUrl, isServer); }); if (!result) { onFailed.Invoke(); return null; } return downloadUrl; } bool UploadFileCos(string uploadFile, string requestSignURL, bool isServer) { if (File.Exists(uploadFile)) { CosXmlConfig config = new CosXmlConfig.Builder() .SetRegion("ap-shanghai") .SetDebugLog(true) .Build(); var cosXml = new CosXmlServer(config, null); try { if (isServer) { _serverRequest = new PutObjectRequest(null, null, uploadFile); _serverRequest.RequestURLWithSign = requestSignURL; _serverRequest.SetCosProgressCallback(delegate(long completed, long total) { _process = (float)completed / total; _processNote = string.Format("uploaded {0} of {1} bytes. {2:##.##} % complete...", completed, serverSize, completed * 100.0/ serverSize); }); PutObjectResult result = cosXml.PutObject(_serverRequest); Debug.LogFormat(result.GetResultInfo()); return result.IsSuccessful(); } else { _clientRequest = new PutObjectRequest(null, null, uploadFile); _clientRequest.RequestURLWithSign = requestSignURL; _clientRequest.SetCosProgressCallback(delegate(long completed, long total) { _process = (float)completed / total; _processNote = string.Format("uploaded {0} of {1} bytes. {2:##.##} % complete...", completed, clientSize, completed * 100.0/ clientSize); }); PutObjectResult result = cosXml.PutObject(_clientRequest); Debug.LogFormat(result.GetResultInfo()); return result.IsSuccessful(); } } catch (COSXML.CosException.CosClientException clientEx) { Debug.LogException(clientEx); } catch (COSXML.CosException.CosServerException serverEx) { Debug.LogException(serverEx); } } return false; } #endregion } }