A C# hot reload framework for Unity3D, based on Mono's MONO_AOT_MODE_INTERP mode.

Overview

PureScript

licensePRs Welcome

一个支持Unity3D的C#热更框架,基于Mono的MONO_AOT_MODE_INTERP模式。

支持在iOS平台Assembly.Load
构建时自动绑定Unity的Il2cpp代码。
支持大部分Unity特性,包括MonoBehaviour、Coroutine。
支持配置程序集运行环境(Il2cpp/aot/interp)
支持Cocoapods自动集成
支持对"magic code"的自定义绑定实现


iOS平台

使用“Mixed Mode Execution” 兼顾性能(aot)和灵活性(interpreter)

Windows平台

使用Jit模式运行,可以导出Il2cpp工程,添加ScriptEngine项目断点调试。

Android平台

请使用Unity的Mono运行时,可以直接调用Assembly.Load,同使用PureScript是等效的,工程结构无需修改。


使用

  1. Clone本工程,拷贝DemoProject/Assets/Plugins/PureScript目录。
  2. 修改 PureScriptBuilder.cs及ScriptEngine/Tools/config.json中的路径配置。
  3. config.json中配置运行在interpreter模式的dll(否则以aot运行),以及运行在Il2cpp运行时内的dll(一般用作Adapter)。

iOS平台

iOS平台需要安装CocoapodsNinja 。并在项目的podfile内添加PureScript引用。 例: */iOS/Podfile-example
导出xcode工程,然后

    pod install  

Windows平台

Windows平台仅用来调试,目前未添加自动集成,在构建项目后,需编译 ScriptEngine/ScriptEngine.vcxproj,替换原来Plugins目录下的的ScriptEngine.dll。
手动调试步骤:

  1. 设置传入ScriptEngine.Setup接口的reloadDir路径为 */ScriptEngine/Managed
  2. Unity导出VS工程。
  3. 需要删除Unity导出目录下的Managed目录例如($(ExportPath)/DemoProject/Managed),否则Mono会默认从此处加载dll,Il2cpp并不会使用此目录,但是每次构建都会导出。
  4. 在导出的解决方案中添加ScriptEngine.vcxproj,并在主项目中添加ScriptEngine项目的依赖(方便调试)。
  5. 修改ScriptEngine.vcxproj中的输出目录为 $(ExportPath)/build/bin/DemoProject_Data/Plugins/,即替换原本的ScriptEngine.dll。
  6. 运行项目

例子

