ILRuntime(二)整合Hotfix到Unity中,脚本生成dll文件

2023-11-09

如果开发的时候按之前的一个Hotfix工程,一个Unity工程,开发会很麻烦。因此我们可以把Hotfix部分的代码放入到Unity当中,并增加一个标记,到时候把这些代码整合成一个dll文件即可。

具体思路

ILRuntime的原理就是热更代码单独生成一个dll文件,然后Unity启动的时候读取这个dll文件,热更新的时候只需要热更dll文件即可。之前的Hotfix工程就是把工程内的代码导成dll文件,我们可以将这些代码放入到Unity当中,使用一个标记来和非热更代码区分开来,比如在文件夹或文件上加@Hotfix的后缀。然后我们可以用一个打dll的工具,把这热更的代码文件打成dll即可。这样操作之后就不需要两个工程来回切,方便了开发。

之前用Hotfix工程生成hotfix.dll的时候,是引用了Assembly-CSharp.dll文件,而当我们把Hotfix代码放入Unity中后,Assembly-CSharp.dll中也会包含这些代码,所以我们打hotfix.dll的时候不能使用它了。需要我们自己先将Unity中没有@Hotfix标记的代码编译成一个unity.dll文件,然后利用这个dll和标记了@Hotfix的代码编译成我们需要的hotfix.dll文件,即可。

 

整合项目

首先我们把Hotfix的脚本放到Unity当中,然后添加@Hotfix后缀用来做区分,如图

 

打DLL工具

然后去制作我们的打dll工具,新建一个控制台应用叫BuildDllTool

我们需要的参数有,Unity Assets目录的路径,生成的dll文件的导出路径,Unity一些系统dll文件的路径(例如UnityEngine.dll等),编译配置路径(这一块内容还不是很了解,因为也是网上找的代码,后面在研究研究。文件这里先分享下: 编译配置 提取码: xub3 ),编译选项。代码如下

using System;
using System.Threading;

namespace BuildDllTool
{
    class Program
    {
        static void Main(string[] args)
        {
            if (args.Length == 5)
            {
                Console.WriteLine("Unity Asset 路径:" + args[0]);
                Console.WriteLine("dll 输出路径:"+ args[1]);
                Console.WriteLine("Unity 系统的 dll 文件路径:" + args[2]);
                Console.WriteLine("编译配置路径:" + args[3]);
                Console.WriteLine("编译选项:" + args[4]);

                var result = ScriptBiuldToDll.Build(args[0], args[1], args[2], args[3], args[4]);

                Console.WriteLine("退出");
            }
            else
            {
                Console.WriteLine("参数不匹配!");
                Console.WriteLine("退出!");
            }
            Thread.Sleep(500);
            System.Diagnostics.Process.GetCurrentProcess().Close();
        }
    }
}

编译dll的代码如下,

using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;

namespace BuildDllTool
{
    class ScriptBiuldToDll
    {
        public enum BuildStatus
        {
            Success = 0,
            Fail
        }

