using System; using System.Collections.Generic; using System.IO; using System.IO.Compression; using UnityEditor; using UnityEngine; using System.Threading.Tasks; using System.Net.Http.Headers; using System.Text; using COSXML; using COSXML.Model.Object; using COSXML.Transfer; using MetaCity.BundleKit.Editor.Utils; using Newtonsoft.Json; namespace MetaCity.BundleKit.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 FailedCallBack _failedCallBack; private Config _config = new Config(); private SerializedObject _portObject; private SerializedProperty _portsProperty; private SerializedProperty _envVerbsProperty; private float _process = 0; private string _processNote = ""; private bool _showProcessBar = false; private bool _isPublishClick = false; private PutObjectRequest _request; private COSXMLUploadTask _uploadTask; protected virtual void OnEnable() { _failedCallBack = CloseProcessBar; _portObject = new SerializedObject(this); _envVerbsProperty = _portObject.FindProperty("environmentVariables"); _portsProperty = _portObject.FindProperty("gameServerPorts"); _isPublishClick = false; LoadConfig(); } protected void OnDestroy() { SaveConfig(); } private void LoadConfig() { if (!Directory.Exists(Constants.TokenConfigPath)) { Directory.CreateDirectory(Constants.TokenConfigPath); } var path=Path.Combine(Constants.TokenConfigPath,"config.json"); 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; } else { Debug.LogError("failed to deserialize config data"); } } private void SaveConfig() { if (!Directory.Exists(Constants.TokenConfigPath)) { Directory.CreateDirectory(Constants.TokenConfigPath); } var path=Path.Combine(Constants.TokenConfigPath,"config.json"); 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 protected virtual void OnGUI() { EditorGUILayout.BeginVertical(); // RenderHelper(); // // EditorGUILayout.Space(10); // // RenderPublishInfo(); // // EditorGUILayout.Space(10); RenderActions(); EditorGUILayout.EndVertical(); } private void RenderHelper() { EditorGUILayout.HelpBox("\n" + "Publish your cloudrendering build zip to metacity platform.\n" + "\n" , MessageType.Info); } private void RenderPublishInfo() { _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++; } private void RenderActions() { MetacityTokenWindow.Validate(); if (_isPublishClick) { _isPublishClick = false; _showProcessBar = true; Publish(); } if (MetacityTokenWindow.IsAccessTokenValid) { if (GUILayout.Button($"Publish")) { _isPublishClick = true; EditorUtility.SetDirty(this); } } else { EditorGUILayout.HelpBox("\n" + "Your access token is invalid. \n" + "Please update your access token.\n" + "\n", MessageType.Warning); if (GUILayout.Button("Update Access Token")) { MetacityTokenWindow.OpenTokenWindow(); } } if (_showProcessBar) { Repaint(); if (EditorUtility.DisplayCancelableProgressBar("Upload File to COS", _processNote, _process)) { _request?.Cancel(); _uploadTask?.Cancel(); CloseProcessBar(); Debug.Log("Publish canceled"); } } } private async void Publish() { bool isValid = await MetacityClient.CheckAccessToken(); if (!isValid) { EditorUtility.SetDirty(this); CloseProcessBar(); return; } if (string.IsNullOrEmpty(_config.templateId)) { var result = await CreateNewTemplateByConfig(_failedCallBack); if(!result) return; } else { var template = await MetacityClient.GetTemplateById(_config.templateId); if (template == null) { var result = await CreateNewTemplateByConfig(_failedCallBack); if(!result) return; } var templateResponse = await MetacityClient.UpdateTemplateById(template, _config.templateId, _config.templateName, _config.description, _config.thumbnail, _config.buildId); if (templateResponse == null) { CloseProcessBar(); } } SaveConfig(); Debug.Log("Template info has been updated."); if (_config.buildNeeded) { var bundles = BuildEntry.LoadLastBuiltCatalog(); _config.assetBundles.Clear(); for (int i = 0; i < bundles.Count; i++) { _config.assetBundles.Add(new AssetBundle() { bundlePath = bundles[i].bundleLocalPath, bundleName = bundles[i].bundleName, bundleType = bundles[i].bundleType, bundleHash = bundles[i].bundleHash, bundleDependencies = bundles[i].bundleDependencies, platform = bundles[i].bundlePlatform }); } if (string.IsNullOrEmpty(_config.buildId)) { var result = await CreateNewCloudrenderingByConfig(_failedCallBack); if (!result) return; } else { var cr = await MetacityClient.GetCloudrenderingById(_config.buildId); if (cr == null) { var result = await CreateNewCloudrenderingByConfig(_failedCallBack); if (!result) return; } } _processNote = "Start upload ..."; var isSuccess = await UploadBundles(_config.assetBundles, _failedCallBack); if (!isSuccess) { return; } bool isPublish = await MetacityClient.Publish( _config.templateId, _config.buildId, _config.serverType.ToString(), _config.assetBundles ); if (isPublish) { Debug.Log("Publish successful."); } 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 MetacityClient.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 MetacityClient.CreateCloudrendering(_config.templateName, _config.description, _config.templateName); if (cr == null) { callBack.Invoke(); return false; } _config.buildId = cr.id; var template = await MetacityClient.GetTemplateById(_config.templateId); if (template == null) { callBack.Invoke(); return false; } var templateResponse = await MetacityClient.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 MetacityClient.GetUserId(); if(string.IsNullOrEmpty(_config.templateId)) return; var template = await MetacityClient.GetTemplateById(_config.templateId); _config.templateName = template.name; _config.thumbnail = template.banner; _config.description = template.description; var assetBundles = await MetacityClient.GetAssetBundleByTemplateId(_config.templateId); if (assetBundles.Count > 0) { _config.assetBundles.Clear(); foreach (var ab in assetBundles) { AssetBundle assetBundle = new AssetBundle() { bundleName = ab.bundleName, bundlePath = ab.bundlePath, bundleType = ab.bundleType, bundleHash = ab.bundleHash, bundleDependencies = ab.bundleDependencies, platform = ab.platform, extraInfo = ab.extraInfo }; _config.assetBundles.Add(assetBundle); } } } #endregion #region Upload cos private async Task UploadBundles(List assetBundles, FailedCallBack onFailed) { CosXmlConfig config = new CosXmlConfig.Builder() .SetRegion("ap-shanghai") .SetDebugLog(true) .Build(); var cosXml = new CosXmlServer(config, null); int totalCount = assetBundles.Count; int count = 1; foreach (var assetBundle in assetBundles) { if (!File.Exists(assetBundle.bundlePath)) { onFailed.Invoke(); return false; } try { var cosPath = $"{assetBundle.bundleName}-{assetBundle.bundleHash}"; string requestSignURL = await MetacityClient.GetUploadUrlAsync(cosPath); _request = new PutObjectRequest(null, null, assetBundle.bundlePath); _request.RequestURLWithSign = requestSignURL; _request.SetCosProgressCallback(delegate(long completed, long total) { _process = (float) completed / total; _processNote = string.Format("Upload({0}/{1})\n" + "uploaded {2} of {3} bytes. {4:##.##} % complete...", count, totalCount, completed, total, completed * 100.0 / total); }); PutObjectResult result = cosXml.PutObject(_request); if (result.IsSuccessful()) { count++; } else { return false; } } catch (COSXML.CosException.CosClientException clientEx) { Debug.LogException(clientEx); } catch (COSXML.CosException.CosServerException serverEx) { Debug.LogException(serverEx); } } return true; } #endregion private async Task PublishAssetBundles(List assetBundles) { bool upload = await UploadBundles(assetBundles, _failedCallBack); if (upload) { return await MetacityClient.PublishAssetBundles(assetBundles); } return false; } } }