#灵感/代码缓存
using HTFW.BuildPackage;
using UnityEditor;
using System;
using System.IO;
using Cysharp.Threading.Tasks;
using HTFW.Edit.BuildPackage;
using xasset.editor;
using HTFW.HotUpdate;
using HTFW.Edit.HotUpdate;
using Newtonsoft.Json;
using System.Text;
using Newtonsoft.Json.Linq;
using System.Collections.Generic;
using System.Net;
using UnityEngine;
public class BuildApp
{
#region 时间戳差值
/// <summary>
/// 将秒数时间戳转换为多久之前。传入时间戳t(t= 当前时间戳() - 指定时间的时间戳 )
/// </summary>
/// <param name="t"></param>
/// <returns></returns>
public static string GetTimeLongAgo(double t)
{
string str = "";
double num;
if (t < 60)
{
str = string.Format("{0}秒", t);
}
else if (t >= 60 && t < 3600)
{
num = Math.Floor(t / 60);
str = string.Format("{0}分钟", num);
}
else if (t >= 3600 && t < 86400)
{
num = Math.Floor(t / 3600);
str = string.Format("{0}小时", num);
}
else if (t > 86400 && t < 2592000)
{
num = Math.Floor(t / 86400);
str = string.Format("{0}天", num);
}
else if (t > 2592000 && t < 31104000)
{
num = Math.Floor(t / 2592000);
str = string.Format("{0}月", num);
}
else
{
num = Math.Floor(t / 31104000);
str = string.Format("{0}年", num);
}
return str;
}
#endregion
#region 文件处理
public static void CopyDirectory(string sourceDir, string targetDir)
{
DirectoryInfo dir = new DirectoryInfo(sourceDir);
DirectoryInfo[] dirs = dir.GetDirectories();
// If the source directory does not exist, throw an exception.
if (!dir.Exists)
{
throw new DirectoryNotFoundException($"Source directory does not exist or could not be found: {sourceDir}");
}
// If the destination directory does not exist, create it.
if (!Directory.Exists(targetDir))
{
Directory.CreateDirectory(targetDir);
}
// Get the files in the directory and copy them to the new location.
FileInfo[] files = dir.GetFiles();
foreach (FileInfo file in files)
{
string tempPath = Path.Combine(targetDir, file.Name);
file.CopyTo(tempPath, false);
}
// If copying subdirectories, copy them and their contents to the new location.
foreach (DirectoryInfo subdir in dirs)
{
string tempPath = Path.Combine(targetDir, subdir.Name);
CopyDirectory(subdir.FullName, tempPath);
}
}
#endregion
#region 网络处理
public static JObject PostJson(string url, Dictionary<string, object> param)
{
string paramStr = JsonConvert.SerializeObject(param);
byte[] data = Encoding.UTF8.GetBytes(paramStr);
var request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "POST";
request.ContentType = "application/json";
Stream newStream = request.GetRequestStream();
newStream.Write(data, 0, data.Length);
newStream.Close();
request.ServicePoint.Expect100Continue = false;
request.ProtocolVersion = HttpVersion.Version11;
var response = (HttpWebResponse)request.GetResponse();
var responseString = new StreamReader(response.GetResponseStream()).ReadToEnd();
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
return JObject.Parse(responseString);
}
return null;
}
public static JObject GetJson(string url)
{
var request = (HttpWebRequest)WebRequest.Create(url);
var response = (HttpWebResponse)request.GetResponse();
var responseString = new StreamReader(response.GetResponseStream()).ReadToEnd();
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
return JObject.Parse(responseString);
}
return null;
}
/// <summary>
/// 发送消息到飞书机器人
/// </summary>
public static void PostLark()
{
// if (Directory.Exists(RemoteHotUpdateRootPath))
// {
// Directory.Delete(RemoteHotUpdateRootPath, true);
// }
// CopyDirectory(SourceHotUpdateRootPath, RemoteHotUpdateRootPath);
Dictionary<string, object> dict = new Dictionary<string, object>();
Dictionary<string,string> content = new Dictionary<string, string>();
string timeDesc = GetTimeLongAgo((DateTime.Now.ToUniversalTime().Ticks - 621355968000000000) / 10000000 - startTime);
if (!isUpdate)
{
content.Add("text","本次打包完成,是整包\n" +
$"本次打包时间:{timeDesc}\n" +
$"对应的SVN分支是:{svn}\n" +
$"安卓迭代的版本号是:{bundleVersionCode}\n" +
$"本次打包对应分支:{Branch}\n" +
$"本次打包版本描述:{Description}\n" +
$"本次打包对应大版本:{Version}\n" +
$"本次打包最小强更版本:{MinHotUpdateVersion}\n" +
$"版本信息,是否上传到正式后台:{isUploadToReleaseServer}\n" +
$"安装包下载路径:{HFSRemotePath}{PlayerPrefs.GetString(AppPackageNameKey)}");
}
else
{
content.Add("text","本次打包完成,是热更包\n" +
$"本次热更时间:{timeDesc}\n" +
$"对应的SVN分支是:{svn}\n" +
$"本次对应热更版本号:{HotUpdateVersion}\n" +
$"安卓迭代的版本号是:{bundleVersionCode}\n" +
$"本次打包对应分支:{Branch}\n" +
$"本次打包版本描述:{Description}\n" +
$"本次打包对应大版本:{Version}\n" +
$"本次打包最小强更版本:{MinHotUpdateVersion}\n" +
$"安装包下载路径:{HFSRemotePath}{PlayerPrefs.GetString(AppPackageNameKey)}");
}
dict.Add("msg_type", "text");
dict.Add("content", content);
string url = "https://open.larksuite.com/open-apis/bot/v2/hook/94281312-1071-4de8-a640-0878e8aab174";
PostJson(url, dict).ToString();
}
#endregion
private static string AppPackageNameKey = "AppPackageNameKey";
public static string svn = String.Empty; //svn分支地址
public static BuildPackageConfig.BuildTargetType buildTarget; //目标平台
public static int bundleVersionCode; //Android迭代版本号(每次打整包此处值++)
public static BuildPackageConfig.AndroidBuildType androidBuildType; //打包类型
public static bool isUploadToReleaseServer; //是否版本信息上传到正式后台(false:内网,true:外网)
public static string Branch = String.Empty; //版本分支,需要在 http://192.168.1.31:8888/platform_new/#/login?redirect=/index 确认,是否存在该分支
public static string Version = String.Empty; //打包 大版本
public static bool isUpdate = false; //是否热更 布尔
public static string _update = String.Empty; //是否热更 文本显示
public static string HotUpdateVersion = String.Empty; //热更版本号
public static string MinHotUpdateVersion = String.Empty; //最小热更版本 小于这个版本 会强制热更
public static string Description = String.Empty; //版本描述
public static string HFSRemotePath = String.Empty; //HFS IP服务器
public static string HFSLocalPath = String.Empty;
public static BuildPackageConst.ResServerType assetsServerType; //上传资源服务器类型
public static string appName = String.Empty; //app名字
private static long startTime; // 开始打包时间(以秒为单位)
#region 打整包
public static void BuildAPK()
{
startTime = (DateTime.Now.ToUniversalTime().Ticks - 621355968000000000) / 10000000;
string[] args = Environment.GetCommandLineArgs();
foreach (var s in args)
{
if (s.Contains("--svn_"))
{
string _svn = s.Split('_')[1];
svn = _svn;
}
else if (s.Contains("--BuildTarget:"))
{
string _buildTarget = s.Split(':')[1];
if (_buildTarget == "Android")
{
buildTarget = BuildPackageConfig.BuildTargetType.Android;
}
else if (_buildTarget == "Windows")
{
buildTarget = BuildPackageConfig.BuildTargetType.Windows;
}
else if (_buildTarget == "Ios")
{
buildTarget = BuildPackageConfig.BuildTargetType.Ios;
}
}
else if (s.Contains("--BundleVersionCode:"))
{
string _bundleVersionCode = s.Split(':')[1];
bundleVersionCode = int.Parse(_bundleVersionCode);
}
else if (s.Contains("--AndroidBuildType:"))
{
string _AndroidBuildType = s.Split(':')[1];
if (_AndroidBuildType == "开发包")
{
androidBuildType = BuildPackageConfig.AndroidBuildType.开发包;
}
else if (_AndroidBuildType == "谷歌包")
{
androidBuildType = BuildPackageConfig.AndroidBuildType.谷歌包;
}
}
else if (s.Contains("--Branch:"))
{
string _Branch = s.Split(':')[1];
Branch = _Branch;
}
else if (s.Contains("--Version:"))
{
string _Version = s.Split(':')[1];
Version = _Version;
}
else if (s.Contains("--HotUpdate:"))
{
_update = s.Split(':')[1];
bool.TryParse(_update, out isUpdate);
}
else if (s.Contains("--IsUploadToReleaseServer:"))
{
string _IsUploadToReleaseServer = s.Split(':')[1];
bool.TryParse(_IsUploadToReleaseServer, out isUploadToReleaseServer);
}
else if (s.Contains("--HotUpdateVersion:"))
{
string _HotUpdateVersion = s.Split(':')[1];
HotUpdateVersion = _HotUpdateVersion;
}
else if (s.Contains("--MinHotUpdateVersion:"))
{
string _MinHotUpdateVersion = s.Split(':')[1];
MinHotUpdateVersion = _MinHotUpdateVersion;
}
else if (s.Contains("--Description:"))
{
string _Description = s.Split(':')[1];
Description = _Description;
}
else if (s.Contains("--HFSRemotePath_"))
{
string _HFSRemotePath = s.Split('_')[1];
HFSRemotePath = _HFSRemotePath;
}
else if (s.Contains("--HFSLocalPath."))
{
string _HFSLocalPath = s.Split('.')[1];
HFSLocalPath = _HFSLocalPath;
}
else if (s.Contains("--AssetsServerType."))
{
string _AssetsServerType = s.Split(':')[1];
if (_AssetsServerType == "测试资源服HFS")
{
assetsServerType = BuildPackageConst.ResServerType.测试资源服HFS;
}
else
{
assetsServerType = BuildPackageConst.ResServerType.正式资源服AmazonCDN;
}
}
}
BuildPackageConfig buildPackageConfig =
AssetDatabase.LoadAssetAtPath<BuildPackageConfig>("Assets/Scripts/BuildPackage/BuildPackageConfig.asset");
SetupGameMode(buildPackageConfig.CurrentBuildType == BuildPackageConfig.AndroidBuildType.开发包);
HotUpdateUtil.ClearLastPackageVersionInfo();
buildPackageConfig.AssetsServerType = assetsServerType;
if (buildPackageConfig.AssetsServerType == BuildPackageConst.ResServerType.测试资源服HFS)
{
buildPackageConfig.CDNCfg = BuildPackageEditorUtil.FindCDNConfig("HFSConfig");
buildPackageConfig.VersionInfo.RemoteAssetsHost = HFSRemotePath;
buildPackageConfig.CDNCfg.RemoteAssetsHost = HFSRemotePath;
}
else
{
buildPackageConfig.CDNCfg = BuildPackageEditorUtil.FindCDNConfig("AmazonCDNConfig");
}
buildPackageConfig.BuildTarget = buildTarget;
buildPackageConfig.BundleVersionCode = bundleVersionCode;
buildPackageConfig.CurrentBuildType = androidBuildType;
buildPackageConfig.IsUploadToReleaseServer = isUploadToReleaseServer;
buildPackageConfig.VersionInfo.Branch = Branch;
buildPackageConfig.VersionInfo.Version = Version;
buildPackageConfig.VersionInfo.HotUpdateVersion = HotUpdateVersion;
buildPackageConfig.VersionInfo.MinHotUpdateVersion = MinHotUpdateVersion;
buildPackageConfig.VersionInfo.Description = Description;
bool hasExtension = buildPackageConfig.BuildTarget != BuildPackageConfig.BuildTargetType.Windows;
if (!isUpdate)
{
appName = BuildPackageEditorUtil.GetPackageName(buildPackageConfig, hasExtension).Replace(" ","");
PlayerPrefs.SetString(AppPackageNameKey,appName);
}
EditorUtility.SetDirty(buildPackageConfig);
AssetDatabase.SaveAssetIfDirty(buildPackageConfig);
if (isUpdate)
{
BuildHotUpdate();
return;
}
//设置CustomSetting
BuildPackageEditorUtil.SetupCustomSetting(buildPackageConfig);
//设置ProjectSetting
BuildPackageEditorUtil.SetupProjectSetting(buildPackageConfig);
BuildPackageEditorUtil.UploadPackageVersionInfo(!buildPackageConfig.IsUploadToReleaseServer,
buildPackageConfig.VersionInfo);
var uploadCDN = CDNBase.GetCDN(buildPackageConfig.CDNCfg);
//监听资源打包,资源有变更时,执行回调上传资源
Builder.PostprocessBuildBundles = changes =>
{
//设置 DownloadURL,确保上传updateinfo.json前,下载地址已经设置正确
Builder.SetUpdateInfoDownloadURL(buildPackageConfig.VersionInfo.GetRemoteAssetsRootPath(true));
BuildHotUpdatePackageEditorUtil.UploadAA(true, changes, buildPackageConfig.VersionInfo, uploadCDN,
isSuccess => { uploadCDN.Destroy(); },
false);
};
//打整包AA资源
AssetDatabase.Refresh();
BuildPackageEditorUtil.BuildAA(buildPackageConfig.VersionInfo);
AssetDatabase.Refresh();
//保存版本信息到本地
BuildPackageEditorUtil.SavePackageVersionInfo(buildPackageConfig.VersionInfo);
AssetDatabase.Refresh();
if (buildPackageConfig.BuildTarget == BuildPackageConfig.BuildTargetType.Android)
{
EditorUserBuildSettings.androidCreateSymbolsZip = buildPackageConfig.CreateSymbolsZip;
}
else
{
EditorUserBuildSettings.androidCreateSymbolsZip = false;
}
//设置包体资源为完整资源
if (buildPackageConfig.CompleteResPkg)
{
BuildPackageEditorUtil.SetAssetsSplitModeToIncludeAll();
}
//开始打整包
AssetDatabase.Refresh();
BuildPlayer(appName,buildPackageConfig.IsDevelopmentBuild);
//打开打包文件夹
// EditorUtility.RevealInFinder(output);
//还原游戏模式设置
SetupGameMode(true);
//还原包体资源设置
if (buildPackageConfig.CompleteResPkg)
{
BuildPackageEditorUtil.ResetAssetsSplitMode();
}
//刷新
AssetDatabase.Refresh();
PostLark();
}
public static bool SetupGameMode(bool isDevelopMode)
{
string scriptPath = "Assets/Scripts/ConstDefine/GlobalConst.cs";
string developmentStr =
"public static Eliminate.GlobalEnum.GameMode GameMode = Eliminate.GlobalEnum.GameMode.Development;";
string releaseStr =
"public static Eliminate.GlobalEnum.GameMode GameMode = Eliminate.GlobalEnum.GameMode.Release;";
if (!File.Exists(scriptPath))
{
return false;
}
string content = File.ReadAllText(scriptPath);
if (isDevelopMode)
{
if (content.Contains(developmentStr))
return true;
else if (content.Contains(releaseStr))
content = content.Replace(releaseStr, developmentStr);
else
{
return false;
}
}
else
{
if (content.Contains(developmentStr))
content = content.Replace(developmentStr, releaseStr);
else if (content.Contains(releaseStr))
return true;
else
{
return false;
}
}
File.WriteAllText(scriptPath, content);
return true;
}
/// <summary>
/// 打包(运行文件)
/// </summary>
/// <param name="cfg">打包配置文件</param>
/// <returns></returns>
public static void BuildPlayer(string appName, bool isDevelopmentBuild)
{
string output = $"{HFSLocalPath}/{appName}";
// string output = BuildPackageEditorUtil.GetLocalPackageFullPath(cfg);
if (File.Exists(output))
{
File.Delete(output);
}
var buildOptions = isDevelopmentBuild ? BuildOptions.Development : BuildOptions.None;
BuildPipeline.BuildPlayer(BuildPackageEditorUtil.FindEnableEditorScenes(), output, EditorUserBuildSettings.activeBuildTarget,
buildOptions);
}
#endregion
#region 打热更包
static bool isUploadAllResUpdate = true;
static bool isFinishUploadRes = false;
static bool isUploadResSuccess = false;
public static void BuildHotUpdate()
{
string assetPath = "Assets/Scripts/HotUpdate/Edit/BuildHotUpdatePackageConfig.asset";
BuildHotUpdatePackageConfig buildHotUpdatePackageConfig =
AssetDatabase.LoadAssetAtPath<BuildHotUpdatePackageConfig>(assetPath);
// Internal_BuildPackageHotUpdate(buildHotUpdatePackageConfig);
buildHotUpdatePackageConfig.AssetsServerType = assetsServerType;
if (buildHotUpdatePackageConfig.AssetsServerType == BuildPackageConst.ResServerType.测试资源服HFS)
{
buildHotUpdatePackageConfig.CDNCfg = BuildPackageEditorUtil.FindCDNConfig("HFSConfig");
buildHotUpdatePackageConfig.VersionInfo.RemoteAssetsHost = HFSRemotePath;
buildHotUpdatePackageConfig.CDNCfg.RemoteAssetsHost = HFSRemotePath;
}
else
{
buildHotUpdatePackageConfig.CDNCfg = BuildPackageEditorUtil.FindCDNConfig("AmazonCDNConfig");
}
//初始化VersionInfo
buildHotUpdatePackageConfig.VersionInfo.InitCDNConfig(buildHotUpdatePackageConfig.CDNCfg.RemoteAssetsHost,
buildHotUpdatePackageConfig.CDNCfg.RemoteHotUpdateRootPath);
buildHotUpdatePackageConfig.IsUploadToReleaseServer = isUploadToReleaseServer;
buildHotUpdatePackageConfig.VersionInfo.Branch = Branch;
buildHotUpdatePackageConfig.VersionInfo.Version = Version;
buildHotUpdatePackageConfig.VersionInfo.HotUpdateVersion = HotUpdateVersion;
buildHotUpdatePackageConfig.VersionInfo.MinHotUpdateVersion = MinHotUpdateVersion;
buildHotUpdatePackageConfig.VersionInfo.Description = Description;
// buildHotUpdatePackageConfig.VersionInfo.HotUpdateVersion = (int.Parse(BuildPackageEditorUtil.LoadPackageVersionInfo().HotUpdateVersion) + 1).ToString(); //热更版本号 自增
EditorUtility.SetDirty(buildHotUpdatePackageConfig);
AssetDatabase.SaveAssetIfDirty(buildHotUpdatePackageConfig);
//保存版本信息到本地
BuildPackageEditorUtil.SavePackageVersionInfo(buildHotUpdatePackageConfig.VersionInfo);
AssetDatabase.Refresh();
//-------------------- 版本信息检查 --------------------//
if (buildHotUpdatePackageConfig.CDNCfg == null || !buildHotUpdatePackageConfig.CDNCfg.IsEnable || !buildHotUpdatePackageConfig.CDNCfg.IsValid())
{
// Debug.LogError("需要有效 CDN 配置!");
return;
}
//是否使用正式后台
buildHotUpdatePackageConfig.VersionInfo.IsReleaseServer = buildHotUpdatePackageConfig.IsUploadToReleaseServer;
//检测版本信息填写是否合法
// Debug.Log("开始检测版本信息填写是否合法...");
if (!CheckPackageVersionInfoValid(buildHotUpdatePackageConfig.VersionInfo))
{
// Debug.LogError("版本信息填写不合法, 打包失败!");
return;
}
// Debug.Log("版本信息填写合法.");
//-------------------- 打热更包 --------------------//
// Debug.Log("开始检查打包资源前,本地资源状态跟远端是否一致...");
// if (!CheckLocalResStatusHotUpdate(buildHotUpdatePackageConfig))
// {
// return;
// }
//-------------------- 监听资源打包--------------------//
var uploadCDN = CDNBase.GetCDN(buildHotUpdatePackageConfig.CDNCfg);
//监听资源打包,打包完成后执行后处理,上传热更AA资源到CDN
Builder.PostprocessBuildBundles = changes =>
{
//设置 DownloadURL,确保上传updateinfo.json前,下载地址已经设置正确
Builder.SetUpdateInfoDownloadURL(buildHotUpdatePackageConfig.VersionInfo.GetRemoteAssetsRootPath(true));
BuildHotUpdatePackageEditorUtil.UploadAA(true, changes, buildHotUpdatePackageConfig.VersionInfo, uploadCDN,
isSuccess =>
{
isFinishUploadRes = true;
isUploadResSuccess = isSuccess;
uploadCDN.Destroy();
},
false);
};
//打热更AA资源
// Debug.Log("开始打热更AA资源...");
BuildHotUpdatePackageEditorUtil.BuildAA(buildHotUpdatePackageConfig.VersionInfo);
// Debug.Log("热更AA资源打包完成!");
//等待资源上传逻辑完成
// while (!isFinishUploadRes)
// {
// UniTask.Delay(100); // 每隔一段时间进行检查,避免阻塞主线程
// }
//由于内部使用了Unity的网络请求Api,所以必须回到unity主线程调用该异步函数
UploadPackageVersionInfoHotUpdate(isUploadResSuccess, buildHotUpdatePackageConfig.VersionInfo,buildHotUpdatePackageConfig).Forget();
}
public static bool CheckPackageVersionInfoValid(PackageVersionInfo info)
{
if (info == null)
{
// Debug.LogError("版本信息不能为空!");
return false;
}
if (!info.CheckValid())
{
// Debug.LogError("版本信息输入有误!");
return false;
}
return true;
}
/// <summary>
/// 上传版本信息
/// </summary>
/// <param name="isUploadResSuccess"></param>
/// <param name="info"></param>
static async UniTask UploadPackageVersionInfoHotUpdate(bool isUploadResSuccess, PackageVersionInfo info,BuildHotUpdatePackageConfig buildHotUpdatePackageConfig)
{
if (isUploadResSuccess)
{
//上传版本信息到后台
// Debug.Log("上传版本信息到后台...");
if (!await BuildPackageEditorUtil.UploadPackageVersionInfo(!buildHotUpdatePackageConfig.IsUploadToReleaseServer, info)){
}
}
else
{
// Debug.LogError($"上传资源失败!热更包打包失败.");
}
PostLark();
}
#endregion
}