        static public BuildStatus Build(string unityAssetsPath, string dllPath, string unitySystemDllPath, string compilerDirectoryPath, string define)
        {
            //编译项目的base.dll
            Console.WriteLine("准备编译dll 10%");

            //清空dll的存放文件夹
            if (Directory.Exists(dllPath))
            {
                Directory.Delete(dllPath, true);
            }
            Directory.CreateDirectory(dllPath);

            //Unity 中存放脚本的文件
            string[] searchPath = new string[] { "Scripts", "ThridPartys" };
            for (int i = 0; i < searchPath.Length; i++)
            {
                searchPath[i] = Path.Combine(unityAssetsPath, searchPath[i]);
            }

            //找出所有的脚本
            List<string> files = new List<string>();
            foreach (var s in searchPath)
            {
                var fs = Directory.GetFiles(s, "*.*", SearchOption.AllDirectories).ToList();
                var _fs = fs.FindAll(f =>
                {
                    var _f = f.ToLower();
                    var exten = Path.GetExtension(_f);
                    if ((!_f.Contains("editor")) && (exten.Equals(".dll") || exten.Equals(".cs")))
                    {
                        return true;
                    }
                    return false;
                });

                files.AddRange(_fs);
            }

            files = files.Distinct().ToList();
            for (int i = 0; i < files.Count; i++)
            {
                files[i] = files[i].Replace('/', '\\').Trim('\\');
            }

            Console.WriteLine("开始整理script 20%");

            //项目中用到的dll
            var refDlls = files.FindAll(f => f.EndsWith(".dll"));
            //unity内脚本,用于先生成unity的dll文件,供hotfix.dll编译用
            var unityCs = files.FindAll(f => !f.EndsWith(".dll") && !f.Contains("@Hotfix"));
            //热更脚本,用于生成hotfix.dll
            var hotfixCs = files.FindAll(f => !f.EndsWith(".dll") && f.Contains("@Hotfix"));

            //临时目录
            var tempDirect = "d:/bd_temp";
            if (Directory.Exists(tempDirect))
            {
                Directory.Delete(tempDirect, true);
            }
            Directory.CreateDirectory(tempDirect);

            //除去不需要引用的dll
            for (int i = refDlls.Count - 1; i >= 0; i--)
            {
                var str = refDlls[i];
                if (str.Contains("Editor") || str.Contains("iOS") || str.Contains("Android") || str.Contains("StreamingAssets"))
                {
                    refDlls.RemoveAt(i);
                }
            }

            //拷贝dll到临时目录
            for (int i = 0; i < refDlls.Count; i++)
            {
                var copyto = Path.Combine(tempDirect, Path.GetFileName(refDlls[i]));
                File.Copy(refDlls[i], copyto, true);
                refDlls[i] = copyto;
            }

            //添加系统的dll
            refDlls.Add("System.dll");
            refDlls.Add("System.Core.dll");
            refDlls.Add("System.XML.dll");
            refDlls.Add("System.Data.dll");

            //添加Unity系统的dll
            string[] dllPaths = unitySystemDllPath.Split(',');
            foreach (string dll in dllPaths)
            {
                var dllfile = Directory.GetFiles(dll, "*.dll", SearchOption.AllDirectories);
                foreach (var d in dllfile)
                {
                    if (Path.GetFileNameWithoutExtension(d).StartsWith("Assembly-CSharp"))
                    {
                        continue;
                    }
                    refDlls.Add(d);
                }
            }

            var unityDllPath = dllPath + "unity.dll";

            Console.WriteLine("复制编译代码 30%");

            //拷贝非热更的cs文件到临时目录
            for (int i = 0; i < unityCs.Count; i++)
            {
                var copyto = Path.Combine(tempDirect, Path.GetFileName(unityCs[i]));
                int count = 1;
                while (File.Exists(copyto))
                {
                    //为解决mono.exe error: 文件名太长问题
                    copyto = copyto.Replace(".cs", "") + count + ".cs";
                    count++;
                }

                File.Copy(unityCs[i], copyto);
                unityCs[i] = copyto;
            }

            //检测dll,移除无效dll
            for (int i = refDlls.Count - 1; i >= 0; i--)
            {
                var r = refDlls[i];
                if (File.Exists(r))
                {
                    var fs = File.ReadAllBytes(r);
                    try
                    {
                        var assm = Assembly.Load(fs);
                    }
                    catch
                    {
                        Console.WriteLine("移除无效的 dll :" + r);
                        refDlls.RemoveAt(i);
                    }
                }
            }

            Console.WriteLine("[1/2]开始编译 unity.dll 40%");

            BuildStatus unityResult = BuildStatus.Success;
            //编译 unity.dll
            try
            {
                unityResult = BuildDll(refDlls.ToArray(), unityCs.ToArray(), unityDllPath, compilerDirectoryPath, define);
            }
            catch (Exception e)
            {
                Console.WriteLine("unity.dll 编译失败:" + e);
                throw;
            }
            Console.WriteLine("[2/2]开始编译hotfix.dll 70%");

            //将unity.dll加入
            refDlls.Add(unityDllPath);

            //编译hotfix.dll
            var hotfixDllPath = dllPath + "hotfix.dll";
            BuildStatus hotfixResult = BuildStatus.Success;
            try
            {
                hotfixResult = BuildDll(refDlls.ToArray(), hotfixCs.ToArray(), hotfixDllPath, compilerDirectoryPath, define);
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                throw;
            }

            Console.WriteLine("清理临时文件 95%");
            Directory.Delete(tempDirect, true);

            if (unityResult == BuildStatus.Success && unityResult == hotfixResult)
            {
                Console.WriteLine("编译成功!");
                return BuildStatus.Success;
            }
            else
            {
                Console.WriteLine("编译失败!");
                return BuildStatus.Fail;
            }
        }