以下两段代码是等效的,详细参考DemoProject/*/MonoEntry.cs。

ScriptEngine.Setup(reloadDir, "TestEntry.dll");

// equal to:

Assembly assembly = Assembly.Load("TestEntry.dll");
Type type = assembly.GetType("MonoEntry");
MethodInfo mi = type.GetMethod("Main");
var res = mi.Invoke(null, null)

注意 需要热更新的程序集如果是Unity自动生成的工程,会自动引用一堆的无用dll,比如UntiyEditor*.dll,和一堆根本不会用到的System*.dll ,因为安装包内并没有带上这些,加载会失败。这时需要手动删掉这些用不到的引用。或者再建一个工程,手动管理引用,后面也可以考虑做自动化工具,strip掉没用的引用。


实现

PureScript 封装了Mono运行时,c/csharp代码生成器,pod项目自动集成,实现了两种绑定方式。两种方式均在构建时自动生成绑定代码,调用方几乎无感知,具体参考DemoProject。 两种绑定均支持调用和回调。

  • Internal call 绑定: 由c/cpp实现,直接使用Unity的dll,同时把Unity的Internal call绑定到UnityEngine实现,几乎没有性能损失,调用方无需修改。
    例:ScriptEngine/generated/icall_binding_gen.c。
    如果碰到“Unity magic”代码自动绑定有问题,可以实现自己的绑定。
    例:ScriptEngine/custom/icall_binding.c。
    或者在CSharp层用Adapter绑定。

  • Adapter绑定: 由纯CSharp实现,分别在Mono端和Il2cpp端生成绑定代码,具有更好的兼容和灵活性。Mono运行时内的dll调用Il2cpp内的dll时用到,在config.json中配置需要运行在Il2cpp内又需要在Mono内调用的dll,构建时自动生成绑定代码,在aot执行,调用时自动替换到绑定代码,调用方无需修改。 例:DemoProject/*/AdapterTest

  1. 绑定分两部分,构建时会自动生成Adapter.gen.dll(Il2cpp内执行)和Adapter.wrapper.dll(Mono内执行),生成代码参考:ScriptEngine/Adapter/glue/*.cs。
  2. 运行时ScriptEngine.Setup会将需要绑定的接口生成代理对象,然后序列化到非托管内存中,同时将内存指针报错到ScriptEngine中,参考:ScriptEngine.c。
  3. Mono运行时启动后,执行Main函数时会首先从ScriptEngine读取内存指针,然后反序列化为代理,供Wrapper调用。
  4. Mono运行时内,调用被Adapter绑定过的程序集时会自动指向Adapter.wrapper.dll内的Wrapper实现。
    注:代理的序列化与反序列化参考
Marshal.GetFunctionPointerForDelegate
Marshal.GetDelegateForFunctionPointer

自动绑定过程

  • 构建Unity项目时PureScriptBuilder.cs内注册了回调,并通过hook的方式分别在StripAssemblies前/后添加了绑定调用,此处是要再Il2cpp前将已经Strip的Assemblies拷贝出一份,并且进行Adapter绑定的代码生成。
  • 第一次绑定调用是做Adapter绑定的代码生成,生产物是Adapter.gen.dll和Adapter.wrapper.dll,这步需要在Strip前,否则会触发Il2cpp构建错误。
  • 第二次绑定调用是在Strip后,此次绑定会将已经Strip的Assemblies拷贝出一份。
    然后进行icall绑定,生产物是ScriptEngine/generated/.c
    如果是iOS平台,会进行aot构建,首先生成build.ninja,然后通过ninja生成ScriptEngine/aot/
    .a

正式项目请将Mono库(mono*.dll/mono*.a) 替换为自己编译的。

如果大家对这个方案有兴趣再补充详细文档,同时欢迎提交PR或者Star。

有问题请联系 [email protected]

Comments
  • windows平台跑Demo的时候会出问题

    windows平台跑Demo的时候会出问题

    今天一直在尝试,打了个windows下的il2cpp来测试,遇到了很多dll相关的主要是路径的问题,最后到了这里返回的res是-1,然后就不能继续下去了 image 按照我的理解,从ScriptEngine.Setup调用SetupMono启动TestEntry.dll这里应该是对应了intepreter模式的入口调用,应该是比较关键的地方。 接下来我只能用调试mono的方式来看为什么调用出错了。如果仓库主能完善一下文档追加一些原理和问题的faq,再来一个可复现的demo就好了。

    opened by quicklyslow 3
  • vs的工程配置里面有本地路径,修改为依赖环境变量

    vs的工程配置里面有本地路径,修改为依赖环境变量

    vs的工程有两个问题导致无法编译成功,一是依赖的本地的include和lib路径,二是没有对Release进行正确的配置。 因为要依赖UnityEditor下的il2cpp目录和mono的目录,修改为依赖环境变量,需要在环境变量里新增 1.UnityEditorPath 指向 Editor目录(例如C:\Program Files\unity\Editor) 2.Mono 指向安装后的Mono sdk目录(例如C:\Program Files\Mono)

    opened by quicklyslow 0
  • class的field不会导出在wrapper class中,如果这个field在mono内被引用,导出和构建过程不会有问题,但是运行之后才发现crash

    class的field不会导出在wrapper class中,如果这个field在mono内被引用,导出和构建过程不会有问题,但是运行之后才发现crash

    可否在生成了wrapper class的dll之后,再生成一次运行在mono层的dll,这样可以在编译时就发现问题,防止到运行之后的crash问题。

    binder.json 的CSharpIgnorTypes可以产生类似的问题,如果配置了某些被忽略的type,但是这些在mono层被引用到了,运行中如果某个地方引用到了相关的成员或者函数,会产生crash,而构建过程中这些问题不会暴露出来。

    opened by floatyears 0
  • 在生成UnityBind时对generic delegate parameter进行UnBox没有展开非泛型的参数类型

    在生成UnityBind时对generic delegate parameter进行UnBox没有展开非泛型的参数类型

    例如:

    public class TestArgs
    {
      public string name;
    }
    
    public class TestClassA
    {
    public delegate System.EventHandler<TestArgs> testDelegate; 
    }
    

    testDelegate展开之后类型是 EventHandler(object sender, TestArgs e),此delegate含有两个参数,但是目前会错误的只包含了一个TestArgs类型的参数。

    opened by floatyears 0
  • class内定义的delegate如果作为此class的构造函数的参数传入,会导致生成的UnityBind有语法错误,并且wrapper class的处理也有误

    class内定义的delegate如果作为此class的构造函数的参数传入,会导致生成的UnityBind有语法错误,并且wrapper class的处理也有误

    例如这个class:

    public class TestDelegateAsCtorArg
    {
        public delegate void Callback();
    
        public TestDelegateAsCtorArg(Callback callback)
        {
            SetCallBack(callback);
        }
        public void SetCallBack(Callback callback)
        {
            if(callback != null)
            {
                callback();
            }
        }
    }
    

    生成的wrapper class的代码:

    ......
    public  global::TestDelegateAsCtorArg.Callback _TestDelegateAsCtorArg__ctor_callback;
    public GCHandle _TestDelegateAsCtorArg__ctor_callback_ref;
    static Delegate5605cacf TestDelegateAsCtorArg__ctor_callbackAction = OnTestDelegateAsCtorArg__ctor_callback;
    static void OnTestDelegateAsCtorArg__ctor_callback(IntPtr arg0_h)
    {
      Exception __e = null;
      try
      {
        var arg0Obj = ObjectStore.Get<global::TestDelegateAsCtorArg>(arg0_h);
        arg0Obj._TestDelegateAsCtorArg__ctor_callback();
      }
      catch(Exception e)
      {
        __e = e;
      }
      if(__e != null)
      ScriptEngine.OnException(__e.ToString());
    }
    public  global::TestDelegateAsCtorArg.Callback _TestDelegateAsCtorArg_SetCallBack_callback;
    public GCHandle _TestDelegateAsCtorArg_SetCallBack_callback_ref;
    static Delegate5605cacf TestDelegateAsCtorArg_SetCallBack_callbackAction = OnTestDelegateAsCtorArg_SetCallBack_callback;
    static void OnTestDelegateAsCtorArg_SetCallBack_callback(IntPtr arg0_h)
    {
    	Exception __e = null;
    	try
    	{
    		var arg0Obj = ObjectStore.Get<global::TestDelegateAsCtorArg>(arg0_h);
    		arg0Obj._TestDelegateAsCtorArg_SetCallBack_callback();
    	}
    	catch(Exception e)
    	{
    		__e = e;
    	}
    	if(__e != null)
    	ScriptEngine.OnException(__e.ToString());
    }
    ......
    
    public TestDelegateAsCtorArg(Callback callback)
    {
      if(typeof(TestDelegateAsCtorArg) == GetWType())
      {
        var TestDelegateAsCtorArg__ctor_callback_p = Marshal.GetFunctionPointerForDelegate(TestDelegateAsCtorArg__ctor_callbackAction);
        var h = MonoBind.TestDelegateAsCtorArg__ctor_6(TestDelegateAsCtorArg__ctor_callback_p);
        ScriptEngine.CheckException();
        SetHandle(h);
        ObjectStore.Store(this, h);
      }
    }
    public void SetCallBack(Callback callback)
    {
      _TestDelegateAsCtorArg_SetCallBack_callback = callback;
      ObjectStore.RefMember(this,ref _TestDelegateAsCtorArg_SetCallBack_callback_ref,_TestDelegateAsCtorArg_SetCallBack_callback);
      var TestDelegateAsCtorArg_SetCallBack_callback_p = Marshal.GetFunctionPointerForDelegate(TestDelegateAsCtorArg_SetCallBack_callbackAction);
      MonoBind.TestDelegateAsCtorArg_SetCallBack(this.Handle, TestDelegateAsCtorArg_SetCallBack_callback_p);
      ScriptEngine.CheckException();
      return ;
    }
    

    生成的UnityBind的代码:

    static Delegatef55bb3df _EventDelegate__ctor_call;
    static void OnEventDelegate__ctor_call(this global::EventDelegate arg0)
    {
    var arg0_h = ObjectStore.Store(arg0);
    _EventDelegate__ctor_call(arg0_h);
    ScriptEngine.CheckException();
    }
    
    [MonoPInvokeCallback(typeof(EventDelegate__ctor_12_Type))]
    static IntPtr EventDelegate__ctor_12 (IntPtr call_p)
    {
    Exception __e = null;
    try
    {
    	_EventDelegate__ctor_call = call_p == IntPtr.Zero ? null: Marshal.GetDelegateForFunctionPointer<Delegatef55bb3df>(call_p);
    	var _value = new global::EventDelegate(thizObj.OnEventDelegate__ctor_call); //语法错误
    	var _valueHandle = ObjectStore.Store(_value);
    	return _valueHandle;
    }
    catch(Exception _e_)
    {
    	__e = _e_;
    }
    if(__e != null)
    ScriptEngine.OnException(__e.ToString());
    return default(IntPtr);
    }
    

    项目目前对于delegate的处理应该是将mono层传入的delegate作为function pointer,赋值给wrapper class的static成员。mono内的wrapper class传入的delegate没有正确赋值,UnityBInd内被封装之后的delegate的实例则依赖于class的实例来获得,而此时还在构造constructor的参数,无法获取到实例,导致最后生成的代码产生语法错误。

    opened by floatyears 0
Releases(v0.3.0)
Owner
null
A single file header-only live reload solution for C, written in C++

cr.h A single file header-only live reload solution for C, written in C++: simple public API, 3 functions to use only (and another to export); works a

Danny Angelo Carminati Grein 1.2k Nov 20, 2022
roost low profile ergonomic ortholinear hot-swappable kailh-choc keyboard

Roost keyboard Based off the Sweep v2.1, added 2mm or so horizontal space, added another set of keys and slots for a fat power switch. Moved battery c

Alksdeef 38 Oct 8, 2022
Thick, girthy, hot, griplling tentacles.

Thick, girthy, hot, griplling tentacles. please go away i did the huge mistake of giving these idiots write access I've come to make an announcement:

null 3 Feb 8, 2022
A self-contained minimal library for interacting with Linux hot-plug events

libue Zero dependency minimal library for interacting with Linux hot-plug events. Installation Just drop the header file into your C project. Usage #i

QP Hou 22 Nov 7, 2022
If the button pressed esp will reset and App mode will on. App mode will on then led will on, network is connected led will off.

DHT22-to-Google-sheet-Reset-Using-ESP8266-LED-Switch If button pressed esp will reset and App mode will on. App mode will on then led will on, network

Md. Harun-Or-Rashid 3 Aug 17, 2022
Sloth 🦥 is a coverage guided fuzzing framework for fuzzing Android Native libraries that makes use of libFuzzer and QEMU user-mode emulation

Sloth ?? Sloth is a fuzzing setup that makes use of libFuzzer and QEMU’s user-mode emulation (qemu/linux-user) on x86_64/aarch64 host to emulate aarch

Chaithu 82 Nov 3, 2022
Night mode on any camera. Based on HDR+.

Burst Photo This is a macOS app written in Swift / SwiftUI / Metal that implements a simplified version of HDR+, the computational photography pipelin

Martin Marek 137 Nov 18, 2022
A C/C++ minor mode for Emacs powered by libclang

Irony-Mode A C/C++ minor mode powered by libclang irony-mode is an Emacs minor-mode that aims at improving the editing experience for the C, C++ and O

Guillaume Papin 896 Nov 27, 2022
I2C hardware test terminal/master mode emulator.

I2C Master Mode Emulator The I2C master mode emulator allows communication with I2C devices by sending or receiving data to/from the I2C bus. To issue

Dilshan R Jayakody 19 Nov 5, 2022
Minimal tool for measuring cost of mode switch

CPU mode switch statistics The mode-switch-stat tool measures the cost of CPU mode switch, the round trip between user and kernel mode. At present, th

Steven Cheng 12 Feb 22, 2022
OffensivePH - use old Process Hacker driver to bypass several user-mode access controls

offensiveph OffensivePH is a post-exploitation tool that utilizes an old Process Hacker driver to bypass several user-mode access controls. Usage Comp

Red Section 275 Nov 21, 2022
x64 Windows kernel code execution via user-mode, arbitrary syscall, vulnerable IOCTLs demonstration

anycall x64 Windows kernel code execution via user-mode, arbitrary syscall, vulnerable IOCTLs demonstration Read: https://www.godeye.club/2021/05/14/0

Kento Oki 155 Nov 19, 2022
Install the Homebrew Channel to the vWii Menu from Wii U Mode.

Install a channel to the vWii Menu from Wii U Mode. In its current state, it simply installs the Homebrew Channel.

Puzzle 33 Oct 12, 2022
Loads a signed kernel driver which allows you to map any driver to kernel mode without any traces of the signed / mapped driver.

CosMapper Loads a signed kernel driver (signed with leaked cert) which allows you to map any driver to kernel mode without any traces of the signed /

null 154 Nov 25, 2022
privacier - cs:go kernel mode cheat

privacier - cs:go kernel mode cheat Simple CS:GO Cheat based on IOCTL driver with pretty simple features to practice in development. Goals Using lazy_

Stepan Kuznetsov 24 Sep 11, 2022
Windows kernel-mode driver emulating well-known USB game controllers.

Windows kernel-mode driver emulating well-known USB game controllers.

Virtual Gamepad Emulation Framework 1.7k Nov 23, 2022
WinChipHead CH341 linux driver for I2C / SPI and GPIO mode

WinChipHead (沁恒) CH341 linux driver for I2C / SPI and GPIO mode The CH341 is declined in several flavors, and may support one or more of UART, SPI, I2

Frank Zago 19 Nov 19, 2022
Physical Tic-Tac-Toe smart board with PvP mode and two levels of AI. Built atop a custom PCB connected to an Arduino Mega 2560.

TicTacToe_SmartBoard The files in TicTacToe_SmartBoard are based on the files in https://wiki.illinois.edu/wiki/display/ECE110HLSF15/Tic-Tac-Toe+Smart

Hyrum Dickinson 2 Jul 22, 2022
GTA Online survival missions in Single Player mode

SurvivalsModCPPVersion You liked the survival missions from GTA Online? Well, me too. This mod aims to recreate the same gamemode in Single Player mod

null 1 Nov 23, 2021