mirror of
https://gitee.com/jisol/jisol-game/
synced 2025-06-26 11:24:46 +00:00
297 lines
11 KiB
C#
297 lines
11 KiB
C#
|
using HybridCLR;
|
|||
|
using System;
|
|||
|
using System.Collections;
|
|||
|
using System.Collections.Generic;
|
|||
|
using System.IO;
|
|||
|
using System.Linq;
|
|||
|
using HotMain;
|
|||
|
using UnityEngine;
|
|||
|
using UnityEngine.Networking;
|
|||
|
using YooAsset;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 脚本工作流程:
|
|||
|
/// 1.下载资源,用YooAsset资源框架进行下载
|
|||
|
/// 1)资源文件,ab包等
|
|||
|
/// 2)热更新dll
|
|||
|
/// 3) AOT泛型补充元数据dll
|
|||
|
/// 2.给AOT dll补充元数据,通过RuntimeApi.LoadMetadataForAOTAssembly()
|
|||
|
/// 3.通过实例化一个prefab,运行热更新代码
|
|||
|
/// </summary>
|
|||
|
public class LoadDll : MonoBehaviour
|
|||
|
{
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 资源系统运行模式
|
|||
|
/// </summary>
|
|||
|
public EPlayMode PlayMode = EPlayMode.OfflinePlayMode;
|
|||
|
|
|||
|
//补充元数据dll的列表,
|
|||
|
//通过RuntimeApi.LoadMetadataForAOTAssembly()函数来补充AOT泛型的原始元数据
|
|||
|
public static List<string> AOTMetaAssemblyNames { get; } = new List<string>()
|
|||
|
{
|
|||
|
"mscorlib.dll",
|
|||
|
"System.dll",
|
|||
|
"System.Core.dll",
|
|||
|
};
|
|||
|
|
|||
|
void Start()
|
|||
|
{
|
|||
|
//StartCoroutine(DownLoadAssets(this.StartGame));
|
|||
|
|
|||
|
StartCoroutine(DownLoadAssetsByYooAssets(this.StartGame));
|
|||
|
}
|
|||
|
|
|||
|
private static Dictionary<string, byte[]> s_assetDatas = new Dictionary<string, byte[]>();
|
|||
|
|
|||
|
public static byte[] GetAssetData(string dllName)
|
|||
|
{
|
|||
|
return s_assetDatas[dllName];
|
|||
|
}
|
|||
|
|
|||
|
private string GetWebRequestPath(string asset)
|
|||
|
{
|
|||
|
var path = $"{Application.streamingAssetsPath}/{asset}";
|
|||
|
if (!path.Contains("://"))
|
|||
|
{
|
|||
|
path = "file://" + path;
|
|||
|
}
|
|||
|
if (path.EndsWith(".dll"))
|
|||
|
{
|
|||
|
path += ".bytes";
|
|||
|
}
|
|||
|
return path;
|
|||
|
}
|
|||
|
|
|||
|
IEnumerator DownLoadAssetsByYooAssets(Action onDownloadComplete)
|
|||
|
{
|
|||
|
// 1.初始化资源系统
|
|||
|
YooAssets.Initialize();
|
|||
|
|
|||
|
string packageName = "DefaultPackage";
|
|||
|
// 创建默认的资源包
|
|||
|
var package = YooAssets.TryGetPackage(packageName) ?? YooAssets.CreatePackage(packageName);
|
|||
|
|
|||
|
// 设置该资源包为默认的资源包,可以使用YooAssets相关加载接口加载该资源包内容。
|
|||
|
YooAssets.SetDefaultPackage(package);
|
|||
|
|
|||
|
if (PlayMode == EPlayMode.EditorSimulateMode)
|
|||
|
{
|
|||
|
Debug.Log("编辑器模式");
|
|||
|
//编辑器模拟模式
|
|||
|
var initParameters = new EditorSimulateModeParameters();
|
|||
|
initParameters.EditorFileSystemParameters =
|
|||
|
FileSystemParameters.CreateDefaultEditorFileSystemParameters(
|
|||
|
EditorSimulateModeHelper.SimulateBuild(EDefaultBuildPipeline.ScriptableBuildPipeline, "DefaultPackage")
|
|||
|
);
|
|||
|
yield return package.InitializeAsync(initParameters);
|
|||
|
}
|
|||
|
else if (PlayMode == EPlayMode.HostPlayMode)
|
|||
|
{
|
|||
|
Debug.Log("在线模式");
|
|||
|
// 注意:GameQueryServices.cs 太空战机的脚本类,详细见StreamingAssetsHelper.cs
|
|||
|
string defaultHostServer = "http://127.0.0.1/CDN/Android/v1.0";
|
|||
|
string fallbackHostServer = "http://127.0.0.1/CDN/Android/v1.0";
|
|||
|
IRemoteServices remoteServices = new RemoteServices(defaultHostServer, fallbackHostServer);
|
|||
|
var cacheFileSystem = FileSystemParameters.CreateDefaultCacheFileSystemParameters(remoteServices);
|
|||
|
var buildinFileSystem = FileSystemParameters.CreateDefaultBuildinFileSystemParameters();
|
|||
|
|
|||
|
var initParameters = new HostPlayModeParameters();
|
|||
|
initParameters.BuildinFileSystemParameters = buildinFileSystem;
|
|||
|
initParameters.CacheFileSystemParameters = cacheFileSystem;
|
|||
|
|
|||
|
yield return package.InitializeAsync(initParameters);
|
|||
|
}
|
|||
|
else if (PlayMode == EPlayMode.OfflinePlayMode)
|
|||
|
{
|
|||
|
Debug.Log("离线模式");
|
|||
|
// var buildinFileSystem = FileSystemParameters.CreateDefaultBuildinRawFileSystemParameters();
|
|||
|
// var initParameters = new OfflinePlayModeParameters();
|
|||
|
// initParameters.BuildinFileSystemParameters = buildinFileSystem;
|
|||
|
// yield return package.InitializeAsync(initParameters);
|
|||
|
// var initParametersOfflinePlayMode = new OfflinePlayModeParameters();
|
|||
|
// yield return package.InitializeAsync(initParametersOfflinePlayMode);
|
|||
|
|
|||
|
var createParameters = new OfflinePlayModeParameters();
|
|||
|
createParameters.BuildinFileSystemParameters = FileSystemParameters.CreateDefaultBuildinFileSystemParameters(new FileStreamDecryption());
|
|||
|
yield return package.InitializeAsync(createParameters);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//2.获取资源版本
|
|||
|
var operation = package.RequestPackageVersionAsync();
|
|||
|
yield return operation;
|
|||
|
|
|||
|
if (operation.Status != EOperationStatus.Succeed)
|
|||
|
{
|
|||
|
//更新失败
|
|||
|
Debug.LogError(operation.Error);
|
|||
|
//TODO
|
|||
|
yield break;
|
|||
|
}
|
|||
|
string PackageVersion = operation.PackageVersion;
|
|||
|
|
|||
|
//3.更新补丁清单
|
|||
|
var operation2 = package.UpdatePackageManifestAsync(PackageVersion);
|
|||
|
yield return operation2;
|
|||
|
|
|||
|
if (operation2.Status != EOperationStatus.Succeed)
|
|||
|
{
|
|||
|
//更新失败
|
|||
|
Debug.LogError(operation2.Error);
|
|||
|
//TODO:
|
|||
|
yield break;
|
|||
|
}
|
|||
|
|
|||
|
//4.下载补丁包
|
|||
|
yield return Download();
|
|||
|
//TODO:判断是否下载成功...
|
|||
|
|
|||
|
var assets = new List<string>
|
|||
|
{
|
|||
|
"HotSamples.dll",
|
|||
|
}.Concat(AOTMetaAssemblyNames);
|
|||
|
|
|||
|
foreach (var asset in assets)
|
|||
|
{
|
|||
|
var handle = package.LoadRawFileAsync(asset);
|
|||
|
yield return handle;
|
|||
|
byte[] fileData = handle.GetRawFileData();
|
|||
|
s_assetDatas[asset] = fileData;
|
|||
|
Debug.Log($"dll:{asset} size:{fileData.Length}");
|
|||
|
}
|
|||
|
|
|||
|
onDownloadComplete();
|
|||
|
}
|
|||
|
|
|||
|
IEnumerator Download()
|
|||
|
{
|
|||
|
int downloadingMaxNum = 10;
|
|||
|
int failedTryAgain = 3;
|
|||
|
int timeout = 60;
|
|||
|
var package = YooAssets.GetPackage("DefaultPackage");
|
|||
|
var downloader = package.CreateResourceDownloader(downloadingMaxNum, failedTryAgain, timeout);
|
|||
|
|
|||
|
//没有需要下载的资源
|
|||
|
if (downloader.TotalDownloadCount == 0)
|
|||
|
{
|
|||
|
yield break;
|
|||
|
}
|
|||
|
|
|||
|
//需要下载的文件总数和总大小
|
|||
|
int totalDownloadCount = downloader.TotalDownloadCount;
|
|||
|
long totalDownloadBytes = downloader.TotalDownloadBytes;
|
|||
|
|
|||
|
//注册回调方法
|
|||
|
downloader.OnDownloadErrorCallback = OnDownloadErrorFunction;
|
|||
|
downloader.OnDownloadProgressCallback = OnDownloadProgressUpdateFunction;
|
|||
|
downloader.OnDownloadOverCallback = OnDownloadOverFunction;
|
|||
|
downloader.OnStartDownloadFileCallback = OnStartDownloadFileFunction;
|
|||
|
|
|||
|
//开启下载
|
|||
|
downloader.BeginDownload();
|
|||
|
yield return downloader;
|
|||
|
|
|||
|
//检测下载结果
|
|||
|
if (downloader.Status == EOperationStatus.Succeed)
|
|||
|
{
|
|||
|
//下载成功
|
|||
|
Debug.Log("更新完成!");
|
|||
|
//TODO:
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
//下载失败
|
|||
|
Debug.LogError("更新失败!");
|
|||
|
//TODO:
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 开始下载
|
|||
|
/// </summary>
|
|||
|
/// <param name="fileName"></param>
|
|||
|
/// <param name="sizeBytes"></param>
|
|||
|
/// <exception cref="NotImplementedException"></exception>
|
|||
|
private void OnStartDownloadFileFunction(string fileName, long sizeBytes)
|
|||
|
{
|
|||
|
Debug.Log(string.Format("开始下载:文件名:{0}, 文件大小:{1}", fileName, sizeBytes));
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 下载完成
|
|||
|
/// </summary>
|
|||
|
/// <param name="isSucceed"></param>
|
|||
|
/// <exception cref="NotImplementedException"></exception>
|
|||
|
private void OnDownloadOverFunction(bool isSucceed)
|
|||
|
{
|
|||
|
Debug.Log("下载" + (isSucceed ? "成功" : "失败"));
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 更新中
|
|||
|
/// </summary>
|
|||
|
/// <param name="totalDownloadCount"></param>
|
|||
|
/// <param name="currentDownloadCount"></param>
|
|||
|
/// <param name="totalDownloadBytes"></param>
|
|||
|
/// <param name="currentDownloadBytes"></param>
|
|||
|
/// <exception cref="NotImplementedException"></exception>
|
|||
|
private void OnDownloadProgressUpdateFunction(int totalDownloadCount, int currentDownloadCount, long totalDownloadBytes, long currentDownloadBytes)
|
|||
|
{
|
|||
|
Debug.Log(string.Format("文件总数:{0}, 已下载文件数:{1}, 下载总大小:{2}, 已下载大小:{3}", totalDownloadCount, currentDownloadCount, totalDownloadBytes, currentDownloadBytes));
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 下载出错
|
|||
|
/// </summary>
|
|||
|
/// <param name="fileName"></param>
|
|||
|
/// <param name="error"></param>
|
|||
|
/// <exception cref="NotImplementedException"></exception>
|
|||
|
private void OnDownloadErrorFunction(string fileName, string error)
|
|||
|
{
|
|||
|
Debug.LogError(string.Format("下载出错:文件名:{0}, 错误信息:{1}", fileName, error));
|
|||
|
}
|
|||
|
|
|||
|
void StartGame()
|
|||
|
{
|
|||
|
LoadMetadataForAOTAssemblies();
|
|||
|
|
|||
|
#if !UNITY_EDITOR
|
|||
|
System.Reflection.Assembly.Load(GetAssetData("HotSamples.dll"));
|
|||
|
#endif
|
|||
|
|
|||
|
//委托加载方式,加载prefab
|
|||
|
var package = YooAssets.GetPackage("DefaultPackage");
|
|||
|
AssetHandle handle = package.LoadAssetAsync<GameObject>("HotPrefab");
|
|||
|
handle.Completed += Handle_Completed;
|
|||
|
|
|||
|
//AssetBundle prefabAb = AssetBundle.LoadFromMemory(GetAssetData("prefabs"));
|
|||
|
//GameObject testPrefab = Instantiate(prefabAb.LoadAsset<GameObject>("HotUpdatePrefab.prefab"));
|
|||
|
}
|
|||
|
|
|||
|
private void Handle_Completed(AssetHandle obj)
|
|||
|
{
|
|||
|
GameObject go = obj.InstantiateSync();
|
|||
|
Debug.Log($"Prefab name is {go.name}");
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 为aot assembly加载原始metadata, 这个代码放aot或者热更新都行。
|
|||
|
/// 一旦加载后,如果AOT泛型函数对应native实现不存在,则自动替换为解释模式执行
|
|||
|
/// </summary>
|
|||
|
private static void LoadMetadataForAOTAssemblies()
|
|||
|
{
|
|||
|
/// 注意,补充元数据是给AOT dll补充元数据,而不是给热更新dll补充元数据。
|
|||
|
/// 热更新dll不缺元数据,不需要补充,如果调用LoadMetadataForAOTAssembly会返回错误
|
|||
|
///
|
|||
|
HomologousImageMode mode = HomologousImageMode.SuperSet;
|
|||
|
foreach (var aotDllName in AOTMetaAssemblyNames)
|
|||
|
{
|
|||
|
byte[] dllBytes = GetAssetData(aotDllName);
|
|||
|
// 加载assembly对应的dll,会自动为它hook。一旦aot泛型函数的native函数不存在,用解释器版本代码
|
|||
|
LoadImageErrorCode err = RuntimeApi.LoadMetadataForAOTAssembly(dllBytes, mode);
|
|||
|
Debug.Log($"LoadMetadataForAOTAssembly:{aotDllName}. mode:{mode} ret:{err}");
|
|||
|
}
|
|||
|
}
|
|||
|
}
|