        /// <summary>
        /// 编译dll
        /// </summary>
        static public BuildStatus BuildDll(string[] refAssemblies, string[] codefiles, string output, string compilerDirectoryPath, string define)
        {
            // 设定编译参数,DLL代表需要引入的Assemblies
            CompilerParameters cp = new CompilerParameters();
            cp.GenerateExecutable = false;
            //在内存中生成
            cp.GenerateInMemory = true;
            //生成调试信息
            if (define.IndexOf("IL_DEBUG") >= 0)
            {
                cp.IncludeDebugInformation = true;
            }
            else
            {
                cp.IncludeDebugInformation = false;
            }

            //cp.TempFiles = new TempFileCollection(".", true);
            cp.OutputAssembly = output;
            //warning和 error分开,不然各种warning当成error,改死你
            cp.TreatWarningsAsErrors = false;
            cp.WarningLevel = 1;
            //编译选项
            cp.CompilerOptions = "-langversion:latest /optimize /unsafe /define:" + define;

            if (refAssemblies != null)
            {
                foreach (var d in refAssemblies)
                {
                    cp.ReferencedAssemblies.Add(d);
                }
            }

            // 编译代理
            CodeDomProvider provider;
            if (string.IsNullOrEmpty(compilerDirectoryPath))
            {
                provider = CodeDomProvider.CreateProvider("CSharp");
            }
            else
            {
                provider = CodeDomProvider.CreateProvider("cs", new Dictionary<string, string> {
                    { "CompilerDirectoryPath", compilerDirectoryPath }
                });
            }

            CompilerResults cr = provider.CompileAssemblyFromFile(cp, codefiles);
            if (true == cr.Errors.HasErrors)
            {
                StringBuilder sb = new StringBuilder();
                foreach (CompilerError ce in cr.Errors)
                {
                    sb.Append(ce.ToString());
                    sb.Append(Environment.NewLine);
                }
                Console.WriteLine(sb);
            }
            else
            {
                return BuildStatus.Success;
            }
            return BuildStatus.Fail;
        }
    }
}

然后我们将其生成为exe程序,放到Unity项目中(例如:Unity项目/Tools/BuildHotfixDll文件夹下)。然后前面的编译配置也可放在该文件夹下。

 

Unity中调用

然后我们在Editor下添加菜单栏,用来调用我们的exe文件生成dll文件即可。我们在Editor目录下创建ILRuntimeBuildWindow.cs

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using UnityEditor;
using UnityEngine;
using Debug = UnityEngine.Debug;

namespace EditorTool
{
	public class ILRuntimeBuildWindow : EditorWindow
    {
        Vector2 mLogScroll;
        string mLogs = string.Empty;

        public void OnGUI()
        {
            GUILayout.BeginVertical();
            {
                GUILayout.Label("脚本打包");
                GUILayout.Space(5);
                GUILayout.BeginHorizontal();
                {
                    if (GUILayout.Button("1.编译 Hotfix.dll", GUILayout.Width(200), GUILayout.Height(30)))
                    {
                        mLogs = string.Empty;
                        string outpath = Application.streamingAssetsPath + "/hotfix_dll/";
                        BuildDLL(Application.dataPath + "/", outpath);
                        AssetDatabase.Refresh();
                    }
                }
                GUILayout.EndHorizontal();

                if (!string.IsNullOrEmpty(mLogs))
                {
                    mLogScroll = EditorGUILayout.BeginScrollView(mLogScroll, GUILayout.Height(400));
                    mLogs = EditorGUILayout.TextArea(mLogs);
                    EditorGUILayout.EndScrollView();
                }
            }
            GUILayout.EndVertical();
        }

