1428 lines
53 KiB
C#
1428 lines
53 KiB
C#
using System;
|
||
using System.Collections;
|
||
using System.Collections.Generic;
|
||
using System.IO;
|
||
using System.Text;
|
||
using UnityEditor;
|
||
using UnityEditor.Build.Reporting;
|
||
using UnityEngine;
|
||
using UnityEditor.Build.Player;
|
||
//using Newtonsoft.Json;
|
||
using System.Runtime.CompilerServices;
|
||
using System.Linq;
|
||
using UnityEditor.Build.Content;
|
||
|
||
|
||
|
||
//using Base;
|
||
|
||
#if UNITY_EDITOR
|
||
using HybridCLR.Editor;
|
||
using HybridCLR.Editor.Commands;
|
||
#endif
|
||
|
||
public class HybridCLRBuild
|
||
{
|
||
static string sPlatform = "pc";
|
||
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 Dictionary<string, string> m_dicFilelist = new Dictionary<string, string>();
|
||
static string m_strListFileMD5;
|
||
static string m_strDllFileMD5;
|
||
static string m_strResFileMD5;
|
||
|
||
static string m_strOldVersion = "";
|
||
static string m_aesPassword = "#@pOrhgf2026091211325890vciu%c-X";
|
||
|
||
#if ENV_INTRANET
|
||
public static string UpdatePath = $"\\\\{GetLocal()}\\g\\App\\CDNRes\\public\\{BuildSettings.GameResVersion}\\";
|
||
#elif ENV_EXTRANET || ENV_PRODUCTION
|
||
public static string UpdatePath = Application.dataPath + "/../../../CDNRes/public/"+ BuildSettings.GameResVersion + "/";
|
||
#else
|
||
public static string UpdatePath = $"../XDevNode/Data/";//XWorld/
|
||
#endif
|
||
|
||
public static string GetUpdatePath()
|
||
{
|
||
return UpdatePath;
|
||
}
|
||
|
||
static string AppDataPath
|
||
{
|
||
get { return Application.dataPath.ToLower(); }//Assets目录
|
||
}
|
||
|
||
public static byte[] ChangeBytes(byte[] src, byte xor = 0xb4)
|
||
{
|
||
for (int i = 0; i < src.Length; i++)
|
||
{
|
||
src[i] = (byte)(src[i] ^ xor);
|
||
}
|
||
return src;
|
||
}
|
||
|
||
private static bool IsUnityAssetBundleData(byte[] data)
|
||
{
|
||
if (data == null || data.Length < 5)
|
||
return false;
|
||
|
||
return data[0] == (byte)'U'
|
||
&& data[1] == (byte)'n'
|
||
&& data[2] == (byte)'i'
|
||
&& data[3] == (byte)'t'
|
||
&& data[4] == (byte)'y';
|
||
}
|
||
|
||
private static string GetDataHead(byte[] data)
|
||
{
|
||
if (data == null || data.Length == 0)
|
||
return "<empty>";
|
||
|
||
int len = Math.Min(16, data.Length);
|
||
StringBuilder sb = new StringBuilder(len * 3);
|
||
for (int i = 0; i < len; i++)
|
||
{
|
||
byte b = data[i];
|
||
sb.Append(b >= 32 && b <= 126 ? (char)b : '.');
|
||
}
|
||
sb.Append(" / ");
|
||
for (int i = 0; i < len; i++)
|
||
{
|
||
if (i > 0)
|
||
sb.Append(' ');
|
||
sb.Append(data[i].ToString("X2"));
|
||
}
|
||
return sb.ToString();
|
||
}
|
||
|
||
public static void ClearDir(string path, bool bDeleteSelf = false)
|
||
{
|
||
if (!Directory.Exists(path))
|
||
return;
|
||
if (!bDeleteSelf)
|
||
{
|
||
System.IO.DirectoryInfo di = new DirectoryInfo(path);
|
||
foreach (FileInfo file in di.EnumerateFiles())
|
||
{
|
||
file.Delete();
|
||
}
|
||
foreach (DirectoryInfo dir in di.EnumerateDirectories())
|
||
{
|
||
dir.Delete(true);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (Directory.Exists(path))
|
||
{
|
||
Directory.Delete(path, true);
|
||
path = path + ".meta";
|
||
File.Delete(path);
|
||
}
|
||
}
|
||
}
|
||
|
||
static string packageVer = "G7";
|
||
//static URLConfig uconfig;
|
||
public static string GetLocal()
|
||
{
|
||
//if (uconfig == null)
|
||
//{
|
||
// string configPath = Application.streamingAssetsPath + "/config.json";
|
||
// string configJson = File.ReadAllText(configPath);
|
||
// uconfig = JsonConvert.DeserializeObject<URLConfig>(configJson);
|
||
//}
|
||
//if (uconfig != null)
|
||
//{
|
||
// return uconfig?.local;
|
||
//}
|
||
return "127.0.0.1";
|
||
}
|
||
|
||
|
||
[MenuItem("HybridCLR/测试IP", false, 2001)]
|
||
public static void TestDebug()
|
||
{
|
||
Debug.Log($"更新地址:{GetUpdatePath()}");
|
||
}
|
||
|
||
//需要导出的aot文件,运行的时候会全部更新
|
||
public static List<string> AOTMetaAssemblyNames { get; } = new List<string>()
|
||
{
|
||
"mscorlib.dll",
|
||
"System.dll",
|
||
"System.Core.dll",
|
||
"ZString.dll",//解决泛型问题
|
||
"System.Runtime.CompilerServices.Unsafe.dll",
|
||
"UnityEngine.AndroidJNIModule.dll"
|
||
//"UnityEngine.UnityWebRequestModule.dll",
|
||
};
|
||
|
||
[MenuItem("HybridCLR/GenAll(+xlua)", false, 2000)]
|
||
public static void GenLuaAndHybridCLRAll()
|
||
{
|
||
//xlua wrap文件
|
||
CSObjectWrapEditor.Generator.GenAll();
|
||
//hybricCLR 生成
|
||
PrebuildCommand.GenerateAll();
|
||
//CSObjectWrapEditor.Generator.ClearAll();
|
||
}
|
||
|
||
[MenuItem("HybridCLR/更新dll(PC)", false, 2001)]
|
||
public static void BuildDllUpdatePC()
|
||
{
|
||
BuildHotFix(BuildTargetGroup.Standalone, BuildTarget.StandaloneWindows64, "Client/Client.exe");
|
||
}
|
||
[MenuItem("HybridCLR/更新dll(Android)", false, 2002)]
|
||
public static void BuildDllUpdateAndroid()
|
||
{
|
||
BuildHotFix(BuildTargetGroup.Android, BuildTarget.Android);
|
||
}
|
||
//[MenuItem("HybridCLR/更新dll(iOS)", false, 2003)]
|
||
//public static void BuildDllUpdateiOS()
|
||
//{
|
||
// BuildHotFix(BuildTargetGroup.iOS, BuildTarget.iOS);
|
||
//}
|
||
[MenuItem("HybridCLR/更新dll(iOS)", false, 2003)]
|
||
public static void BuildDllUpdateiOS()
|
||
{
|
||
BuildHotFix(BuildTargetGroup.iOS, BuildTarget.iOS);
|
||
}
|
||
[MenuItem("HybridCLR/更新dll(WebGL)", false, 2004)]
|
||
public static void BuildDllUpdateWebGL()
|
||
{
|
||
BuildHotFix(BuildTargetGroup.WebGL, BuildTarget.WebGL);
|
||
}
|
||
|
||
[MenuItem("打包/生成底包/微信小游戏底包", false, 1030)]
|
||
public static void BuildWX()
|
||
{//彻底更新
|
||
//xlua wrap文件
|
||
//CSObjectWrapEditor.Generator.GenAll();
|
||
//hybricCLR 生成
|
||
//PrebuildCommand.GenerateAll();
|
||
//微信小游戏
|
||
Debug.Log($"BuildTarget : {EditorUserBuildSettings.activeBuildTarget}");
|
||
WeChatWASM.WXProjectConf config = WeChatWASM.WXConvertCore.config.ProjectConf;
|
||
Debug.Log($"WXPConfig projectName : {config.projectName}");
|
||
Debug.Log($"WXPConfig Appid : {config.Appid}");
|
||
Debug.Log($"WXPConfig CDN : {config.CDN}");
|
||
Debug.Log($"WXPConfig assetLoadType : {config.assetLoadType}");
|
||
Debug.Log($"WXPConfig compressDataPackage : {config.compressDataPackage}");
|
||
Debug.Log($"WXPConfig VideoUrl : {config.VideoUrl}");
|
||
Debug.Log($"WXPConfig Orientation : {config.Orientation}");
|
||
|
||
WeChatWASM.CompileOptions options = WeChatWASM.WXConvertCore.config.CompileOptions;
|
||
Debug.Log($"WXOp DevelopBuild : {options.DevelopBuild}");
|
||
Debug.Log($"WXOp AutoProfile : {options.AutoProfile}");
|
||
Debug.Log($"WXOp ScriptOnly : {options.ScriptOnly}");
|
||
Debug.Log($"WXOp Il2CppOptimizeSize : {options.Il2CppOptimizeSize}");
|
||
Debug.Log($"WXOp profilingFuncs : {options.profilingFuncs}");
|
||
Debug.Log($"WXOp ProfilingMemory : {options.ProfilingMemory}");
|
||
Debug.Log($"WXOp DeleteStreamingAssets : {options.DeleteStreamingAssets}");
|
||
Debug.Log($"WXOp CleanBuild : {options.CleanBuild}");
|
||
Debug.Log($"WXOp CustomNodePath : {options.CustomNodePath}");
|
||
Debug.Log($"WXOp Webgl2 : {options.Webgl2}");
|
||
Debug.Log($"WXOp fbslim : {options.fbslim}");
|
||
|
||
WeChatWASM.WXConvertCore.DoExport();
|
||
|
||
//生成底包版本号base.version
|
||
string basever = md5file($"{WeChatWASM.WXConvertCore.config.ProjectConf.DST}/webgl/Build/webgl.data");
|
||
basever = $"var ver=\"{basever}\"";
|
||
File.WriteAllText($"{WeChatWASM.WXConvertCore.config.ProjectConf.DST}/webgl/ver.js", basever);
|
||
|
||
|
||
//复制底包内容到服务器上
|
||
string dst = GetUpdatePath();// "\\\\192.168.9.231\\g\\App\\G7\\";
|
||
dst = dst.Substring(0, dst.Length - 16);
|
||
string[] args = Environment.GetCommandLineArgs();
|
||
string dstpath = GetCommandLineValue(args, "-dstpath");
|
||
if (dstpath != null && dstpath != "")
|
||
{
|
||
dst = dstpath;
|
||
}
|
||
bool bCopyHtml = false;
|
||
if (!File.Exists($"{dst}index.html"))
|
||
{
|
||
bCopyHtml = true;
|
||
}
|
||
string src = $"{WeChatWASM.WXConvertCore.config.ProjectConf.DST}\\webgl\\";
|
||
src = src.Replace("\\", "/");
|
||
//删除原文件
|
||
string[] names = Directory.GetFiles(dst);
|
||
foreach (string file in names)
|
||
{
|
||
if (file.Contains("index.html"))
|
||
continue;
|
||
File.Delete(file);
|
||
}
|
||
//复制
|
||
names = Directory.GetFiles(src, "*.*", SearchOption.AllDirectories);
|
||
foreach (string file in names)
|
||
{
|
||
if (file.Contains("StreamingAssets.unity3d") || (!bCopyHtml && file.Contains("index.html")))
|
||
continue;
|
||
string dstfile = file.Replace(src, "");
|
||
dstfile = $"{dst}{dstfile}";
|
||
if (!Directory.Exists(Path.GetDirectoryName(dstfile)))
|
||
Directory.CreateDirectory(Path.GetDirectoryName(dstfile));
|
||
if (File.Exists(dstfile))
|
||
File.Delete(dstfile);
|
||
File.Copy(file, dstfile, true);
|
||
}
|
||
}
|
||
|
||
|
||
public static void BuildHotFix(BuildTargetGroup targetGroup, BuildTarget target, string targetName = "", bool bUpdateDll = true)
|
||
{
|
||
//bool bLuaPackage = false;
|
||
string[] EncryptFile = { "a.unity3d", "h.unity3d","xhotfix/base.unity3d", "StreamingAssets.unity3d", "bs.unity3d" };//
|
||
|
||
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";
|
||
//bLuaPackage = true;
|
||
}
|
||
else
|
||
{
|
||
sPlatform = "pc";
|
||
}
|
||
maps.Clear();
|
||
|
||
//基础更新(大厅/基础资源)产物输出到仓库 CDN/XWorld/<platform>/,
|
||
//由 Server/Cdn.Runner 以 --root <仓库>/CDN 提供,客户端经 DevDiscovery 拿 CDN 根后从 <根>/XWorld/<platform>/ 增量下载。
|
||
//(CWD = Unity 工程根 Client/,故 ../CDN = 仓库根 CDN)。产物已是 md5 命名,匹配 XResLoader.LoadManifest("XWorld")。
|
||
UpdatePath = $"../CDN/XWorld/{sPlatform}/";
|
||
|
||
//if (bUpdateDll)
|
||
//{//EncryptFile剔除其中的aot.unity,现在一般不更新aot.all,只打到底包
|
||
// EncryptFile = EncryptFile.Where(x => !x.Equals("aot.unity3d")).ToArray();
|
||
//}
|
||
|
||
string outPath = $"{AppDataPath}/StreamingAssets/";//这个不要直接清 还有其他文件
|
||
string hotfixDir = $"{outPath}xhotfix";
|
||
string aotDir = $"{outPath}xaot";
|
||
ClearDir(hotfixDir);
|
||
ClearDir(aotDir);
|
||
if (!Directory.Exists(outPath))
|
||
Directory.CreateDirectory(outPath);
|
||
if (!Directory.Exists(hotfixDir))
|
||
Directory.CreateDirectory(hotfixDir);
|
||
if (!Directory.Exists(aotDir))
|
||
Directory.CreateDirectory(aotDir);
|
||
foreach (var f in EncryptFile)
|
||
{//unity需要删除旧文件,不然这个会不更新,还是用老的加密ab文件,后面就会重复加密。
|
||
string file = $"{AppDataPath}/StreamingAssets/{f}";
|
||
if (File.Exists(file))
|
||
{
|
||
File.Delete(file);
|
||
}
|
||
file = $"{AppDataPath}/StreamingAssets/{f}.manifest";
|
||
if (File.Exists(file))
|
||
{
|
||
File.Delete(file);
|
||
}
|
||
}
|
||
|
||
//生成一次临时包,以便正确生成dll
|
||
|
||
//生成xlua的wrap代码
|
||
CSObjectWrapEditor.Generator.GenAll();
|
||
|
||
//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/");
|
||
|
||
//string[] SCENES = FindHotfixScenes();
|
||
//BuildPipeline.BuildPlayer(SCENES, targetName, target, BuildOptions.None);
|
||
|
||
//打入更新的资源,dll
|
||
string aotAssembliesSrcDir = SettingsUtil.GetAssembliesPostIl2CppStripDir(target);
|
||
if (!File.Exists($"{aotAssembliesSrcDir}/mscorlib.dll"))
|
||
{//aot dll 检查
|
||
StripAOTDllCommand.GenerateStripedAOTDlls();
|
||
AssetDatabase.Refresh();
|
||
}
|
||
//HybridCLR dll update
|
||
BuildAssetsCommand.BuildAndCopyABAOTHotUpdateDlls();
|
||
AssetDatabase.Refresh();
|
||
|
||
//lua 打包
|
||
BuildLuaAB("bs.unity3d");
|
||
|
||
PackDllFile("h.unity3d", $"{AppDataPath}/StreamingAssets/xhotfix/");
|
||
//if (!bUpdateDll)
|
||
//PackDllFile("a.unity3d", $"{AppDataPath}/StreamingAssets/xaot/");
|
||
|
||
//打包基础代码入口资源,这个为挂载GameLauncher脚本的预设
|
||
AddBuildMap($"xhotfix/base.unity3d", "*.*", $"Assets/base/Prefab");
|
||
AssetDatabase.Refresh();
|
||
|
||
//渲染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();
|
||
|
||
//改名StreamingAssets,加个后缀
|
||
if (File.Exists($"{outPath}StreamingAssets"))
|
||
{
|
||
File.Move($"{outPath}StreamingAssets", $"{outPath}StreamingAssets.unity3d");
|
||
}
|
||
|
||
|
||
//生成更新列表
|
||
//BuildFileIndex(sPlatform);//StreamingAssets 里的除了\\xaot\\ 和 \\xhotfix (dll目录)外所有文件
|
||
//添加dll更新列表
|
||
m_dicFilelist.Clear();
|
||
m_strDllFileMD5 = "";
|
||
m_strResFileMD5 = "";
|
||
//资源列表
|
||
BuildFileIndex("");//暂时不分平台
|
||
//dll列表
|
||
BuildDllsFileIndex("", bUpdateDll);
|
||
SaveUpdateVer("");
|
||
|
||
string update_xaot_dir = $"{GetUpdatePath()}xaot";
|
||
string update_xhotfix_dir = $"{GetUpdatePath()}xhotfix";
|
||
string update_dllfiles = $"{GetUpdatePath()}dllfiles.txt";
|
||
string update_files = $"{GetUpdatePath()}files.txt";
|
||
string update_ver = $"{GetUpdatePath()}updatever.txt";
|
||
//复制到目标地址
|
||
//先清理之前的
|
||
if (Directory.Exists(GetUpdatePath()))
|
||
{
|
||
ClearDir(update_xaot_dir);
|
||
ClearDir(update_xhotfix_dir);
|
||
|
||
//string [] files = Directory.GetFiles(GetUpdatePath(), "*.*");
|
||
//for (int i = 0; i < files.Length; i++)
|
||
//{
|
||
// if (files[i].Contains("basepack.bytes") || files[i].Contains("config.json"))
|
||
// continue;
|
||
// File.Delete(files[i]);
|
||
//}
|
||
}
|
||
|
||
string OldVerString = "";
|
||
if (File.Exists(update_ver))
|
||
{
|
||
OldVerString = File.ReadAllText(update_ver);
|
||
}
|
||
//CopyPath(outPath, GetUpdatePath());
|
||
//CopyPath($"{outPath}xaot", update_xaot_dir);
|
||
CopyPath($"{outPath}xhotfix", update_xhotfix_dir);//这里面有个base.unity3d
|
||
File.Copy($"{outPath}dllfiles.txt", update_dllfiles, true);
|
||
File.Copy($"{outPath}files.txt", $"{GetUpdatePath()}files.txt", true);
|
||
File.Copy($"{outPath}updatever.txt", update_ver, true);
|
||
int copyCount = CopyUpdateFilesFromIndex(outPath, GetUpdatePath());
|
||
Debug.Log($"Copy update resource files: {copyCount}");
|
||
|
||
//复制资源
|
||
|
||
//加密
|
||
byte[] w = System.Text.Encoding.UTF8.GetBytes($"{m_aesPassword}");
|
||
AESManager.Set(ChangeBytes(w));
|
||
foreach (string f in EncryptFile)
|
||
{
|
||
if (!File.Exists($"{AppDataPath}/StreamingAssets/{f}"))
|
||
continue;
|
||
|
||
//if (!f.Contains("bs.unity3d"))
|
||
//if (target != BuildTarget.WebGL)
|
||
{
|
||
byte[] srcdata = File.ReadAllBytes($"{AppDataPath}/StreamingAssets/{f}");
|
||
byte[] encryptdata = AESManager.Encrypt(srcdata);
|
||
//byte[] retdata = AESManager.Decrypt(encryptdata);
|
||
|
||
File.WriteAllBytes($"{AppDataPath}/StreamingAssets/{f}", encryptdata);
|
||
//File.WriteAllBytes($"{AppDataPath}/StreamingAssets/{f}_Encrypt", encryptdata);
|
||
}
|
||
string file = $"{GetUpdatePath()}{f}";
|
||
if (File.Exists(file))
|
||
{
|
||
File.Delete(file);
|
||
}
|
||
//File.Move($"{AppDataPath}/StreamingAssets/{f}_Encrypt", file);
|
||
File.Copy($"{AppDataPath}/StreamingAssets/{f}", file, true);
|
||
}
|
||
//xlua处理
|
||
//if (target == BuildTarget.WebGL && bLuaPackage)
|
||
//{
|
||
// File.Copy($"{outPath}bs.unity3d", $"{GetUpdatePath()}bs.unity3d", true);
|
||
//}
|
||
|
||
//以后修复
|
||
//GYPackager.BuildServerListMd5(target);
|
||
|
||
Debug.Log($"Dest Path : {GetUpdatePath()}");
|
||
//if (target == BuildTarget.WebGL)
|
||
|
||
{//给文件加上md5后缀,以便方便webgl小程序使用UnityWebRequest自动缓存
|
||
//先删除旧的
|
||
string finally_update_dllfiles;
|
||
string finally_update_files;
|
||
if (OldVerString != "")
|
||
{
|
||
string[] vers = OldVerString.Split(',');
|
||
|
||
vers[0] = GetFileMd5Name($"{GetUpdatePath()}dllfiles.txt",vers[0]);
|
||
vers[1] = GetFileMd5Name($"{GetUpdatePath()}files.txt",vers[1]);
|
||
|
||
//foreach (var ver in vers)
|
||
{
|
||
//删除旧的更新文件
|
||
|
||
if (File.Exists(vers[0]))
|
||
{
|
||
string dllString = File.ReadAllText(vers[0]);
|
||
foreach (string f in EncryptFile)
|
||
{
|
||
int pos = dllString.IndexOf(f);
|
||
|
||
if (pos != -1)
|
||
{
|
||
string md5 = dllString.Substring(pos + f.Length + 1, 8);
|
||
if (md5 != null && md5.Length == 8)
|
||
{
|
||
string ab = GetFileMd5Name($"{GetUpdatePath()}{f}", GetFixedMd5(md5));
|
||
if (File.Exists(ab))
|
||
File.Delete(ab);
|
||
}
|
||
}
|
||
}
|
||
//删除旧的dllfiles.txt文件
|
||
File.Delete(vers[0]);
|
||
}
|
||
if (File.Exists(vers[1]))
|
||
{
|
||
IEnumerable<string> lines = File.ReadLines(vers[1]);
|
||
foreach (var line in lines)
|
||
{
|
||
string[] items = line.Split(",");
|
||
if (items.Length > 2)
|
||
{
|
||
string path = items[0].Substring(1);//路径去掉左边的‘{’符号
|
||
if (items[1] != null && items[1].Length == 8)//第二个是md5码
|
||
{
|
||
string ab = GetFileMd5Name($"{GetUpdatePath()}{path}",GetFixedMd5(items[1]));
|
||
if (File.Exists(ab))
|
||
File.Delete(ab);
|
||
}
|
||
}
|
||
}
|
||
//删除旧的dllfiles.txt文件
|
||
File.Delete(vers[1]);
|
||
}
|
||
}
|
||
}
|
||
foreach (var file in m_dicFilelist)
|
||
{
|
||
string destFile = GetFileMd5Name($"{GetUpdatePath()}{file.Key}", GetFixedMd5(file.Value));
|
||
string srcFile = $"{GetUpdatePath()}{file.Key}";
|
||
if (!File.Exists(srcFile))
|
||
continue;
|
||
if (!File.Exists(destFile))
|
||
File.Move(srcFile, destFile);
|
||
}
|
||
|
||
//新的文件MD5, m_strDllFileMD5
|
||
finally_update_dllfiles = GetFileMd5Name($"{GetUpdatePath()}dllfiles.txt", GetFixedMd5(m_strDllFileMD5));
|
||
if(!File.Exists(finally_update_dllfiles))
|
||
File.Move(update_dllfiles, finally_update_dllfiles);
|
||
finally_update_files = GetFileMd5Name($"{GetUpdatePath()}files.txt", GetFixedMd5(m_strResFileMD5));
|
||
if (!File.Exists(finally_update_files))
|
||
File.Move(update_files, finally_update_files);
|
||
|
||
}
|
||
|
||
//可靠产出最终 updatever.txt(明文):它是 XResLoader.LoadManifest("XWorld") 的入口文件,须与
|
||
//files_<resmd5>.txt / dllfiles_<dllmd5>.txt 配套。中途的 File.Copy 在重定向到 CDN 后偶发不落盘,
|
||
//故在所有改名之后用已算出的 md5 显式重写一遍,保证存在且写在最后不被后续步骤清掉。
|
||
if (!string.IsNullOrEmpty(m_strDllFileMD5) && !string.IsNullOrEmpty(m_strResFileMD5))
|
||
{
|
||
Directory.CreateDirectory(GetUpdatePath());
|
||
File.WriteAllText($"{GetUpdatePath()}updatever.txt", $"{m_strDllFileMD5},{m_strResFileMD5}");
|
||
Debug.Log($"updatever.txt => {GetUpdatePath()}updatever.txt ({m_strDllFileMD5},{m_strResFileMD5})");
|
||
}
|
||
else
|
||
{
|
||
Debug.LogError("updatever.txt 未生成:dll/res 清单 md5 为空(SaveUpdateVer 可能提前返回)");
|
||
}
|
||
|
||
//清理outPath
|
||
//。。。
|
||
Debug.Log($"构建完成{DateTime.Now.ToString()}");
|
||
|
||
//清理中间打包目录
|
||
string temppath = $"{AppDataPath}/a";
|
||
ClearDir(temppath, true);
|
||
temppath = $"{AppDataPath}/h";
|
||
ClearDir(temppath, true);
|
||
//Lua
|
||
string luapath = $"{AppDataPath}/l";
|
||
ClearDir(luapath, true);
|
||
//xlua相关
|
||
//CSObjectWrapEditor.Generator.ClearAll();
|
||
}
|
||
|
||
|
||
static int CopyUpdateFilesFromIndex(string srcRoot, string dstRoot)
|
||
{
|
||
if (string.IsNullOrEmpty(srcRoot) || string.IsNullOrEmpty(dstRoot))
|
||
return 0;
|
||
|
||
if (!srcRoot.EndsWith("/") && !srcRoot.EndsWith("\\"))
|
||
srcRoot += "/";
|
||
if (!dstRoot.EndsWith("/") && !dstRoot.EndsWith("\\"))
|
||
dstRoot += "/";
|
||
|
||
int count = 0;
|
||
foreach (var item in m_dicFilelist)
|
||
{
|
||
string relativePath = item.Key;
|
||
if (string.IsNullOrEmpty(relativePath))
|
||
continue;
|
||
|
||
relativePath = relativePath.Replace('\\', '/');
|
||
if (relativePath.EndsWith(".meta", StringComparison.OrdinalIgnoreCase)
|
||
|| relativePath.EndsWith(".manifest", StringComparison.OrdinalIgnoreCase)
|
||
|| relativePath.EndsWith(".txt", StringComparison.OrdinalIgnoreCase))
|
||
{
|
||
continue;
|
||
}
|
||
|
||
string srcFile = Path.Combine(srcRoot, relativePath.Replace('/', Path.DirectorySeparatorChar));
|
||
if (!File.Exists(srcFile))
|
||
{
|
||
Debug.LogWarning($"Update file listed but missing: {srcFile}");
|
||
continue;
|
||
}
|
||
|
||
string dstFile = Path.Combine(dstRoot, relativePath.Replace('/', Path.DirectorySeparatorChar));
|
||
string dstDir = Path.GetDirectoryName(dstFile);
|
||
if (!Directory.Exists(dstDir))
|
||
Directory.CreateDirectory(dstDir);
|
||
|
||
File.Copy(srcFile, dstFile, true);
|
||
count++;
|
||
}
|
||
|
||
return count;
|
||
}
|
||
|
||
static void CopyPath(string srcPath, string 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)) Directory.CreateDirectory(path);
|
||
|
||
if (File.Exists(newpath))
|
||
{
|
||
File.Delete(newpath);
|
||
}
|
||
|
||
File.Copy(f, newpath, true);
|
||
UpdateProgress(n++, files.Count, newpath);
|
||
}
|
||
}
|
||
EditorUtility.ClearProgressBar();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 遍历目录及其子目录
|
||
/// </summary>
|
||
static void Recursive(string path)
|
||
{
|
||
if (path.Contains("\\xaot\\") || path.Contains("\\xhotfix\\")
|
||
|| 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;
|
||
files.Add(filename.Replace('\\', '/'));
|
||
}
|
||
foreach (string dir in dirs)
|
||
{
|
||
if (dir.IndexOf(".svn") >= 0)
|
||
{
|
||
continue;
|
||
}
|
||
paths.Add(dir.Replace('\\', '/'));
|
||
Recursive(dir);
|
||
}
|
||
}
|
||
|
||
private static string[] FindHotfixScenes()
|
||
{//只导出基于热更的基础场景
|
||
List<string> LauncherSceneNames = new List<string>();
|
||
LauncherSceneNames.Insert(0, "Assets/Launcher/Launcher_Hotfix.unity");
|
||
|
||
return LauncherSceneNames.ToArray();
|
||
}
|
||
|
||
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);
|
||
Debug.LogWarning($"AddBuildMap(dir) : {build.assetBundleName}({files.Length})");
|
||
}
|
||
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);
|
||
Debug.LogWarning($"AddBuildMap : {build.assetBundleName}({files.Length})");
|
||
}
|
||
}
|
||
}
|
||
|
||
public static bool PackAotFile(BuildTargetGroup targetGroup, BuildTarget target, string bundleName, string dllpath)
|
||
{
|
||
maps.Clear();
|
||
paths.Clear();
|
||
files.Clear();
|
||
|
||
if (target == BuildTarget.Android)
|
||
{
|
||
sPlatform = "android";
|
||
}
|
||
else if (target == BuildTarget.iOS)
|
||
{
|
||
sPlatform = "ios";
|
||
}
|
||
else if (target == BuildTarget.WebGL)
|
||
{
|
||
sPlatform = "webgl";
|
||
}
|
||
else
|
||
{
|
||
sPlatform = "pc";
|
||
}
|
||
UpdatePath = $"../CDN/XWorld/{sPlatform}/";
|
||
|
||
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);
|
||
var inputPath = $"{BasePath}/client/";
|
||
var outputPath = $"{BasePath}/clientObfuz/";
|
||
|
||
//打aot不需要这个
|
||
//ScriptCompilationResult scriptCompilationResult = PlayerBuildInterface.CompilePlayerScripts(scriptCompilationSettings, inputPath);
|
||
//AssetDatabase.Refresh();
|
||
|
||
string aotAssembliesSrcDir = SettingsUtil.GetAssembliesPostIl2CppStripDir(target);
|
||
if (!File.Exists($"{aotAssembliesSrcDir}/mscorlib.dll"))
|
||
{//aot dll 检查
|
||
StripAOTDllCommand.GenerateStripedAOTDlls();
|
||
AssetDatabase.Refresh();
|
||
}
|
||
HybridCLR.Editor.BuildAssetsCommand.CopyAOTAssembliesToStreamingAssets();
|
||
AssetDatabase.Refresh();
|
||
|
||
PackDllFile(bundleName, dllpath);
|
||
|
||
string outPath = $"{AppDataPath}/StreamingAssets/";
|
||
string bundlePath = $"{outPath}{bundleName}";
|
||
if (File.Exists(bundlePath))
|
||
File.Delete(bundlePath);
|
||
if (File.Exists($"{bundlePath}.manifest"))
|
||
File.Delete($"{bundlePath}.manifest");
|
||
|
||
var manifest = BuildPipeline.BuildAssetBundles(outPath, maps.ToArray(), BuildAssetBundleOptions.ChunkBasedCompression/*| BuildAssetBundleOptions.DisableWriteTypeTree*/, target);
|
||
|
||
if (manifest == null)
|
||
{
|
||
Debug.LogError("BuildAssetBundles 返回 null!原因通常是:1.资源重复打包 2.资源路径不存在 3.有残留的.meta错误。请查看控制台前一条报错。");
|
||
return false;
|
||
}
|
||
|
||
AssetDatabase.Refresh();
|
||
//string outBundleName = $"{outPath}/{bundlePath}";
|
||
if (!File.Exists(bundlePath))
|
||
{
|
||
Debug.LogError($"BuildAssetBundles 失败!{bundlePath} 不存在。请查看控制台前一条报错。");
|
||
return false;
|
||
}
|
||
Debug.Log($"BuildAssetBundles {bundlePath}成功");
|
||
|
||
//encrypt
|
||
string f = "a.unity3d";
|
||
byte[] w = System.Text.Encoding.UTF8.GetBytes($"{m_aesPassword}");
|
||
AESManager.Set(ChangeBytes(w));
|
||
byte[] srcdata = File.ReadAllBytes($"{AppDataPath}/StreamingAssets/{f}");
|
||
if (!IsUnityAssetBundleData(srcdata))
|
||
{
|
||
Debug.LogError($"{f} before encrypt is not a Unity AssetBundle. head:{GetDataHead(srcdata)}");
|
||
return false;
|
||
}
|
||
byte[] encryptdata = AESManager.Encrypt(srcdata);
|
||
byte[] retdata = AESManager.Decrypt(encryptdata);
|
||
if (!IsUnityAssetBundleData(retdata))
|
||
{
|
||
Debug.LogError($"{f} encrypt/decrypt check failed. head:{GetDataHead(retdata)}");
|
||
return false;
|
||
}
|
||
File.WriteAllBytes($"{AppDataPath}/StreamingAssets/{f}", encryptdata);
|
||
|
||
|
||
//清理临时目录
|
||
string temppath = $"{AppDataPath}/aot";
|
||
if (Directory.Exists(temppath))
|
||
{
|
||
Directory.Delete(temppath, true);
|
||
temppath = temppath + ".meta";
|
||
File.Delete(temppath);
|
||
}
|
||
temppath = $"{AppDataPath}/StreamingAssets/res";
|
||
if (Directory.Exists(temppath))
|
||
{
|
||
Directory.Delete(temppath, true);
|
||
temppath = temppath + ".meta";
|
||
File.Delete(temppath);
|
||
}
|
||
temppath = $"{AppDataPath}/StreamingAssets/xaot";
|
||
if (Directory.Exists(temppath))
|
||
{
|
||
Directory.Delete(temppath, true);
|
||
temppath = temppath + ".meta";
|
||
File.Delete(temppath);
|
||
}
|
||
temppath = $"{AppDataPath}/hotfix";
|
||
if (Directory.Exists(temppath))
|
||
{
|
||
Directory.Delete(temppath, true);
|
||
temppath = temppath + ".meta";
|
||
File.Delete(temppath);
|
||
}
|
||
|
||
SaveMd5UpdateFiles();
|
||
return true;
|
||
}
|
||
|
||
static void SaveMd5UpdateFiles()
|
||
{
|
||
//处理生成带md5文件
|
||
Debug.Log($"处理生成带md5文件");
|
||
string dllmd5 = File.ReadAllText($"{GetUpdatePath()}updatever.txt");
|
||
if (dllmd5 != m_strOldVersion)
|
||
{
|
||
Debug.Log($"处理生成带md5文件有更新");
|
||
}
|
||
else
|
||
{
|
||
Debug.Log($"处理生成带md5文件没有有更新");
|
||
}
|
||
{//有改变
|
||
string new_dllfiles = $"{GetUpdatePath()}dllfiles_{dllmd5}.txt";
|
||
File.Copy($"{AppDataPath}/StreamingAssets/dllfiles.txt", $"{GetUpdatePath()}dllfiles_{dllmd5}.txt", true);
|
||
string[] dllString = File.ReadAllLines(new_dllfiles);
|
||
//检查原version文件,删除旧的dllfiles.txt_md5文件和加密的dll文件
|
||
if (m_strOldVersion.Length >= 8)
|
||
{
|
||
string old_dllfiles = $"{GetUpdatePath()}dllfiles_{m_strOldVersion}.txt";
|
||
if (File.Exists(old_dllfiles))
|
||
{
|
||
//查找原来的文件,删除
|
||
string[] dlls = File.ReadAllLines(old_dllfiles);
|
||
//比较dlls和dllString的不同,删除旧的
|
||
var filesToDelete = dlls.Except(dllString);
|
||
foreach (string f in filesToDelete)
|
||
{
|
||
string trimmedString = f.Trim('{', '}');
|
||
string[] line = trimmedString.Split(',');
|
||
string fn = GetFileMd5Name(line[0], line[1]);
|
||
string file = $"{GetUpdatePath()}{fn}";
|
||
if (File.Exists(file))
|
||
{
|
||
File.Delete(file);
|
||
}
|
||
}
|
||
|
||
File.Delete(old_dllfiles);
|
||
}
|
||
}
|
||
//生成新的带md5文件
|
||
foreach (string f in dllString)
|
||
{
|
||
string trimmedString = f.Trim('{', '}');
|
||
string[] line = trimmedString.Split(',');
|
||
string fn = GetFileMd5Name(line[0], line[1]);
|
||
string srcfile = $"{GetUpdatePath()}{line[0]}";
|
||
string destfile = $"{GetUpdatePath()}{fn}";
|
||
if (!File.Exists(destfile))
|
||
{
|
||
File.Copy(srcfile, destfile, true);
|
||
}
|
||
}
|
||
}
|
||
|
||
/*/生成版本文件(代码md5,YooAssets版本号,资源版本号)
|
||
string updateShowVer = File.ReadAllText($"{GetUpdatePath()}updateShowVer.txt");
|
||
|
||
string serverinfo_file = $"{GetUpdatePath()}serverinfo.txt";
|
||
ServerConfig serverConfig = new ServerConfig();
|
||
if (BuildSettings.GameEnvironment == EBaseGameEnv.Local)
|
||
{
|
||
serverConfig.cdn = "http://192.168.130.30/cdn/";
|
||
serverConfig.resver = BuildSettings.GameResVersion; //"10000101";//先测试用101
|
||
serverConfig.resver_update = updateShowVer;//updateShowVer.txt
|
||
serverConfig.codemd5 = dllmd5;//updatever.txt
|
||
serverConfig.yooasset_ver = $"{yoopack2md5},{yoopack3md5}";//PatchManifest_Pack2.version 和PatchManifest_Pack3.version
|
||
serverConfig.login = "172.25.50.43:3001";
|
||
serverConfig.login_backup = "172.25.50.43:3001";
|
||
serverConfig.notice = "http://192.168.130.30/cdn/";
|
||
serverConfig.date = "2025082610220612";
|
||
serverConfig.gateway = new string[]{
|
||
"114.207.53.71",
|
||
"114.207.53.72",
|
||
};
|
||
}
|
||
else
|
||
{
|
||
serverConfig.cdn = "http://fish.funturegame.com:8081/download/cdn/";
|
||
serverConfig.resver = BuildSettings.GameResVersion;// "10000000";
|
||
serverConfig.resver_update = updateShowVer;//updateShowVer.txt
|
||
serverConfig.codemd5 = dllmd5;//updatever.txt
|
||
serverConfig.yooasset_ver = $"{yoopack2md5},{yoopack3md5}";//PatchManifest_Pack2.version 和PatchManifest_Pack3.version
|
||
serverConfig.login = "fish.funturegame.com:3001";
|
||
serverConfig.login_backup = "114.207.53.82:3001";
|
||
serverConfig.notice = "https://version_t1.funturio.com/";
|
||
serverConfig.date = "2025082610220612";
|
||
serverConfig.gateway = new string[]{
|
||
"114.207.53.71",
|
||
"114.207.53.72",
|
||
};
|
||
serverConfig.ext = new Dictionary<string, string>(){
|
||
{"xconsole","100"},
|
||
{"logserver","http://fish.funturegame.com:8089/api/v1/logs/batch"}
|
||
};
|
||
}
|
||
string jsonString = Newtonsoft.Json.JsonConvert.SerializeObject(serverConfig);
|
||
File.WriteAllText(serverinfo_file, jsonString);
|
||
//*/
|
||
}
|
||
|
||
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>();
|
||
string dirname = bundleName.Substring(0, nPos);
|
||
string temppath = $"{AppDataPath}/{dirname}";
|
||
if (!Directory.Exists(temppath))
|
||
{
|
||
Directory.CreateDirectory(temppath);
|
||
}
|
||
else
|
||
{
|
||
ClearDir(temppath);
|
||
}
|
||
|
||
//byte[] buffer, aesdata;
|
||
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 BuildLuaAB(string bundleName)
|
||
{
|
||
string luapath = $"{Application.dataPath}/l/";
|
||
if(Directory.Exists(luapath))
|
||
ClearDir(luapath);
|
||
//Assets下的lua目录
|
||
CopyScriptsFile($"{Application.dataPath}/lua/", luapath, "bytes");
|
||
AssetDatabase.Refresh();//新增文件需要刷新
|
||
|
||
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);
|
||
}
|
||
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);
|
||
}
|
||
|
||
newpath = newpath.ToLower();
|
||
|
||
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 void UpdateProgress(int progress, int progressMax, string desc)
|
||
{
|
||
string title = "Processing...[" + progress + " - " + progressMax + "]";
|
||
float value = (float)progress / (float)progressMax;
|
||
EditorUtility.DisplayProgressBar(title, desc, value);
|
||
}
|
||
|
||
static void BuildDllsFileIndex(string dir = "", bool bUpdateDll = true)
|
||
{
|
||
List<string> files = new List<string>();
|
||
string resPath = $"{AppDataPath}/StreamingAssets/{dir}/";
|
||
///----------------------创建文件列表-----------------------
|
||
string newFilePath = resPath + "dllfiles.txt";
|
||
if (File.Exists(newFilePath)) File.Delete(newFilePath);
|
||
if (!Directory.Exists($"{resPath}xaot/"))
|
||
Directory.CreateDirectory($"{resPath}xaot/");
|
||
if (!Directory.Exists($"{resPath}xhotfix/"))
|
||
Directory.CreateDirectory($"{resPath}xhotfix/");
|
||
//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;
|
||
}
|
||
//记录基础 ab
|
||
string[] BaseABFile = { "a.unity3d", "h.unity3d"};//, "bs.unity3d", "StreamingAssets"
|
||
foreach (var f in BaseABFile)
|
||
{
|
||
if (f == "a.unity3d" && bUpdateDll)
|
||
continue;
|
||
string file = $"{AppDataPath}/StreamingAssets/{f}";
|
||
|
||
if (File.Exists(file))
|
||
{
|
||
long size = 0;
|
||
string md5 = GetFixedMd5(md5file(file, out size));
|
||
string value = file.Replace($"{AppDataPath}/StreamingAssets/", string.Empty);
|
||
sw.WriteLine($"{{{value},{md5},{size}}}");
|
||
m_dicFilelist[value] = md5;
|
||
}
|
||
}
|
||
|
||
sw.Close(); fs.Close();
|
||
}
|
||
|
||
static void BuildFileIndex(string dir = "")
|
||
{
|
||
string resPath = $"{AppDataPath}/StreamingAssets/{dir}/";
|
||
///----------------------创建文件列表-----------------------
|
||
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];
|
||
if (file.EndsWith(".txt", StringComparison.OrdinalIgnoreCase))
|
||
continue;
|
||
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);
|
||
//if (!value.Contains("/") && value != "StreamingAssets.unity3d" && value != "bs.unity3d")//去除根路径文件
|
||
if(value == "h.unity3d")//剔除热更
|
||
continue;
|
||
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 = $"{AppDataPath}/StreamingAssets/{dir}/";
|
||
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}");
|
||
}
|
||
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 string GetPlatformString(BuildTarget target)
|
||
{
|
||
string sPlatform;
|
||
if (target == BuildTarget.Android)
|
||
{
|
||
sPlatform = "android";
|
||
}
|
||
else if (target == BuildTarget.iOS)
|
||
{
|
||
sPlatform = "ios";
|
||
}
|
||
else if (target == BuildTarget.WebGL)
|
||
{
|
||
sPlatform = "webgl";
|
||
}
|
||
else
|
||
{
|
||
sPlatform = "pc";
|
||
}
|
||
return sPlatform;
|
||
}
|
||
private static string GetVersionResDir(BuildTarget target)
|
||
{
|
||
string dir;
|
||
dir = Application.dataPath + $"/../../../CDNRes/public/G7/{GetPlatformString(target)}/";
|
||
//if (BuildSettings.GameEnvironment == EGameEnvType.Intranet)
|
||
//{
|
||
// dir = $"\\\\{GetLocal()}\\g\\App\\CDNRes\\public\\{BuildSettings.GameResVersion}\\{GetPlatformString(target)}\\";
|
||
//}
|
||
//else
|
||
//{
|
||
// dir = Application.dataPath + $"/../../../CDNRes/public/{BuildSettings.GameResVersion}/{GetPlatformString(target)}/";
|
||
//}
|
||
return dir;
|
||
}
|
||
public static void BuildServerListMd5(BuildTarget target, string packageName = "", string OutputPackageDir = "")
|
||
{
|
||
string dir = GetVersionResDir(target);
|
||
if (!Directory.Exists(dir))
|
||
{
|
||
Debug.LogError($"目录不存在:{dir}");
|
||
return;
|
||
}
|
||
StringBuilder sb = new StringBuilder();
|
||
//将三个包的Hash
|
||
for (int i = 1; i <= 3; i++)
|
||
{
|
||
string packName = $"Pack{i}";
|
||
string currentDir = dir;
|
||
if (packName == packageName)
|
||
{
|
||
currentDir = OutputPackageDir;
|
||
}
|
||
string verFile = $"{currentDir}PatchManifest_{packName}.version";
|
||
if (File.Exists(verFile))
|
||
{
|
||
string verName = File.ReadAllText(verFile);
|
||
string hashFile = $"{currentDir}PatchManifest_{packName}_{verName}.hash";
|
||
sb.AppendLine(File.ReadAllText(hashFile));
|
||
}
|
||
}
|
||
string package = $"{dir}{packageVer}";
|
||
if (File.Exists(package))
|
||
{
|
||
sb.AppendLine(File.ReadAllText(package));
|
||
}
|
||
string updatever = $"{dir}updatever.txt";
|
||
if (File.Exists(updatever))
|
||
{
|
||
sb.AppendLine(File.ReadAllText(updatever));
|
||
}
|
||
|
||
Debug.Log($"源hash:\n{sb}");
|
||
string md5 = Packager.md5(sb.ToString());
|
||
string md5File = $"{dir}server_res_list_md5.txt";
|
||
if (File.Exists(md5File))
|
||
{
|
||
File.Delete(md5File);
|
||
}
|
||
byte[] bytes = Encoding.UTF8.GetBytes(md5);
|
||
using (FileStream fs = File.Create(md5File))
|
||
{
|
||
fs.Write(bytes, 0, bytes.Length);
|
||
fs.Flush();
|
||
fs.Close();
|
||
}
|
||
Debug.Log($"md5生成成功:{md5} {md5File}");
|
||
}
|
||
|
||
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 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 string GetFixedMd5(string md5)
|
||
{
|
||
return md5.Substring(0, 8);//只取8位
|
||
}
|
||
|
||
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}";
|
||
}
|
||
|
||
public static string GetCommandLineValue(string[] array, string key)
|
||
{
|
||
foreach (var item in array)
|
||
{
|
||
if (item.Contains(key))
|
||
{
|
||
string value = item.Split('=')[1];
|
||
|
||
return value;
|
||
}
|
||
}
|
||
return "";
|
||
}
|
||
|
||
}
|