Verifying EventRefs in pre-build script

I’m wondering if there is any way to verify all EventRefs in a pre-build script?

If you change the path of an event in FMOD studio and don’t update the references in editor these events obviously wont play so I’m hoping for a way to find all the EventRefs and check that they are valid before doing a build.

Unless there’s already a way to do this I’m guessing this would require reflection to find all strings that have the [EventRef] attribute but I’m not really sure how to use reflection to do this. Hooking into the prebuild script I’m good with.

There isn’t currently a feature for this built in to the integration, you would have to do it manually. The [EventRef] objects are created in memory and attached to the FMODStudioCache object in the integration.

I have created a task to look into this further and see if it is something we can add.

Hi Cameron, thanks for looking in this.

I managed to create a bit of a solution using reflection. It’s not able to check every possible reference but it should work for our needs.

Basically I created a editor menu option to get every monobehaviour in the scene and check if it has a field with the [EventRef] attribute using reflection. Currently it only will find string or any IEnumerable of string (i.e. string[] or List<string>) with the attribute. Custom classes that have an [EventRef] attribute field wont be found. This could be done recursively but I didn’t want to deal with checking for cycles in the recursion and it wasn’t needed for our project currently. Once all the field with that attribute are found I use EventManager.EventFromPath(ref) to verify if it’s a valid reference.

Here’s a rough copy of the code:

foreach (GameObject gameObject in rootObjects)
            {
                MonoBehaviour[] allBehvaiours = gameObject.GetComponentsInChildren<MonoBehaviour>();

                for (int i = 0; i < allBehvaiours.Length; i++)
                {
                    Type componentType = allBehvaiours[i].GetType();

                    FieldInfo[] fields = componentType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);

                    foreach (FieldInfo item in fields)
                    {
                        if (HasAttribute(item, typeof(EventRefAttribute)))
                        {
                            if (item.FieldType == typeof(string))
                            {
                                string output = item.GetValue(allBehvaiours[i]) as string;

                                if (!IsValidEventRef(output))
                                {
                                    Debug.LogError($"Unable to find FMOD Event {output} on object of type {componentType.ToString()} from gameobject {allBehvaiours[i].gameObject.name}");
                                    allValid = false;
                                }
                            }
                            else if (typeof(IEnumerable).IsAssignableFrom(item.FieldType))
                            {
                                foreach (var listItem in (IEnumerable)item.GetValue(allBehvaiours[i]))
                                {
                                    if (listItem.GetType() == typeof(string))
                                    {
                                        string listOutput = listItem as string;
                                        if (!IsValidEventRef(listOutput))
                                        {
                                                 Debug.LogError($"Unable to find FMOD Event {listOutput} on object 
                                                {valueObject.ToString()}");
                                        }
                            }
                                    }
                                }
                            }
                        }
                    }
                }
            }

And the helper functions:

private static bool HasAttribute(MemberInfo provider, params Type[] attributeTypes)
{
    Attribute[] allAttributes = Attribute.GetCustomAttributes(provider, typeof(Attribute), true);

    if (allAttributes.Length == 0)
        return false;

    return allAttributes.Where(a => attributeTypes.Any(x => a.GetType() == x || x.IsAssignableFrom(a.GetType()))).Any();
}

    public static bool IsValidEventRef(string reference)
    {
        if (string.IsNullOrEmpty(reference)) return true;

        EditorEventRef eventRef = EventManager.EventFromPath(reference);

        return eventRef != null;
    }

I hope that makes sense, it’s a lot of code to paste into this form. Not sure if it’s the best way but seems to work for finding event references that are not valid. (In the code I ignore empty event reference strings but that can be changed easily). I also have a method that takes a list of scriptable objects and does a similar check. The code is basically the same.

Let me know if you can think of an better way but maybe this will help anyone else looking for a similar solution.