ExecutionEngineException: Attempting to call method 'System.Func`1[[System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]::.ctor' for which no ahead of time (AOT) code was generated.
What is the current behavior?
DialogueRunner.SetProject( project ); does not work on android and iOS using IL2CPP. It fails with error:
ExecutionEngineException: Attempting to call method 'System.Func`1[[System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]::.ctor' for which no ahead of time (AOT) code was generated.
10-04 01:27:38.707 17240 17265 E Unity : at System.Delegate.CreateDelegate (System.Type type, System.Object firstArgument, System.Reflection.MethodInfo method, System.Boolean throwOnBindFailure, System.Boolean allowClosed) [0x00000] in <00000000000000000000000000000000>:0
10-04 01:27:38.707 17240 17265 E Unity : at System.Delegate.CreateDelegate (System.Type type, System.Reflection.MethodInfo method) [0x00000] in <00000000000000000000000000000000>:0
10-04 01:27:38.707 17240 17265 E Unity : at Yarn.Unity.ActionManager.FindAllActions (System.Collections.Generic.IEnumerable`1[T] assemblyNames) [0x00000] in <00000000000000000000000000000000>:0
10-04 01:27:38.707 17240 17265 E Unity : at Yarn.Unity.DialogueRunner.SetProject (Yarn.Unity.YarnProject newProject) [0x00000] in <00000000000000000000000000000000>:0
Please provide the steps to reproduce, and if possible a minimal demo of the problem:
- Create a new Unity Project in Unity 2021.3.8f1
- Add Yarn Spinner 2.2.1 with Package Manager
- Make sure Target Platform is Android
- Go to Player Settings and set Scripting Backend for Android to IL2CPP
- Enter a package name for Android Builds: e.g: "com.DefaultCompany.YarnSpinnerAOTRepro"
- Enter a bundle identifier for iOS Builds: e.g: "com.DefaultCompany.YarnSpinnerAOTRepro"
- Create a new gameObject
- Add DialogueRunner and this script to it:
using UnityEngine;
using Yarn.Unity;
public class Repro : MonoBehaviour
{
public YarnProject Project;
void Start()
{
var dialogueRunner = GetComponent<DialogueRunner>();
dialogueRunner.SetProject(Project);
dialogueRunner.StartDialogue("Start");
}
}
- Create a new Yarn Project
- Create a new Yarn Script with a node titled "Start"
- Assign the Yarn Script to the Yarn Project.
- Assign the Yarn Project to Repro.cs script in the inspector.
- Save Scene
- Make a build.
- Install on phone and use something like adb logcat or (console.app on iOS) to see error in console. (Searching for "ExecutionEngineException" should take you right too it.)
What is the expected behavior?
No error in console. I should be able to call DialogueRunner.StartDialogue("Start"); without getting "DialogueException: Cannot load node Start: No nodes have been loaded." error.
Please tell us about your environment:
- Yarn Spinner Version: 2.2.1
- Unity Version: 2021.3.8f1
Other information
Error reproduced using Huawei P20 lite running Android 9 and iPhone Pro Max 13 running iOS 16 Repro project is available for download here: https://github.com/johste93/YarnSpinnerAOTIssueRepro
This seems to be the same issue as #163 specifically with IL2CPP, there is a hack found in that issue report that other people have reported working on the discord that you can try as well.
Thank you! Setting "IL2CPP Code generation" to "Faster (smaller) build" does work, but in my case the performance loss was not worth it.
The problem seem to be that YarnSpinner uses reflection to find and load functions and commands attributed with [YarnFunction] and [YarnCommand] and then converts them to a generic System.Func type at runtime. Because some of these types are not referanced elsewhere in my project the IL2CPP compiler generates no AOT code for them.
I was able to work around the issue by adding this script to my project:
using System;
using UnityEngine;
/// <summary>
/// When you create a IL2CPP build, Unity will try to strip unused code from your project.
/// Yarnspinner uses reflection to find and load functions and commands attributed with [YarnFunction] and [YarnCommand]
/// and then converts them to a generic System.Func type at runtime. Because there are no other referances to some of
/// these unique Func types, the IL2CPP compiler generates no AOT code for these. At the time of writing there only seem
/// to be two DefaultActions that require a direct referance for the compiler to generate them.
/// There is probably a better way of doing this using a link.xml or something, but thats beyond my skills.
/// </summary>
public static class AOTLinker
{
[RuntimeInitializeOnLoadMethod] //<--- This executes after first Scene loaded
static void AOTPatch()
{
Func<Single, Single, Single> func1 = (x, y) => x+y; //<--- Matches signature of Yarn.Unity.DefaultActions.RandomRange
Func<Single, int, Single> func2 = (x, y) => x+y; //<--- Matches signature of Yarn.Unity.DefaultActions.RoundPlaces
}
}
I am also having this issue on Android. From reading the IL2CPP documentation, having a dummy use of a generic type with specific arguments seems to be the intended way to ensure AOT gen. Perhaps this can be done for the default actions they force in, and this can be noted in the documentation for creating your own,
This is related to #163 closing as duplicate, thanks.