        public void BuildDLL(string codeSource, string export, Action compileFinishedCallback = null, Action<string> outPutReceivedEvent = null)
        {
            string exePath = Environment.CurrentDirectory + "/Tools/BuildHotfixDll/BuildDllTool.exe";
            if (!File.Exists(exePath))
            {
                Debug.Log("编译工具不存在!");
                return;
            }

            //这里是引入unity所有引用的dll
            var u3dUI = string.Format(@"{0}\UnityExtensions\Unity", EditorApplication.applicationContentsPath);
            var u3dEngine = string.Format(@"{0}\Managed\UnityEngine", EditorApplication.applicationContentsPath);
            string libDll = Environment.CurrentDirectory + "/Library/ScriptAssemblies";
            string dllPath = u3dUI + "," + u3dEngine + "," + libDll;

            if (Directory.Exists(u3dUI) == false || Directory.Exists(u3dEngine) == false || Directory.Exists(libDll) == false)
            {
                EditorUtility.DisplayDialog("提示", "dll文件目录不存在,请修改ILRuntimeBuildWindow类中,u3dUI u3dEngine libDll的dll目录", "OK");
                return;
            }

            //编译配置文件目录
            string compilerDirectoryPath = Environment.CurrentDirectory + "/Tools/BuildHotfixDll/roslyn";

            var define = GetScriptingDefineSymbols();

            //执行exe文件,传递参数
            var p = new Process();
            p.EnableRaisingEvents = true;
            p.StartInfo.UseShellExecute = false;
            p.StartInfo.CreateNoWindow = true;
            p.StartInfo.FileName = exePath;
            p.StartInfo.Arguments = string.Format("{0} {1} {2} {3} {4}", codeSource, export, dllPath, compilerDirectoryPath, define);
            p.Exited += (sender, e) =>
            {
                compileFinishedCallback?.Invoke();
            };
            p.OutputDataReceived += (sender, e) =>
            {
                mLogs += (e.Data + "\n");
            };
            p.StartInfo.RedirectStandardOutput = true;
            p.StartInfo.StandardOutputEncoding = Encoding.GetEncoding("gb2312");
            p.Start();
            p.BeginOutputReadLine();
            p.WaitForExit();
            EditorUtility.ClearProgressBar();
        }

        //获取编译选项
        string GetScriptingDefineSymbols()
        {
            List<string> validDefines = new List<string>();
            foreach (var define in EditorUserBuildSettings.activeScriptCompilationDefines)
            {
                if (!define.Contains("UNITY_EDITOR"))
                {
                    validDefines.Add(define);
                }
            }
            return string.Join(";", validDefines);
        }
    }
}

然后将其加入菜单栏中调用即可

using UnityEditor;

namespace EditorTool
{
	public class EditorToolMenu
	{
        [MenuItem("Tools/Build Hotfix Dll")]
        public static void ExecuteBuildDLL()
        {
            var window = (ILRuntimeBuildWindow)EditorWindow.GetWindow(typeof(ILRuntimeBuildWindow), false, "Build Hotfix Dll");
            window.Show();
        }
    }
}

选择菜单栏Tool ->Build Hotfix Dll打开我们的工具窗口,点击编译即可。编译成功如下

注意这样打出的dll是没有pdb文件的,因此我们可以在ILRuntimeHelp类中读取dll的代码处,去掉读取pdb的部分即可。

 

补充

1.若要将PackageManager中的内容打入到dll中,只需要在searchPath中添加Library/PackageCache的绝对路径即可。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

