您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
386 行
13 KiB
386 行
13 KiB
using UnityEditor;
|
|
using UnityEngine;
|
|
using UnityEngine.Networking;
|
|
using UnityEngine.Assertions;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using SimpleJSON;
|
|
using System.IO;
|
|
using System.Net.Security;
|
|
using System.Security.Cryptography.X509Certificates;
|
|
using System.Net;
|
|
|
|
using Debug = UnityEngine.Debug;
|
|
|
|
namespace ConnectedGames.Build
|
|
{
|
|
public class BuildUpload
|
|
{
|
|
const string k_CloudArtifactsUrl = "https://build-artifact-api.cloud.unity3d.com/api/v1/projects/";
|
|
const string k_CloudBuildUrl = "https://build-api.cloud.unity3d.com/api/v1/orgs/";
|
|
|
|
double m_Perc;
|
|
double m_PercentTotal;
|
|
Task m_UploadingTask;
|
|
UnityWebRequest m_Request;
|
|
Stopwatch m_Timer = new Stopwatch();
|
|
|
|
[SerializeField] int m_BuildId;
|
|
[SerializeField] string m_Label;
|
|
[SerializeField] string m_Platform = "";
|
|
[SerializeField] FileInfo m_UploadedFile;
|
|
TusClient.TusClient m_TusClient;
|
|
[SerializeField] string m_ClientApi;
|
|
[SerializeField] string m_ProjectId;
|
|
[SerializeField] string m_OrgId;
|
|
[SerializeField] string m_ProjectName;
|
|
[SerializeField] string m_AccessToken;
|
|
|
|
List<UploadTask> m_TaskList = new List<UploadTask>();
|
|
int m_CurrentTask;
|
|
bool m_IsError;
|
|
string m_ErrorMessage = "";
|
|
|
|
public double Progress
|
|
{
|
|
get { return m_PercentTotal + m_Perc; }
|
|
}
|
|
|
|
public bool IsError
|
|
{
|
|
get { return m_IsError; }
|
|
}
|
|
|
|
public string ErrorMessage
|
|
{
|
|
get { return m_ErrorMessage; }
|
|
}
|
|
|
|
public string Label
|
|
{
|
|
get { return m_Label; }
|
|
set { m_Label = value; }
|
|
}
|
|
|
|
public FileInfo UploadedFile
|
|
{
|
|
get { return m_UploadedFile; }
|
|
set { m_UploadedFile = value; }
|
|
}
|
|
|
|
public string ClientAPI
|
|
{
|
|
get { return m_ClientApi; }
|
|
set { m_ClientApi = value; }
|
|
}
|
|
|
|
public string Platform
|
|
{
|
|
get { return m_Platform; }
|
|
set { m_Platform = value; }
|
|
}
|
|
|
|
public long ElapsedTime
|
|
{
|
|
get { return m_Timer.ElapsedMilliseconds / 1000; }
|
|
}
|
|
|
|
public void UploadingDelegate(long bytesTransferred, long bytesTotal)
|
|
{
|
|
m_Perc = bytesTransferred / (double)bytesTotal * (100.0 - m_PercentTotal);
|
|
//Debug.Log(string.Format("Up {0:0.00}% {1} of {2}", m_Perc, bytesTransferred, bytesTotal));
|
|
}
|
|
|
|
class UploadTask
|
|
{
|
|
public UploadTask(Action start, Action end)
|
|
{
|
|
startAction = start;
|
|
endAction = end;
|
|
}
|
|
|
|
public Action startAction;
|
|
public Action endAction;
|
|
}
|
|
|
|
public BuildUpload(string label, string path, string clientApi, string platform) : this()
|
|
{
|
|
m_Label = label;
|
|
m_UploadedFile = new FileInfo(path);
|
|
m_ClientApi = clientApi;
|
|
m_Platform = platform.ToLower();
|
|
}
|
|
|
|
public BuildUpload(string label, string path, string clientApi, string platform, string projectId, string orgId, string projectName, string accessToken) : this(label, path, clientApi, platform)
|
|
{
|
|
m_ProjectId = projectId;
|
|
m_OrgId = orgId;
|
|
m_ProjectName = projectName;
|
|
m_AccessToken = accessToken;
|
|
}
|
|
|
|
public BuildUpload()
|
|
{
|
|
Action start = StartBuildGet;
|
|
Action end = EndBuildGet;
|
|
UploadTask task = new UploadTask(start, end);
|
|
m_TaskList.Add(task);
|
|
start = new Action(this.StartBuildCreate);
|
|
end = new Action(this.EndBuildCreate);
|
|
task = new UploadTask(start, end);
|
|
m_TaskList.Add(task);
|
|
start = new Action(this.StartArtifactCreate);
|
|
end = new Action(this.EndArtifactCreate);
|
|
task = new UploadTask(start, end);
|
|
m_TaskList.Add(task);
|
|
start = new Action(this.StartFileUpload);
|
|
end = new Action(this.EndFileUpload);
|
|
task = new UploadTask(start, end);
|
|
m_TaskList.Add(task);
|
|
|
|
m_Timer.Start();
|
|
}
|
|
|
|
void CheckConnection()
|
|
{
|
|
if (!string.IsNullOrEmpty(m_ProjectId))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (string.IsNullOrEmpty(CloudProjectSettings.accessToken) || string.IsNullOrEmpty(CloudProjectSettings.projectId))
|
|
{
|
|
throw new UnityException("Unity Cloud Services need to be configured for this project in order to use the deployment options.");
|
|
}
|
|
|
|
m_ProjectId = CloudProjectSettings.projectId;
|
|
m_OrgId = CloudProjectSettings.organizationId;
|
|
m_ProjectName = CloudProjectSettings.projectName;
|
|
m_AccessToken = CloudProjectSettings.accessToken;
|
|
}
|
|
|
|
void CheckParameters()
|
|
{
|
|
if (m_Label == null || m_Label.Length == 0)
|
|
{
|
|
throw new UnityException("Build label needs to be set");
|
|
}
|
|
if (m_UploadedFile == null)
|
|
{
|
|
throw new UnityException("No file name provided for upload.");
|
|
}
|
|
if (m_ClientApi == null || m_ClientApi.Length == 0)
|
|
{
|
|
throw new UnityException("Client API needs to be set, see https://developer.cloud.unity3d.com/login/me");
|
|
}
|
|
}
|
|
|
|
public void StartUpload()
|
|
{
|
|
CheckConnection();
|
|
CheckParameters();
|
|
m_TusClient = null; //TODO: should be checked before
|
|
m_TaskList[m_CurrentTask].startAction();
|
|
}
|
|
|
|
public bool IsDone()
|
|
{
|
|
if (m_UploadingTask != null)
|
|
{
|
|
if (m_UploadingTask.IsCompleted)
|
|
m_TaskList[m_CurrentTask].endAction();
|
|
else
|
|
return false;
|
|
}
|
|
else if (m_Request != null)
|
|
{
|
|
if (!m_Request.isDone)
|
|
return false;
|
|
m_TaskList[m_CurrentTask].endAction();
|
|
m_Request = null;
|
|
if (m_IsError)
|
|
{
|
|
m_CurrentTask = 0;
|
|
return true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return true;
|
|
}
|
|
m_CurrentTask++;
|
|
if (m_CurrentTask < m_TaskList.Count)
|
|
{
|
|
m_TaskList[m_CurrentTask].startAction();
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void StartBuildGet()
|
|
{
|
|
string targetUrl = k_CloudBuildUrl + m_OrgId + "/projects/" + m_ProjectId + "/buildtargets/_local/builds";
|
|
m_Request = UnityWebRequest.Get(targetUrl);
|
|
m_Request.downloadHandler = (DownloadHandler) new DownloadHandlerBuffer();
|
|
m_Request.SetRequestHeader("Authorization", "Bearer " + m_AccessToken);
|
|
m_Request.SendWebRequest();
|
|
}
|
|
|
|
void EndBuildGet()
|
|
{
|
|
HandleEnd("Cannot get build list", 200);
|
|
|
|
JSONNode N = JSON.Parse(m_Request.downloadHandler.text);
|
|
JSONArray arr = N.AsArray;
|
|
Assert.IsNotNull(arr);
|
|
for (int i = 0; i < arr.Count; ++i)
|
|
{
|
|
int id = arr[i]["build"].AsInt;
|
|
m_BuildId = (id > m_BuildId) ? id : m_BuildId;
|
|
}
|
|
++m_BuildId;
|
|
}
|
|
|
|
void StartBuildCreate()
|
|
{
|
|
JSONNode node = new JSONObject();
|
|
node["platform"] = m_Platform;
|
|
node["label"] = m_Label;
|
|
|
|
string payload = node.ToString();
|
|
|
|
byte[] bodyRaw = Encoding.UTF8.GetBytes(payload);
|
|
|
|
string targetUrl = k_CloudBuildUrl + m_OrgId + "/projects/" + m_ProjectId + "/buildtargets/_local/builds";
|
|
m_Request = new UnityWebRequest(targetUrl, "POST");
|
|
m_Request.uploadHandler = (UploadHandler) new UploadHandlerRaw(bodyRaw);
|
|
m_Request.downloadHandler = (DownloadHandler) new DownloadHandlerBuffer();
|
|
m_Request.SetRequestHeader("Authorization", "Bearer " + m_AccessToken);
|
|
m_Request.SetRequestHeader("Content-Type", "application/json");
|
|
m_Request.SendWebRequest();
|
|
}
|
|
|
|
void EndBuildCreate()
|
|
{
|
|
HandleEnd("Cannot create build", 202);
|
|
}
|
|
|
|
void HandleEnd(string errorMessage, int desiredCode)
|
|
{
|
|
Debug.Log("HandleEnd: " + m_Request.downloadHandler.text + " err?:" + m_Request.isNetworkError + " code: " + m_Request.responseCode + " desired: " + desiredCode);
|
|
if (m_Request.isNetworkError || m_Request.responseCode != desiredCode)
|
|
{
|
|
m_IsError = true;
|
|
m_ErrorMessage = errorMessage + ": '" + m_Request.error + "' " + " using " + m_Request.url
|
|
+ "\nTarget: " + m_UploadedFile.Name
|
|
+ "\n" + m_Request.downloadHandler.text;
|
|
}
|
|
Assert.IsNotNull(m_Request);
|
|
m_PercentTotal += 1;
|
|
}
|
|
|
|
void StartArtifactCreate()
|
|
{
|
|
JSONNode node = new JSONObject();
|
|
node["name"] = ".ZIP file";
|
|
node["primary"] = true;
|
|
node["public"] = false;
|
|
node["files"] = new JSONArray();
|
|
node["files"][0]["filename"] = m_UploadedFile.Name;
|
|
node["files"][0]["size"] = m_UploadedFile.Length;
|
|
string payload = node.ToString();
|
|
byte[] bodyRaw = Encoding.UTF8.GetBytes(payload);
|
|
|
|
string targetUrl = k_CloudArtifactsUrl + m_ProjectId + "/buildtargets/_local/builds/" + m_BuildId.ToString() + "/artifacts";
|
|
|
|
m_Request = new UnityWebRequest(targetUrl, "POST");
|
|
m_Request.uploadHandler = (UploadHandler) new UploadHandlerRaw(bodyRaw);
|
|
m_Request.downloadHandler = (DownloadHandler) new DownloadHandlerBuffer();
|
|
m_Request.SetRequestHeader("Authorization", "Bearer " + m_AccessToken);
|
|
m_Request.SetRequestHeader("Content-Type", "application/json");
|
|
m_Request.SendWebRequest();
|
|
}
|
|
|
|
void EndArtifactCreate()
|
|
{
|
|
HandleEnd("Cannot create artifact", 201);
|
|
}
|
|
|
|
void StartFileUpload()
|
|
{
|
|
string targetUrl = k_CloudArtifactsUrl + m_ProjectName + "/buildtargets/_local/builds/" + m_BuildId.ToString() + "/artifacts/.ZIP file/upload/" + m_UploadedFile.Name;
|
|
m_UploadingTask = new Task(() => this.FileUploadTask(targetUrl));
|
|
m_UploadingTask.Start();
|
|
}
|
|
|
|
void FileUploadTask(string targetUrl) //TODO: blocking call
|
|
{
|
|
Assert.IsNull(m_TusClient);
|
|
m_TusClient = new TusClient.TusClient();
|
|
m_TusClient.Uploading += UploadingDelegate;
|
|
|
|
m_TusClient.Autorization = "Basic " + m_ClientApi;
|
|
ServicePointManager.ServerCertificateValidationCallback = MyRemoteCertificateValidationCallback;
|
|
//string fileURL = m_TusClient.Create(targetUrl, m_UploadedFile);
|
|
try
|
|
{
|
|
m_TusClient.Upload(targetUrl, m_UploadedFile);
|
|
}
|
|
catch (TusClient.TusException e)
|
|
{
|
|
if (e.Status == System.Net.WebExceptionStatus.RequestCanceled)
|
|
{
|
|
Debug.Log("Upload Cancelled");
|
|
m_IsError = true;
|
|
}
|
|
else
|
|
{
|
|
Debug.Log("Another error");
|
|
m_IsError = true;
|
|
}
|
|
}
|
|
m_TusClient = null;
|
|
}
|
|
|
|
void EndFileUpload()
|
|
{
|
|
if (m_UploadingTask.IsFaulted)
|
|
{
|
|
var te = m_UploadingTask.Exception.InnerExceptions[0] as TusClient.TusException;
|
|
Debug.Log(te.FullMessage);
|
|
Debug.Log("Error uploading " + m_UploadingTask.Exception.ToString() + m_UploadingTask.ToString());
|
|
}
|
|
m_UploadingTask = null;
|
|
m_Timer.Stop();
|
|
Debug.Log("Upload Finished");
|
|
}
|
|
|
|
public bool MyRemoteCertificateValidationCallback(System.Object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
|
|
{
|
|
bool isOk = true;
|
|
// If there are errors in the certificate chain, look at each error to determine the cause.
|
|
if (sslPolicyErrors != SslPolicyErrors.None)
|
|
{
|
|
for (int i = 0; i < chain.ChainStatus.Length; i++)
|
|
{
|
|
if (chain.ChainStatus[i].Status != X509ChainStatusFlags.RevocationStatusUnknown)
|
|
{
|
|
chain.ChainPolicy.RevocationFlag = X509RevocationFlag.EntireChain;
|
|
chain.ChainPolicy.RevocationMode = X509RevocationMode.Online;
|
|
chain.ChainPolicy.UrlRetrievalTimeout = new TimeSpan(0, 1, 0);
|
|
chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllFlags;
|
|
bool chainIsValid = chain.Build((X509Certificate2)certificate);
|
|
if (!chainIsValid)
|
|
{
|
|
isOk = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return isOk;
|
|
}
|
|
}
|
|
}
|