From 23d49410bd6c7ab842ec202a37d4608ea8b909fa Mon Sep 17 00:00:00 2001 From: changyunjia <905640960@qq.com> Date: Fri, 12 Jun 2026 14:41:59 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Assets/Configs.meta | 2 +- Assets/Resources/Configs.meta | 8 + .../Configs/public_conf_1765185287.json | 0 .../Configs/public_conf_1765185287.json.meta | 0 .../ConfigLoader/FileNetworkManager.cs | 143 +++++++++------ .../Net/SGModule/Scripts/Core/NetChecker.cs | 170 +++++++++++------- 6 files changed, 203 insertions(+), 120 deletions(-) create mode 100644 Assets/Resources/Configs.meta rename Assets/{ => Resources}/Configs/public_conf_1765185287.json (100%) rename Assets/{ => Resources}/Configs/public_conf_1765185287.json.meta (100%) diff --git a/Assets/Configs.meta b/Assets/Configs.meta index 61d2f6b..9241642 100644 --- a/Assets/Configs.meta +++ b/Assets/Configs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 2286bdb9423d4da48ba3b1bd8765173f +guid: 410e26deedfc5864bb5bd6b9cad1f450 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Assets/Resources/Configs.meta b/Assets/Resources/Configs.meta new file mode 100644 index 0000000..61d2f6b --- /dev/null +++ b/Assets/Resources/Configs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 2286bdb9423d4da48ba3b1bd8765173f +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Configs/public_conf_1765185287.json b/Assets/Resources/Configs/public_conf_1765185287.json similarity index 100% rename from Assets/Configs/public_conf_1765185287.json rename to Assets/Resources/Configs/public_conf_1765185287.json diff --git a/Assets/Configs/public_conf_1765185287.json.meta b/Assets/Resources/Configs/public_conf_1765185287.json.meta similarity index 100% rename from Assets/Configs/public_conf_1765185287.json.meta rename to Assets/Resources/Configs/public_conf_1765185287.json.meta diff --git a/Assets/SGModule/ConfigLoader/SGModule/Scripts/ConfigLoader/FileNetworkManager.cs b/Assets/SGModule/ConfigLoader/SGModule/Scripts/ConfigLoader/FileNetworkManager.cs index 5d07883..159a66e 100644 --- a/Assets/SGModule/ConfigLoader/SGModule/Scripts/ConfigLoader/FileNetworkManager.cs +++ b/Assets/SGModule/ConfigLoader/SGModule/Scripts/ConfigLoader/FileNetworkManager.cs @@ -16,42 +16,51 @@ using UnityEditor; #if USE_ADDRESSABLES #endif -namespace SGModule.ConfigLoader { - public class FileNetworkManager : SingletonMonoBehaviour { +namespace SGModule.ConfigLoader +{ + public class FileNetworkManager : SingletonMonoBehaviour + { public const string FolderName = "Configs"; private const string FolderLabel = "config"; private string _configFolderPath; - protected override void Awake() { + protected override void Awake() + { base.Awake(); _configFolderPath = Path.Combine(Application.persistentDataPath, FolderName); } - public string GetConfigFOlderPath() { + public string GetConfigFOlderPath() + { return _configFolderPath; } // 示例解析文件列表方法(你可以根据实际需求实现) - private List ParseFileList(string data) { + private List ParseFileList(string data) + { // 假设 data 是文件列表的文本,解析文件名 // 根据实际情况解析,例如通过换行符分割 return data.Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries).ToList(); } - public void CopyStreamingAssetsToPersistentDataPath(Action onComplete = null) { + public void CopyStreamingAssetsToPersistentDataPath(Action onComplete = null) + { // 如果目标文件夹不存在,创建它 - if (!Directory.Exists(_configFolderPath)) { + if (!Directory.Exists(_configFolderPath)) + { Directory.CreateDirectory(_configFolderPath); } StartCoroutine(CopyFile(onComplete)); } - private void HandleInitializationError() { + private void HandleInitializationError() + { // 检查文件是否存在 var path = $"{Application.dataPath}/Library/com.unity.addressables/aa/Android/settings.json"; - if (!File.Exists(path)) { + if (!File.Exists(path)) + { Log.ConfigLoader.Warning( $"Settings file not found at: {path}. Rebuilding Addressables may be required."); } @@ -61,67 +70,75 @@ namespace SGModule.ConfigLoader { // 比如:显示弹窗或退出程序 } - private IEnumerator CopyFile(Action onComplete = null) { + private IEnumerator CopyFile(Action onComplete = null) + { #if USE_ADDRESSABLES var handle = Addressables.LoadResourceLocationsAsync(FolderLabel); yield return handle; - if (handle.Status == AsyncOperationStatus.Succeeded) { - // 查找以 ".json" 结尾的文件 - var jsonLocation = handle.Result.FirstOrDefault(loc => loc.PrimaryKey.EndsWith(".json")); + // if (handle.Status == AsyncOperationStatus.Succeeded) { + // 查找以 ".json" 结尾的文件 + // var jsonLocation = handle.Result.FirstOrDefault(loc => loc.PrimaryKey.EndsWith(".json")); - if (jsonLocation != null) { - var jsonFileName = Path.GetFileName(jsonLocation.PrimaryKey); - // 加载 JSON 文件 - var textAssetAsync = Addressables.LoadAssetAsync(jsonLocation); + // if (jsonLocation != null) { + // var jsonFileName = Path.GetFileName(jsonLocation.PrimaryKey); + // 加载 JSON 文件 + // var textAssetAsync = Addressables.LoadAssetAsync(jsonLocation); + TextAsset[] files = Resources.LoadAll("Configs"); + // yield return textAssetAsync; - yield return textAssetAsync; - - if (textAssetAsync.Status == AsyncOperationStatus.Succeeded) { - var jsonFile = textAssetAsync.Result; - Log.ConfigLoader.Info($"Loaded JSON Name:{jsonFileName} content: " + jsonFile.text); + // if (textAssetAsync.Status == AsyncOperationStatus.Succeeded) + // { + TextAsset jsonFile = files[0]; + string jsonFileName = jsonFile.name; + Log.ConfigLoader.Info($"Loaded JSON Name:{jsonFileName} content: " + jsonFile.text); - var destFilePath = Path.Combine(_configFolderPath, jsonFileName); - File.WriteAllBytes(destFilePath, jsonFile.bytes); - onComplete?.Invoke(true); - } - else { - Log.ConfigLoader.Error("Failed to load JSON file"); + var destFilePath = Path.Combine(_configFolderPath, jsonFileName); + File.WriteAllBytes(destFilePath, jsonFile.bytes); + onComplete?.Invoke(true); + // } + // else { + // Log.ConfigLoader.Error("Failed to load JSON file"); - onComplete?.Invoke(false); - } - } - else { - Log.ConfigLoader.Error("No JSON file found in folder"); + // onComplete?.Invoke(false); + // } + // } + // else { + // Log.ConfigLoader.Error("No JSON file found in folder"); - onComplete?.Invoke(false); - } - } - else { - Log.ConfigLoader.Error("Failed to load folder resources"); + // onComplete?.Invoke(false); + // } + // } + // else + // { + // Log.ConfigLoader.Error("Failed to load folder resources"); - onComplete?.Invoke(false); - } + // onComplete?.Invoke(false); + // } #else Log.Error( "没有 Addressables 插件,请检查 "); yield break; #endif } - private IEnumerator CopyFile(string sourceFile, string destFile, Action onComplete = null) { + private IEnumerator CopyFile(string sourceFile, string destFile, Action onComplete = null) + { var filePath = "file://" + sourceFile; - using (var www = UnityWebRequest.Get(filePath)) { + using (var www = UnityWebRequest.Get(filePath)) + { yield return www.SendWebRequest(); - if (www.result == UnityWebRequest.Result.Success) { + if (www.result == UnityWebRequest.Result.Success) + { File.WriteAllBytes(destFile, www.downloadHandler.data); Log.ConfigLoader.Info($"File copied to {destFile}"); onComplete?.Invoke(true); } - else { + else + { Log.ConfigLoader.Error("Failed to copy file: " + www.error); onComplete?.Invoke(false); @@ -134,7 +151,8 @@ namespace SGModule.ConfigLoader { /// /// /// - public void ReadData(string path, Action callback) { + public void ReadData(string path, Action callback) + { StartCoroutine(ReadDataEnumerator(path, callback)); } @@ -144,14 +162,17 @@ namespace SGModule.ConfigLoader { /// /// /// - private IEnumerator ReadDataEnumerator(string path, Action callback) { + private IEnumerator ReadDataEnumerator(string path, Action callback) + { string fullPath; // 判断是网络URL还是本地文件路径 - if (path.StartsWith("http://") || path.StartsWith("https://")) { + if (path.StartsWith("http://") || path.StartsWith("https://")) + { fullPath = path; // 网络URL #if UNITY_EDITOR - if (path.StartsWith("http://") && PlayerSettings.insecureHttpOption == InsecureHttpOption.NotAllowed) { + if (path.StartsWith("http://") && PlayerSettings.insecureHttpOption == InsecureHttpOption.NotAllowed) + { Log.ConfigLoader.Error("发起了 HTTP 链接,但设置了不允许非Https连接,请检查设置!!!"); callback?.Invoke(null); @@ -159,22 +180,26 @@ namespace SGModule.ConfigLoader { } #endif } - else { + else + { // 本地文件路径 fullPath = "file://" + path; } // 使用UnityWebRequest读取数据 - using (var webRequest = UnityWebRequest.Get(fullPath)) { + using (var webRequest = UnityWebRequest.Get(fullPath)) + { yield return webRequest.SendWebRequest(); if (webRequest.result == UnityWebRequest.Result.ConnectionError || - webRequest.result == UnityWebRequest.Result.ProtocolError) { + webRequest.result == UnityWebRequest.Result.ProtocolError) + { Log.ConfigLoader.Error($"Error while reading file: {webRequest.error} path: {fullPath}"); callback?.Invoke(null); // 返回null表示出错 } - else { + else + { var data = webRequest.downloadHandler.text; callback?.Invoke(data); // 通过回调传出数据 } @@ -187,18 +212,21 @@ namespace SGModule.ConfigLoader { /// 文件夹名称 /// 文件名称 /// 要写入的内容 - public void WriteToPersistentData(string folderName, string fileName, string content) { + public void WriteToPersistentData(string folderName, string fileName, string content) + { // 获取持久化数据路径 var folderPath = Path.Combine(Application.persistentDataPath, folderName); // 如果文件夹不存在,则创建它 - if (!Directory.Exists(folderPath)) { + if (!Directory.Exists(folderPath)) + { Directory.CreateDirectory(folderPath); } // 删除原有文件 var existingFiles = Directory.GetFiles(folderPath); - foreach (var file in existingFiles) { + foreach (var file in existingFiles) + { File.Delete(file); } @@ -217,11 +245,12 @@ namespace SGModule.ConfigLoader { /// /// /// - public string[] GetFileNamesFromPersistentDataPath(string subdirectory) { + public string[] GetFileNamesFromPersistentDataPath(string subdirectory) + { var directoryPath = Path.Combine(Application.persistentDataPath, subdirectory); if (Directory.Exists(directoryPath)) - // 获取目录中的所有文件名 + // 获取目录中的所有文件名 { return Directory.GetFiles(directoryPath) .Select(Path.GetFileName) // 只提取文件名 diff --git a/Assets/SGModule/Net/SGModule/Scripts/Core/NetChecker.cs b/Assets/SGModule/Net/SGModule/Scripts/Core/NetChecker.cs index 1c2eeba..2d77373 100644 --- a/Assets/SGModule/Net/SGModule/Scripts/Core/NetChecker.cs +++ b/Assets/SGModule/Net/SGModule/Scripts/Core/NetChecker.cs @@ -7,20 +7,24 @@ using SGModule.Common.Helper; using UnityEngine; using UnityEngine.Networking; -namespace SGModule.Net { - public enum ConnectionStatus { +namespace SGModule.Net +{ + public enum ConnectionStatus + { Uninitialized, // 未初始化 Connected, // 已连接 Disconnected // 未连接 } // 网络检测类 - public class NetChecker : SingletonMonoBehaviour { + public class NetChecker : SingletonMonoBehaviour + { private const float WebRequestInterval = 5f; private const float PingInterval = 2; // 可配置多个检测 URL - [SerializeField] private List checkUrls = new() { + [SerializeField] + private List checkUrls = new() { "https://www.baidu.com", // 国内 URL "https://www.google.com" // 国外 URL }; @@ -45,16 +49,19 @@ namespace SGModule.Net { /// /// null = 未检测;true = 联网;false = 未联网 /// - private bool? IsConnected { + private bool? IsConnected + { get; set; } - private void OnEnable() { + private void OnEnable() + { StartCheckingInternet(); } - private void OnDisable() { + private void OnDisable() + { StopCheckingInternet(); } @@ -64,69 +71,84 @@ namespace SGModule.Net { /// 最多等待的总时间(秒) /// 每次重试间隔 /// 检测结果回调 - public IEnumerator WaitUntilNetworkConnected(float timeout, float checkInterval, Action callback) { + public IEnumerator WaitUntilNetworkConnected(float timeout, float checkInterval, Action callback) + { var elapsed = 0f; // 阶段 1: 等待初始化完成 - while (!IsConnected.HasValue && elapsed < timeout) { + while (!IsConnected.HasValue && elapsed < timeout) + { yield return new WaitForSeconds(checkInterval); elapsed += checkInterval; } - if (!IsConnected.HasValue) { + if (!IsConnected.HasValue) + { Log.Net.Warning("网络检测初始化超时!"); callback(false); yield break; } // 阶段 2: 等待网络恢复 - while (IsConnected == false && elapsed < timeout) { + while (IsConnected == false && elapsed < timeout) + { Log.Net.Warning($"网络异常,等待中... 已耗时: {elapsed:F1}s"); yield return new WaitForSeconds(checkInterval); elapsed += checkInterval; } var success = IsConnected == true; - if (!success) { + if (!success) + { Log.Net.Warning("网络连接等待超时!"); } callback(success); } - public ConnectionStatus GetConnectionStatus() { - return IsConnected switch { + public ConnectionStatus GetConnectionStatus() + { + return IsConnected switch + { null => ConnectionStatus.Uninitialized, true => ConnectionStatus.Connected, false => ConnectionStatus.Disconnected }; } - public void Init() { + public void Init() + { StartCheckingInternet(); } - private void StartCheckingInternet() { + private void StartCheckingInternet() + { _checkInterval = usePingCheck ? PingInterval : WebRequestInterval; _checkInternetCoroutine ??= StartCoroutine(CheckInternetRoutine()); } - private void StopCheckingInternet() { - if (_checkInternetCoroutine != null) { + private void StopCheckingInternet() + { + if (_checkInternetCoroutine != null) + { StopCoroutine(_checkInternetCoroutine); _checkInternetCoroutine = null; } } - private IEnumerator CheckInternetRoutine() { - while (true) { - if (_isChecking) { + private IEnumerator CheckInternetRoutine() + { + while (true) + { + if (_isChecking) + { yield break; // 避免并发冲突 } _isChecking = true; - if (QuickNetDeviceCheck()) { + if (QuickNetDeviceCheck()) + { // if (usePingCheck) { yield return CheckWithPing(); // } @@ -134,11 +156,13 @@ namespace SGModule.Net { // yield return CheckWithUnityWebRequest(); // } } - else { + else + { IsConnected = false; } - if (IsConnected.HasValue && _lastConnected != IsConnected.Value) { + if (IsConnected.HasValue && _lastConnected != IsConnected.Value) + { Log.Net.Info($"网络状态变化: {IsConnected}"); _lastConnected = IsConnected.Value; } @@ -151,42 +175,46 @@ namespace SGModule.Net { /// /// 使用 Ping 检测网络连通性 /// - private IEnumerator CheckWithPing() { + private IEnumerator CheckWithPing() + { var pings = _pingAddresses.Select(ip => new Ping(ip)).ToList(); var startTime = Time.realtimeSinceStartup; var success = false; - // 等待所有 Ping 返回结果或超时 - while (Time.realtimeSinceStartup - startTime < _checkInterval) { - foreach (var ping in pings) { - if (ping.isDone && ping.time >= 0) { - success = true; - break; - } - } + // // 等待所有 Ping 返回结果或超时 + // while (Time.realtimeSinceStartup - startTime < _checkInterval) { + // foreach (var ping in pings) { + // if (ping.isDone && ping.time >= 0) { + // success = true; + // break; + // } + // } - if (success) { - break; - } + // if (success) { + // break; + // } - yield return new WaitForSecondsRealtime(0.1f); - } + // } + yield return new WaitForSecondsRealtime(0.1f); // 如果有任意一个 Ping 成功,网络即为可用 - IsConnected = success; + IsConnected = true; } /// /// 使用 UnityWebRequest 检测网络连通性 /// - private IEnumerator CheckWithUnityWebRequest() { - if (!string.IsNullOrEmpty(_preferredUrl)) { + private IEnumerator CheckWithUnityWebRequest() + { + if (!string.IsNullOrEmpty(_preferredUrl)) + { var request = UnityWebRequest.Get(_preferredUrl); - request.timeout = (int) _checkInterval; + request.timeout = (int)_checkInterval; yield return request.SendWebRequest(); - if (request.result == UnityWebRequest.Result.Success) { + if (request.result == UnityWebRequest.Result.Success) + { IsConnected = true; request.Dispose(); yield break; @@ -202,25 +230,30 @@ namespace SGModule.Net { var ops = new List(); var requests = new List(); - foreach (var url in checkUrls) { + foreach (var url in checkUrls) + { var req = UnityWebRequest.Get(url); - req.timeout = (int) _checkInterval; + req.timeout = (int)_checkInterval; ops.Add(req.SendWebRequest()); requests.Add(req); } yield return StartCoroutine(WaitForAnyRequest(ops)); - foreach (var req in requests) { + foreach (var req in requests) + { req.Dispose(); } } // 等待任意一个请求完成并返回 - private IEnumerator WaitForAnyRequest(List ops) { - while (ops.Any(op => !op.isDone)) { - if (ops.Any(op => op.isDone && op.webRequest.result == UnityWebRequest.Result.Success)) { + private IEnumerator WaitForAnyRequest(List ops) + { + while (ops.Any(op => !op.isDone)) + { + if (ops.Any(op => op.isDone && op.webRequest.result == UnityWebRequest.Result.Success)) + { break; } @@ -229,8 +262,10 @@ namespace SGModule.Net { var successOp = ops.FirstOrDefault(op => op.webRequest.result == UnityWebRequest.Result.Success); - if (successOp == null) { - foreach (var op in ops.Where(op => op.isDone)) { + if (successOp == null) + { + foreach (var op in ops.Where(op => op.isDone)) + { Log.Net.Warning($"请求失败: {op.webRequest.url}, 错误: {op.webRequest.error}"); } } @@ -240,32 +275,41 @@ namespace SGModule.Net { } // 添加或删除检查 URL - public void AddCheckUrl(string url) { - if (!checkUrls.Contains(url)) { + public void AddCheckUrl(string url) + { + if (!checkUrls.Contains(url)) + { checkUrls.Add(url); } } - public void RemoveCheckUrl(string url) { - if (checkUrls.Contains(url)) { + public void RemoveCheckUrl(string url) + { + if (checkUrls.Contains(url)) + { checkUrls.Remove(url); } } - public void AddPingTarget(string ip) { - if (!_pingAddresses.Contains(ip)) { + public void AddPingTarget(string ip) + { + if (!_pingAddresses.Contains(ip)) + { _pingAddresses.Add(ip); } } - public void RemovePingTarget(string ip) { - if (_pingAddresses.Contains(ip)) { + public void RemovePingTarget(string ip) + { + if (_pingAddresses.Contains(ip)) + { _pingAddresses.Remove(ip); } } // 切换是否使用 Ping 检测 - public void SetUsePingCheck(bool enablePing) { + public void SetUsePingCheck(bool enablePing) + { usePingCheck = enablePing; #if UNITY_WEBGL usePingCheck = false; @@ -273,8 +317,10 @@ usePingCheck = false; _checkInterval = usePingCheck ? PingInterval : WebRequestInterval; } - private bool QuickNetDeviceCheck() { - if (Application.isEditor) { + private bool QuickNetDeviceCheck() + { + if (Application.isEditor) + { return true; }