远程代码加载方案

comradexiao / 2024-10-17 / 原文

原理

通过http获取到远程代码,并下载加来替换掉本次使用的代码

编辑器启动本地服务器(代码示例)

#if !NO_HYBRIDCLR
using HybridCLR;
#endif 
using System;
using System.Reflection;
using UnityEngine;

/// <summary>
/// 启动流程-加载并应用dll
/// </summary>
public class LoadDllLaunchTask : LaunchTask
{
    /// <inheritdoc/>
    public override void Run()
    {
        TestLogManager.Instance.StartRecord(TestLogManager.TestLogKey.Launch_LoadDll);
        LoadDllHotFix();
    }

    /// <inheritdoc/>
    protected override void OnFinished()
    {

    }

    /// <summary>
    /// 加载dll热修内容
    /// </summary>
    private void LoadDllHotFix()
    {
        byte[] hotBytes = null;
        LoadDlls((name, bytes) => {
            if (name.ToLower() == "hotfixassembly")
            {
                hotBytes = bytes;
            }
            else
            {
                RuntimeApi.LoadMetadataForAOTAssembly(bytes, HomologousImageMode.SuperSet);
                Debug.Log($"加载AOT成功 {name}");
            }
            SetComplete();
        }, () => {
            if (hotBytes == null)
            {
                Debug.LogError("加载热更代码失败");
                return;
            }

            Assembly.Load(hotBytes);
            Debug.Log($"加载热更代码成功");

            TestLogManager.Instance.EndRecord(TestLogManager.TestLogKey.Launch_LoadDll);
            SetComplete();
        });
    }

    private void LoadDlls(Action<string, byte[]> dllLoaded, Action allCompleted)
    {
        string RemoteDllURL = Launch.Instance.RemoteDllURL;
        if (string.IsNullOrEmpty(RemoteDllURL))
        {
            ResourceLoader.LoadAllAsset("Dll", (objects, str) =>
            {
                if (objects == null)
                {
                    Debug.LogError("加载热更代码失败");
                    return;
                }

                for (int i = 0; i < objects.Length; i++)
                {
                    TextAsset textAsset = objects[i] as TextAsset;
                    dllLoaded(textAsset.name, textAsset.bytes);
                }

                allCompleted();
            });
        }
        else
        {
            RemoteDllLoader loader = new RemoteDllLoader();
            loader.Initialize(RemoteDllURL, dict =>
            {
                foreach (var pair in dict)
                {
                    dllLoaded(pair.Key, pair.Value);
                }

                allCompleted();
            });
        }
    }
}


使用远程代码资源(代码示例)

using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using UnityEngine;
using UnityEngine.Networking;

/// <summary>
///  远程加载本机Dll
/// </summary>
public class RemoteDllLoader
{
    #region Properties
    private string serverURL;
    private ZipArchive zipArchive;
    #endregion

    #region Public Methods
    /// <summary>
    ///     初始化
    /// </summary>
    /// <param name="serverURL">服务器地址</param>
    /// <param name="completed">完成回调</param>
    public void Initialize(string serverURL, Action<Dictionary<string, byte[]>> completed)
    {
        serverURL = serverURL.TrimEnd('/');
        this.serverURL = serverURL;
        var request = UnityWebRequest.Get(this.serverURL + "?code_zip=true");
        var asyncOp = request.SendWebRequest();
        asyncOp.completed += asyncOperation =>
        {
#if UNITY_2020_1_OR_NEWER
            var isNetError = !(request.result == UnityWebRequest.Result.InProgress ||
                               request.result == UnityWebRequest.Result.Success);
#else
            bool isNetError = request.isHttpError || request.isNetworkError;
#endif

            if (isNetError)
            {
                Debug.LogErrorFormat("请求代码资源失败:{0}\n{1}", request.url, request.error);
            }
            else
            {
                var data = request.downloadHandler.data;
                Debug.Log("请求代码资源成功");
                zipArchive = new ZipArchive(new MemoryStream(data), ZipArchiveMode.Read);
            }

            Dictionary<string, byte[]> codeMap = new Dictionary<string, byte[]>();
            for (var i = 0; i < zipArchive.Entries.Count; i++)
            {
                var entry = zipArchive.Entries[i];
                using (var stream = entry.Open())
                {
                    byte[] bytes = new byte[entry.Length];
                    stream.Read(bytes, 0, bytes.Length);
                    codeMap.Add(entry.Name, bytes);
                }
            }

            completed(codeMap);
        };
    }

    #endregion

    #region Internal Methods
    #endregion
}

adb连接

adb reverse tcp:1234 tcp:1234