1922 lines
72 KiB
C#
1922 lines
72 KiB
C#
using UnityEditor;
|
||
using UnityEngine;
|
||
using System;
|
||
using System.IO;
|
||
using System.Text;
|
||
using System.Collections.Generic;
|
||
using System.Security.Cryptography;
|
||
using System.Diagnostics;
|
||
//using XGame;
|
||
using UnityEditor.Build.Player;
|
||
using HybridCLR.Editor;
|
||
using Debug = UnityEngine.Debug;
|
||
|
||
|
||
#if !UNITY_WEBGL
|
||
using HybridCLR;
|
||
using UnityEditor.Build.Content;
|
||
|
||
using UnityEditor.Build.Reporting;
|
||
|
||
|
||
#if UNITY_EDITOR
|
||
using HybridCLR.Editor.Commands;
|
||
#endif
|
||
#endif
|
||
public class Packager {
|
||
//#if UNITY_STANDALONE_WIN
|
||
static public BuildTarget buildTaget = BuildTarget.StandaloneWindows;
|
||
//其他平台。。。。。。
|
||
//#endif
|
||
public static string OutputName = "LocalTest";
|
||
public static string platform = string.Empty;
|
||
|
||
static Dictionary<string, string> m_mapResNameDic = new Dictionary<string, string>();
|
||
static List<AssetBundleBuild> maps = new List<AssetBundleBuild>();
|
||
static List<string> paths = new List<string>();
|
||
static List<string> files = new List<string>();
|
||
static long UpdateDT = 0;
|
||
|
||
static string sPlatform = "pc";
|
||
|
||
static Dictionary<string, string> m_dicFilelist = new Dictionary<string, string>();
|
||
static string m_strDllFileMD5;
|
||
static string m_strResFileMD5;
|
||
|
||
public static string ProjectDir => Directory.GetParent(Application.dataPath).ToString();
|
||
public static string HybridCLRDataDir { get; } = $"{ProjectDir}/HybridCLRData";
|
||
public static string LocalIl2CppDir => $"{HybridCLRDataDir}/LocalIl2CppData/il2cpp";
|
||
public static string AssembliesPostIl2CppStripDir => $"{HybridCLRDataDir}/AssembliesPostIl2CppStrip";
|
||
public static string GetAssembliesPostIl2CppStripDir(BuildTarget target)
|
||
{
|
||
return $"{AssembliesPostIl2CppStripDir}/{target}";
|
||
}
|
||
|
||
//需要导出的aot文件,运行的时候会全部更新
|
||
public static List<string> AOTMetaAssemblyNames { get; } = new List<string>()
|
||
{
|
||
"mscorlib.dll",
|
||
"System.dll",
|
||
"System.Core.dll", // 如果使用了Linq,需要这个
|
||
};
|
||
|
||
static Dictionary<string , int> DicAssetsMap = new Dictionary<string, int>();
|
||
public static string[] HotfixDir = { "res/scene", "res/character","res/effect", "res/ui", "arts"};//shader单独打
|
||
|
||
public static string GetUpdatePath()
|
||
{
|
||
#if UNITY_WEBGL
|
||
return $"{XGame.AppConst.WebGL}/StreamingAssets/";
|
||
#else
|
||
return $"{XGame.AppConst.UpdatePath}{sPlatform}\\";//$"d:/HTTP/update/{sPlatform}/";//
|
||
#endif
|
||
}
|
||
public static string DataPath
|
||
{
|
||
get
|
||
{
|
||
|
||
return Application.dataPath + "/StreamAssets/";
|
||
}
|
||
}
|
||
|
||
public static string md5(string source)
|
||
{
|
||
MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
|
||
byte[] data = System.Text.Encoding.UTF8.GetBytes(source);
|
||
byte[] md5Data = md5.ComputeHash(data, 0, data.Length);
|
||
md5.Clear();
|
||
|
||
string destString = "";
|
||
for (int i = 0; i < md5Data.Length; i++)
|
||
{
|
||
destString += System.Convert.ToString(md5Data[i], 16).PadLeft(2, '0');
|
||
}
|
||
destString = destString.PadLeft(32, '0');
|
||
return destString;
|
||
}
|
||
public static string md5file(string file)
|
||
{
|
||
long size = 0;
|
||
return md5file(file, out size);
|
||
}
|
||
public static string md5file(string file, out long size)
|
||
{
|
||
try
|
||
{
|
||
FileStream fs = new FileStream(file, FileMode.Open);
|
||
System.Security.Cryptography.MD5 md5 = new System.Security.Cryptography.MD5CryptoServiceProvider();
|
||
byte[] retVal = md5.ComputeHash(fs);
|
||
size = fs.Length;
|
||
fs.Close();
|
||
StringBuilder sb = new StringBuilder();
|
||
for (int i = 0; i < retVal.Length; i++)
|
||
{
|
||
sb.Append(retVal[i].ToString("x2"));
|
||
}
|
||
return sb.ToString();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
throw new Exception("md5file() fail, error:" + ex.Message);
|
||
}
|
||
}
|
||
public static void CopyLuaBytesFiles(string sourceDir, string destDir, bool appendext = true, string searchPattern = "*.lua", SearchOption option = SearchOption.AllDirectories)
|
||
{
|
||
if (!Directory.Exists(sourceDir))
|
||
{
|
||
return;
|
||
}
|
||
|
||
string[] files = Directory.GetFiles(sourceDir, searchPattern, option);
|
||
int len = sourceDir.Length;
|
||
|
||
if (sourceDir[len - 1] == '/' || sourceDir[len - 1] == '\\')
|
||
{
|
||
--len;
|
||
}
|
||
|
||
for (int i = 0; i < files.Length; i++)
|
||
{
|
||
string str = files[i].Remove(0, len);
|
||
string dest = destDir + "/" + str;
|
||
if (appendext) dest += ".bytes";
|
||
string dir = Path.GetDirectoryName(dest);
|
||
Directory.CreateDirectory(dir);
|
||
File.Copy(files[i], dest, true);
|
||
}
|
||
}
|
||
|
||
///-----------------------------------------------------------
|
||
static string[] exts = { ".txt", ".xml", ".lua", ".assetbundle", ".json" };
|
||
static bool CanCopy(string ext) { //能不能复制
|
||
foreach (string e in exts) {
|
||
if (ext.Equals(e)) return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 载入素材
|
||
/// </summary>
|
||
static UnityEngine.Object LoadAsset(string file) {
|
||
if (file.EndsWith(".lua")) file += ".txt";
|
||
return AssetDatabase.LoadMainAssetAtPath("Assets/" + file);
|
||
}
|
||
|
||
public static void BuildDLL()
|
||
{
|
||
UnityEngine.Debug.LogFormat("编译DLL");
|
||
}
|
||
public static void BuildBasePack(string OutputPath, BuildTargetGroup targetGroup, BuildTarget target)
|
||
{
|
||
//生成最终底包
|
||
string[] SCENES = FindEnabledEditorScenes();
|
||
if (EditorUserBuildSettings.activeBuildTarget != target)
|
||
EditorUserBuildSettings.SwitchActiveBuildTarget(targetGroup, target);
|
||
CSObjectWrapEditor.Generator.ClearAll();
|
||
CSObjectWrapEditor.Generator.GenAll();
|
||
HybridCLR.Editor.Commands.LinkGeneratorCommand.GenerateLinkXml();
|
||
|
||
PlayerSettings.Android.keystoreName = "Android/android.keystore";
|
||
PlayerSettings.Android.keyaliasName = "xw";
|
||
PlayerSettings.keystorePass = "abcd12";
|
||
PlayerSettings.keyaliasPass = "abcd12";
|
||
|
||
PlayerSettings.Android.androidIsGame = true;
|
||
PlayerSettings.SetIncrementalIl2CppBuild(BuildTargetGroup.Android, true);
|
||
EditorUserBuildSettings.buildScriptsOnly = false;
|
||
EditorUserBuildSettings.androidBuildSystem = AndroidBuildSystem.Gradle;
|
||
//Release
|
||
EditorUserBuildSettings.development = false;
|
||
EditorUserBuildSettings.allowDebugging = false;
|
||
EditorUserBuildSettings.androidBuildType = AndroidBuildType.Release;
|
||
|
||
AssetDatabase.Refresh();
|
||
//保证这个打出来的使用设置好的路径
|
||
//PlayerSettings.SetScriptingDefineSymbolsForGroup(targetGroup, "LOCAL");
|
||
BuildPipeline.BuildPlayer(SCENES, OutputPath, target, BuildOptions.None);
|
||
//CSObjectWrapEditor.Generator.ClearAll();
|
||
UnityEngine.Debug.Log("Build OK!");
|
||
}
|
||
|
||
public static bool BuildAPK(string apkBuildName = "", bool bHotfix = true, bool bRelease = true)
|
||
{
|
||
//HybridCLR.BuildConfig.Setup(); //设置il2cpp的环境变量
|
||
string[] args = Environment.GetCommandLineArgs();
|
||
Debug.LogFormat("CommandLineArgs:{0}", string.Join(",", args));
|
||
|
||
if (bHotfix)
|
||
{
|
||
HybridCLR.Editor.SettingsUtil.Enable = true;
|
||
}
|
||
else
|
||
{
|
||
HybridCLR.Editor.SettingsUtil.Enable = false;
|
||
}
|
||
|
||
|
||
//切换到目标平台
|
||
if (EditorUserBuildSettings.activeBuildTarget != BuildTarget.Android)
|
||
EditorUserBuildSettings.SwitchActiveBuildTarget(BuildTargetGroup.Android, BuildTarget.Android);
|
||
|
||
CSObjectWrapEditor.Generator.ClearAll();
|
||
CSObjectWrapEditor.Generator.GenAll();
|
||
PlayerSettings.allowUnsafeCode = true;
|
||
PlayerSettings.SetManagedStrippingLevel(BuildTargetGroup.Android, ManagedStrippingLevel.Low);
|
||
|
||
PlayerSettings.SplashScreen.show = false;
|
||
PlayerSettings.SplashScreen.showUnityLogo = false;
|
||
|
||
PlayerSettings.Android.keystoreName = "Assets/60s.keystore";
|
||
PlayerSettings.Android.keyaliasName = "60s";
|
||
PlayerSettings.keystorePass = "abcd12";
|
||
PlayerSettings.keyaliasPass = "abcd12";
|
||
PlayerSettings.Android.preferredInstallLocation = AndroidPreferredInstallLocation.Auto;
|
||
// s = GetCommandLineValue(args, "-svn_version");
|
||
// PlayerSettings.Android.bundleVersionCode = string.IsNullOrEmpty(s) ? 1 : int.Parse(s); // 上传符号表文件时需要该值在一次打包中不改变
|
||
//PlayerSettings.Android.bundleVersionCode = int.Parse(BuildSettings.VersionCode.Replace(".", ""));
|
||
PlayerSettings.Android.startInFullscreen = true;
|
||
PlayerSettings.Android.androidIsGame = true;
|
||
|
||
PlayerSettings.SetIncrementalIl2CppBuild(BuildTargetGroup.Android, true);
|
||
|
||
EditorUserBuildSettings.buildScriptsOnly = false;
|
||
EditorUserBuildSettings.androidBuildSystem = AndroidBuildSystem.Gradle;
|
||
|
||
BuildOptions buildOptions = BuildOptions.None;
|
||
if (bRelease)
|
||
{
|
||
EditorUserBuildSettings.development = false;
|
||
EditorUserBuildSettings.allowDebugging = false;
|
||
EditorUserBuildSettings.androidBuildType = AndroidBuildType.Release;
|
||
//IL2CPP C++ 编译配置默认是 Debug(带 -g 调试符号),对 HybridCLR 生成的超大 lump 做 codegen 时
|
||
//易触发 NDK clang 段错误(clang frontend command failed due to signal)。发布包统一用 Release 去掉 -g。
|
||
PlayerSettings.SetIl2CppCompilerConfiguration(BuildTargetGroup.Android, Il2CppCompilerConfiguration.Release);
|
||
Debug.Log("开始打包安卓发布版本");
|
||
}
|
||
else
|
||
{
|
||
EditorUserBuildSettings.development = true;
|
||
EditorUserBuildSettings.allowDebugging = true;
|
||
EditorUserBuildSettings.androidBuildType = AndroidBuildType.Debug;
|
||
PlayerSettings.SetIl2CppCompilerConfiguration(BuildTargetGroup.Android, Il2CppCompilerConfiguration.Debug);
|
||
buildOptions = BuildOptions.Development;
|
||
|
||
Debug.Log("开始打包安卓测试版本");
|
||
}
|
||
// 获取当前的宏定义
|
||
string currentDefines = PlayerSettings.GetScriptingDefineSymbolsForGroup(BuildTargetGroup.Android);
|
||
|
||
{
|
||
PlayerSettings.stripEngineCode = true;
|
||
}
|
||
|
||
AssetDatabase.Refresh();
|
||
|
||
//打包前自检:底包里随包发布的加密 AB 必须是"已加密"状态(运行时 LoadDll 会无条件 AES 解密)。
|
||
//若 a.unity3d 等是原始未加密 AB(头部为 UnityFS / 长度非16倍数),真机会在 StartGame 解密时
|
||
//抛 "Offset and length were out of bounds" 并 "Decrypt Failed"。这里提前拦截,避免出坏包。
|
||
if (!VerifyEncryptedBaseBundles())
|
||
{
|
||
Debug.LogError("APK 打包中断:底包加密 AB 自检未通过(见上条日志)。请用『打包/生成底包/Android最小包(或完整包)』重新生成(内部会加密 a.unity3d),且不要在其后再点『更新dll(Android)』(那会删掉 a.unity3d)。");
|
||
return false;
|
||
}
|
||
|
||
try
|
||
{
|
||
string[] LauncherSceneNames;
|
||
//build一下link.xml
|
||
HybridCLR.Editor.Commands.LinkGeneratorCommand.GenerateLinkXml();
|
||
LauncherSceneNames = FindEnabledEditorScenes();
|
||
|
||
BuildReport report =
|
||
BuildPipeline.BuildPlayer(LauncherSceneNames, apkBuildName, BuildTarget.Android, buildOptions);
|
||
|
||
//CSObjectWrapEditor.Generator.ClearAll();//如果不清 可能源码参数修改导致导致编译错误 打完包默认清理一下
|
||
if (report.summary.result == BuildResult.Succeeded)
|
||
{
|
||
Debug.Log("安卓打包成功!");
|
||
return true;
|
||
}
|
||
else
|
||
{
|
||
Debug.LogFormat("安卓打包失败,totalErrors:{0}!", report.summary.totalErrors);
|
||
return false;
|
||
}
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
Debug.Log(e.ToString());
|
||
EditorApplication.Exit(1);
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
//校验随底包发布的加密 AB 是否真的处于"已加密"状态。
|
||
//判定:已加密数据不应以 AssetBundle 魔数(UnityFS/Unity)开头,且 AES-CBC 产物长度必为16倍数。
|
||
//a.unity3d 为必需(缺失或未加密都拦截);h.unity3d / bs.unity3d 仅在存在时校验。
|
||
static bool VerifyEncryptedBaseBundles()
|
||
{
|
||
bool ok = true;
|
||
// name -> 是否必需存在
|
||
var targets = new Dictionary<string, bool>
|
||
{
|
||
{ "a.unity3d", true },
|
||
{ "h.unity3d", false },
|
||
{ "bs.unity3d", false },
|
||
};
|
||
foreach (var kv in targets)
|
||
{
|
||
string file = $"{AppDataPath}/StreamingAssets/{kv.Key}";
|
||
if (!File.Exists(file))
|
||
{
|
||
if (kv.Value)
|
||
{
|
||
Debug.LogError($"加密 AB 缺失:{file} 不存在。a.unity3d(AOT元数据包)须先经『生成底包/Android…』(PackAotFile)产出并加密。");
|
||
ok = false;
|
||
}
|
||
continue;
|
||
}
|
||
|
||
byte[] head = new byte[5];
|
||
int n;
|
||
long size;
|
||
using (var fs = File.OpenRead(file))
|
||
{
|
||
size = fs.Length;
|
||
n = fs.Read(head, 0, head.Length);
|
||
}
|
||
|
||
bool isRawAB = n >= 5 && head[0] == (byte)'U' && head[1] == (byte)'n'
|
||
&& head[2] == (byte)'i' && head[3] == (byte)'t' && head[4] == (byte)'y';
|
||
bool blockAligned = (size % 16) == 0;
|
||
|
||
if (isRawAB || !blockAligned)
|
||
{
|
||
Debug.LogError($"加密 AB 自检失败:{kv.Key} 看起来未加密(头部RawAB={isRawAB}, size={size}, size%16={size % 16})。" +
|
||
"运行时会无条件 AES 解密它 → 真机必崩。请走会加密的底包菜单重新生成。");
|
||
ok = false;
|
||
}
|
||
}
|
||
return ok;
|
||
}
|
||
|
||
//[MenuItem("打包/增量编译LocalTest底包(包含C#,Config,Script,UI)", false, 101)]
|
||
public static void BuildWindowsRuntime() {
|
||
|
||
//生成最终pc底包
|
||
string[] SCENES = FindEnabledEditorScenes();
|
||
|
||
//保证这个打出来的使用设置好的路径
|
||
//PlayerSettings.SetScriptingDefineSymbolsForGroup(BuildTargetGroup.Standalone, "LOCAL");
|
||
|
||
BuildPipeline.BuildPlayer(SCENES, "../" + OutputName + "/Client.exe", BuildTarget.StandaloneWindows64, BuildOptions.None);
|
||
|
||
|
||
//增量更新
|
||
string UpdateFile = AppDataPath + "/../FileTime.txt";
|
||
//获得上回时间
|
||
FileInfo fi = new FileInfo(UpdateFile);
|
||
|
||
if (fi.Exists)
|
||
{
|
||
UpdateDT = fi.LastWriteTime.ToFileTime();
|
||
}
|
||
else
|
||
{
|
||
File.Create(UpdateFile).Dispose();
|
||
UpdateDT = 0;
|
||
}
|
||
|
||
//脚本和配置
|
||
|
||
//源文件编译
|
||
string srcPath, outPath;
|
||
//Script
|
||
srcPath = AppDataPath + "/../../Scripts/";
|
||
outPath = AppDataPath + "/../../Scripts_c/";
|
||
//不转luac
|
||
CopyPath(srcPath, outPath);
|
||
//CompileScript(srcPath, outPath);
|
||
//Config
|
||
srcPath = AppDataPath + "/../../Config/Config/";
|
||
outPath = AppDataPath + "/../../Scripts_c/Base/Config/";
|
||
//配置不转luac
|
||
CopyPath(srcPath, outPath);
|
||
//CompileScript(srcPath, outPath);
|
||
|
||
UpdateDT = DateTime.Now.ToFileTime();
|
||
using (StreamWriter sw = new StreamWriter(UpdateFile))
|
||
{
|
||
sw.Write(UpdateDT);
|
||
}
|
||
|
||
CopyScript_c();//复制到目标端
|
||
|
||
//UI 资源
|
||
//BuildUI(BuildTarget.StandaloneWindows64);
|
||
|
||
UnityEngine.Debug.Log("Local Version OK!");
|
||
}
|
||
#if !UNITY_WEBGL
|
||
[MenuItem("打包/生成底包/Win64最小包", false, 101)]
|
||
public static void BuildWindowsBase()
|
||
{
|
||
string outPath = $"{AppDataPath}/StreamingAssets/";
|
||
ClearDir(outPath);
|
||
|
||
//生成最终pc底包
|
||
string[] SCENES = FindEnabledEditorScenes();
|
||
|
||
//保证这个打出来的使用设置好的路径
|
||
//PlayerSettings.SetScriptingDefineSymbolsForGroup(BuildTargetGroup.Standalone, "LOCAL");
|
||
//PrebuildCommand.GenerateAll();
|
||
//AssetDatabase.Refresh();
|
||
BuildReport buildReport = BuildPipeline.BuildPlayer(SCENES, "../" + OutputName + "/Client.exe", BuildTarget.StandaloneWindows64, BuildOptions.CompressWithLz4);
|
||
if (buildReport == null || buildReport.summary.result != BuildResult.Succeeded)
|
||
{
|
||
UnityEngine.Debug.LogError("Build Base Failed!");
|
||
}
|
||
else
|
||
{
|
||
UnityEngine.Debug.Log("Build Base OK!");
|
||
}
|
||
AssetDatabase.Refresh();
|
||
|
||
//BuildUpdate(BuildTargetGroup.Standalone, BuildTarget.StandaloneWindows64);
|
||
//AssetDatabase.Refresh();
|
||
}
|
||
[MenuItem("打包/生成底包/Win64完整包", false, 101)]
|
||
public static void BuildWindowsFull()
|
||
{
|
||
string outPath = $"{AppDataPath}/StreamingAssets/";
|
||
ClearDir(outPath);
|
||
BuildUpdate(BuildTargetGroup.Standalone, BuildTarget.StandaloneWindows64);
|
||
|
||
//生成最终pc底包
|
||
string[] SCENES = FindEnabledEditorScenes();
|
||
|
||
//保证这个打出来的使用设置好的路径
|
||
//PlayerSettings.SetScriptingDefineSymbolsForGroup(BuildTargetGroup.Standalone, "LOCAL");
|
||
PrebuildCommand.GenerateAll();
|
||
BuildPipeline.BuildPlayer(SCENES, "../" + OutputName + "/Client.exe", BuildTarget.StandaloneWindows64, BuildOptions.CompressWithLz4);
|
||
|
||
UnityEngine.Debug.Log("Build Full OK!");//*/
|
||
|
||
}
|
||
|
||
[MenuItem("打包/生成底包/Android最小包", false, 102)]
|
||
public static void BuildAndroidBase()
|
||
{
|
||
//ring outPath = $"{AppDataPath}/StreamingAssets/";
|
||
//earDir(outPath);
|
||
if (true == HybridCLRBuild.PackAotFile(BuildTargetGroup.Android, BuildTarget.Android, "a.unity3d", $"{Application.dataPath.ToLower()}/StreamingAssets/xaot/"))
|
||
{
|
||
BuildAPK("Client.apk", true, true);
|
||
}
|
||
//BuildBasePack("Client.apk", BuildTargetGroup.Android, BuildTarget.Android);
|
||
//HybridCLRBuild.BuildDllUpdateAndroid();
|
||
//BuildUpdate(BuildTargetGroup.Android, BuildTarget.Android);
|
||
//earDir(outPath);
|
||
}
|
||
[MenuItem("打包/生成底包/Android完整包", false, 102)]
|
||
public static void BuildAndroidFull()
|
||
{
|
||
string outPath = $"{AppDataPath}/StreamingAssets/";
|
||
HybridCLRBuild.ClearDir(outPath);
|
||
//BuildUpdate(BuildTargetGroup.Android, BuildTarget.Android);
|
||
//HybridCLRBuild.BuildDllUpdateAndroid();
|
||
HybridCLRBuild.BuildHotFix(BuildTargetGroup.Android, BuildTarget.Android, "client", false);
|
||
if (true == HybridCLRBuild.PackAotFile(BuildTargetGroup.Android, BuildTarget.Android, "a.unity3d", $"{Application.dataPath.ToLower()}/StreamingAssets/xaot/"))
|
||
{
|
||
BuildAPK("Client.apk", true, true);
|
||
}
|
||
}
|
||
|
||
[MenuItem("打包/生成基础更新Win64", false, 201)]
|
||
public static void BuildUpdateWin64()
|
||
{
|
||
//BuildUpdate(BuildTargetGroup.Standalone, BuildTarget.StandaloneWindows64);
|
||
HybridCLRBuild.BuildHotFix(BuildTargetGroup.Standalone, BuildTarget.StandaloneWindows64);
|
||
}
|
||
[MenuItem("打包/生成基础更新Android", false, 202)]
|
||
public static void BuildUpdateAndroid()
|
||
{
|
||
//BuildUpdate(BuildTargetGroup.Android, BuildTarget.Android);
|
||
HybridCLRBuild.BuildDllUpdateAndroid();
|
||
}
|
||
|
||
[MenuItem("打包/生成游戏更新", false, 1030)]
|
||
public static void BuildCurGameUpdate()
|
||
{
|
||
//加载../../XDevNode/xid.txt
|
||
string xid = null;
|
||
if(File.Exists("../XDevNode/xid.txt"))
|
||
{
|
||
xid = File.ReadAllText("../XDevNode/xid.txt");
|
||
}
|
||
if(string.IsNullOrEmpty(xid))
|
||
{
|
||
Debug.LogWarning("./../XDevNode/xid.txt 不存在。自动生成");
|
||
ulong id = Generate64BitId();
|
||
byte[] bytes = BitConverter.GetBytes(id);
|
||
|
||
// 如果是大端系统需要反转数组(如网络传输场景)
|
||
if (BitConverter.IsLittleEndian == false)
|
||
{
|
||
Array.Reverse(bytes);
|
||
}
|
||
|
||
// 转换为Base64字符串
|
||
xid = Convert.ToBase64String(bytes);
|
||
File.WriteAllText("../XDevNode/xid.txt", xid);
|
||
}
|
||
if(xid.Length > 24)
|
||
{
|
||
xid = xid.Substring(0, 24);
|
||
}
|
||
//以xid为目录打包资源
|
||
string outPath = $"../XDevNode/{xid}/";
|
||
//根据平台打更新
|
||
ClearDir(outPath);
|
||
BuildGameUpdate(BuildTargetGroup.Android, BuildTarget.Android, xid);
|
||
|
||
}
|
||
|
||
public static ulong Generate64BitId()
|
||
{
|
||
RandomNumberGenerator rng = RandomNumberGenerator.Create();
|
||
DateTime Epoch1601 = new DateTime(1601, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
||
Span<byte> randomBytes = stackalloc byte[8];
|
||
rng.GetBytes(randomBytes);
|
||
|
||
var timestamp = (ulong)((DateTime.UtcNow - Epoch1601).Ticks / 10);
|
||
return ((timestamp >> 22) & 0x3FFFFFFFFFF) << 22
|
||
| (BitConverter.ToUInt64(randomBytes) & 0x3FFFFF);
|
||
}
|
||
|
||
public static void BuildGameUpdate(BuildTargetGroup targetGroup, BuildTarget target, string xid)
|
||
{
|
||
if (EditorUserBuildSettings.activeBuildTarget != target)
|
||
{
|
||
EditorUserBuildSettings.SwitchActiveBuildTarget(targetGroup, target);
|
||
}
|
||
if (target == BuildTarget.Android)
|
||
{
|
||
platform = "android";
|
||
}
|
||
else if (target == BuildTarget.iOS)
|
||
{
|
||
platform = "ios";
|
||
}
|
||
else if (target == BuildTarget.WebGL)
|
||
{
|
||
platform = "webgl";
|
||
//bLuaPackage = true;
|
||
}
|
||
else
|
||
{
|
||
platform = "pc";
|
||
}
|
||
|
||
string outPath = $"../XDevNode/{xid}/{platform}/";
|
||
ClearDir(outPath);
|
||
|
||
if (!Directory.Exists(outPath))
|
||
Directory.CreateDirectory(outPath);
|
||
AssetDatabase.Refresh();
|
||
maps.Clear();
|
||
|
||
//lua 打包
|
||
BuildLuaAB("s.unity3d");
|
||
|
||
//渲染shader ab
|
||
//shader也跟下面的资源一起打吧
|
||
//PackAllPathFile("game_shader.unity3d", $"Assets/Game/Shader", "Shadervariants");
|
||
AddBuildMap("game_shader.unity3d", "*.*", "Assets/Game/Shader");
|
||
|
||
//资源 ab
|
||
string[] HotfixRes = { "Game/Art" };//热更资源目录, 按目录打资源
|
||
string namePre = $"Game_Art";
|
||
foreach (string respath in HotfixRes)
|
||
{
|
||
string tempPath = $"{ AppDataPath}/{respath}/";
|
||
string buildPath = $"Assets/{respath}/";//路径必须以Assets开始
|
||
string[] dirs = Directory.GetDirectories(tempPath, "*", SearchOption.AllDirectories);
|
||
for (int i = 0; i < dirs.Length; i++)
|
||
{
|
||
string path = dirs[i];
|
||
if (path.Contains(".svn"))
|
||
continue;
|
||
string pathname = dirs[i].Replace(tempPath, string.Empty);
|
||
string name = pathname.Replace('\\', '_').Replace('/', '_');//Lua ab文件使用.替换目录分隔符
|
||
name = name.ToLower() + ".unity3d";
|
||
|
||
AddBuildMap($"{namePre}_{name}", "*.*", buildPath + pathname);
|
||
//AssetDatabase.Refresh();
|
||
}
|
||
AddBuildMap($"{namePre}_{respath}.unity3d", "*.*", $"Assets/{respath}/");
|
||
//AssetDatabase.Refresh();
|
||
}
|
||
|
||
//打ab包
|
||
BuildPipeline.BuildAssetBundles(outPath, maps.ToArray(), BuildAssetBundleOptions.ChunkBasedCompression/*| BuildAssetBundleOptions.DisableWriteTypeTree*/, target);
|
||
AssetDatabase.Refresh();
|
||
|
||
//改名资源依赖文件,加个后缀
|
||
if (File.Exists($"{outPath}{platform}"))
|
||
{
|
||
File.Move($"{outPath}{platform}", $"{outPath}abdepend.unity3d");
|
||
}
|
||
//打索引文件和版本文件
|
||
m_dicFilelist.Clear();
|
||
BuildFileIndex($"{outPath}");
|
||
string resmd5 = md5file($"{outPath}files.txt");
|
||
m_strResFileMD5 = GetFixedMd5(resmd5);
|
||
File.WriteAllText($"{outPath}/updatever.txt", $"{m_strResFileMD5}");
|
||
|
||
|
||
//加密
|
||
string[] EncryptFile = { "abdepend.unity3d", "s.unity3d" };
|
||
foreach (string f in EncryptFile)
|
||
{
|
||
string file = $"{outPath}{f}";
|
||
if (File.Exists(file))
|
||
{
|
||
byte[] srcdata = File.ReadAllBytes(file);
|
||
byte[] encryptdata = AESManager.Encrypt(srcdata);
|
||
byte[] retdata = AESManager.Decrypt(encryptdata);
|
||
File.WriteAllBytes(file, encryptdata);
|
||
}
|
||
}
|
||
//根据md5列表修改ab包名称
|
||
foreach (var file in m_dicFilelist)
|
||
{
|
||
string destFile = GetFileMd5Name($"{outPath}{file.Key}", GetFixedMd5(file.Value));
|
||
string srcFile = $"{outPath}{file.Key}";
|
||
if (!File.Exists(srcFile))
|
||
continue;
|
||
if (!File.Exists(destFile))
|
||
File.Move(srcFile, destFile);
|
||
}
|
||
//文件列表添加MD5
|
||
string newFilePath = GetFileMd5Name($"{outPath}files.txt", GetFixedMd5(m_strResFileMD5));
|
||
if (File.Exists(newFilePath))
|
||
File.Delete(newFilePath);
|
||
File.Move($"{outPath}files.txt", newFilePath);
|
||
|
||
}
|
||
|
||
#else
|
||
//[MenuItem("打包/生成WebGL端", false, 301)]
|
||
public static void BuildWebGL()
|
||
{
|
||
string outPath = $"{AppDataPath}/StreamingAssets/";
|
||
ClearDir(outPath);
|
||
BuildBasePack("Client", BuildTargetGroup.WebGL, BuildTarget.WebGL);
|
||
//BuildUpdate(BuildTargetGroup.WebGL, BuildTarget.WebGL);
|
||
//放到AppConst.WebGL
|
||
CopyPath($"{System.Environment.CurrentDirectory}/Client", XGame.AppConst.WebGL);
|
||
}
|
||
//[MenuItem("打包/生成WebGL更新(旧)", false, 302)]
|
||
public static void BuildUpdateWebGL()
|
||
{
|
||
BuildUpdate(BuildTargetGroup.WebGL, BuildTarget.WebGL);
|
||
}
|
||
#endif
|
||
|
||
public static void BuildUpdate(BuildTargetGroup targetGroup, BuildTarget target)
|
||
{
|
||
m_mapResNameDic.Clear();
|
||
if (EditorUserBuildSettings.activeBuildTarget != target)
|
||
{
|
||
EditorUserBuildSettings.SwitchActiveBuildTarget(targetGroup, target);
|
||
}
|
||
if (target == BuildTarget.Android)
|
||
{
|
||
sPlatform = "android";
|
||
}
|
||
else if (target == BuildTarget.iOS)
|
||
{
|
||
sPlatform = "ios";
|
||
}
|
||
else if (target == BuildTarget.WebGL)
|
||
{
|
||
sPlatform = "webgl";
|
||
}
|
||
else
|
||
{
|
||
sPlatform = "pc";
|
||
}
|
||
maps.Clear();
|
||
string outPath = $"{AppDataPath}/StreamingAssets/";
|
||
//if (targetGroup != BuildTargetGroup.Standalone)
|
||
//{
|
||
// outPath = $"{AppDataPath}/StreamingAssets/{sPlatform}/";
|
||
//}
|
||
ClearDir(outPath);
|
||
if (!Directory.Exists(outPath))
|
||
Directory.CreateDirectory(outPath);
|
||
#if !UNITY_WEBGL
|
||
//生成一次临时包,以便正确生成dll
|
||
//string[] SCENES = FindEnabledEditorScenes();
|
||
//保证这个打出来的使用设置好的路径
|
||
//PlayerSettings.SetScriptingDefineSymbolsForGroup(BuildTargetGroup.Standalone, "LOCAL");
|
||
//BuildPipeline.BuildPlayer(SCENES, "Client", target, BuildOptions.None);
|
||
//打入更新的资源,dll
|
||
//dll update
|
||
//BuildAssetsCommand.BuildAndCopyABAOTHotUpdateDlls();
|
||
#endif
|
||
//runtime 编译
|
||
ScriptCompilationSettings scriptCompilationSettings = default;
|
||
scriptCompilationSettings.group = targetGroup;
|
||
scriptCompilationSettings.target = target;
|
||
PlayerSettings.stripEngineCode = true;
|
||
PlayerSettings.allowUnsafeCode = true;
|
||
string BasePath = System.Environment.CurrentDirectory;
|
||
PlayerSettings.SetScriptingBackend(targetGroup, ScriptingImplementation.IL2CPP);
|
||
ScriptCompilationResult scriptCompilationResult = PlayerBuildInterface.CompilePlayerScripts(scriptCompilationSettings, $"{BasePath}/client/");
|
||
|
||
//HybridCLR dll update
|
||
BuildAssetsCommand.BuildAndCopyABAOTHotUpdateDlls();
|
||
|
||
//CopyScriptsFile($"{AppDataPath}/lua/", $"{AppDataPath}/luabytes/", "bytes");
|
||
BuildLuaAB("bs.unity3d");
|
||
//目录列表
|
||
string[] res = HotfixDir;//{ "res", "ui", "luabytes"};//必须是Assets目录下的目录
|
||
//看是否三种资源要分开特殊处理打包
|
||
//美术资源基于目录和预设相关, shader完全打包,ui需要打图集,lua脚本完全打包
|
||
|
||
|
||
//加密dll并把dll打入unity ab 包
|
||
PackDllFile("h.unity3d", $"{AppDataPath}/StreamingAssets/xhotfix/");
|
||
PackDllFile("a.unity3d", $"{AppDataPath}/StreamingAssets/xaot/");
|
||
//打包基础代码入口资源,这个为挂载GameLuncher脚本的预设
|
||
//PackDirFile($"{AppDataPath}/res/base", "res.unity3d");
|
||
AddBuildMap($"res/res.unity3d", "*.*", $"Assets/res/base/");
|
||
//打包shader到单一文件,方便开始一次加载
|
||
if (target == BuildTarget.WebGL)
|
||
PackAllPathFile("res/res_shader.unity3d", $"Assets/res/shader", "Shadervariants");
|
||
else
|
||
PackAllPathFile("res/res_shader.unity3d", $"Assets/res/shader", "Shadervariants_webgl");
|
||
|
||
foreach (string respath in res)
|
||
{
|
||
string tempPath = $"{ AppDataPath}/{respath}/";
|
||
string buildPath = $"Assets/{respath}/";//路径必须以Assets开始
|
||
string[] dirs = Directory.GetDirectories(tempPath, "*", SearchOption.AllDirectories);
|
||
for (int i = 0; i < dirs.Length; i++)
|
||
{
|
||
string path = dirs[i];
|
||
string pathname = dirs[i].Replace(tempPath, string.Empty);
|
||
string name = pathname.Replace('\\', '_').Replace('/', '_');//Lua ab文件使用.替换目录分隔符
|
||
name = name.ToLower() + ".unity3d";
|
||
|
||
AddBuildMap($"{respath}/{name}", "*.*", buildPath + pathname);
|
||
}
|
||
AddBuildMap($"{respath}/{respath}.unity3d", "*.*", $"Assets/{respath}/");
|
||
AssetDatabase.Refresh();
|
||
}
|
||
|
||
//统一打ab包
|
||
BuildPipeline.BuildAssetBundles(outPath, maps.ToArray(), BuildAssetBundleOptions.ChunkBasedCompression, target);
|
||
AssetDatabase.Refresh();
|
||
|
||
//复制res.unity3d基础游戏开始模块到xhotfix目录,此目录为开始强制更新dll即有
|
||
string resfile = $"{outPath}xhotfix/res.unity3d";
|
||
if (File.Exists(resfile))
|
||
File.Delete(resfile);
|
||
string dir = $"{outPath}xhotfix";
|
||
if (!Directory.Exists(dir))
|
||
{
|
||
Directory.CreateDirectory(dir);
|
||
Directory.CreateDirectory($"{outPath}xaot");
|
||
}
|
||
File.Copy($"{outPath}res/res.unity3d", resfile);
|
||
|
||
string platform = "";
|
||
//if (targetGroup != BuildTargetGroup.Standalone)
|
||
// platform = sPlatform;
|
||
//生成更新列表
|
||
m_dicFilelist.Clear();
|
||
m_strDllFileMD5 = "";
|
||
m_strResFileMD5 = "";
|
||
BuildFileIndex(platform);//StreamingAssets 里的除了\\xaot\\ 和 \\xhotfix (dll目录)外所有文件
|
||
//添加dll更新列表
|
||
BuildDllsFileIndex(platform);
|
||
SaveUpdateVer(platform);
|
||
|
||
//复制到目标地址
|
||
//先清理之前的
|
||
if (Directory.Exists(GetUpdatePath()))
|
||
{
|
||
ClearDir($"{GetUpdatePath()}xaot");
|
||
ClearDir($"{GetUpdatePath()}xhotfix");
|
||
ClearDir($"{GetUpdatePath()}arts");//原来的资源目录
|
||
ClearDir($"{GetUpdatePath()}res");
|
||
string[] files = Directory.GetFiles(GetUpdatePath(), "*.*");
|
||
for (int i = 0; i < files.Length; i++)
|
||
{
|
||
File.Delete(files[i]);
|
||
}
|
||
}
|
||
//加密
|
||
string[] EncryptFile = {"a.unity3d", "h.unity3d", "bs.unity3d" };
|
||
foreach (string f in EncryptFile)
|
||
{
|
||
byte[] srcdata = File.ReadAllBytes($"{AppDataPath}/StreamingAssets/{f}");
|
||
byte[] encryptdata = AESManager.Encrypt(srcdata);
|
||
byte[] retdata = AESManager.Decrypt(encryptdata);
|
||
File.WriteAllBytes($"{AppDataPath}/StreamingAssets/{f}", encryptdata);
|
||
}
|
||
|
||
CopyPath(outPath, GetUpdatePath());
|
||
//CopyPath($"{outPath}xaot", $"{GetUpdatePath()}xaot");
|
||
//CopyPath($"{outPath}xhotfix", $"{GetUpdatePath()}xhotfix");
|
||
//File.Copy($"{outPath}dllfiles.txt", $"{GetUpdatePath()}dllfiles.txt", true);
|
||
//File.Copy($"{outPath}updatever.txt", $"{GetUpdatePath()}updatever.txt", true);
|
||
|
||
if (target == BuildTarget.WebGL)
|
||
{//给文件加上md5后缀,以便方便webgl小程序使用UnityWebRequest自动缓存
|
||
foreach (var file in m_dicFilelist)
|
||
{
|
||
File.Move($"{GetUpdatePath()}{file.Key}", $"{GetUpdatePath()}{GetFileMd5Name(file.Key,file.Value)}");//{file.Key}_{file.Value}
|
||
}
|
||
File.Move($"{GetUpdatePath()}dllfiles.txt", $"{GetUpdatePath()}{GetFileMd5Name("dllfiles.txt", m_strDllFileMD5)}");
|
||
File.Move($"{GetUpdatePath()}files.txt", $"{GetUpdatePath()}{GetFileMd5Name("files.txt", m_strResFileMD5)}");
|
||
//File.Move($"{GetUpdatePath()}updatever.txt", $"{GetUpdatePath()}update.ver");
|
||
}
|
||
|
||
//删除临时luabytes文件
|
||
//ClearDir($"{AppDataPath}/luabytes/");
|
||
}
|
||
|
||
static string GetFileMd5Name(string filename, string md5)
|
||
{
|
||
int pos = filename.LastIndexOf('.');
|
||
if (pos != -1)
|
||
{
|
||
return $"{filename.Substring(0, pos)}_{md5}{filename.Substring(pos)}";
|
||
}
|
||
else
|
||
return $"{filename}_{md5}";
|
||
}
|
||
|
||
static void BuildLuaAB(string bundleName)
|
||
{
|
||
string luapath = $"{Application.dataPath}/l/";
|
||
ClearDir(luapath);
|
||
CopyScriptsFile($"{AppDataPath}/../../lua/", luapath, "bytes");
|
||
var files = Directory.GetFiles($"Assets/l", "*.bytes", SearchOption.AllDirectories);
|
||
for (int i = 0; i < files.Length; i++)
|
||
{
|
||
files[i] = files[i].Replace('\\', '/');
|
||
}
|
||
AssetBundleBuild build = new AssetBundleBuild();
|
||
build.assetBundleName = bundleName;
|
||
build.assetNames = files;
|
||
maps.Add(build);
|
||
}
|
||
|
||
public static void ClearDir(string path)
|
||
{
|
||
if (!Directory.Exists(path))
|
||
return;
|
||
|
||
if (Directory.Exists(path))
|
||
{
|
||
Directory.Delete(path, true);
|
||
if(path.EndsWith("/"))
|
||
path = path.Substring(0, path.Length - 1);
|
||
path = path + ".meta";
|
||
if (Directory.Exists(path))
|
||
File.Delete(path);
|
||
}
|
||
}
|
||
|
||
public static void CompileConfig()
|
||
{
|
||
string srcPath, outPath;
|
||
//Config
|
||
UpdateDT = 0;
|
||
srcPath = AppDataPath + "/../../Config/Config/";
|
||
outPath = AppDataPath + "/../../Scripts_c/Base/Config/";
|
||
CompileScript(srcPath, outPath);
|
||
}
|
||
//[MenuItem("打包/完整编译Script", false, 104)]
|
||
public static void CompileScript()
|
||
{
|
||
string srcPath, outPath;
|
||
//Script
|
||
UpdateDT = 0;
|
||
srcPath = AppDataPath + "/../../Scripts/";
|
||
outPath = AppDataPath + "/../../Scripts_c/";
|
||
CompileScript(srcPath, outPath);
|
||
}
|
||
|
||
//[MenuItem("打包/增量编译Script,Config", false, 105)]
|
||
public static void CompileConfigScript()
|
||
{
|
||
//增量更新
|
||
string UpdateFile = AppDataPath + "/../FileTime.txt";
|
||
//获得上回时间
|
||
FileInfo fi = new FileInfo(UpdateFile);
|
||
|
||
if (fi.Exists)
|
||
{
|
||
UpdateDT = fi.LastWriteTime.ToFileTime();
|
||
}
|
||
else
|
||
{
|
||
File.Create(UpdateFile).Dispose();
|
||
UpdateDT = 0;
|
||
}
|
||
string srcPath, outPath;
|
||
//Config
|
||
srcPath = AppDataPath + "/../../Config/Config/";
|
||
outPath = AppDataPath + "/../../Scripts_c/Base/Config/";
|
||
CompileScript(srcPath, outPath);
|
||
//Script
|
||
srcPath = AppDataPath + "/../../Scripts/";
|
||
outPath = AppDataPath + "/../../Scripts_c/";
|
||
CompileScript(srcPath, outPath);
|
||
UpdateDT = DateTime.Now.ToFileTime();
|
||
using (StreamWriter sw = new StreamWriter(UpdateFile))
|
||
{
|
||
sw.Write(UpdateDT);
|
||
}
|
||
}
|
||
//[MenuItem("打包/编译c#底包", false, 104)]
|
||
public static void CompileCSharp()
|
||
{
|
||
//生成最终pc底包
|
||
string[] SCENES = FindEnabledEditorScenes();
|
||
|
||
//保证这个打出来的使用设置好的路径
|
||
//PlayerSettings.SetScriptingDefineSymbolsForGroup(BuildTargetGroup.Standalone, "LOCAL");
|
||
|
||
BuildPipeline.BuildPlayer(SCENES, "../" + OutputName + "/Client.exe", BuildTarget.StandaloneWindows64, BuildOptions.None);
|
||
}
|
||
|
||
//[MenuItem("打包/生成Windows UI 资源ab", false, 102)]
|
||
public static void CreateWindowsUI()
|
||
{
|
||
string OutputPath = AppDataPath + "/../pc/base/ui";
|
||
BuildUI(BuildTarget.StandaloneWindows64, OutputPath);
|
||
}
|
||
//[MenuItem("打包/生成Android UI 资源ab", false, 103)]
|
||
public static void CreateAndroidUI()
|
||
{
|
||
string OutputPath = AppDataPath + "/../android/base/ui";
|
||
BuildUI(BuildTarget.Android, OutputPath);
|
||
}
|
||
|
||
//public static void BuildUI(UnityEditor.BuildTarget target)
|
||
|
||
//[MenuItem("打包/生成测试UI 资源ab", false, 102)]
|
||
public static void BuildUI(UnityEditor.BuildTarget target, string OutputPath = "")
|
||
{
|
||
maps.Clear();
|
||
string resPath = AppDataPath + "/UI/";
|
||
string outPath;
|
||
if (OutputPath == null || OutputPath == "" )
|
||
{
|
||
outPath = AppDataPath + "/../../" + OutputName + "/Client_Data/UI/";
|
||
}
|
||
else
|
||
outPath = OutputPath;
|
||
|
||
if (!Directory.Exists(outPath))
|
||
{
|
||
Directory.CreateDirectory(outPath);
|
||
}
|
||
|
||
string[] dirs = Directory.GetDirectories(resPath, "*", SearchOption.AllDirectories);
|
||
for (int i = 0; i < dirs.Length; i++)
|
||
{
|
||
string path = dirs[i];
|
||
string pathname = dirs[i].Replace(resPath, string.Empty);
|
||
string name = pathname.Replace('\\', '_').Replace('/', '_');
|
||
name = name.ToLower() + ".unity3d";
|
||
|
||
AddBuildMap(name, "*.*", "Assets/UI/" + pathname);//路径必须以Assets开始
|
||
}
|
||
AssetDatabase.Refresh();
|
||
|
||
BuildPipeline.BuildAssetBundles(outPath, maps.ToArray(), BuildAssetBundleOptions.ChunkBasedCompression, target);
|
||
AssetDatabase.Refresh();
|
||
}
|
||
|
||
static void AddBuildMap(string bundleName, string pattern, string path, bool bDirPack = true)
|
||
{
|
||
var files = Array.FindAll(Directory.GetFiles(path, pattern), x => !x.EndsWith(".meta"));
|
||
if (files.Length == 0) return;
|
||
|
||
for (int i = 0; i < files.Length; i++)
|
||
{
|
||
files[i] = files[i].Replace('\\', '/');
|
||
}
|
||
if (bDirPack)
|
||
{
|
||
AssetBundleBuild build = new AssetBundleBuild();
|
||
build.assetBundleName = bundleName;
|
||
build.assetNames = files;
|
||
maps.Add(build);
|
||
}
|
||
else
|
||
{
|
||
string[] singlefile;
|
||
string abname = bundleName.Substring(0, bundleName.IndexOf(".unity3d"));
|
||
abname += "_";
|
||
for (int i = 0; i < files.Length; i++)
|
||
{
|
||
AssetBundleBuild build = new AssetBundleBuild();
|
||
singlefile = new string[] { files[i] };
|
||
build.assetBundleName = abname + Path.GetFileNameWithoutExtension(files[i]).ToLower() + ".unity3d";// bundleName;
|
||
build.assetNames = singlefile;
|
||
maps.Add(build);
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
//[MenuItem("打包/生成Android Scripts资源ab", false, 106)]
|
||
public static void CreateAndroidScriptsAB()
|
||
{
|
||
string OutputPath = AppDataPath + "/../android/base/scripts";
|
||
BuildScriptsAB(BuildTarget.Android, OutputPath);
|
||
}
|
||
|
||
public static void BuildScriptsAB(UnityEditor.BuildTarget target, string OutputPath = "")
|
||
{
|
||
string fixname = "bytes";//"txt"; //"bytes";//
|
||
//Main.lua Agent.lua test.lua + Base目录下文件独一份 其他打包 Base目录下其他文件
|
||
maps.Clear();
|
||
string resPath = AppDataPath + "/../../Scripts_c/Base/";//Base/Config/
|
||
string tempPath = AppDataPath + "/lua/";//
|
||
string outPath;
|
||
if (OutputPath == null || OutputPath == "")
|
||
{
|
||
outPath = AppDataPath + "/../../" + OutputName + "/Client_Data/lua/";
|
||
}
|
||
else
|
||
outPath = OutputPath;
|
||
|
||
if (Directory.Exists(outPath))
|
||
{
|
||
DirectoryInfo dir = new DirectoryInfo(outPath);
|
||
dir.Delete(true);
|
||
}
|
||
|
||
if (!Directory.Exists(outPath))
|
||
{
|
||
Directory.CreateDirectory(outPath);
|
||
}
|
||
|
||
//复制大部分lua 文件到 tempPath
|
||
CopyScriptsFile(resPath, tempPath, fixname);
|
||
|
||
//复制最后三个文件 Main.lua Agent.lua test.lua
|
||
File.Copy(AppDataPath + "/../../Scripts_c/Main.lua", tempPath + "Main." + fixname, true);
|
||
File.Copy(AppDataPath + "/../../Scripts_c/Agent.lua", tempPath + "Agent." + fixname, true);
|
||
File.Copy(AppDataPath + "/../../Scripts_c/Test.lua", tempPath + "Test." + fixname, true);
|
||
string[] dirs = Directory.GetDirectories(tempPath, "*", SearchOption.AllDirectories);
|
||
for (int i = 0; i < dirs.Length; i++)
|
||
{
|
||
string path = dirs[i];
|
||
string pathname = dirs[i].Replace(tempPath, string.Empty);
|
||
string name = pathname.Replace('\\', '_').Replace('/', '_');//Lua ab文件使用.替换目录分隔符
|
||
name = name.ToLower() + ".unity3d";
|
||
|
||
AddBuildMap(name, "*.*", "Assets/lua/" + pathname);//路径必须以Assets开始
|
||
}
|
||
AddBuildMap("bs.unity3d", "*.*", "Assets/lua/");
|
||
AssetDatabase.Refresh();
|
||
|
||
BuildPipeline.BuildAssetBundles(outPath, maps.ToArray(), BuildAssetBundleOptions.ChunkBasedCompression, target);
|
||
AssetDatabase.Refresh();
|
||
|
||
//删除临时lua目录,此处不应删除,以免unity重新生成每个文件的一些索引,导致每次复制进来相同的文件也会产生打包变化
|
||
//DirectoryInfo di = new DirectoryInfo(tempPath);
|
||
//di.Delete(true);
|
||
}
|
||
static void CopyScriptsFile(string srcPath, string outPath, string fixname)
|
||
{
|
||
if (!Directory.Exists(outPath))
|
||
{
|
||
Directory.CreateDirectory(outPath);
|
||
}
|
||
string[] luaPaths = { srcPath };//AppDataPath + "/../../Scripts_c/" };
|
||
|
||
for (int i = 0; i < luaPaths.Length; i++)
|
||
{
|
||
paths.Clear(); files.Clear();
|
||
string luaDataPath = luaPaths[i].ToLower();
|
||
Recursive(luaDataPath);
|
||
int n = 0;
|
||
foreach (string f in files)
|
||
{
|
||
if (f.EndsWith(".meta")) continue;
|
||
string newfile = f.Replace(luaDataPath, "");
|
||
string newpath;
|
||
if (newfile.IndexOf(".lua") < 0)//转2进制文件
|
||
{
|
||
newpath = outPath + newfile + ".bytes";
|
||
}
|
||
else
|
||
{
|
||
newpath = outPath + newfile.Replace(".lua", "." + fixname);
|
||
}
|
||
|
||
string path = Path.GetDirectoryName(newpath);
|
||
if (!Directory.Exists(path)) Directory.CreateDirectory(path);
|
||
|
||
if (File.Exists(newpath))
|
||
{
|
||
File.Delete(newpath);
|
||
}
|
||
|
||
File.Copy(f, newpath, true);
|
||
UpdateProgress(n++, files.Count, newpath);
|
||
}
|
||
}
|
||
EditorUtility.ClearProgressBar();
|
||
}
|
||
|
||
static public void CopyPath(string srcPath, string outPath)
|
||
{
|
||
srcPath = srcPath.Replace("\\", "/"); ;
|
||
UnityEngine.Debug.Log($"{srcPath} => {outPath}");
|
||
if (!Directory.Exists(outPath))
|
||
{
|
||
Directory.CreateDirectory(outPath);
|
||
}
|
||
string[] luaPaths = { srcPath };//AppDataPath + "/../../Scripts_c/" };
|
||
|
||
for (int i = 0; i < luaPaths.Length; i++)
|
||
{
|
||
paths.Clear(); files.Clear();
|
||
string luaDataPath = luaPaths[i];//.ToLower();
|
||
Recursive(luaDataPath);
|
||
int n = 0;
|
||
foreach (string f in files)
|
||
{
|
||
if (f.EndsWith(".meta") || f.EndsWith(".manifest")) continue;
|
||
string newfile = f.Replace(luaDataPath, "");
|
||
string newpath = outPath + newfile;
|
||
string path = Path.GetDirectoryName(newpath);
|
||
if (!Directory.Exists(path))
|
||
{
|
||
UnityEngine.Debug.Log($"{path}");
|
||
Directory.CreateDirectory(path);
|
||
}
|
||
|
||
if (File.Exists(newpath))
|
||
{
|
||
File.Delete(newpath);
|
||
}
|
||
|
||
File.Copy(f, newpath, true);
|
||
UpdateProgress(n++, files.Count, newpath);
|
||
}
|
||
}
|
||
EditorUtility.ClearProgressBar();
|
||
}
|
||
static void CopyScript_c()
|
||
{
|
||
string resPath = AppDataPath + "/../../" + OutputName + "/Client_Data";
|
||
string luaPath = resPath + "/Scripts_c/";
|
||
|
||
//----------复制Lua文件----------------
|
||
if (!Directory.Exists(luaPath))
|
||
{
|
||
Directory.CreateDirectory(luaPath);
|
||
}
|
||
string[] luaPaths = { AppDataPath + "/../../Scripts_c/" };
|
||
|
||
for (int i = 0; i < luaPaths.Length; i++)
|
||
{
|
||
paths.Clear(); files.Clear();
|
||
string luaDataPath = luaPaths[i].ToLower();
|
||
Recursive(luaDataPath);
|
||
int n = 0;
|
||
foreach (string f in files)
|
||
{
|
||
if (f.EndsWith(".meta")) continue;
|
||
string newfile = f.Replace(luaDataPath, "");
|
||
string newpath = luaPath + newfile;
|
||
string path = Path.GetDirectoryName(newpath);
|
||
if (!Directory.Exists(path)) Directory.CreateDirectory(path);
|
||
|
||
if (File.Exists(newpath))
|
||
{
|
||
File.Delete(newpath);
|
||
}
|
||
//if (AppConst.LuaByteMode)
|
||
//{
|
||
// EncodeLuaFile(f, newpath);//luajit
|
||
//}
|
||
//else
|
||
//{
|
||
File.Copy(f, newpath, true);
|
||
//}
|
||
UpdateProgress(n++, files.Count, newpath);
|
||
}
|
||
}
|
||
EditorUtility.ClearProgressBar();
|
||
}
|
||
|
||
|
||
//把原始script脚本和config 转为统一的luac版
|
||
static void CompileScript(string srcPath, string outPath)
|
||
{// AppDataPath appname_Data/ Client_Data/
|
||
//string resPath = AppDataPath + "/../../" + OutputName + "/Client_Data";
|
||
//string luaPath = resPath + "/Scripts_c/";
|
||
|
||
//----------复制Lua文件----------------
|
||
if (!Directory.Exists(outPath))
|
||
{
|
||
Directory.CreateDirectory(outPath);
|
||
}
|
||
string[] luaPaths = { srcPath };//AppDataPath + "/../../Scripts_c/" };
|
||
|
||
for (int i = 0; i < luaPaths.Length; i++)
|
||
{
|
||
paths.Clear(); files.Clear();
|
||
string luaDataPath = luaPaths[i].ToLower();
|
||
Recursive(luaDataPath);
|
||
int n = 0;
|
||
foreach (string f in files)
|
||
{
|
||
if (f.EndsWith(".meta")) continue;
|
||
//时间判定
|
||
|
||
FileInfo fi = new FileInfo(f);
|
||
if (fi.Exists && fi.LastWriteTime.ToFileTime() < UpdateDT && fi.CreationTime.ToFileTime() < UpdateDT)
|
||
{
|
||
continue;
|
||
}
|
||
|
||
string newfile = f.Replace(luaDataPath, "");
|
||
string newpath = outPath + newfile;
|
||
string path = Path.GetDirectoryName(newpath);
|
||
if (!Directory.Exists(path)) Directory.CreateDirectory(path);
|
||
|
||
if (File.Exists(newpath))
|
||
{
|
||
File.Delete(newpath);
|
||
}
|
||
|
||
EncodeLuac(f, newpath);
|
||
UpdateProgress(n++, files.Count, newpath);
|
||
}
|
||
}
|
||
EditorUtility.ClearProgressBar();
|
||
}
|
||
|
||
private static string[] FindEnabledEditorScenes()
|
||
{
|
||
List<string> EditorScenes = new List<string>();
|
||
foreach (EditorBuildSettingsScene scene in EditorBuildSettings.scenes)
|
||
{
|
||
if (!scene.enabled) continue;
|
||
EditorScenes.Add(scene.path);
|
||
}
|
||
return EditorScenes.ToArray();
|
||
}
|
||
|
||
static void PackDirFile(string path, string bundleName)
|
||
{
|
||
var filelist = Directory.GetFiles(path);
|
||
AssetBundleBuild build = new AssetBundleBuild();
|
||
build.assetBundleName = bundleName;
|
||
build.assetNames = filelist;
|
||
maps.Add(build);
|
||
}
|
||
|
||
static private List<string> GetAllDependencies(string mainAssetPath)
|
||
{
|
||
List<string> result = new List<string>();
|
||
string[] depends = AssetDatabase.GetDependencies(mainAssetPath, true);
|
||
foreach (string assetPath in depends)
|
||
{
|
||
// 注意:排除主资源对象
|
||
if (assetPath != mainAssetPath)
|
||
result.Add(assetPath);
|
||
}
|
||
return result;
|
||
}
|
||
|
||
static void PackDllFile(string bundleName, string dllpath)
|
||
{
|
||
//string resPath = $"{AppDataPath}/StreamingAssets/xhotfix/";
|
||
List<string> files = new List<string>();
|
||
files.AddRange(Directory.GetFiles(dllpath));
|
||
//files.AddRange(Directory.GetFiles($"{AppDataPath}/StreamingAssets/xaot/"));
|
||
//int nPos = resPath.IndexOf("assets/StreamingAssets/", StringComparison.OrdinalIgnoreCase);
|
||
int nPos = bundleName.IndexOf('.');
|
||
//加密
|
||
List<string> DllFiles = new List<string>();
|
||
//byte[] buffer, aesdata;
|
||
string dirname = bundleName.Substring(0, nPos);
|
||
string temppath = $"{AppDataPath}/{dirname}";
|
||
if (!Directory.Exists(temppath))
|
||
{
|
||
Directory.CreateDirectory(temppath);
|
||
}
|
||
else
|
||
{
|
||
ClearDir(temppath);
|
||
}
|
||
foreach (var file in files)
|
||
{
|
||
if (file.EndsWith(".dll"))
|
||
{
|
||
//buffer = File.ReadAllBytes(file);
|
||
//aesdata = buffer;// AESManager.Encrypt(buffer);
|
||
string name = Path.GetFileNameWithoutExtension(file);//file.Replace(".dll", ".bytes");
|
||
string filepath = $"{temppath}/{name}.bytes";
|
||
//File.WriteAllBytes(filepath, aesdata);
|
||
File.Move(file, filepath);
|
||
name = $"assets/{dirname}/{name}.bytes";
|
||
DllFiles.Add(name);
|
||
}
|
||
}
|
||
|
||
AssetBundleBuild build = new AssetBundleBuild();
|
||
build.assetBundleName = bundleName;
|
||
build.assetNames = DllFiles.ToArray();// filelist;
|
||
maps.Add(build);
|
||
}
|
||
static void PackAllPathFile(string bundleName, string path, string nopath = null)
|
||
{
|
||
paths.Clear(); files.Clear();
|
||
Recursive(path);
|
||
|
||
string[] filelist = files.ToArray();
|
||
|
||
List<string> total = new List<string>(filelist);
|
||
foreach (var file in filelist)
|
||
{
|
||
if (!m_mapResNameDic.ContainsKey(file))
|
||
{
|
||
m_mapResNameDic[file] = "1";
|
||
}
|
||
}
|
||
//依赖文件
|
||
foreach (var file in filelist)
|
||
{
|
||
List<string> depends = GetAllDependencies(file);
|
||
foreach (var fd in depends)
|
||
{
|
||
if (fd.EndsWith(".hlsl"))
|
||
continue;
|
||
if (!m_mapResNameDic.ContainsKey(fd))
|
||
{
|
||
m_mapResNameDic[fd] = "1";
|
||
total.Add(fd);
|
||
}
|
||
}
|
||
}
|
||
|
||
if (nopath != null)
|
||
{
|
||
for (int i = total.Count - 1; i >= 0; i--)
|
||
{
|
||
var file = total[i];
|
||
if (file.Contains(nopath))
|
||
{
|
||
string[] pathParts = file.Replace('\\','/').Split('/');
|
||
foreach (var p in pathParts)
|
||
{
|
||
if(string.Equals(p, nopath, StringComparison.OrdinalIgnoreCase))//if (p == nopath)
|
||
{
|
||
//删除不合理的
|
||
total.RemoveAt(i);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
AssetBundleBuild build = new AssetBundleBuild();
|
||
build.assetBundleName = bundleName;
|
||
build.assetNames = total.ToArray();// filelist;
|
||
maps.Add(build);
|
||
UnityEngine.Debug.Log($"ab:{bundleName} has {filelist.Length} file");
|
||
}
|
||
|
||
//生成更新文件
|
||
static void BuildDllsFileIndex(string dir = "")
|
||
{
|
||
List<string> files = new List<string>();
|
||
string resPath = $"{AppDataPath}/StreamingAssets/{dir}/";
|
||
///----------------------创建文件列表-----------------------
|
||
string newFilePath = resPath + "dllfiles.txt";
|
||
if (File.Exists(newFilePath)) File.Delete(newFilePath);
|
||
files.AddRange(Directory.GetFiles($"{resPath}xaot/"));
|
||
files.AddRange(Directory.GetFiles($"{resPath}xhotfix/"));
|
||
FileStream fs = new FileStream(newFilePath, FileMode.CreateNew);
|
||
StreamWriter sw = new StreamWriter(fs);
|
||
foreach (var dllfile in files)
|
||
{
|
||
if (!dllfile.EndsWith(".dll") && !dllfile.EndsWith(".unity3d"))
|
||
continue;
|
||
long size = 0;
|
||
string md5 = GetFixedMd5(md5file(dllfile, out size));
|
||
string value = dllfile.Replace(resPath, string.Empty);
|
||
sw.WriteLine($"{{{value},{md5},{size}}}");
|
||
m_dicFilelist[value] = md5;
|
||
}
|
||
sw.Close(); fs.Close();
|
||
|
||
}
|
||
static string GetFixedMd5(string md5)
|
||
{
|
||
return md5.Substring(0, 8);//只取8位
|
||
}
|
||
static void BuildFileIndex(string dir = "") {
|
||
string resPath;
|
||
if (dir != "")
|
||
{
|
||
resPath = dir;
|
||
}
|
||
else
|
||
{
|
||
resPath = $"{AppDataPath}/StreamingAssets/";
|
||
}
|
||
///----------------------创建文件列表-----------------------
|
||
string newFilePath = resPath + "files.txt";
|
||
if (File.Exists(newFilePath)) File.Delete(newFilePath);
|
||
|
||
paths.Clear(); files.Clear();
|
||
Recursive(resPath);//此处去掉了dll热更相关的目录
|
||
|
||
FileStream fs = new FileStream(newFilePath, FileMode.CreateNew);
|
||
StreamWriter sw = new StreamWriter(fs);
|
||
for (int i = 0; i < files.Count; i++) {
|
||
string file = files[i];
|
||
string ext = Path.GetExtension(file);
|
||
if (file.EndsWith(".meta") || file.Contains(".DS_Store") || file.EndsWith(".manifest")) continue;
|
||
|
||
long size = 0;
|
||
string md5 = GetFixedMd5(md5file(file, out size));
|
||
string value = file.Replace(resPath, string.Empty);
|
||
sw.WriteLine($"{{{value},{md5},{size}}}");
|
||
m_dicFilelist[value] = md5;
|
||
}
|
||
sw.Close(); fs.Close();
|
||
|
||
//版本文件ver.txt
|
||
//string vermd5 = md5file(newFilePath);
|
||
//File.WriteAllText($"{resPath}/updatever.txt", vermd5);
|
||
}
|
||
|
||
static void SaveUpdateVer(string dir = "")
|
||
{
|
||
string resPath;
|
||
if (dir != "")
|
||
{
|
||
resPath = dir;
|
||
}
|
||
else
|
||
{
|
||
resPath = $"{AppDataPath}/StreamingAssets/";
|
||
}
|
||
string resfilelist = $"{resPath}files.txt";
|
||
string dllfilelist = $"{resPath}dllfiles.txt";
|
||
if (!File.Exists(dllfilelist) || !File.Exists(resfilelist))
|
||
{
|
||
UnityEngine.Debug.LogError("需要先生成更新列表文件");
|
||
return;//需要先
|
||
}
|
||
string resmd5 = md5file(resfilelist);
|
||
string dllmd5 = md5file(dllfilelist);
|
||
m_strResFileMD5 = GetFixedMd5(resmd5);
|
||
m_strDllFileMD5 = GetFixedMd5(dllmd5);
|
||
File.WriteAllText($"{resPath}/updatever.txt", $"{m_strDllFileMD5},{m_strResFileMD5}");
|
||
}
|
||
/// <summary>
|
||
/// 数据目录
|
||
/// </summary>
|
||
static string AppDataPath {
|
||
get { return Application.dataPath.ToLower(); }//Assets目录
|
||
}
|
||
|
||
/// <summary>
|
||
/// 遍历目录及其子目录
|
||
/// </summary>
|
||
static void Recursive(string path) {
|
||
if (path.Contains("\\xaot\\")|| path.Contains("\\xhotfix\\"))
|
||
{//忽略
|
||
return;
|
||
}
|
||
string[] names = Directory.GetFiles(path);
|
||
string[] dirs = Directory.GetDirectories(path);
|
||
foreach (string filename in names) {
|
||
string ext = Path.GetExtension(filename);
|
||
if (ext.Equals(".meta")) continue;
|
||
if (ext.Equals(".hlsl")) continue;
|
||
files.Add(filename.Replace('\\', '/'));
|
||
}
|
||
foreach (string dir in dirs) {
|
||
if (dir.IndexOf(".svn") >= 0)
|
||
{
|
||
continue;
|
||
}
|
||
paths.Add(dir.Replace('\\', '/'));
|
||
Recursive(dir);
|
||
}
|
||
}
|
||
|
||
static void UpdateProgress(int progress, int progressMax, string desc) {
|
||
string title = "Processing...[" + progress + " - " + progressMax + "]";
|
||
float value = (float)progress / (float)progressMax;
|
||
EditorUtility.DisplayProgressBar(title, desc, value);
|
||
}
|
||
|
||
public static void EncodeLuajit(string srcFile, string outFile) {
|
||
if (!srcFile.ToLower().EndsWith(".lua")) {
|
||
File.Copy(srcFile, outFile, true);
|
||
return;
|
||
}
|
||
bool isWin = true;
|
||
string luaexe = string.Empty;
|
||
string args = string.Empty;
|
||
string exedir = string.Empty;
|
||
string currDir = Directory.GetCurrentDirectory();
|
||
if (Application.platform == RuntimePlatform.WindowsEditor) {
|
||
isWin = true;
|
||
luaexe = "luajit.exe";
|
||
args = "-b -g " + srcFile + " " + outFile;
|
||
exedir = AppDataPath.Replace("assets", "") + "LuaEncoder/luajit/";
|
||
} else if (Application.platform == RuntimePlatform.OSXEditor) {
|
||
isWin = false;
|
||
luaexe = "./luajit";
|
||
args = "-b -g " + srcFile + " " + outFile;
|
||
exedir = AppDataPath.Replace("assets", "") + "LuaEncoder/luajit_mac/";
|
||
}
|
||
Directory.SetCurrentDirectory(exedir);
|
||
ProcessStartInfo info = new ProcessStartInfo();
|
||
info.FileName = luaexe;
|
||
info.Arguments = args;
|
||
info.WindowStyle = ProcessWindowStyle.Hidden;
|
||
info.UseShellExecute = isWin;
|
||
info.ErrorDialog = true;
|
||
UnityEngine.Debug.Log(info.FileName + " " + info.Arguments);
|
||
|
||
Process pro = Process.Start(info);
|
||
pro.WaitForExit();
|
||
Directory.SetCurrentDirectory(currDir);
|
||
}
|
||
|
||
public static void EncodeLuac(string srcFile, string outFile)
|
||
{
|
||
if (!srcFile.ToLower().EndsWith(".lua"))
|
||
{
|
||
File.Copy(srcFile, outFile, true);
|
||
return;
|
||
}
|
||
bool isWin = true;
|
||
string luaexe = string.Empty;
|
||
string args = string.Empty;
|
||
string exedir = string.Empty;
|
||
string currDir = Directory.GetCurrentDirectory();
|
||
if (Application.platform == RuntimePlatform.WindowsEditor)
|
||
{
|
||
isWin = true;
|
||
luaexe = "luac53_64_compatible.exe";//for /r %%v in (*.lua) do luac -o %%v %%v
|
||
args = " -o " + outFile + " -s " + srcFile;
|
||
exedir = AppDataPath.Replace("assets", "") + "LuaEncoder/luac/";
|
||
}
|
||
|
||
Directory.SetCurrentDirectory(exedir);
|
||
ProcessStartInfo info = new ProcessStartInfo();
|
||
info.FileName = luaexe;
|
||
info.Arguments = args;
|
||
info.WindowStyle = ProcessWindowStyle.Hidden;
|
||
info.UseShellExecute = isWin;
|
||
info.ErrorDialog = true;
|
||
//UnityEngine.Debug.Log(info.FileName + " " + info.Arguments);
|
||
|
||
Process pro = Process.Start(info);
|
||
pro.WaitForExit();
|
||
Directory.SetCurrentDirectory(currDir);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 打安卓包
|
||
/// </summary>
|
||
public static void BuildPlayer()
|
||
{
|
||
List<string> levels = new List<string>();
|
||
foreach (EditorBuildSettingsScene scene in EditorBuildSettings.scenes)
|
||
{
|
||
if (!scene.enabled) continue;
|
||
levels.Add(scene.path);
|
||
}
|
||
|
||
string[] args = Environment.GetCommandLineArgs();
|
||
//File.WriteAllLines("log0.txt", args);
|
||
|
||
List<string> currentArgs = new List<string>();
|
||
bool flag = false;
|
||
for (int i = 0; i < args.Length; i++)
|
||
{
|
||
if (args[i] == "-executeMethod")
|
||
{
|
||
flag = true;
|
||
continue;
|
||
}
|
||
else
|
||
{
|
||
if (args[i].StartsWith("-"))
|
||
{
|
||
flag = false;
|
||
}
|
||
}
|
||
if (flag)
|
||
{
|
||
currentArgs.Add(args[i]);
|
||
}
|
||
}
|
||
|
||
UnityEditor.PlayerSettings.productName = "G2Online";
|
||
UnityEditor.PlayerSettings.applicationIdentifier = "com.gygame.g2Online";
|
||
|
||
string timeFlag = currentArgs.Count < 2 ? DateTime.Now.ToString("YYYYMMddHHmmss") : currentArgs[1];
|
||
bool isDebugAPK = currentArgs.Count > 2 ? (int.Parse(currentArgs[2]) > 0) : false;//开发者调试模式包
|
||
EditorUserBuildSettings.SwitchActiveBuildTarget(BuildTargetGroup.Android, BuildTarget.Android);
|
||
UnityEditor.PlayerSettings.SetScriptingBackend(BuildTargetGroup.Android, ScriptingImplementation.IL2CPP);
|
||
UnityEditor.PlayerSettings.Android.targetArchitectures = AndroidArchitecture.ARM64;
|
||
|
||
BuildOptions options = BuildOptions.CompressWithLz4;
|
||
if (isDebugAPK)
|
||
{
|
||
options = BuildOptions.CompressWithLz4 | BuildOptions.AllowDebugging | BuildOptions.WaitForPlayerConnection | BuildOptions.Development | BuildOptions.ConnectWithProfiler;
|
||
}
|
||
UnityEditor.Build.Reporting.BuildReport report = BuildPipeline.BuildPlayer(levels.ToArray(), string.Format("Client_{0}.apk", timeFlag), BuildTarget.Android, options);
|
||
}
|
||
|
||
public static void BuildAndroidLocal()
|
||
{
|
||
List<string> levels = new List<string>();
|
||
foreach (EditorBuildSettingsScene scene in EditorBuildSettings.scenes)
|
||
{
|
||
if (!scene.enabled) continue;
|
||
levels.Add(scene.path);
|
||
}
|
||
|
||
string[] args = Environment.GetCommandLineArgs();
|
||
//File.WriteAllLines("log0.txt", args);
|
||
|
||
List<string> currentArgs = new List<string>();
|
||
bool flag = false;
|
||
for (int i = 0; i < args.Length; i++)
|
||
{
|
||
if (args[i] == "-executeMethod")
|
||
{
|
||
flag = true;
|
||
continue;
|
||
}
|
||
else
|
||
{
|
||
if (args[i].StartsWith("-"))
|
||
{
|
||
flag = false;
|
||
}
|
||
}
|
||
if (flag)
|
||
{
|
||
currentArgs.Add(args[i]);
|
||
}
|
||
}
|
||
|
||
UnityEditor.PlayerSettings.productName = "G2Local";
|
||
UnityEditor.PlayerSettings.applicationIdentifier = "com.gygame.g2local";
|
||
|
||
string timeFlag = currentArgs.Count < 2 ? DateTime.Now.ToString("YYYYMMddHHmmss") : currentArgs[1];
|
||
bool isDebugAPK = currentArgs.Count > 2 ? (int.Parse(currentArgs[2]) > 0) : false;//开发者调试模式包
|
||
EditorUserBuildSettings.SwitchActiveBuildTarget(BuildTargetGroup.Android, BuildTarget.Android);
|
||
UnityEditor.PlayerSettings.SetScriptingBackend(BuildTargetGroup.Android, ScriptingImplementation.IL2CPP);
|
||
UnityEditor.PlayerSettings.Android.targetArchitectures = AndroidArchitecture.ARM64;
|
||
|
||
BuildOptions options = BuildOptions.CompressWithLz4;
|
||
if (isDebugAPK)
|
||
{
|
||
options = BuildOptions.CompressWithLz4 | BuildOptions.AllowDebugging | BuildOptions.WaitForPlayerConnection | BuildOptions.Development | BuildOptions.ConnectWithProfiler;
|
||
}
|
||
UnityEditor.Build.Reporting.BuildReport report = BuildPipeline.BuildPlayer(levels.ToArray(), string.Format("Client_{0}.apk", timeFlag), BuildTarget.Android, options);
|
||
}
|
||
|
||
public static void BuildAndroidAPK()
|
||
{
|
||
List<string> levels = new List<string>();
|
||
foreach (EditorBuildSettingsScene scene in EditorBuildSettings.scenes)
|
||
{
|
||
if (!scene.enabled) continue;
|
||
levels.Add(scene.path);
|
||
}
|
||
|
||
string[] args = Environment.GetCommandLineArgs();
|
||
//File.WriteAllLines("log0.txt", args);
|
||
List<string> currentArgs = new List<string>();
|
||
bool flag = false;
|
||
for (int i = 0; i < args.Length; i++)
|
||
{
|
||
if (args[i] == "-executeMethod")
|
||
{
|
||
flag = true;
|
||
continue;
|
||
}
|
||
else
|
||
{
|
||
if (args[i].StartsWith("-"))
|
||
{
|
||
flag = false;
|
||
}
|
||
}
|
||
if (flag)
|
||
{
|
||
currentArgs.Add(args[i]);
|
||
}
|
||
}
|
||
string packFileName = currentArgs.Count < 2 ? UnityEditor.PlayerSettings.productName : currentArgs[1];
|
||
|
||
if (!string.IsNullOrEmpty(packFileName) && packFileName.ToLower().IndexOf("online")>-1)
|
||
{
|
||
UnityEditor.PlayerSettings.productName = "G2Online";
|
||
UnityEditor.PlayerSettings.applicationIdentifier = "com.gygame.g2Online";
|
||
}
|
||
else
|
||
{
|
||
UnityEditor.PlayerSettings.productName = "G2Local";
|
||
UnityEditor.PlayerSettings.applicationIdentifier = "com.gygame.g2local";
|
||
}
|
||
|
||
bool isDebugAPK = currentArgs.Count > 2 ? (int.Parse(currentArgs[2]) > 0) : false;//开发者调试模式包
|
||
EditorUserBuildSettings.SwitchActiveBuildTarget(BuildTargetGroup.Android, BuildTarget.Android);
|
||
UnityEditor.PlayerSettings.SetScriptingBackend(BuildTargetGroup.Android, ScriptingImplementation.IL2CPP);
|
||
UnityEditor.PlayerSettings.Android.targetArchitectures = AndroidArchitecture.ARM64;
|
||
|
||
BuildOptions options = BuildOptions.CompressWithLz4;
|
||
if (isDebugAPK)
|
||
{
|
||
options = BuildOptions.CompressWithLz4 | BuildOptions.AllowDebugging | BuildOptions.WaitForPlayerConnection | BuildOptions.Development | BuildOptions.ConnectWithProfiler;
|
||
}
|
||
UnityEditor.Build.Reporting.BuildReport report = BuildPipeline.BuildPlayer(levels.ToArray(), string.Format("{0}.apk", packFileName), BuildTarget.Android, options);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 打包UWA包
|
||
/// </summary>
|
||
public static void BuildAndroidUWA()
|
||
{
|
||
List<string> levels = new List<string>();
|
||
foreach (EditorBuildSettingsScene scene in EditorBuildSettings.scenes)
|
||
{
|
||
if (!scene.enabled) continue;
|
||
levels.Add(scene.path);
|
||
}
|
||
|
||
string[] args = Environment.GetCommandLineArgs();
|
||
List<string> currentArgs = new List<string>();
|
||
bool flag = false;
|
||
for (int i = 0; i < args.Length; i++)
|
||
{
|
||
if (args[i] == "-executeMethod")
|
||
{
|
||
flag = true;
|
||
continue;
|
||
}
|
||
else
|
||
{
|
||
if (args[i].StartsWith("-"))
|
||
{
|
||
flag = false;
|
||
}
|
||
}
|
||
if (flag)
|
||
{
|
||
currentArgs.Add(args[i]);
|
||
}
|
||
}
|
||
|
||
string timeFlag = currentArgs.Count < 2 ? DateTime.Now.ToString("YYYYMMddHHmmss") : currentArgs[1];
|
||
EditorUserBuildSettings.SwitchActiveBuildTarget(BuildTargetGroup.Android, BuildTarget.Android);
|
||
BuildOptions options = BuildOptions.CompressWithLz4 | BuildOptions.AllowDebugging | BuildOptions.Development;
|
||
|
||
UnityEditor.PlayerSettings.productName = "G2";
|
||
UnityEditor.PlayerSettings.applicationIdentifier = "com.gygame.g2";
|
||
|
||
string defs = PlayerSettings.GetScriptingDefineSymbolsForGroup(BuildTargetGroup.Android);
|
||
PlayerSettings.SetScriptingDefineSymbolsForGroup(BuildTargetGroup.Android, "UWA;INSTANCE_OC");
|
||
UnityEditor.PlayerSettings.SetScriptingBackend(BuildTargetGroup.Android, ScriptingImplementation.Mono2x);
|
||
UnityEditor.PlayerSettings.Android.targetArchitectures = AndroidArchitecture.ARMv7;
|
||
|
||
UnityEditor.Build.Reporting.BuildReport report = BuildPipeline.BuildPlayer(levels.ToArray(), string.Format("Client_{0}.apk", timeFlag), BuildTarget.Android, options);
|
||
|
||
PlayerSettings.SetScriptingDefineSymbolsForGroup(BuildTargetGroup.Android, defs);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 打包UWA包
|
||
/// </summary>
|
||
public static void BuildAndroidUWAAUTO()
|
||
{
|
||
List<string> levels = new List<string>();
|
||
foreach (EditorBuildSettingsScene scene in EditorBuildSettings.scenes)
|
||
{
|
||
if (!scene.enabled) continue;
|
||
levels.Add(scene.path);
|
||
}
|
||
|
||
string[] args = Environment.GetCommandLineArgs();
|
||
List<string> currentArgs = new List<string>();
|
||
bool flag = false;
|
||
for (int i = 0; i < args.Length; i++)
|
||
{
|
||
if (args[i] == "-executeMethod")
|
||
{
|
||
flag = true;
|
||
continue;
|
||
}
|
||
else
|
||
{
|
||
if (args[i].StartsWith("-"))
|
||
{
|
||
flag = false;
|
||
}
|
||
}
|
||
if (flag)
|
||
{
|
||
currentArgs.Add(args[i]);
|
||
}
|
||
}
|
||
|
||
string timeFlag = currentArgs.Count < 2 ? DateTime.Now.ToString("YYYYMMddHHmmss") : currentArgs[1];
|
||
EditorUserBuildSettings.SwitchActiveBuildTarget(BuildTargetGroup.Android, BuildTarget.Android);
|
||
BuildOptions options = BuildOptions.CompressWithLz4 | BuildOptions.AllowDebugging | BuildOptions.Development;
|
||
|
||
UnityEditor.PlayerSettings.productName = "G2";
|
||
UnityEditor.PlayerSettings.applicationIdentifier = "com.gygame.g2";
|
||
|
||
string defs = PlayerSettings.GetScriptingDefineSymbolsForGroup(BuildTargetGroup.Android);
|
||
PlayerSettings.SetScriptingDefineSymbolsForGroup(BuildTargetGroup.Android, "UWA_AUTO");
|
||
UnityEditor.PlayerSettings.SetScriptingBackend(BuildTargetGroup.Android, ScriptingImplementation.Mono2x);
|
||
UnityEditor.PlayerSettings.Android.targetArchitectures = AndroidArchitecture.ARMv7;
|
||
|
||
UnityEditor.Build.Reporting.BuildReport report = BuildPipeline.BuildPlayer(levels.ToArray(), string.Format("Client_{0}.apk", timeFlag), BuildTarget.Android, options);
|
||
|
||
PlayerSettings.SetScriptingDefineSymbolsForGroup(BuildTargetGroup.Android, defs);
|
||
}
|
||
|
||
//在这里找出你当前工程所有的场景文件,假设你只想把部分的scene文件打包 那么这里可以写你的条件判断 总之返回一个字符串数组。
|
||
static string[] GetBuildScenes()
|
||
{
|
||
List<string> names = new List<string>();
|
||
|
||
foreach (EditorBuildSettingsScene e in EditorBuildSettings.scenes)
|
||
{
|
||
if (e == null)
|
||
continue;
|
||
if (e.enabled)
|
||
names.Add(e.path);
|
||
}
|
||
return names.ToArray();
|
||
}
|
||
//得到项目的名称
|
||
public static string projectName
|
||
{
|
||
get
|
||
{
|
||
//在这里分析shell传入的参数, 还记得上面我们说的哪个 project-$1 这个参数吗?
|
||
//这里遍历所有参数,找到 project开头的参数, 然后把-符号 后面的字符串返回,
|
||
//这个字符串就是 91 了。。
|
||
foreach (string arg in System.Environment.GetCommandLineArgs())
|
||
{
|
||
if (arg.StartsWith("project"))
|
||
{
|
||
return arg.Split("-"[0])[1];
|
||
}
|
||
}
|
||
return "test";
|
||
}
|
||
}
|
||
//[MenuItem("打包/生成ios工程", false, 106)]
|
||
//shell脚本直接调用这个静态方法
|
||
public static void BuildForIOS()
|
||
{
|
||
//这里就是构建xcode工程的核心方法了,
|
||
//参数1 需要打包的所有场景
|
||
//参数2 需要打包的名子, 这里取到的就是 shell传进来的字符串 91
|
||
//参数3 打包平台
|
||
BuildOptions options = BuildOptions.None;//BuildOptions.CompressWithLz4;
|
||
BuildPipeline.BuildPlayer(GetBuildScenes(), "G2", BuildTarget.iOS, options);
|
||
}
|
||
|
||
}
|