Files
AIGame/AIClient/Assets/AOT/Scripts/Patch/PatchRunner.cs
T
udxandCursor c94b8fefeb Add MainHUD gameplay UI, net handshake updates, and server maintenance mode.
Introduce UiManager/MainHUD prefab flow, strengthen GameNet/WsClient, and add AIServer maintenance endpoint support.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-21 17:34:42 +08:00

234 lines
8.5 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections;
using AOT.Boot;
using AOT.UI;
using UnityEngine;
using YooAsset;
namespace AOT.Patch
{
/// <summary>
/// 启动补丁:version 门控 → YooAsset 初始化/更新 → 回调。
/// </summary>
public sealed class PatchRunner
{
readonly MonoBehaviour _host;
readonly BootSettings _settings;
readonly BootPatchView _view;
public PatchRunner(MonoBehaviour host, BootSettings settings, BootPatchView view)
{
_host = host;
_settings = settings;
_view = view;
}
public IEnumerator Run(Action<BootContext, string> onDone)
{
_view?.SetStatus("检查版本…");
_view?.SetProgress(0.05f);
VersionInfo version = null;
string versionError = null;
yield return new VersionClient(_settings.VersionUrl, _settings.AllowStreamingVersionFallback)
.Fetch((info, err) =>
{
version = info;
versionError = err;
});
if (version == null)
{
// 无 CDN 时仍允许编辑器直通;真机 Host/Web 则失败。
if (!CanContinueWithoutVersion())
{
onDone?.Invoke(null, $"获取 version.json 失败: {versionError}");
yield break;
}
version = new VersionInfo
{
appVersionMin = _settings.AppVersion,
appVersionMax = _settings.AppVersion,
resVersion = "local",
cdnRoot = _settings.DefaultCdnRoot,
packages = new[] { _settings.PackageName },
};
_view?.SetStatus($"版本回退本地 ({versionError})");
}
else
{
_view?.SetStatus($"远程版本 {version.resVersion}");
}
if (VersionInfo.CompareVersion(_settings.AppVersion, version.appVersionMin) < 0)
{
onDone?.Invoke(null, $"壳版本过低,请更新微信小游戏包(当前 {_settings.AppVersion} < 最低 {version.appVersionMin})");
yield break;
}
string cdnRoot = string.IsNullOrEmpty(version.cdnRoot) ? _settings.DefaultCdnRoot : version.cdnRoot;
var ctx = new BootContext
{
AppVersion = _settings.AppVersion,
ResVersion = version.resVersion,
CdnRoot = cdnRoot,
PackageName = _settings.PackageName,
HttpBase = _settings.HttpBase,
WsUrlTemplate = _settings.WsUrlTemplate,
Notice = version.notice ?? string.Empty,
};
#if UNITY_EDITOR
if (_settings.EditorDirectHotUpdate)
{
_view?.SetStatus("编辑器直通热更(跳过资源补丁)");
_view?.SetProgress(0.9f);
onDone?.Invoke(ctx, null);
yield break;
}
#endif
_view?.SetStatus("初始化资源系统…");
_view?.SetProgress(0.15f);
if (!YooAssets.IsInitialized)
YooAssets.Initialize();
if (!YooAssets.TryGetPackage(_settings.PackageName, out var package))
package = YooAssets.CreatePackage(_settings.PackageName);
ctx.Package = package;
var playMode = ResolvePlayMode();
var initOp = CreateInitializeOperation(package, playMode, cdnRoot);
if (initOp == null)
{
onDone?.Invoke(null, $"不支持的 PlayMode: {playMode}");
yield break;
}
yield return initOp;
if (initOp.Status != EOperationStatus.Succeeded)
{
onDone?.Invoke(null, $"YooAsset 初始化失败: {initOp.Error}");
yield break;
}
_view?.SetStatus("请求资源版本…");
_view?.SetProgress(0.35f);
var versionOp = package.RequestPackageVersionAsync();
yield return versionOp;
if (versionOp.Status != EOperationStatus.Succeeded)
{
onDone?.Invoke(null, $"请求 PackageVersion 失败: {versionOp.Error}");
yield break;
}
_view?.SetStatus($"更新清单 {versionOp.PackageVersion}");
_view?.SetProgress(0.5f);
var manifestOp = package.LoadPackageManifestAsync(new LoadPackageManifestOptions(versionOp.PackageVersion, 60));
yield return manifestOp;
if (manifestOp.Status != EOperationStatus.Succeeded)
{
onDone?.Invoke(null, $"更新清单失败: {manifestOp.Error}");
yield break;
}
_view?.SetStatus("下载资源…");
var downloader = package.CreateResourceDownloader(new ResourceDownloaderOptions(10, 3));
if (downloader.TotalDownloadCount > 0)
{
downloader.DownloadProgressChanged += info =>
{
_view?.SetProgress(0.5f + info.Progress * 0.4f);
_view?.SetStatus($"下载 {info.CurrentDownloadCount}/{info.TotalDownloadCount}");
};
downloader.StartDownload();
yield return downloader;
if (downloader.Status != EOperationStatus.Succeeded)
{
onDone?.Invoke(null, $"下载失败: {downloader.Error}");
yield break;
}
}
_view?.SetProgress(0.95f);
_view?.SetStatus("资源就绪");
onDone?.Invoke(ctx, null);
}
bool CanContinueWithoutVersion()
{
#if UNITY_EDITOR
return _settings.EditorDirectHotUpdate;
#else
return false;
#endif
}
EPlayMode ResolvePlayMode()
{
#if UNITY_EDITOR
return _settings.PlayMode;
#elif UNITY_WEBGL
return EPlayMode.WebPlayMode;
#else
return _settings.PlayMode == EPlayMode.EditorSimulateMode
? EPlayMode.HostPlayMode
: _settings.PlayMode;
#endif
}
InitializePackageOperation CreateInitializeOperation(ResourcePackage package, EPlayMode playMode, string cdnRoot)
{
if (playMode == EPlayMode.EditorSimulateMode)
{
#if UNITY_EDITOR
var buildResult = EditorSimulateBuildInvoker.Build(_settings.PackageName, (int)EBundleType.VirtualAssetBundle);
var options = new EditorSimulateModeOptions
{
EditorFileSystemParameters = FileSystemParameters.CreateDefaultEditorFileSystemParameters(buildResult.PackageRootDirectory)
};
return package.InitializePackageAsync(options);
#else
return null;
#endif
}
if (playMode == EPlayMode.OfflinePlayMode)
{
var options = new OfflinePlayModeOptions
{
BuiltinFileSystemParameters = FileSystemParameters.CreateDefaultBuiltinFileSystemParameters()
};
return package.InitializePackageAsync(options);
}
if (playMode == EPlayMode.HostPlayMode)
{
var remote = new CdnRemoteService(cdnRoot);
var options = new HostPlayModeOptions
{
BuiltinFileSystemParameters = FileSystemParameters.CreateDefaultBuiltinFileSystemParameters(),
CacheFileSystemParameters = FileSystemParameters.CreateDefaultSandboxFileSystemParameters(remote)
};
options.BuiltinFileSystemParameters.AddParameter(EFileSystemParameter.CopyBuiltinPackageManifest, true);
return package.InitializePackageAsync(options);
}
if (playMode == EPlayMode.WebPlayMode)
{
// 微信小游戏正式接入时替换为 WechatFileSystem;M0 先用默认 WebNetwork。
var remote = new CdnRemoteService(cdnRoot);
var options = new WebPlayModeOptions
{
WebNetworkFileSystemParameters = FileSystemParameters.CreateDefaultWebNetworkFileSystemParameters(remote, true)
};
return package.InitializePackageAsync(options);
}
return null;
}
}
}