ILRuntime(二)整合Hotfix到Unity中,脚本生成dll文件 的相关文章

  • CEF使用的几个注意点

    转自http www cnblogs com gongxijun p 4857977 html CEF为chrome浏览器的切入其他浏览器中的轻量级框架 开发的客户端的时候 这是作为界面显示的首先 可以增强客户的易变性 可塑性 在开发的过程
  • 计算机网络中的泊松分布与指数分布

    一 概率论知识 1 1 泊松分布 一个时间段里某件事发生的概率 用泊松分布来描述 1 2 指数分布 两个事件的间隔时间为某个值的概率 用指数分布来描述 1 3 两者关系 其中 服从泊松分布的事件 其间隔时间服从指数分布 二 计算机网络知识
  • 深度学习撞墙了?

    转自 机器之心 早在 2016 年 Hinton 就说过 我们不用再培养放射科医生了 如今几年过去 AI 并没有取代任何一位放射科医生 问题出在哪儿 近年来 AI 在大数据 大模型的深度学习之路上一路狂奔 但很多核心问题依然没有解决 比如如
  • redis监控指标

    服务器系统数据采集 Redis Server数据采集 Redis响应时间数据采集 Redis监控Screen b b b 一 服务器存活监控 1 gt ping监控告警 2 gt CPU 3 gt 丢包率 b 二 Redis Server监
  • 使用数字陷波器滤除工频信号

    在实际测量时经常会受到工频信号 交流50Hz 的干扰 有时干扰还很大 有用信号完全被淹没了 可以应用数字陷波器来消除工频信号的干扰 数字陷波器函数如下 函数 iirnotch 功能 数字陷波器设计 调用格式 b a iirnotch Wo
  • vscode报错:SyntaxError: Cannot use import statement outside a module

    报错内容 E workspace2 cronmall ui npmdemo modulees61 02 js 2 import getList save from 01 js SyntaxError Cannot use import st
  • DOM 事件相关知识总结——事件绑定、事件流(事件冒泡、捕获)

    1 事件绑定方式 1 直接给元素添加事件属性
  • 第七届年蓝桥杯省赛C/C++ A组 最大比例

    题目 X星球的某个大奖赛设了M级奖励 每个级别的奖金是一个正整数 并且 相邻的两个级别间的比例是个固定值 也就是说 所有级别的奖金数构成了一个等比数列 比如 16 24 36 54 其等比值为 3 2 现在 我们随机调查了一些获奖者的奖金数
  • 读标准01-IEEE1451-智能传感器接口标准介绍

    老规矩 这里排版不是最佳 并且下文有个别误字太麻烦去改 这里保持第一版 2021 7 14 方便的话请移步 Github Gitee 页面看最新的 或下载下来用 markdown 编辑器看排版更佳 Github仓库 Gitee仓库 记录经过
  • 听说现在流行卷应用?开发者们都开始调用文心API开发了?!

    随着大模型的涌现 我们喜悦于其远远超越小模型的性能 但又不得不面临大模型开发难的困境 训练难 微调难 部署难 开发者难以将其投入实际生产 不仅面临资源的限制 更面临高精数据难寻 时间成本过高等问题 为了让平台更多开发者可以进行大模型开发 体
  • vue import..from..说明

    import Vue from vue import axios from axios import App from App import store from store import router from router import
  • Java中IO流——详解字节流之FileOutputStream和FileInputStream

    文章目录 前言 一 Java流的概述 二 常用字节流分类 1 字节输出输入流 1 FileOutputStream 2 FileInputStream 3 复制小练习 4 异常捕获 总结 前言 我们之前学习了Java中的异常机制和File类
  • 【区块链】Python开发EOS机器人与WAX链游脚本常用工具

    前言 众所周知 开发EOS机器人与WAX链游脚本 我们都需要调用eosio chain api https developers eos io manuals eos latest nodeos plugins chain api plug

