public ReferenceCollection GetReferences(ITreeNode element, ReferenceCollection oldReferences) { if (ResolveUtil.CheckThatAllReferencesBelongToElement <UnityEventTargetReference>(oldReferences, element)) { return(oldReferences); } if (!(element is IPlainScalarNode methodNameValue)) { return(ReferenceCollection.Empty); } // E.g. element is the m_MethodName scalar value "ButtonClickedHandler" in this structure: // m_OnClick: // m_PersistentCalls: // m_Calls: // - m_Target: {fileID: 1870695363} // m_MethodName: ButtonClickedHandler // m_Mode: 3 // m_Arguments: // m_ObjectArgument: {fileID: 0} // m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine // m_IntArgument: 1 // m_FloatArgument: 0 // m_StringArgument: // m_BoolArgument: 0 // m_CallState: 2 // m_TypeName: UnityEngine.UI.Button+ButtonClickedEvent, UnityEngine.UI, Version=1.0.0.0, // Culture=neutral, PublicKeyToken=null var methodNameMapEntry = BlockMappingEntryNavigator.GetByValue(methodNameValue); var callMapNode = BlockMappingNodeNavigator.GetByEntrie(methodNameMapEntry); var callsSequenceEntry = SequenceEntryNavigator.GetByValue(callMapNode); var callsSequenceNode = BlockSequenceNodeNavigator.GetByEntrie(callsSequenceEntry); var callsMapEntry = BlockMappingEntryNavigator.GetByValue(callsSequenceNode); // callsMapEntry should be "m_Calls" (and contain a value that is a sequence node). If it's not null, // everything else is also not null if (callsMapEntry == null) { return(ReferenceCollection.Empty); } if (methodNameMapEntry.Key.MatchesPlainScalarText("m_MethodName") && callsMapEntry.Key.MatchesPlainScalarText("m_Calls")) { // If we have a guid, that means this event handler exists inside another asset. That asset might be // a .dll, in which case we don't want to add a reference (the primary purpose of these references // is to enable Find Usages of methods, not navigation *from* YAML). Or it might be e.g. a prefab. // This would be a reference to a prefab that contains a MonoScript asset that has the method // TODO: Create an index of other assets that we could target var fileID = callMapNode.FindMapEntryBySimpleKey("m_Target")?.Value.AsFileID(); if (fileID != null && !fileID.IsNullReference && fileID.guid == null) { var reference = new UnityEventTargetReference(methodNameValue, fileID); return(new ReferenceCollection(reference)); } } return(ReferenceCollection.Empty); }
public ReferenceCollection GetReferences(ITreeNode element, ReferenceCollection oldReferences) { if (ResolveUtil.CheckThatAllReferencesBelongToElement <UnityEventTargetReference>(oldReferences, element)) { return(oldReferences); } if (!(element is IPlainScalarNode methodNameValue)) { return(ReferenceCollection.Empty); } // E.g. element is the m_MethodName scalar value "ButtonClickedHandler" in this structure: // m_OnClick: // m_PersistentCalls: // m_Calls: // - m_Target: {fileID: 1870695363} // m_MethodName: ButtonClickedHandler // m_Mode: 3 // m_Arguments: // m_ObjectArgument: {fileID: 0} // m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine // m_IntArgument: 1 // m_FloatArgument: 0 // m_StringArgument: // m_BoolArgument: 0 // m_CallState: 2 // m_TypeName: UnityEngine.UI.Button+ButtonClickedEvent, UnityEngine.UI, Version=1.0.0.0, // Culture=neutral, PublicKeyToken=null var methodNameMapEntry = BlockMappingEntryNavigator.GetByContent(ContentNodeNavigator.GetByValue(methodNameValue)); var callMapNode = BlockMappingNodeNavigator.GetByEntrie(methodNameMapEntry); var callsSequenceEntry = SequenceEntryNavigator.GetByValue(callMapNode); var callsSequenceNode = BlockSequenceNodeNavigator.GetByEntrie(callsSequenceEntry); var callsMapEntry = BlockMappingEntryNavigator.GetByContent(ContentNodeNavigator.GetByValue(callsSequenceNode)); // callsMapEntry should be "m_Calls" (and contain a value that is a sequence node). If it's not null, // everything else is also not null if (callsMapEntry == null) { return(ReferenceCollection.Empty); } if (methodNameMapEntry.Key.MatchesPlainScalarText("m_MethodName") && callsMapEntry.Key.MatchesPlainScalarText("m_Calls")) { var fileID = callMapNode.FindMapEntryBySimpleKey("m_Target")?.Content.Value.AsFileID(); if (fileID != null && !fileID.IsNullReference) { var text = callMapNode.Entries.FirstOrDefault(t => t.Key.MatchesPlainScalarText("m_Mode"))?.Content.Value .GetPlainScalarText(); var argMode = EventHandlerArgumentMode.Unknown; if (int.TryParse(text, out var mode)) { if (1 <= mode && mode <= 6) { argMode = (EventHandlerArgumentMode)mode; } } var arguments = callMapNode.Entries.FirstOrDefault(t => t.Key.MatchesPlainScalarText("m_Arguments"))?.Content.Value as IBlockMappingNode; var typeNameRecord = arguments?.Entries.FirstOrDefault(t => t.Key.MatchesPlainScalarText("m_ObjectArgumentAssemblyTypeName"))?.Content.Value; var type = typeNameRecord?.GetPlainScalarText()?.Split(',').FirstOrDefault(); if (type.IsNullOrEmpty() && mode == 1) { type = null; } var reference = new UnityEventTargetReference(methodNameValue, argMode, type, fileID); return(new ReferenceCollection(reference)); } } return(ReferenceCollection.Empty); }
public ReferenceCollection GetReferences(ITreeNode element, ReferenceCollection oldReferences) { if (ResolveUtil.CheckThatAllReferencesBelongToElement <UnityEventTargetReference>(oldReferences, element)) { return(oldReferences); } if (!(element is IPlainScalarNode methodNameValue)) { return(ReferenceCollection.Empty); } // E.g. element is the m_MethodName scalar value "ButtonClickedHandler" in this structure: // m_OnClick: // m_PersistentCalls: // m_Calls: // - m_Target: {fileID: 1870695363} // m_MethodName: ButtonClickedHandler // m_Mode: 3 // m_Arguments: // m_ObjectArgument: {fileID: 0} // m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine // m_IntArgument: 1 // m_FloatArgument: 0 // m_StringArgument: // m_BoolArgument: 0 // m_CallState: 2 // m_TypeName: UnityEngine.UI.Button+ButtonClickedEvent, UnityEngine.UI, Version=1.0.0.0, // Culture=neutral, PublicKeyToken=null // Note that m_TypeName was removed in Unity 2018.4 var methodNameMapEntry = BlockMappingEntryNavigator.GetByContent(ContentNodeNavigator.GetByValue(methodNameValue)); var callMapNode = BlockMappingNodeNavigator.GetByEntrie(methodNameMapEntry); var callsMapEntry = BlockMappingEntryNavigator.GetByContent( ContentNodeNavigator.GetByValue( BlockSequenceNodeNavigator.GetByEntrie(SequenceEntryNavigator.GetByValue(callMapNode)))); // callsMapEntry should be "m_Calls" (and contain a value that is a sequence node). If it's not null, // everything else is also not null if (callsMapEntry == null) { return(ReferenceCollection.Empty); } // Get the event type name, if it's serialised. This field was removed in Unity 2018.4, but remember that // the serialised value will remain in the scene until it is re-saved. // The only way to get the type name in Unity 2018.4+ is to get the type of the event (e.g. m_OnClick). This // requires getting the type referenced in the m_Script serialised field, which might be in a referenced // assembly, and could be arbitrarily deep in a custom class. // The Event System has this problem, as the EventTrigger MonoBehaviour serialises a list of // EventTrigger.Event instances, and it's EventTrigger.Event.callback that gives us the UnityEvent derived // event type // The only way to do this would be to resolve all types and fields between m_PersistentCalls and the "root" // of the object, taking [FormerlySerialisedAs] into account var persistentCallsMapNode = BlockMappingNodeNavigator.GetByEntrie( BlockMappingEntryNavigator.GetByContent( ContentNodeNavigator.GetByValue(BlockMappingNodeNavigator.GetByEntrie(callsMapEntry)))); var eventTypeName = persistentCallsMapNode?.FindMapEntryBySimpleKey("m_TypeName")?.Content?.Value ?.GetPlainScalarText(); if (methodNameMapEntry.Key.MatchesPlainScalarText("m_MethodName") && callsMapEntry.Key.MatchesPlainScalarText("m_Calls")) { var fileID = callMapNode.FindMapEntryBySimpleKey("m_Target")?.Content.Value.AsFileID(); if (fileID != null && !fileID.IsNullReference) { var modeText = callMapNode.FindMapEntryBySimpleKey("m_Mode")?.Content.Value.GetPlainScalarText(); var argMode = EventHandlerArgumentMode.EventDefined; if (int.TryParse(modeText, out var mode)) { if (1 <= mode && mode <= 6) { argMode = (EventHandlerArgumentMode)mode; } } var arguments = callMapNode.FindMapEntryBySimpleKey("m_Arguments")?.Content.Value as IBlockMappingNode; var argumentTypeName = arguments.FindMapEntryBySimpleKey("m_ObjectArgumentAssemblyTypeName")?.Content .Value.GetPlainScalarText(); var type = argumentTypeName?.Split(',').FirstOrDefault(); if (argMode == EventHandlerArgumentMode.EventDefined) { type = eventTypeName?.Split(',').FirstOrDefault(); } else if (argMode == EventHandlerArgumentMode.Void) { type = null; } var reference = new UnityEventTargetReference(methodNameValue, argMode, type, fileID); return(new ReferenceCollection(reference)); } } return(ReferenceCollection.Empty); }