遠端程式碼載入方案

请明月發表於2024-10-15

原理

透過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

相關文章