How to change AssemblyName after compilation to load as a mod in Unity3d

.net, c#, reflection, unity-game-engine

Solution

The solution I ended up going with was using Mono.Cecil. Luckily, the Mono.Cecil library is available in the Unity editor, no need to deploy any extra libraries with my mod tools. Mono.Cecil worked great for renaming the assembly after it was compiled. Here is some code I'm using to make it happen:

     // 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();
     }

Problem

I have been investigating adding mod support to a Unity3D game. I am new to Unity (but not .NET), so if it seems like I am missing something obvious, please let me know because I probably am. I would like to be able to export objects, including custom scripts, directly from the editor. It is easy enough to get the assembly associated with the script. Any loose scripts in the project seem to get compiled down to Assembly-`CSharp.dll`. The problem comes in when I try to load the assembly at runtime in another Unity project. Calling `Assembly.Load(byte[])` returns to me the assembly of the current project, not the one represented by the byte array. I am assuming this is because the `AssemblyName` of the assembly from the mod project and the host project are both identical (`Assembly-CSharp`, `Version=0.0.0.0`, `Culture=neutral`, `PublicKeyToken=null`). As a test, I updated my Visual Studio project for the mod project, renaming it from Assembly-CSharp to something different. This gets wiped out by Unity, but I can do a build before that happens and get the assembly I am looking for with a name other than Assembly-CSharp. Loading this via `Assembly.Load(byte[])` seems to do the trick in a very simple 'import a script to rotate a cube' sort of scenario. While this appears to work, I am really looking for something more auto-magic single step from the Unity editor. Now, on to the question... If I understand the problem, what I am looking for is a programmatic way to change the `AssemblyName` of an assembly after compilation. All suggestions & advice welcome. Some possible routes I have investigated or considered: 1) A magic API method to change `AssemblyName` and save it. This would be ideal, but I have yet to stumble across anything useful here. 2) Edit raw bytes of an assembly to change the name? Not sure if this is possible, but if I could safely and reliably overwrite some of the raw bytes of an assembly to change its name, that would be swell. 3) Create a new dynamic assembly containing the types in the original assembly. My experience with dynamic assemblies is limited. I can create one, but what I am unsure how to do (if possible) is copy types defined in the original assembly to the new dynamic assembly. 4) Use the `CSharpCodeProvider` to manually compile the assembly. Assuming Unity supports it, then I assume this might work, seems like a pain though. I have not yet investigated what it would take to locate all scripts and references needed for compilation. I'd like to avoid it if possible. 5) *Some magic with `ilasm/ildasm.*` Assuming there is unity equivalents.. Is this something do-able with theses tools? 6) Programmatically update the `csproj` file and recompile. Seems pretty hacky and a pain. Guessing I would need to have code to support both Visual Studio and MonoDevelop. I would prefer to avoid external tools and recompilation so one of the first three options would be ideal. Thoughts, ideas? Thanks! Bonus Question: My 'import a script to rotate a cube' test seemed to work, however in the log during the loading of the assembly, I see the two messages below. That is not the assembly name I used, not sure where it came from. Should I be concerned? Is this going to come back to bite me later? ``` > 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 ```

Original source