您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
635 行
22 KiB
635 行
22 KiB
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<GameServerPort> gameServerPorts = new List<GameServerPort>();
|
|
public List<EnvironmentVariable> environmentVariables = new List<EnvironmentVariable>();
|
|
|
|
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<Config>(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<bool> 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<bool> 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<string> 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
|
|
}
|
|
}
|
|
|
|
|