I’m trying to ‘intercept’ some method and property calls using Mono.Cecil. Is this portable? Including closed platforms? I know photon fusion does this, and I can’t see any reason why IL2CPP/Mono should not work. What should I be aware of? If anyone can shed some light on it I would be grateful.
Any platform with signed code binaries (iOS and Android for example) will likely not support injecting fresh code.
Hey Kurt. Thanks for the reply. I’m not planning to insert code at runtime. It will happen only in the editor. Precisely at UnityEditor.Compilation.CompilationPipeline.assemblyCompilationFinished
do you think that will still apply?
If you’re talking about injecting code into the dll on compilation that will then be part of the dll on delivery. That should be fine in theory.
Even IL2CPP should likely work since you’ve already generated the IL and now the IL2CPP pipeline takes over and will convert that IL.
…
Of course since I have not actually done this myself, I can’t say for certain. And I highly doubt this is directly supported by Unity in the sense of any guarantee (otherwise it’d be documented).
What you should do is create a demo project as a sort of unit test that injects all the potential things you’d like to inject, run it through every potential target platform, and test it. That’ll give you the true answer to your question.
This is especially since even if someone has had experience comes along, you haven’t exactly shared exactly what you plan to inject, and there may very well be gotchas that we’re unaware of. So test test test!
^ ^ ^ ^ very important… good point Lord.
Unfortunately I have no experience in this so I can’t answer.
I can’t say unity directly supports it but it’s somewhat “OK”.
Unity has a Mono.Cecil package marked for “internal use only” yet it says “To avoid assembly clashes, please use this package if you intend to use Mono.Cecil.” in it’s description.
I’ll of course test on all platforms we are targeting. That’s 11 including major console platforms. What worries me is the potential future platforms. I’ll test on all possible platforms and put the results here.
BTW: What I’m trying to do is ease the use of some boilerplate code. We have a container class loosely defined like
unsafe public class Property<T> where T : unmanaged
{
internal Simulation simulation {get; set;}
internal int offset;
public T value {
get => *((T *) (simulation.currentFrame + offset));
set => *((T *) (simulation.currentFrame + offset)) = value;
}
}
that must be used like
Property<float> floatProperty = new Property<float>();
void func()
{
floatProperty.value = 3.14f;
}
Since this will virtually be used for all variables in the game, it’s problematic. I’m trying to turn that code into by “overwriting” bodies of get and set methods.
[SimulatedPropery]
public float floatProperty {get; set;}
To my knowledge there are several libraries for unity that can do that but our use was limited and I didn’t want to introduce another dependency.
I can confirm following simple implementation works for Windows 64, Android armv7, Nintendo Switch, XBoxOne and PlayStation 4.
Note : I’ve only tried “debug signing” but I don’t expect any differences with “release signing”. Also I’ve only tested with IL2CPP. Mono might behave differently.
I has to be it’s own assembly definition. Create an empty directory and place both files inside
ILWeaver.cs
using System;
using System.Linq;
using System.Collections.Generic;
using System.Reflection;
using System.IO;
using UnityEngine;
using UnityEditor;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Cecil.Rocks;
using Mono.Collections.Generic;
public class ILWeaver
{
[InitializeOnLoadMethod]
static void init()
{
List<string> assemblies = new List<string>();
UnityEditor.Compilation.CompilationPipeline.compilationStarted += (o) => {
EditorApplication.LockReloadAssemblies();
assemblies.Clear();
};
UnityEditor.Compilation.CompilationPipeline.assemblyCompilationFinished += (asmName, message) => {
assemblies.Add(asmName);
};
UnityEditor.Compilation.CompilationPipeline.compilationFinished += (o) => {
foreach(var asmName in assemblies) {
AssemblyDefinition asmDef = AssemblyDefinition.ReadAssembly(asmName, new ReaderParameters() {
ReadWrite = true,
});
// Do your weaving here
asmDef.Write();
asmDef.Dispose();
}
EditorApplication.UnlockReloadAssemblies();
};
}
}
ILWeaver.asmdef
{
"name": "ILWeaver",
"rootNamespace": "",
"references": [
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": true,
"overrideReferences": true,
"precompiledReferences": [
"Mono.Cecil.dll",
"Mono.Cecil.Rocks.dll",
"Mono.Cecil.Pdb.dll",
"Mono.Cecil.Mdb.dll"
],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}