using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using TinaX;
using TinaX.Utils;
using TinaX.VFSKit;
using TinaXEditor.VFSKit.Versions;
using TinaXEditor.VFSKit.Utils;
using TinaXEditor.VFSKitInternal;
using TinaX.VFSKitInternal.Utils;
using TinaX.VFSKit.Const;
using TinaXEditor.VFSKit.Const;
using UnityEngine;
using TinaX.VFSKitInternal;
using UnityEditor;
using TinaX.IO;
namespace TinaXEditor.VFSKit
{
public class PatchGenerator : IPathchGenerator
{
public bool EnableEditorGUI { get; set; } = true;
public bool EnableLog { get; set; } = false;
public void MakePatchFromVersionLibrary(string branchName, long current_versionCode, long target_versionCode, string output_path)
{
if(current_versionCode <= target_versionCode)
{
Debug.LogError($"[TinaX.VFS][PatchGenerator] Failed to make patch: current versionCode must greater than target versionCode");
return;
}
//检查分支和版本是否存在
if(VFSManagerEditor.VersionManager.TryGetVersionBranch(branchName,out var branch))
{
//检查版本
VersionRecord record_current;
VersionRecord record_target;
if(branch.TryGetVersion(current_versionCode, out record_current) && branch.TryGetVersion(target_versionCode, out record_target))
{
//读取到两者的数据,读取两者的记录
string platform_name = TinaX.Utils.XPlatformUtil.GetNameText(branch.Platform);
string target_data_root_path = VFSEditorUtil.GetVersionDataFolderPath_InProjectVersion(branchName, target_versionCode);
string current_data_root_path = VFSEditorUtil.GetVersionDataFolderPath_InProjectVersion(branchName, current_versionCode);
string current_binary_path = VFSEditorUtil.Get_AssetsBinaryFolderPath_InVersion(branchName, current_versionCode);
string current_binary_zip_path = Path.Combine(current_binary_path, VFSEditorConst.VFS_VERSION_AssetsBinary_Zip_Name);
bool current_zip_file_not_found = false;
bool UseSourcePackagesFolder = false;
if (!File.Exists(current_binary_zip_path))
{
current_zip_file_not_found = true;
//文件不存在,检查sourcepackages那边的文件能不能用
if (this.IsSourcePackageFilesCanUse(ref branch, ref record_current, platform_name))
{
//能用
UseSourcePackagesFolder = true;
}
else
UseSourcePackagesFolder = false;
}
if (current_zip_file_not_found)
{
if (UseSourcePackagesFolder)
{
makePatchBySourcePackages(ref branch, ref record_target, current_versionCode, target_data_root_path, Path.GetDirectoryName(output_path), output_path);
}
else
{
Debug.LogError(IsChinese ? "当前版本的记录中并没有存档二进制文件,所以无法制作补丁。" : "Binaries are not archived in the records of the current version, so patches cannot be made.");
}
}
else
{
makePatchByVersionBinary(ref branch, ref record_target, current_versionCode, target_data_root_path, current_data_root_path, current_binary_path, Path.GetDirectoryName(output_path), output_path);
}
}
else
{
Debug.LogError($"[TinaX.VFS][PatchGenerator] Failed to make patch: target versionCode or current versionCode is invalid. ");
}
}
else
{
Debug.LogError($"[TinaX.VFS][PatchGenerator] Failed to make patch: branch name \"{branchName}\" is invalid. ");
}
}
///
/// 判断:是否能用source packages中的资源与给定的build_id是否相同
///
///
private bool IsSourcePackageFilesCanUse(ref VersionBranch branch,ref VersionRecord record, string platform_name)
{
string source_packages_root_path = VFSEditorUtil.GetSourcePackagesFolderPath(platform_name);
if (!Directory.Exists(source_packages_root_path)) return false;
string build_info_source = (branch.BType == VersionBranch.BranchType.MainPackage) ? VFSUtil.GetMainPackage_BuildInfo_Path(source_packages_root_path) : VFSUtil.GetExtensionGroup_BuildInfo_Path(source_packages_root_path, branch.ExtensionGroupName);
if (!File.Exists(build_info_source)) return false;
try
{
var build_info_obj = XConfig.GetJson(build_info_source, AssetLoadType.SystemIO, false);
return (record.build_id == build_info_obj.BuildID);
}
catch
{
return false;
}
}
private void makePatchBySourcePackages(ref VersionBranch branch, ref VersionRecord target_record, long cur_version_code, string target_data_root_path,string save_folder,string save_file_name)
{
string platform_name = TinaX.Utils.XPlatformUtil.GetNameText(branch.Platform);
string source_packages_root_folder = VFSEditorUtil.GetSourcePackagesFolderPath(platform_name);
string target_asset_hash_path = Path.Combine(target_data_root_path, VFSConst.AssetsHashFileName);
string asset_hash_path_source = (branch.BType == VersionBranch.BranchType.MainPackage) ? VFSEditorUtil.GetMainPackage_AssetsHashFilePath_InSourcePackagesFolder(platform_name) : VFSEditorUtil.GetExtensionGroup_AssetsHashFilePath_InSourcePackagesFolder(platform_name, branch.ExtensionGroupName);
bool isMainPackage = (branch.BType == VersionBranch.BranchType.MainPackage);
if (!File.Exists(target_asset_hash_path)) Debug.LogError($"[TinaX.VFS][PatchGenerator] The record corresponding to the target version is lost. ");
if (!File.Exists(asset_hash_path_source)) Debug.LogError($"[TinaX.VFS][PatchGenerator] The record corresponding to the source packages is lost. ");
var asset_hash_obj_target = XConfig.GetJson(target_asset_hash_path, AssetLoadType.SystemIO, false);
var asset_hash_obj_source = XConfig.GetJson(asset_hash_path_source, AssetLoadType.SystemIO, false);
PatchRecords records = new PatchRecords();
if(isMainPackage)
{
var source_vfsconfig = XConfig.GetJson(VFSUtil.GetVFSConfigFilePath_InPackages(source_packages_root_folder), AssetLoadType.SystemIO, false);
var config_mgr = new ConfigMiniManager(source_vfsconfig,
groupName => {
return Path.Combine(VFSUtil.GetMainPackage_AssetBundleManifests_Folder(source_packages_root_folder), groupName.GetMD5(true, true) + ".json");
});
var target_vfsconfig = XConfig.GetJson(Path.Combine(target_data_root_path, VFSConst.Config_Runtime_FileName), AssetLoadType.SystemIO, false);
var config_mgr_target = new ConfigMiniManager(target_vfsconfig,
groupName => {
return Path.Combine(target_data_root_path, VFSConst.MainPackage_AssetBundleManifests_Folder, groupName.GetMD5(true, true) + ".json");
});
string ext_name_source = source_vfsconfig.AssetBundleFileExtension;
if (!ext_name_source.StartsWith("."))
ext_name_source = "." + ext_name_source;
ext_name_source = ext_name_source.ToLower();
//string ext_name_target = target_vfsconfig.AssetBundleFileExtension;
//if (!ext_name_target.StartsWith("."))
// ext_name_target = "." + ext_name_target;
//ext_name_target = ext_name_target.ToLower();
if (EnableEditorGUI)
EditorUtility.DisplayProgressBar("Patch Gen", IsChinese ? "正在对比Assets" : "Comparing assets", 0);
int counter = 0;
int counter_t = 0;
int len = asset_hash_obj_source.Files.Length;
//开始遍历逻辑
foreach (var item in asset_hash_obj_source.Files)
{
if (EnableEditorGUI)
{
counter++;
if(len > 100)
{
counter_t++;
if(counter_t > 50)
{
counter_t = 0;
EditorUtility.DisplayProgressBar("Patch Gen", (IsChinese ? "正在对比Assets" : "Comparing assets") + $"{counter} / {len}", counter / len);
}
}
else
{
EditorUtility.DisplayProgressBar("Patch Gen", (IsChinese ? "正在对比Assets" : "Comparing assets") + $"{counter} / {len}", counter / len);
}
}
//在组里查询一下
if (config_mgr.TryQueryAsset(item.p, out var group))
{
if (group.HandleMode == GroupHandleMode.LocalOrRemote || group.HandleMode == GroupHandleMode.RemoteOnly)
continue;
//查询target里面有没有这个资源
if(asset_hash_obj_target.TryGetFileHashValue(item.p,out string hash_target))
{
string asset_bundle_name = group.GetAssetBundleNameOfAsset(item.p) + ext_name_source;
//有,检查hash
if (!hash_target.Equals(item.h))
{
//不一致
if (EnableLog)
Debug.Log(" [Modify Asset]" + item.p);
if (!records.Modify_ReadWrite.Contains(asset_bundle_name))
records.Modify_ReadWrite.Add(asset_bundle_name);
}
}
else
{
//没有,是新增资源
string asset_bundle_name = group.GetAssetBundleNameOfAsset(item.p) + ext_name_source;
if (EnableLog)
Debug.Log(" [New Asset]" + item.p);
if (!records.Add_ReadWrite.Contains(asset_bundle_name))
records.Add_ReadWrite.Add(asset_bundle_name);
}
}
}
if (EnableEditorGUI)
{
counter = 0;
counter_t = 0;
len = 0;
foreach(var group in config_mgr_target.mGroups)
{
if(group.FilesHash_VirtualDisk != null)
{
if (group.FilesHash_VirtualDisk.Files != null)
len += group.FilesHash_VirtualDisk.Files.Length;
}
}
}
//遍历target,寻找被删除的资源
foreach(var group in config_mgr_target.mGroups)
{
if(group.FilesHash_VirtualDisk != null && group.FilesHash_VirtualDisk.Files != null)
{
foreach(var item in group.FilesHash_VirtualDisk.Files)
{
if (EnableEditorGUI)
{
counter++;
if (len > 100)
{
counter_t++;
if (counter_t > 50)
{
counter_t = 0;
EditorUtility.DisplayProgressBar("Patch Gen", (IsChinese ? "正在对比Assets" : "Comparing assets") + $"{counter} / {len}", counter / len);
}
}
else
{
EditorUtility.DisplayProgressBar("Patch Gen", (IsChinese ? "正在对比Assets" : "Comparing assets") + $"{counter} / {len}", counter / len);
}
}
//找到我有的,但是current没有的
if (!config_mgr.TryFindAssetBundleName(item.p))
{
if (!records.Delete_ReadWrite.Contains(item.p))
records.Delete_ReadWrite.Add(item.p);
if (EnableLog)
Debug.Log(" [Delete AssetBundle]" + item.p);
}
}
}
}
if (EnableEditorGUI)
EditorUtility.ClearProgressBar();
}
else
{
var group_option_source = XConfig.GetJson(VFSUtil.GetExtensionPackages_GroupOptions_FilePath(source_packages_root_folder, branch.ExtensionGroupName), AssetLoadType.SystemIO, false);
var group_option_target = XConfig.GetJson(Path.Combine(target_data_root_path, VFSConst.ExtensionGroup_GroupOption_FileName), AssetLoadType.SystemIO, false);
var group_source = new VFSEditorGroup(group_option_source);
var group_target = new VFSEditorGroup(group_option_target);
//ab hash
group_source.SetVirtualDiskFileHash(XConfig.GetJson(VFSUtil.GetExtensionGroup_AssetBundleHashFileFilePath(source_packages_root_folder, branch.ExtensionGroupName), AssetLoadType.SystemIO, false));
group_target.SetVirtualDiskFileHash(XConfig.GetJson(VFSEditorUtil.GetVersionData_AssetBundle_HashFile_FolderOrFilePath(true, branch.BranchName, target_record.versionCode), AssetLoadType.SystemIO, false));
var ext_group_info_source = XConfig.GetJson(VFSUtil.GetExtensionGroup_GroupInfo_Path_InGroupPath(VFSUtil.GetExtensionGroupFolder(source_packages_root_folder, branch.ExtensionGroupName)), AssetLoadType.SystemIO, false);
string ab_ext_source = ext_group_info_source.AssetBundleExtension;
if (!ab_ext_source.StartsWith("."))
ab_ext_source = "." + ab_ext_source;
ab_ext_source = ab_ext_source.ToLower();
if (EnableEditorGUI)
EditorUtility.DisplayProgressBar("Patch Gen", IsChinese ? "正在对比Assets" : "Comparing assets", 0);
int counter = 0;
int counter_t = 0;
int len = asset_hash_obj_source.Files.Length;
//开始遍历
foreach (var item in asset_hash_obj_source.Files)
{
if (EnableEditorGUI)
{
counter++;
if (len > 100)
{
counter_t++;
if (counter_t > 50)
{
counter_t = 0;
EditorUtility.DisplayProgressBar("Patch Gen", (IsChinese ? "正在对比Assets" : "Comparing assets") + $"{counter} / {len}", counter / len);
}
}
else
{
EditorUtility.DisplayProgressBar("Patch Gen", (IsChinese ? "正在对比Assets" : "Comparing assets") + $"{counter} / {len}", counter / len);
}
}
//查询
if (group_source.IsAssetPathMatch(item.p))
{
//检查target里有没有
if (asset_hash_obj_target.TryGetFileHashValue(item.p, out string hash_target))
{
//有,检查hash
if (!hash_target.Equals(item.h))
{
//不一致
string asset_bundle_name = group_source.GetAssetBundleNameOfAsset(item.p) + ab_ext_source;
if (EnableLog)
Debug.Log(" [Modify Asset]" + item.p);
if (!records.Modify_ReadWrite.Contains(asset_bundle_name))
records.Modify_ReadWrite.Add(asset_bundle_name);
}
}
else
{
//没有,是新增资源
string asset_bundle_name = group_source.GetAssetBundleNameOfAsset(item.p) + ab_ext_source;
if (EnableLog)
Debug.Log(" [New Asset]" + item.p);
if (!records.Add_ReadWrite.Contains(asset_bundle_name))
records.Add_ReadWrite.Add(asset_bundle_name);
}
}
}
if(group_target.FilesHash_VirtualDisk.Files != null)
{
if (EnableEditorGUI)
{
counter = 0;
counter_t = 0;
len = group_target.FilesHash_VirtualDisk.Files.Length;
}
//遍历target,查找删除的资源
foreach (var item in group_target.FilesHash_VirtualDisk.Files)
{
if (EnableEditorGUI)
{
counter++;
if (len > 100)
{
counter_t++;
if (counter_t > 50)
{
counter_t = 0;
EditorUtility.DisplayProgressBar("Patch Gen", (IsChinese ? "正在对比Assets" : "Comparing assets") + $"{counter} / {len}", counter / len);
}
}
else
{
EditorUtility.DisplayProgressBar("Patch Gen", (IsChinese ? "正在对比Assets" : "Comparing assets") + $"{counter} / {len}", counter / len);
}
}
if (!group_source.FilesHash_VirtualDisk.TryGetFileHashValue(item.p,out _))
{
if (!records.Delete_ReadWrite.Contains(item.p))
records.Delete_ReadWrite.Add(item.p);
if (EnableLog)
Debug.Log(" [Delete AssetBundle]" + item.p);
}
}
}
if (EnableEditorGUI)
EditorUtility.ClearProgressBar();
}
PatchInfo patchInfo = new PatchInfo
{
branch = branch.BranchName,
patchCode = cur_version_code,
platform = branch.Platform,
targetVersionCode = target_record.versionCode,
extension = !isMainPackage,
extensionGroupName = branch.ExtensionGroupName
};
TinaX.IO.XDirectory.CreateIfNotExists(save_folder);
string patch_temp_folder_path = Path.Combine(save_folder, "tmp_"+StringHelper.GetRandom(6));
while (Directory.Exists(patch_temp_folder_path))
{
patch_temp_folder_path = Path.Combine(save_folder, "tmp_" + StringHelper.GetRandom(6));
}
Directory.CreateDirectory(patch_temp_folder_path);
//保存文件
string patch_info_save_path = Path.Combine(patch_temp_folder_path, VFSConst.Patch_Info_FileName);
string patch_record_save_path = Path.Combine(patch_temp_folder_path, VFSConst.Patch_Record_FileName);
XConfig.SaveJson(patchInfo, patch_info_save_path, AssetLoadType.SystemIO);
records.SaveReady();
XConfig.SaveJson(records, patch_record_save_path, AssetLoadType.SystemIO);
string patch_assets_path = Path.Combine(patch_temp_folder_path, VFSConst.Patch_Assets_Folder_Name);
Directory.CreateDirectory(patch_assets_path);
//复制文件
foreach(var item in records.Add_ReadWrite)
{
var assetbundle_path = VFSUtil.GetAssetBundlePathFromPackages(branch.BType != VersionBranch.BranchType.MainPackage,
source_packages_root_folder,
item,
branch.ExtensionGroupName);
if(!File.Exists(assetbundle_path))
{
Debug.LogError((IsChinese ? "制作补丁时发生错误,找不到文件:": "An error occurred while making the patch, the file could not be found:") + assetbundle_path);
continue;
}
string target_path = Path.Combine(patch_assets_path, item);
TinaX.IO.XDirectory.CreateIfNotExists(Path.GetDirectoryName(target_path));
File.Copy(assetbundle_path, target_path);
}
foreach (var item in records.Modify_ReadWrite)
{
var assetbundle_path = VFSUtil.GetAssetBundlePathFromPackages(branch.BType != VersionBranch.BranchType.MainPackage,
source_packages_root_folder,
item,
branch.ExtensionGroupName);
if (!File.Exists(assetbundle_path))
{
Debug.LogError((IsChinese ? "制作补丁时发生错误,找不到文件:" : "An error occurred while making the patch, the file could not be found:") + assetbundle_path);
continue;
}
string target_path = Path.Combine(patch_assets_path, item);
XDirectory.CreateIfNotExists(Path.GetDirectoryName(target_path));
File.Copy(assetbundle_path, target_path);
}
#region 相关文件
//Manifest加入补丁
if(isMainPackage)
{
string manifest_folder = VFSUtil.GetMainPackage_AssetBundleManifests_Folder(source_packages_root_folder);
if (Directory.Exists(manifest_folder))
{
string target_path = Path.Combine(patch_temp_folder_path, VFSConst.MainPackage_AssetBundleManifests_Folder);
XDirectory.CopyDir(manifest_folder, target_path);
}
}
else
{
string manifest_path = VFSUtil.GetExtensionGroups_AssetBundleManifests_FilePath(source_packages_root_folder, branch.ExtensionGroupName);
if (File.Exists(manifest_path))
{
string target_path = Path.Combine(patch_temp_folder_path, VFSConst.AssetBundleManifestFileName);
File.Copy(manifest_path, target_path, true);
}
}
//options
if (!isMainPackage)
{
string options_path = VFSUtil.GetExtensionPackages_GroupOptions_FilePath(source_packages_root_folder, branch.ExtensionGroupName);
if (File.Exists(options_path))
{
string target_path = Path.Combine(patch_temp_folder_path, VFSConst.ExtensionGroup_GroupOption_FileName);
File.Copy(options_path, target_path, true);
}
}
//vfs config
if (isMainPackage)
{
string conf_path = VFSUtil.GetVFSConfigFilePath_InPackages(source_packages_root_folder);
if (File.Exists(conf_path))
{
string target_path = Path.Combine(patch_temp_folder_path, VFSConst.Config_Runtime_FileName);
File.Copy(conf_path, target_path);
}
}
#endregion
//打包
string[] files = Directory.GetFiles(patch_temp_folder_path);
int total_len = files.Length;
long zip_counter = 0;
int zip_counter_t = 0;
//打包
ZipUtil.ZipDirectory(patch_temp_folder_path, save_file_name, fileName =>
{
if(EnableEditorGUI || EnableLog)
{
zip_counter++;
if (EnableLog)
{
Debug.Log($" [Make Patch]({zip_counter}/{total_len}):" + fileName);
}
if (EnableEditorGUI)
{
if (total_len > 100)
{
zip_counter_t++;
if (zip_counter_t >= 20)
{
zip_counter_t = 0;
if (EnableEditorGUI) EditorUtility.DisplayProgressBar("Create Zip", $"{zip_counter}/{total_len}\n{fileName}", zip_counter / total_len);
}
}
else
{
if (EnableEditorGUI) EditorUtility.DisplayProgressBar("Create Zip", $"{zip_counter}/{total_len}\n{fileName}", zip_counter / total_len);
}
}
}
});
if (EnableEditorGUI)
EditorUtility.ClearProgressBar();
Directory.Delete(patch_temp_folder_path, true); //Debug临时注释掉
Application.OpenURL(new Uri(save_folder).ToString());
}
//通过版本中存档的二进制记录制作补丁
private void makePatchByVersionBinary(ref VersionBranch branch, ref VersionRecord target_record, long cur_version_code, string target_data_root_path, string current_data_root_path ,string current_binary_root_path, string save_folder, string save_file_name)
{
string platform_name = TinaX.Utils.XPlatformUtil.GetNameText(branch.Platform);
string target_asset_hash_path = Path.Combine(target_data_root_path, VFSConst.AssetsHashFileName);
string current_asset_hash_path = Path.Combine(current_data_root_path, VFSConst.AssetsHashFileName);
bool isMainPackage = (branch.BType == VersionBranch.BranchType.MainPackage);
if (!File.Exists(target_asset_hash_path)) Debug.LogError($"[TinaX.VFS][PatchGenerator] The record corresponding to the target version is lost. ");
if (!File.Exists(current_asset_hash_path)) Debug.LogError($"[TinaX.VFS][PatchGenerator] The record corresponding to the source packages is lost. ");
var asset_hash_obj_target = XConfig.GetJson(target_asset_hash_path, AssetLoadType.SystemIO, false);
var asset_hash_obj_current = XConfig.GetJson(current_asset_hash_path, AssetLoadType.SystemIO, false);
PatchRecords records = new PatchRecords();
if (isMainPackage)
{
var current_vfsconfig = XConfig.GetJson(Path.Combine(current_data_root_path, VFSConst.Config_Runtime_FileName), AssetLoadType.SystemIO, false);
var config_mgr = new ConfigMiniManager(current_vfsconfig,
groupName => {
return Path.Combine(current_data_root_path, VFSConst.MainPackage_AssetBundleManifests_Folder, groupName.GetMD5(true, true) + ".json");
});
var target_vfsconfig = XConfig.GetJson(Path.Combine(target_data_root_path, VFSConst.Config_Runtime_FileName), AssetLoadType.SystemIO, false);
var config_mgr_target = new ConfigMiniManager(target_vfsconfig,
groupName => {
return Path.Combine(target_data_root_path, VFSConst.MainPackage_AssetBundleManifests_Folder, groupName.GetMD5(true, true) + ".json");
});
string ext_name_source = current_vfsconfig.AssetBundleFileExtension;
if (!ext_name_source.StartsWith("."))
ext_name_source = "." + ext_name_source;
ext_name_source = ext_name_source.ToLower();
if (EnableEditorGUI)
EditorUtility.DisplayProgressBar("Patch Gen", IsChinese ? "正在对比Assets" : "Comparing assets", 0);
int counter = 0;
int counter_t = 0;
int len = asset_hash_obj_current.Files.Length;
//开始遍历逻辑
foreach (var item in asset_hash_obj_current.Files)
{
if (EnableEditorGUI)
{
counter++;
if (len > 100)
{
counter_t++;
if (counter_t > 50)
{
counter_t = 0;
EditorUtility.DisplayProgressBar("Patch Gen", (IsChinese ? "正在对比Assets" : "Comparing assets") + $"{counter} / {len}", counter / len);
}
}
else
{
EditorUtility.DisplayProgressBar("Patch Gen", (IsChinese ? "正在对比Assets" : "Comparing assets") + $"{counter} / {len}", counter / len);
}
}
//在组里查询一下
if (config_mgr.TryQueryAsset(item.p, out var group))
{
if (group.HandleMode == GroupHandleMode.LocalOrRemote || group.HandleMode == GroupHandleMode.RemoteOnly)
continue;
//查询target里面有没有这个资源
if (asset_hash_obj_target.TryGetFileHashValue(item.p, out string hash_target))
{
string asset_bundle_name = group.GetAssetBundleNameOfAsset(item.p) + ext_name_source;
//有,检查hash
if (!hash_target.Equals(item.h))
{
//不一致
if (EnableLog)
Debug.Log(" [Modify Asset]" + item.p);
if (!records.Modify_ReadWrite.Contains(asset_bundle_name))
records.Modify_ReadWrite.Add(asset_bundle_name);
}
}
else
{
//没有,是新增资源
string asset_bundle_name = group.GetAssetBundleNameOfAsset(item.p) + ext_name_source;
if (EnableLog)
Debug.Log(" [New Asset]" + item.p);
if (!records.Add_ReadWrite.Contains(asset_bundle_name))
records.Add_ReadWrite.Add(asset_bundle_name);
}
}
}
if (EnableEditorGUI)
{
counter = 0;
counter_t = 0;
len = 0;
foreach (var group in config_mgr_target.mGroups)
{
if (group.FilesHash_VirtualDisk != null)
{
if (group.FilesHash_VirtualDisk.Files != null)
len += group.FilesHash_VirtualDisk.Files.Length;
}
}
}
//遍历target,寻找被删除的资源
foreach (var group in config_mgr_target.mGroups)
{
if (group.FilesHash_VirtualDisk != null && group.FilesHash_VirtualDisk.Files != null)
{
foreach (var item in group.FilesHash_VirtualDisk.Files)
{
if (EnableEditorGUI)
{
counter++;
if (len > 100)
{
counter_t++;
if (counter_t > 50)
{
counter_t = 0;
EditorUtility.DisplayProgressBar("Patch Gen", (IsChinese ? "正在对比Assets" : "Comparing assets") + $"{counter} / {len}", counter / len);
}
}
else
{
EditorUtility.DisplayProgressBar("Patch Gen", (IsChinese ? "正在对比Assets" : "Comparing assets") + $"{counter} / {len}", counter / len);
}
}
//找到我有的,但是current没有的
if (!config_mgr.TryFindAssetBundleName(item.p))
{
if (!records.Delete_ReadWrite.Contains(item.p))
records.Delete_ReadWrite.Add(item.p);
if (EnableLog)
Debug.Log(" [Delete AssetBundle]" + item.p);
}
}
}
}
if (EnableEditorGUI)
EditorUtility.ClearProgressBar();
}
else
{
var group_option_current = XConfig.GetJson(Path.Combine(current_data_root_path, VFSConst.ExtensionGroup_GroupOption_FileName), AssetLoadType.SystemIO, false);
var group_option_target = XConfig.GetJson(Path.Combine(target_data_root_path, VFSConst.ExtensionGroup_GroupOption_FileName), AssetLoadType.SystemIO, false);
var group_current = new VFSEditorGroup(group_option_current);
var group_target = new VFSEditorGroup(group_option_target);
//ab hash
group_current.SetVirtualDiskFileHash(XConfig.GetJson(VFSEditorUtil.GetVersionData_AssetBundle_HashFile_FolderOrFilePath(true, branch.BranchName, cur_version_code), AssetLoadType.SystemIO, false));
group_target.SetVirtualDiskFileHash(XConfig.GetJson(VFSEditorUtil.GetVersionData_AssetBundle_HashFile_FolderOrFilePath(true, branch.BranchName, target_record.versionCode), AssetLoadType.SystemIO, false));
var ext_group_info_current = XConfig.GetJson(Path.Combine(current_data_root_path, VFSConst.VFS_ExtensionGroupInfo_FileName), AssetLoadType.SystemIO, false);
string ab_ext_source = ext_group_info_current.AssetBundleExtension;
if (!ab_ext_source.StartsWith("."))
ab_ext_source = "." + ab_ext_source;
ab_ext_source = ab_ext_source.ToLower();
if (EnableEditorGUI)
EditorUtility.DisplayProgressBar("Patch Gen", IsChinese ? "正在对比Assets" : "Comparing assets", 0);
int counter = 0;
int counter_t = 0;
int len = asset_hash_obj_current.Files.Length;
//开始遍历
foreach (var item in asset_hash_obj_current.Files)
{
if (EnableEditorGUI)
{
counter++;
if (len > 100)
{
counter_t++;
if (counter_t > 50)
{
counter_t = 0;
EditorUtility.DisplayProgressBar("Patch Gen", (IsChinese ? "正在对比Assets" : "Comparing assets") + $"{counter} / {len}", counter / len);
}
}
else
{
EditorUtility.DisplayProgressBar("Patch Gen", (IsChinese ? "正在对比Assets" : "Comparing assets") + $"{counter} / {len}", counter / len);
}
}
//查询
if (group_current.IsAssetPathMatch(item.p))
{
//检查target里有没有
if (asset_hash_obj_target.TryGetFileHashValue(item.p, out string hash_target))
{
//有,检查hash
if (!hash_target.Equals(item.h))
{
//不一致
string asset_bundle_name = group_current.GetAssetBundleNameOfAsset(item.p) + ab_ext_source;
if (EnableLog)
Debug.Log(" [Modify Asset]" + item.p);
if (!records.Modify_ReadWrite.Contains(asset_bundle_name))
records.Modify_ReadWrite.Add(asset_bundle_name);
}
}
else
{
//没有,是新增资源
string asset_bundle_name = group_current.GetAssetBundleNameOfAsset(item.p) + ab_ext_source;
if (EnableLog)
Debug.Log(" [New Asset]" + item.p);
if (!records.Add_ReadWrite.Contains(asset_bundle_name))
records.Add_ReadWrite.Add(asset_bundle_name);
}
}
}
if (group_target.FilesHash_VirtualDisk.Files != null)
{
if (EnableEditorGUI)
{
counter = 0;
counter_t = 0;
len = group_target.FilesHash_VirtualDisk.Files.Length;
}
//遍历target,查找删除的资源
foreach (var item in group_target.FilesHash_VirtualDisk.Files)
{
if (EnableEditorGUI)
{
counter++;
if (len > 100)
{
counter_t++;
if (counter_t > 50)
{
counter_t = 0;
EditorUtility.DisplayProgressBar("Patch Gen", (IsChinese ? "正在对比Assets" : "Comparing assets") + $"{counter} / {len}", counter / len);
}
}
else
{
EditorUtility.DisplayProgressBar("Patch Gen", (IsChinese ? "正在对比Assets" : "Comparing assets") + $"{counter} / {len}", counter / len);
}
}
if (!group_current.FilesHash_VirtualDisk.TryGetFileHashValue(item.p, out _))
{
if (!records.Delete_ReadWrite.Contains(item.p))
records.Delete_ReadWrite.Add(item.p);
if (EnableLog)
Debug.Log(" [Delete AssetBundle]" + item.p);
}
}
}
if (EnableEditorGUI)
EditorUtility.ClearProgressBar();
}
PatchInfo patchInfo = new PatchInfo
{
branch = branch.BranchName,
patchCode = cur_version_code,
platform = branch.Platform,
targetVersionCode = target_record.versionCode,
extension = !isMainPackage,
extensionGroupName = branch.ExtensionGroupName
};
TinaX.IO.XDirectory.CreateIfNotExists(save_folder);
string patch_temp_folder_path = Path.Combine(save_folder, "tmp_" + StringHelper.GetRandom(6));
while (Directory.Exists(patch_temp_folder_path))
{
patch_temp_folder_path = Path.Combine(save_folder, "tmp_" + StringHelper.GetRandom(6));
}
Directory.CreateDirectory(patch_temp_folder_path);
//解压存档的zip
string binary_path = Path.Combine(current_binary_root_path, VFSEditorConst.VFS_VERSION_AssetsBinary_Zip_Name);
string current_binary_temp_folder_path = Path.Combine(current_binary_root_path, "tmp_" + StringHelper.GetRandom(4));
while (Directory.Exists(current_binary_temp_folder_path)) { current_binary_temp_folder_path = Path.Combine(current_binary_root_path, "tmp_" + StringHelper.GetRandom(4)); }
Directory.CreateDirectory(current_binary_temp_folder_path);
ZipUtil.UnZip(binary_path, current_binary_temp_folder_path);
//保存文件
string patch_info_save_path = Path.Combine(patch_temp_folder_path, VFSConst.Patch_Info_FileName);
string patch_record_save_path = Path.Combine(patch_temp_folder_path, VFSConst.Patch_Record_FileName);
XConfig.SaveJson(patchInfo, patch_info_save_path, AssetLoadType.SystemIO);
records.SaveReady();
XConfig.SaveJson(records, patch_record_save_path, AssetLoadType.SystemIO);
string patch_assets_path = Path.Combine(patch_temp_folder_path, VFSConst.Patch_Assets_Folder_Name);
Directory.CreateDirectory(patch_assets_path);
//复制文件
foreach (var item in records.Add_ReadWrite)
{
var assetbundle_path = Path.Combine(current_binary_temp_folder_path, item);
if (!File.Exists(assetbundle_path))
{
Debug.LogError((IsChinese ? "制作补丁时发生错误,找不到文件:" : "An error occurred while making the patch, the file could not be found:") + assetbundle_path);
continue;
}
string target_path = Path.Combine(patch_assets_path, item);
XDirectory.CreateIfNotExists(Path.GetDirectoryName(target_path));
File.Copy(assetbundle_path, target_path);
}
foreach (var item in records.Modify_ReadWrite)
{
var assetbundle_path = Path.Combine(current_binary_temp_folder_path, item);
if (!File.Exists(assetbundle_path))
{
Debug.LogError((IsChinese ? "制作补丁时发生错误,找不到文件:" : "An error occurred while making the patch, the file could not be found:") + assetbundle_path);
continue;
}
string target_path = Path.Combine(patch_assets_path, item);
XDirectory.CreateIfNotExists(Path.GetDirectoryName(target_path));
File.Copy(assetbundle_path, target_path);
}
#region 相关文件
//Manifest加入补丁
if (isMainPackage)
{
string manifest_folder = Path.Combine(current_data_root_path, VFSConst.MainPackage_AssetBundleManifests_Folder);
if (Directory.Exists(manifest_folder))
{
string target_path = Path.Combine(patch_temp_folder_path, VFSConst.MainPackage_AssetBundleManifests_Folder);
XDirectory.CopyDir(manifest_folder, target_path);
}
}
else
{
string manifest_path = Path.Combine(current_data_root_path, VFSConst.AssetBundleManifestFileName);
if (File.Exists(manifest_path))
{
string target_path = Path.Combine(patch_temp_folder_path, VFSConst.AssetBundleManifestFileName);
File.Copy(manifest_path, target_path, true);
}
}
//options
if (!isMainPackage)
{
string options_path = Path.Combine(current_data_root_path, VFSConst.ExtensionGroup_GroupOption_FileName);
if (File.Exists(options_path))
{
string target_path = Path.Combine(patch_temp_folder_path, VFSConst.ExtensionGroup_GroupOption_FileName);
File.Copy(options_path, target_path, true);
}
}
//vfs config
if (isMainPackage)
{
string conf_path = Path.Combine(current_data_root_path, VFSConst.Config_Runtime_FileName);
if (File.Exists(conf_path))
{
string target_path = Path.Combine(patch_temp_folder_path, VFSConst.Config_Runtime_FileName);
File.Copy(conf_path, target_path);
}
}
#endregion
//打包
string[] files = Directory.GetFiles(patch_temp_folder_path);
int total_len = files.Length;
long zip_counter = 0;
int zip_counter_t = 0;
//打包
ZipUtil.ZipDirectory(patch_temp_folder_path, save_file_name, fileName =>
{
if (EnableEditorGUI || EnableLog)
{
zip_counter++;
if (EnableLog)
{
Debug.Log($" [Make Patch]({zip_counter}/{total_len}):" + fileName);
}
if (EnableEditorGUI)
{
if (total_len > 100)
{
zip_counter_t++;
if (zip_counter_t >= 20)
{
zip_counter_t = 0;
if (EnableEditorGUI) EditorUtility.DisplayProgressBar("Create Zip", $"{zip_counter}/{total_len}\n{fileName}", zip_counter / total_len);
}
}
else
{
if (EnableEditorGUI) EditorUtility.DisplayProgressBar("Create Zip", $"{zip_counter}/{total_len}\n{fileName}", zip_counter / total_len);
}
}
}
});
if (EnableEditorGUI)
EditorUtility.ClearProgressBar();
Directory.Delete(patch_temp_folder_path, true); //Debug临时注释掉
Directory.Delete(current_binary_temp_folder_path, true);
Application.OpenURL(new Uri(save_folder).ToString());
}
private bool? _isChinese;
private bool IsChinese
{
get
{
if (_isChinese == null)
{
_isChinese = (Application.systemLanguage == SystemLanguage.Chinese || Application.systemLanguage == SystemLanguage.ChineseSimplified);
}
return _isChinese.Value;
}
}
class ConfigMiniManager
{
private VFSConfigJson mConfig;
public List mGroups;
public ConfigMiniManager(VFSConfigJson config, Func get_assetbundle_hash_callback)
{
mConfig = config;
mGroups = new List();
foreach(var group_option in mConfig.Groups)
{
if (group_option.GroupAssetsHandleMode == GroupHandleMode.LocalOrRemote || group_option.GroupAssetsHandleMode == GroupHandleMode.RemoteOnly)
continue;
if (group_option.ExtensionGroup)
continue;
mGroups.Add(new VFSEditorGroup(group_option));
}
//加载出assetbundle_hash
foreach(var group in mGroups)
{
string path = get_assetbundle_hash_callback(group.GroupName);
group.SetVirtualDiskFileHash(XConfig.GetJson(path, AssetLoadType.SystemIO, false));
}
}
public bool TryQueryAsset(string asset_path, out VFSEditorGroup group)
{
foreach(var _group in mGroups)
{
if (_group.IsAssetPathMatch(asset_path))
{
group = _group;
return true;
}
}
group = null;
return false;
}
public bool TryFindAssetBundleName(string assetBundleName)
{
foreach(var group in mGroups)
{
if(group.FilesHash_VirtualDisk != null)
{
return group.FilesHash_VirtualDisk.TryGetFileHashValue(assetBundleName, out _);
}
}
return false;
}
}
}
}