diff --git a/Assets/TEngine/Runtime/Core/GameConfig.cs b/Assets/TEngine/Runtime/Core/GameConfig.cs new file mode 100644 index 00000000..45d527a1 --- /dev/null +++ b/Assets/TEngine/Runtime/Core/GameConfig.cs @@ -0,0 +1,449 @@ +using System; +using System.Collections.Generic; +using System.IO; +using UnityEngine; +using UnityEngine.Networking; + +namespace TEngine +{ + [Serializable] + public struct VersonConfig + { + public string AppVersion; //底包版本号 + public string BaseResVersion; //基于底包对应的资源版本号 + public string ResVersion; //资源版本号 + } + + public class GameConfig + { + private static GameConfig _instance; + public const string CONFIG = "version.json"; + public const string RESTALLCONFIG = "restall.txt"; + public const string SUFFIX = "_Version"; + private VersonConfig _versionConfig; + private Dictionary _fileFixList = new Dictionary(); + public static GameConfig Instance + { + get + { + if (_instance == null) + { + _instance = new GameConfig(); + _instance.InitAppVersionInfo(); + } + + return _instance; + } + } + /// + /// 初始化版本信息 + /// + private bool InitAppVersionInfo() + { + string configContent = string.Empty; + var externalPath = Path.Combine(FileSystem.ResourceRoot, CONFIG); + //先找外部目录,找不到再找内部目录 + if (File.Exists(externalPath)) + { + configContent = File.ReadAllText(externalPath); + TLogger.LogInfo($"GameConfig,{externalPath} exit,info:{configContent}"); + } + else + { + configContent = GetInnerVersion(); + //内部目录的版本配置如果没读到,返回初始化配置失败 + if (configContent == string.Empty) + { +#if !UNITY_EDITOR +#if RELEASE_BUILD || _DEVELOPMENT_BUILD_ + TLogger.LogError($"version config not find in InnerPath,please check it"); +#endif + return false; +#endif + } + } + + if (!string.IsNullOrEmpty(configContent)) + { + try + { + _instance._versionConfig = JsonUtility.FromJson(configContent); + } + catch (Exception e) + { + TLogger.LogError($"configContent Conver failed,content{configContent}"); + return false; + } + //设置一下版本 + SetLoadFilePath(FileSystem.ResourceRoot, ResId); + return true; + } + else + { + //防止编辑器没有版本配置文件的情况 +#if UNITY_EDITOR + _instance._versionConfig = new VersonConfig + { + AppVersion = Application.version, + BaseResVersion = "0", + ResVersion = "0", + }; + UpdateConfig(); + SetLoadFilePath(FileSystem.ResourceRoot, ResId); + return true; +#else + TLogger.LogError("version vonfig miss,please check it"); + return false; +#endif + } + } + + /// + /// 获取内部version.json配置 + /// + /// + internal string GetInnerVersion() + { +#if UNITY_ANDROID && !UNITY_EDITOR + var innerPath = Path.Combine(FileSystem.ResourceRootInStreamAsset, CONFIG); +#else + var innerPath = $"file://{Path.Combine(FileSystem.ResourceRootInStreamAsset, CONFIG)}"; +#endif + var www = UnityWebRequest.Get(innerPath); + var request = www.SendWebRequest(); + while (!request.isDone) + { + } + var configContent = www.downloadHandler.text; + + TLogger.LogInfo($"GameConfig,{innerPath} exit,info:{configContent}"); + return configContent; + } + + /// + /// 底包版本号,设置3位 + /// + internal string AppId + { + get + { + return _versionConfig.AppVersion; + } + } + + /// + /// 资源版本号,设置1位 + /// + internal string ResId + { + get + { + return _versionConfig.ResVersion; + } + set + { + _versionConfig.ResVersion = value; + } + } + + //基础母包资源版本号 + internal string BaseResId() + { + return _versionConfig.BaseResVersion; + } + + /// + /// 游戏版本号,提供给外面展示使用 + /// + public string GameBundleVersion + { + get + { + return $"{AppId}.{ResId}"; + } + } + /// + /// 是否重新安装 + /// + /// + internal bool IsReinstall() + { + return File.Exists($"{FileSystem.ResourceRoot}/{RESTALLCONFIG}"); + } + + /// + /// 设置重新安装了 + /// + internal void SignReInstall() + { + ReInstallFinish(); + File.Create($"{FileSystem.ResourceRoot}/{RESTALLCONFIG}"); + } + /// + /// 去掉重新安装标志文件 + /// + internal void ReInstallFinish() + { + DeleteFile($"{FileSystem.ResourceRoot}/{RESTALLCONFIG}"); + } + + /// + /// 删除文件 + /// + /// 文件路径 + public static void DeleteFile(string filePath) + { + try + { + if (File.Exists(filePath)) + { + File.Delete(filePath); + } + } + catch (Exception e) + { + TLogger.LogError(e.ToString()); + } + } + + /// + /// 是否是首次解压 + /// + /// + internal bool IsFirst() + { + TLogger.LogInfo("ConfigInfo:" + _versionConfig.AppVersion + "|" + _versionConfig.ResVersion); + var innerVersion = GetInnerVersion(); + VersonConfig innerVersionConfig; + + if (innerVersion == string.Empty) + { + //配置都找不到,就直接返回解压流程了 + return true; + } + + try + { + innerVersionConfig = JsonUtility.FromJson(innerVersion); + } + catch (Exception e) + { + //如果反序列化失败了,就默认返回给业务逻辑需要解压了 + TLogger.LogError($"GameConfig,JsonUtility.FromJson failed,{innerVersion}"); + return true; + } + //版本号如果不一致,就表示要解压了 + if (innerVersionConfig.AppVersion != _versionConfig.AppVersion) + { + return true; + } + //如果版本号一致,就看是否解压过 + var extrnalPath = Path.Combine(FileSystem.ResourceRoot, CONFIG); + return !File.Exists(extrnalPath); + } + + internal bool ResetVersionConfig() + { + return InitAppVersionInfo(); + } + + /// + /// 写入资源版本号 + /// + /// + internal void WriteVersion(string resId) + { + if (string.IsNullOrEmpty(resId)) + { + TLogger.LogWarning("resversion is null or empty,please check!"); + return; + } + + _versionConfig.ResVersion = resId; + UpdateConfig(); + SetLoadFilePath(FileSystem.ResourceRoot, ResId); + TLogger.LogInfo("GameConfig,WriteVersion to sdk:" + GameConfig.Instance.GameBundleVersion); + } + + private void UpdateConfig() + { + try + { + string path = Path.Combine(FileSystem.ResourceRoot, CONFIG); + if (!File.Exists(path)) + { + MakeAllDirectory(path); + FileStream file = File.Create(path); + file.Close(); + } + File.WriteAllText(path, JsonUtility.ToJson(_versionConfig)); + } + catch (Exception e) + { + TLogger.LogError(e.StackTrace); + } + } + + private static readonly char[] DirectorySeperators = + { + Path.DirectorySeparatorChar, + Path.AltDirectorySeparatorChar, + }; + + /// + /// 创建文件夹 + /// + /// 目标目录地址 + /// + public static void MakeAllDirectory(string path, bool is_last_file = true) + { + try + { + if (is_last_file) + { + path = Path.GetDirectoryName(path); + } + if (path == null) + return; + + var pathFragments = path.Split(DirectorySeperators); + if (pathFragments.Length <= 0) + return; + + path = pathFragments[0]; + if (path != string.Empty && !Directory.Exists(path)) + Directory.CreateDirectory(path); + + for (var i = 1; i < pathFragments.Length; i++) + { + path += Path.DirectorySeparatorChar; + + string pathFragment = pathFragments[i]; + if (string.IsNullOrEmpty(pathFragment)) + continue; + + path += pathFragment; + if (!Directory.Exists(path)) + Directory.CreateDirectory(path); + } + } + catch (Exception e) + { + TLogger.LogError(e.ToString()); + throw; + } + } + + + /// + /// 获取后缀为SUFFIX的目录 + /// + /// + /// + internal List GetExitVersions(string path) + { + if (!Directory.Exists(path)) + { + Directory.CreateDirectory(path); + } + var dir = new DirectoryInfo(path); + var fileinfo = dir.GetFileSystemInfos(); + var tmp = new List(); + foreach (var file in fileinfo) + { + var subName = file.Name; + if (!(file is DirectoryInfo)) continue; + + if (subName.IndexOf(SUFFIX) >= 0) + { + tmp.Add(subName.Split('_')[0]); + } + } + return tmp; + } + /// + /// 检测本地是否存在了该版本 + /// + /// + /// + internal bool CheckLocalVersion(string resVersion) + { + var versionLocal = GetExitVersions(FileSystem.ResourceRoot); + foreach (var item in versionLocal) + { + TLogger.LogInfo(item + "|" + resVersion); + if (item == resVersion) + { + return true; + } + } + + return false; + } + + /// + /// 设置最新的文件路径 + /// + public void SetLoadFilePath(string path, string version, bool userDlc = false) + { + var dirs = GetExitVersions(path); + + dirs.Sort((a, b) => VersionToLong(b) < VersionToLong(a) ? 1 : -1); + + _fileFixList.Clear(); + foreach (var item in dirs) + { + if (VersionToLong(item) > VersionToLong(version)) + { + continue; + } + var subFolder = $"{item}{SUFFIX}/"; + var dirPath = $"{path}/{subFolder}"; + var dir = new DirectoryInfo(dirPath); + var tempFile = dir.GetFileSystemInfos("*", SearchOption.AllDirectories); + foreach (var file in tempFile) + { + var subFilename = file.FullName.FixPath().Replace(subFolder, ""); + subFilename = subFilename.Replace("Assets/../", ""); + if (_fileFixList.ContainsKey(subFilename)) + { + _fileFixList[subFilename] = file.FullName.FixPath(); + } + else + { + _fileFixList.Add(subFilename, file.FullName.FixPath()); + } + } + } + } + /// + /// 版本号转换成long + /// + /// 版本号 + /// + private long VersionToLong(string str) + { + return long.Parse(str.Replace(".", "")); + } + /// + /// 返回资源所在的最新目录 + /// + /// + /// + internal string FilePath(string path) + { + path = path.FixPath(); +#if UNITY_EDITOR_WIN + path = path.Replace("Assets/../", ""); +#endif + if (_fileFixList.ContainsKey(path)) + { + return _fileFixList[path]; + } + else + { + return path; + } + } + } +} diff --git a/Assets/TEngine/Runtime/Core/GameConfig.cs.meta b/Assets/TEngine/Runtime/Core/GameConfig.cs.meta new file mode 100644 index 00000000..f5c334bd --- /dev/null +++ b/Assets/TEngine/Runtime/Core/GameConfig.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 87729e741a5e98543a31159acec95e3b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TResources/DLL/TEngineHotUpdate.dll b/Assets/TResources/DLL/TEngineHotUpdate.dll index 322b3a7b..6fb3927a 100644 Binary files a/Assets/TResources/DLL/TEngineHotUpdate.dll and b/Assets/TResources/DLL/TEngineHotUpdate.dll differ diff --git a/TEngineHotUpdate/TEngineHotUpdate.csproj b/TEngineHotUpdate/TEngineHotUpdate.csproj index e9c7ea64..1e8fac4f 100644 --- a/TEngineHotUpdate/TEngineHotUpdate.csproj +++ b/TEngineHotUpdate/TEngineHotUpdate.csproj @@ -1,5 +1,6 @@  - + Debug @@ -65,6 +66,12 @@ UnityLib\UnityEditor.dll + + UnityLib\UnityEngine.JSONSerializeModule.dll + + + UnityLib\UnityEngine.UnityWebRequestModule.dll + diff --git a/TEngineHotUpdate/UnityLib/UnityEngine.JSONSerializeModule.dll b/TEngineHotUpdate/UnityLib/UnityEngine.JSONSerializeModule.dll new file mode 100644 index 00000000..8e6e8028 Binary files /dev/null and b/TEngineHotUpdate/UnityLib/UnityEngine.JSONSerializeModule.dll differ diff --git a/TEngineHotUpdate/UnityLib/UnityEngine.UnityWebRequestModule.dll b/TEngineHotUpdate/UnityLib/UnityEngine.UnityWebRequestModule.dll new file mode 100644 index 00000000..0585b0e2 Binary files /dev/null and b/TEngineHotUpdate/UnityLib/UnityEngine.UnityWebRequestModule.dll differ diff --git a/TEngineHotUpdate/src/TEngineCore/Core/GameConfig.cs b/TEngineHotUpdate/src/TEngineCore/Core/GameConfig.cs new file mode 100644 index 00000000..6dab2d24 --- /dev/null +++ b/TEngineHotUpdate/src/TEngineCore/Core/GameConfig.cs @@ -0,0 +1,449 @@ +using System; +using System.Collections.Generic; +using System.IO; +using UnityEngine; +using UnityEngine.Networking; + +namespace TEngineCore +{ + [Serializable] + public struct VersonConfig + { + public string AppVersion; //底包版本号 + public string BaseResVersion; //基于底包对应的资源版本号 + public string ResVersion; //资源版本号 + } + + public class GameConfig + { + private static GameConfig _instance; + public const string CONFIG = "version.json"; + public const string RESTALLCONFIG = "restall.txt"; + public const string SUFFIX = "_Version"; + private VersonConfig _versionConfig; + private Dictionary _fileFixList = new Dictionary(); + public static GameConfig Instance + { + get + { + if (_instance == null) + { + _instance = new GameConfig(); + _instance.InitAppVersionInfo(); + } + + return _instance; + } + } + /// + /// 初始化版本信息 + /// + private bool InitAppVersionInfo() + { + string configContent = string.Empty; + var externalPath = Path.Combine(FileSystem.ResourceRoot, CONFIG); + //先找外部目录,找不到再找内部目录 + if (File.Exists(externalPath)) + { + configContent = File.ReadAllText(externalPath); + TLogger.LogInfo($"GameConfig,{externalPath} exit,info:{configContent}"); + } + else + { + configContent = GetInnerVersion(); + //内部目录的版本配置如果没读到,返回初始化配置失败 + if (configContent == string.Empty) + { +#if !UNITY_EDITOR +#if RELEASE_BUILD || _DEVELOPMENT_BUILD_ + TLogger.LogError($"version config not find in InnerPath,please check it"); +#endif + return false; +#endif + } + } + + if (!string.IsNullOrEmpty(configContent)) + { + try + { + _instance._versionConfig = JsonUtility.FromJson(configContent); + } + catch (Exception e) + { + TLogger.LogError($"configContent Conver failed,content{configContent}"); + return false; + } + //设置一下版本 + SetLoadFilePath(FileSystem.ResourceRoot, ResId); + return true; + } + else + { + //防止编辑器没有版本配置文件的情况 +#if UNITY_EDITOR + _instance._versionConfig = new VersonConfig + { + AppVersion = Application.version, + BaseResVersion = "0", + ResVersion = "0", + }; + UpdateConfig(); + SetLoadFilePath(FileSystem.ResourceRoot, ResId); + return true; +#else + TLogger.LogError("version vonfig miss,please check it"); + return false; +#endif + } + } + + /// + /// 获取内部version.json配置 + /// + /// + internal string GetInnerVersion() + { +#if UNITY_ANDROID && !UNITY_EDITOR + var innerPath = Path.Combine(FileSystem.ResourceRootInStreamAsset, CONFIG); +#else + var innerPath = $"file://{Path.Combine(FileSystem.ResourceRootInStreamAsset, CONFIG)}"; +#endif + var www = UnityWebRequest.Get(innerPath); + var request = www.SendWebRequest(); + while (!request.isDone) + { + } + var configContent = www.downloadHandler.text; + + TLogger.LogInfo($"GameConfig,{innerPath} exit,info:{configContent}"); + return configContent; + } + + /// + /// 底包版本号,设置3位 + /// + internal string AppId + { + get + { + return _versionConfig.AppVersion; + } + } + + /// + /// 资源版本号,设置1位 + /// + internal string ResId + { + get + { + return _versionConfig.ResVersion; + } + set + { + _versionConfig.ResVersion = value; + } + } + + //基础母包资源版本号 + internal string BaseResId() + { + return _versionConfig.BaseResVersion; + } + + /// + /// 游戏版本号,提供给外面展示使用 + /// + public string GameBundleVersion + { + get + { + return $"{AppId}.{ResId}"; + } + } + /// + /// 是否重新安装 + /// + /// + internal bool IsReinstall() + { + return File.Exists($"{FileSystem.ResourceRoot}/{RESTALLCONFIG}"); + } + + /// + /// 设置重新安装了 + /// + internal void SignReInstall() + { + ReInstallFinish(); + File.Create($"{FileSystem.ResourceRoot}/{RESTALLCONFIG}"); + } + /// + /// 去掉重新安装标志文件 + /// + internal void ReInstallFinish() + { + DeleteFile($"{FileSystem.ResourceRoot}/{RESTALLCONFIG}"); + } + + /// + /// 删除文件 + /// + /// 文件路径 + public static void DeleteFile(string filePath) + { + try + { + if (File.Exists(filePath)) + { + File.Delete(filePath); + } + } + catch (Exception e) + { + TLogger.LogError(e.ToString()); + } + } + + /// + /// 是否是首次解压 + /// + /// + internal bool IsFirst() + { + TLogger.LogInfo("ConfigInfo:" + _versionConfig.AppVersion + "|" + _versionConfig.ResVersion); + var innerVersion = GetInnerVersion(); + VersonConfig innerVersionConfig; + + if (innerVersion == string.Empty) + { + //配置都找不到,就直接返回解压流程了 + return true; + } + + try + { + innerVersionConfig = JsonUtility.FromJson(innerVersion); + } + catch (Exception e) + { + //如果反序列化失败了,就默认返回给业务逻辑需要解压了 + TLogger.LogError($"GameConfig,JsonUtility.FromJson failed,{innerVersion}"); + return true; + } + //版本号如果不一致,就表示要解压了 + if (innerVersionConfig.AppVersion != _versionConfig.AppVersion) + { + return true; + } + //如果版本号一致,就看是否解压过 + var extrnalPath = Path.Combine(FileSystem.ResourceRoot, CONFIG); + return !File.Exists(extrnalPath); + } + + internal bool ResetVersionConfig() + { + return InitAppVersionInfo(); + } + + /// + /// 写入资源版本号 + /// + /// + internal void WriteVersion(string resId) + { + if (string.IsNullOrEmpty(resId)) + { + TLogger.LogWarning("resversion is null or empty,please check!"); + return; + } + + _versionConfig.ResVersion = resId; + UpdateConfig(); + SetLoadFilePath(FileSystem.ResourceRoot, ResId); + TLogger.LogInfo("GameConfig,WriteVersion to sdk:" + GameConfig.Instance.GameBundleVersion); + } + + private void UpdateConfig() + { + try + { + string path = Path.Combine(FileSystem.ResourceRoot, CONFIG); + if (!File.Exists(path)) + { + MakeAllDirectory(path); + FileStream file = File.Create(path); + file.Close(); + } + File.WriteAllText(path, JsonUtility.ToJson(_versionConfig)); + } + catch (Exception e) + { + TLogger.LogError(e.StackTrace); + } + } + + private static readonly char[] DirectorySeperators = + { + Path.DirectorySeparatorChar, + Path.AltDirectorySeparatorChar, + }; + + /// + /// 创建文件夹 + /// + /// 目标目录地址 + /// + public static void MakeAllDirectory(string path, bool is_last_file = true) + { + try + { + if (is_last_file) + { + path = Path.GetDirectoryName(path); + } + if (path == null) + return; + + var pathFragments = path.Split(DirectorySeperators); + if (pathFragments.Length <= 0) + return; + + path = pathFragments[0]; + if (path != string.Empty && !Directory.Exists(path)) + Directory.CreateDirectory(path); + + for (var i = 1; i < pathFragments.Length; i++) + { + path += Path.DirectorySeparatorChar; + + string pathFragment = pathFragments[i]; + if (string.IsNullOrEmpty(pathFragment)) + continue; + + path += pathFragment; + if (!Directory.Exists(path)) + Directory.CreateDirectory(path); + } + } + catch (Exception e) + { + TLogger.LogError(e.ToString()); + throw; + } + } + + + /// + /// 获取后缀为SUFFIX的目录 + /// + /// + /// + internal List GetExitVersions(string path) + { + if (!Directory.Exists(path)) + { + Directory.CreateDirectory(path); + } + var dir = new DirectoryInfo(path); + var fileinfo = dir.GetFileSystemInfos(); + var tmp = new List(); + foreach (var file in fileinfo) + { + var subName = file.Name; + if (!(file is DirectoryInfo)) continue; + + if (subName.IndexOf(SUFFIX) >= 0) + { + tmp.Add(subName.Split('_')[0]); + } + } + return tmp; + } + /// + /// 检测本地是否存在了该版本 + /// + /// + /// + internal bool CheckLocalVersion(string resVersion) + { + var versionLocal = GetExitVersions(FileSystem.ResourceRoot); + foreach (var item in versionLocal) + { + TLogger.LogInfo(item + "|" + resVersion); + if (item == resVersion) + { + return true; + } + } + + return false; + } + + /// + /// 设置最新的文件路径 + /// + public void SetLoadFilePath(string path, string version, bool userDlc = false) + { + var dirs = GetExitVersions(path); + + dirs.Sort((a, b) => VersionToLong(b) < VersionToLong(a) ? 1 : -1); + + _fileFixList.Clear(); + foreach (var item in dirs) + { + if (VersionToLong(item) > VersionToLong(version)) + { + continue; + } + var subFolder = $"{item}{SUFFIX}/"; + var dirPath = $"{path}/{subFolder}"; + var dir = new DirectoryInfo(dirPath); + var tempFile = dir.GetFileSystemInfos("*", SearchOption.AllDirectories); + foreach (var file in tempFile) + { + var subFilename = file.FullName.FixPath().Replace(subFolder, ""); + subFilename = subFilename.Replace("Assets/../", ""); + if (_fileFixList.ContainsKey(subFilename)) + { + _fileFixList[subFilename] = file.FullName.FixPath(); + } + else + { + _fileFixList.Add(subFilename, file.FullName.FixPath()); + } + } + } + } + /// + /// 版本号转换成long + /// + /// 版本号 + /// + private long VersionToLong(string str) + { + return long.Parse(str.Replace(".", "")); + } + /// + /// 返回资源所在的最新目录 + /// + /// + /// + internal string FilePath(string path) + { + path = path.FixPath(); +#if UNITY_EDITOR_WIN + path = path.Replace("Assets/../", ""); +#endif + if (_fileFixList.ContainsKey(path)) + { + return _fileFixList[path]; + } + else + { + return path; + } + } + } +}