随机推荐

  • mybatis多表联查sql用法示例

    用到sql变量 sql复用
  • ssm打印sql如何开启_ssm环境下配置log4j打印mybatis的sql语句

    首先附上官网的说明文档 mybatis Logging 环境spring4 3 0 springmvc4 3 0 mybatis3 4 0 按官方文档的说明 1 SLF4J 2 Apache Commons Logging 3 Log4j
  • 使用invoke方法解决跨线程访问的问题

    C 中禁止跨线程直接访问控件 InvokeRequired是为了解决这个问题而产生的 当一个控件的InvokeRequired属性值为真时 说明有一个创建它以外的线程想访问它 获取一个值 该值指示调用方在对控件进行方法调用时是否必须调用 I
  • js+bootstrap+jquery+vue实现房贷计算器

    代码链接 loan 使用vue js html css实现房贷的计算 版权声明 本文为CSDN博主 小样还想跑 的原创文章 遵循CC 4 0 BY SA版权协议 转载请附上原文出处链接及本声明
  • 23 种设计模式详解(全23种)

    设计模式的分类 总体来说设计模式分为三大类 创建型模式 共五种 工厂方法模式 抽象工厂模式 单例模式 建造者模式 原型模式 结构型模式 共七种 适配器模式 装饰器模式 代理模式 外观模式 桥接模式 组合模式 享元模式 行为型模式 共十一种
  • 人手一份核武器:Android手机装Kali Linux

    首先这是安卓手机的专属工具 因为Android基于Linux 所以就有了得天独厚的优势 1 先下载好Linux Deploy 前提是本手机已root 2 按下图配置 不过有地方需要说明 Distribute Suite已经改为sana 但无
  • Windows Server 2012 R2 设置 smtp 服务器

    Windows Server 2012 2012 R2 安装和配置 SMTP 服务器 安装 SMTP 服务器 以下是安装 SMTP 服务器功能的步骤 打开 服务器管理器 单击键盘上的 Windows 按钮 输入 服务器管理器 在 结果 窗口
  • FW-1设备配置命令

    DCFW 1800 config hostname FW 1 FW 1 config ip vrouter trust vr FW 1 config vrouter ip route 0 0 0 0 0 202 11 33 26 FW 1
  • cmd创建用户并初始化新用户桌面

    author skate time 2013 12 20 功能 在win2003上创建用户 并初始化新用户的桌面 echo InternetShortcut gt gt MysqlTool url echo URL C Program Fi
  • Qt之pro配置多个子工程/子模块

    简述 进行Qt项目开发的时候 尤其是大型项目 经常涉及多工程 多模块问题 其主要思想还是模块化 目的是为了降低程序复杂度 使程序设计 调试和维护等操作简单化 简述 配置 效果 多工程 多模块 更多参考 配置 效果 多工程 如果需要管理多工程
  • JavaMap集合&Stream流

    1 Map集合 1 1Map集合概述和特点 Map集合概述 interface Map
  • Python-Thread(通俗易懂)

    此类表示在单独的控制线程中运行的活动 有两种方法可以指定该活动 一是将可调用对象传递给构造函数 二是通过覆盖子类中的run 方法 如果你对线程不太理解 我们可以打个比方 把线程数看作车辆数 我们来完成一个简单的客运运输工作 以下为了方便理解
  • 第8届Python编程挑战赛初赛真题剖析-2022年全国青少年信息素养大赛

    导读 超平老师计划推出 全国青少年信息素养大赛Python编程真题解析 50讲 这是超平老师解读Python编程挑战赛系列的第1讲 全国青少年信息素养大赛 原全国青少年电子信息智能创新大赛 是 世界机器人大会青少年机器人设计与信息素养大赛
  • VC++ MapWinGis篇(二)

    添加高德图层 ArcGisProvider h pragma once include BaseProvider h class ArcGisBaseProvider public BaseProvider public ArcGisBas
  • Java RMI 远程代码执行漏洞

    0x01 漏洞描述 Java RMI 远程代码执行漏洞 Java RMI服务是远程方法调用 是J2SE的一部分 能够让程序员开发出基于JAVA的分布式应用 一个RMI对象是一个远程Java对象 可以从另一个Java虚拟机上 甚至跨过网络 调
  • 这篇文章带你了解sql语句是怎么执行的

    一条sql语句是怎么执行的 一 mysql架构分析 二 语句分析 2 1 查询语句 2 2 更新语句 三 总结 mysql有各种版本的架构图 但基本上都可以分为Server层和存储引擎层 一 mysql架构分析 下面是mysql的一个简要架
  • web压测工具http_load原理分析

    01 前言 http load是一款测试web服务器性能的开源工具 从下面的网址可以下载到最新版本的http load http www acme com software http load 这个软件一直在保持着更新 不像webbench
  • el-tree组件展示节点过多时造成页面卡顿、奔溃的解决办法

    解决el tree组件展示节点过多时造成页面卡顿 奔溃 前几天测试提了个BUG 文件列表展示5w个文件页面会卡顿甚至奔溃 项目用的是vue element ui框架 我是使用el tree进行渲染文件列表的 参考网上使用virtual sc
  • Log4j2注入漏洞万字剖析-汇总收藏版(攻击步骤、漏洞原理、2.15.0-RC1绕过原理以及2.15.0、2.16.0修复原理)

    系列文章 2 15 0之前版漏洞相关文章 Log4j2注入漏洞 CVE 2021 44228 万字深度剖析 一 开篇与基础知识 Log4j2注入漏洞 CVE 2021 44228 万字深度剖析 二 漏洞原理 Log4j2注入漏洞 CVE 2
  • ILRuntime(二)整合Hotfix到Unity中,脚本生成dll文件

    如果开发的时候按之前的一个Hotfix工程 一个Unity工程 开发会很麻烦 因此我们可以把Hotfix部分的代码放入到Unity当中 并增加一个标记 到时候把这些代码整合成一个dll文件即可 具体思路 ILRuntime的原理就是热更代码