如何在编译后将AssemblyName更改为在Unity3d中作为mod加载

Tal*_*lek 6 .net c# reflection unity-game-engine

我一直在研究为Unity3D游戏添加mod支持.我是Unity的新手(但不是.NET),所以如果我觉得我遗漏了一些明显的东西,请告诉我,因为我可能是.

我希望能够直接从编辑器导出对象,包括自定义脚本.获取与脚本关联的程序集很容易.项目中任何松散的脚本似乎都被编译成Assembly- CSharp.dll.当我尝试在运行时在另一个Unity项目中加载程序集时,问题就出现了.调用Assembly.Load(byte[])返回当前项目的程序集,而不是字节数组表示的程序集.我假定这是因为AssemblyName从国防部项目的装配和主体项目的都是相同的(Assembly-CSharp,Version=0.0.0.0,Culture=neutral,PublicKeyToken=null).

作为测试,我更新了我的Visual Studio项目的mod项目,将它从Assembly-CSharp重命名为不同的东西.这会被Unity消灭,但我可以在此之前进行构建,并使用Assembly-CSharp以外的名称获取我正在寻找的程序集.加载此过程Assembly.Load(byte[])似乎可以通过一个非常简单的"导入脚本来旋转多维数据集"的方案来实现.虽然这似乎有效,但我真的在Unity编辑器中寻找更多自动魔术单步.

现在,关于这个问题 ......

如果我理解了这个问题,我正在寻找的是AssemblyName编译后更改程序集的编程方法.欢迎所有建议和意见.

我调查或考虑的一些可能路线:

1)一种神奇的API方法来更改AssemblyName和保存它. 这将是理想的,但我还没有发现任何有用的东西.

2)编辑程序集的原始字节以更改名称?不确定这是否可行,但是如果我可以安全可靠地覆盖程序集的一些原始字节来更改其名称,那就会膨胀.

3)创建一个包含原始程序集中类型的新动态程序集. 我对动态装配的经验有限.我可以创建一个,但我不确定如何做(如果可能)是原始程序集中定义的复制类型到新的动态程序集.

4)使用CSharpCodeProvider手动编译程序集.假设Unity支持它,那么我认为这可能会起作用,但似乎很痛苦.我还没有研究如何找到编译所需的所有脚本和引用.如果可能的话,我想避免它.

5)*一些魔术ilasm/ildasm.*假设有统一的等价物.这是否可以使用这些工具?

6)以编程方式更新csproj文件并重新编译.看起来非常h​​acky和痛苦.猜测我需要有代码来支持Visual Studio和MonoDevelop.

我宁愿避免使用外部工具和重新编译,因此前三个选项中的一个是理想选择.思想,想法?谢谢!


奖金问题:我的'导入脚本以旋转立方体'测试似乎有效,但是在加载程序集期间的日志中,我看到下面的两条消息.这不是我使用的程序集名称,不确定它来自何处.我应该担心吗?以后这会回来咬我吗?

> Non platform assembly: data-0B9D7940 (this message is harmless)
> Fallback handler could not load library C:/Dev/Test
> Projects/ModTest/Build/ModHost1/ModHost1_Data/Mono/data-0B9D7940.dll
Run Code Online (Sandbox Code Playgroud)

Tal*_*lek 5

我最终采用的解决方案是使用 Mono.Cecil。幸运的是,Mono.Cecil 库在 Unity 编辑器中可用,无需使用我的 mod 工具部署任何额外的库。Mono.Cecil 非常适合在编译后重命名程序集。这是我用来实现它的一些代码:

     // Have Mono.Cecil load the assembly
     var assemblyDefinition = Mono.Cecil.AssemblyDefinition.ReadAssembly(assemblyFile.FullName);

     // Tell Mono.Cecil to actually change the name
     assemblyDefinition.Name.Name = newAssemblyNameNoExtension;
     assemblyDefinition.MainModule.Name = newAssemblyNameNoExtension;

     // We also need to rename any references to project assemblies (first pass assemblies)
     foreach (var reference in assemblyDefinition.MainModule.AssemblyReferences)
     {
        if (Utilities.IsProjectAssembly(reference.Name))
        {
           reference.Name = Utilities.GetModAssemblyName(reference.Name, this._modName);
        }
     }

     // Build the new assembly
     byte[] bytes;
     using (var ms = new MemoryStream())
     {
        assemblyDefinition.Write(ms, new Mono.Cecil.WriterParameters() { WriteSymbols = true });
        bytes = ms.ToArray();
     }
Run Code Online (Sandbox Code Playgroud)