Last active
April 30, 2020 13:35
-
-
Save ByronMayne/ec107d5a5f7196b132c561dccec38a71 to your computer and use it in GitHub Desktop.
This code is used to inject a callback into Unity's internal build system. They should have this but if you really want to you can just make your own. This only needs Mono.Cecil #Magic
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class Injector | |
{ | |
[MenuItem("Hacks/Inject Build Callback")] | |
public static void BuildCallback() | |
{ | |
Assembly.BeginEditingAssembly(AssemblyTypes.UnityEditor, editAlreadyModifed:true); | |
{ | |
TypeDefinition buildPiplelineType = Assembly.GetType<BuildPipeline>(); | |
MethodDefinition buildInternalMethod = buildPiplelineType.GetMethod("BuildPlayerInternal"); | |
TypeReference MultiCastDelegateTypeRef = Assembly.Import<System.MulticastDelegate>(); | |
TypeDefinition MultiCastDelegateTypeDef = new TypeDefinition("", "OnBuildStartedDelegate", TypeAttributes.Public, MultiCastDelegateTypeRef); | |
MethodDefinition ConstructorMethodDef = new MethodDefinition(".ctor", MethodAttributes.Public | | |
MethodAttributes.CompilerControlled | | |
MethodAttributes.RTSpecialName | | |
MethodAttributes.SpecialName | | |
MethodAttributes.HideBySig, | |
Assembly.Import(typeof(void))); | |
// Constructor | |
ConstructorMethodDef.IsRuntime = true; | |
ConstructorMethodDef.HasThis = true; | |
ConstructorMethodDef.IsHideBySig = true; | |
ConstructorMethodDef.IsPublic = true; | |
ConstructorMethodDef.Parameters.Add(new ParameterDefinition("levels", ParameterAttributes.None, Assembly.Import<string[]>())); | |
ConstructorMethodDef.Parameters.Add(new ParameterDefinition("buildLocation", ParameterAttributes.None, Assembly.Import<string>())); | |
ConstructorMethodDef.Parameters.Add(new ParameterDefinition("buildTarget", ParameterAttributes.None, Assembly.Import<BuildTarget>())); | |
ConstructorMethodDef.Parameters.Add(new ParameterDefinition("buildOptions", ParameterAttributes.None, Assembly.Import<BuildOptions>())); | |
MultiCastDelegateTypeDef.Methods.Add(ConstructorMethodDef); | |
// Invoking Method | |
MethodDefinition BeginInvokeMethodDef = new MethodDefinition("BeginInvoke", MethodAttributes.Public | | |
MethodAttributes.HideBySig | | |
MethodAttributes.NewSlot | | |
MethodAttributes.Virtual, | |
Assembly.Import<System.AsyncCallback>()); | |
BeginInvokeMethodDef.IsRuntime = true; | |
BeginInvokeMethodDef.HasThis = true; | |
BeginInvokeMethodDef.IsHideBySig = true; | |
BeginInvokeMethodDef.IsRuntimeSpecialName = true; | |
BeginInvokeMethodDef.IsSpecialName = true; | |
BeginInvokeMethodDef.IsPublic = true; | |
BeginInvokeMethodDef.IsVirtual = true; | |
BeginInvokeMethodDef.Parameters.Add(new ParameterDefinition("buildLocation", ParameterAttributes.None, Assembly.Import<string>())); | |
BeginInvokeMethodDef.Parameters.Add(new ParameterDefinition("buildTarget", ParameterAttributes.None, Assembly.Import<BuildTarget>())); | |
BeginInvokeMethodDef.Parameters.Add(new ParameterDefinition("buildOptions", ParameterAttributes.None, Assembly.Import<BuildOptions>())); | |
BeginInvokeMethodDef.Parameters.Add(new ParameterDefinition("callback", ParameterAttributes.None, Assembly.Import<System.IAsyncResult>())); | |
BeginInvokeMethodDef.Parameters.Add(new ParameterDefinition("object", ParameterAttributes.None, Assembly.Import<object>())); | |
MultiCastDelegateTypeDef.Methods.Add(BeginInvokeMethodDef); | |
// End Invoke | |
MethodDefinition EndInvokeMethodDef = new MethodDefinition("EndInvoke", MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual, Assembly.Import(typeof(void))); | |
EndInvokeMethodDef.Parameters.Add(new ParameterDefinition("result", ParameterAttributes.None, Assembly.Import<System.IAsyncResult>())); | |
EndInvokeMethodDef.IsRuntime = true; | |
EndInvokeMethodDef.HasThis = true; | |
EndInvokeMethodDef.IsHideBySig = true; | |
EndInvokeMethodDef.IsRuntimeSpecialName = true; | |
EndInvokeMethodDef.IsSpecialName = true; | |
EndInvokeMethodDef.IsPublic = true; | |
MultiCastDelegateTypeDef.Methods.Add(EndInvokeMethodDef); | |
// Invoke | |
MethodDefinition InvokeMethodDef = new MethodDefinition("Invoke", MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual, Assembly.Import(typeof(void))); | |
InvokeMethodDef.Parameters.Add(new ParameterDefinition("levels", ParameterAttributes.None, Assembly.Import<string[]>())); | |
InvokeMethodDef.Parameters.Add(new ParameterDefinition("buildLocation", ParameterAttributes.None, Assembly.Import<string>())); | |
InvokeMethodDef.Parameters.Add(new ParameterDefinition("buildTarget", ParameterAttributes.None, Assembly.Import<BuildTarget>())); | |
InvokeMethodDef.Parameters.Add(new ParameterDefinition("buildOptions", ParameterAttributes.None, Assembly.Import<BuildOptions>())); | |
InvokeMethodDef.IsRuntime = true; | |
InvokeMethodDef.HasThis = true; | |
InvokeMethodDef.IsHideBySig = true; | |
InvokeMethodDef.IsVirtual = true; | |
InvokeMethodDef.IsPublic = true; | |
MultiCastDelegateTypeDef.Methods.Add(InvokeMethodDef); | |
// Add our new delegate type | |
buildPiplelineType.NestedTypes.Add(MultiCastDelegateTypeDef); | |
// Add a field for it | |
FieldDefinition delegateFieldDef = new FieldDefinition("onBuildStarted", FieldAttributes.Public | FieldAttributes.Static, MultiCastDelegateTypeDef.Resolve()); | |
buildPiplelineType.Fields.Add(delegateFieldDef); | |
{ | |
ILProcessor ilProcessor = buildInternalMethod.Body.GetILProcessor(); | |
ilProcessor.InsertBefore(buildInternalMethod.Body.Instructions[0], ilProcessor.Create(OpCodes.Nop)); | |
ilProcessor.InsertBefore(buildInternalMethod.Body.Instructions[0], ilProcessor.Create(OpCodes.Callvirt, InvokeMethodDef)); | |
ilProcessor.InsertBefore(buildInternalMethod.Body.Instructions[0], ilProcessor.Create(OpCodes.Ldarg_3)); | |
ilProcessor.InsertBefore(buildInternalMethod.Body.Instructions[0], ilProcessor.Create(OpCodes.Ldarg_2)); | |
ilProcessor.InsertBefore(buildInternalMethod.Body.Instructions[0], ilProcessor.Create(OpCodes.Ldarg_1)); | |
ilProcessor.InsertBefore(buildInternalMethod.Body.Instructions[0], ilProcessor.Create(OpCodes.Ldarg_0)); | |
ilProcessor.InsertBefore(buildInternalMethod.Body.Instructions[0], ilProcessor.Create(OpCodes.Ldsfld, delegateFieldDef)); | |
ilProcessor.InsertBefore(buildInternalMethod.Body.Instructions[0], ilProcessor.Create(OpCodes.Nop)); | |
} | |
// Add invoke to BuildPlayerWindow | |
TypeDefinition buildPlayerType = Assembly.GetType("UnityEditor", "BuildPlayerWindow"); | |
MethodDefinition buildDefaultMethodDef = buildPlayerType.GetMethod("BuildPlayerWithDefaultSettings", 3); | |
{ | |
UnityEngine.Debug.Log(buildDefaultMethodDef.Parameters[2].Name); | |
ILProcessor ilProcessor = buildDefaultMethodDef.Body.GetILProcessor(); | |
int index = buildDefaultMethodDef.Body.Instructions.Count - 13; | |
ilProcessor.InsertBefore(buildDefaultMethodDef.Body.Instructions[index], ilProcessor.Create(OpCodes.Nop)); | |
ilProcessor.InsertBefore(buildDefaultMethodDef.Body.Instructions[index], ilProcessor.Create(OpCodes.Callvirt, InvokeMethodDef)); | |
ilProcessor.InsertBefore(buildDefaultMethodDef.Body.Instructions[index], ilProcessor.Create(OpCodes.Ldloc, 7)); // 7 Build Options | |
ilProcessor.InsertBefore(buildDefaultMethodDef.Body.Instructions[index], ilProcessor.Create(OpCodes.Ldloc_1)); // Build Target | |
ilProcessor.InsertBefore(buildDefaultMethodDef.Body.Instructions[index], ilProcessor.Create(OpCodes.Ldloc, 5)); // 5 text | |
ilProcessor.InsertBefore(buildDefaultMethodDef.Body.Instructions[index], ilProcessor.Create(OpCodes.Ldloc, 14)); // 14 Levels | |
ilProcessor.InsertBefore(buildDefaultMethodDef.Body.Instructions[index], ilProcessor.Create(OpCodes.Ldsfld, delegateFieldDef)); | |
ilProcessor.InsertBefore(buildDefaultMethodDef.Body.Instructions[index], ilProcessor.Create(OpCodes.Nop)); | |
} | |
} | |
Assembly.EndEditingAssembly(shouldSave: true); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment