5
5
using System . Reflection ;
6
6
using System . Linq ;
7
7
using System . IO ;
8
-
8
+ using YamlDotNet ;
9
+ using YamlDotNet . RepresentationModel ;
9
10
10
11
public class ReferenceFinder : EditorWindow
11
12
{
12
13
string searchedFunction ;
13
14
string searchResult = "" ;
14
15
string valueToSearch = "" ;
15
16
17
+ System . Type typeSearched ;
18
+
16
19
string tempSearchResult = "" ;
17
20
18
21
Vector2 scrollViewPosition ;
@@ -37,7 +40,9 @@ private void Update()
37
40
int purcentage = Mathf . FloorToInt ( currentParseFile / ( float ) filesList . Count * 100.0f ) ;
38
41
searchResult = "Searching " + purcentage + "%" ;
39
42
40
- HandleFile ( filesList [ currentParseFile ] ) ;
43
+ //HandleFile(filesList[currentParseFile]);
44
+ NewHandleFile ( filesList [ currentParseFile ] ) ;
45
+
41
46
currentParseFile += 1 ;
42
47
43
48
if ( currentParseFile == filesList . Count )
@@ -117,7 +122,8 @@ private void Search()
117
122
valueToSearch = func [ 0 ] . Name ;
118
123
}
119
124
120
- valueToSearch = "m_MethodName: " + valueToSearch ;
125
+ typeSearched = type ;
126
+ //valueToSearch = valueToSearch;
121
127
122
128
//Now that we checked the function name exist and is valid, we list all object that is suceptible of having an unity event data to it, meaning Scene & prefab.
123
129
filesList = new List < string > ( ) ;
@@ -127,10 +133,177 @@ private void Search()
127
133
currentParseFile = 0 ;
128
134
}
129
135
136
+ public class YamlVisitorEvent : IYamlVisitor
137
+ {
138
+ public ReferenceFinder referenceFinder ;
139
+ public bool havePersistentCall = false ;
140
+ public HashSet < string > idToCheck = new HashSet < string > ( ) ;
141
+
142
+ public void Visit ( YamlStream stream )
143
+ {
144
+
145
+ }
146
+
147
+ public void Visit ( YamlDocument document )
148
+ {
149
+
150
+ }
151
+
152
+ public void Visit ( YamlScalarNode scalar )
153
+ {
154
+
155
+ }
156
+
157
+ public void Visit ( YamlSequenceNode sequence )
158
+ {
159
+
160
+ }
161
+
162
+ public void Visit ( YamlMappingNode mapping )
163
+ {
164
+ foreach ( var n in mapping )
165
+ {
166
+ n . Value . Accept ( this ) ;
167
+ if ( ( ( YamlScalarNode ) n . Key ) . Value == "m_PersistentCalls" )
168
+ {
169
+ var callsSequence = n . Value [ "m_Calls" ] as YamlSequenceNode ;
170
+
171
+ foreach ( var call in callsSequence )
172
+ {
173
+ if ( ( ( YamlScalarNode ) call [ "m_MethodName" ] ) . Value == referenceFinder . valueToSearch )
174
+ {
175
+ havePersistentCall = true ;
176
+ idToCheck . Add ( ( ( YamlScalarNode ) call [ "m_Target" ] [ "fileID" ] ) . Value ) ;
177
+ }
178
+ }
179
+ }
180
+ }
181
+ }
182
+ }
183
+
184
+ private void NewHandleFile ( string file )
185
+ {
186
+ bool filenameWrote = false ;
187
+
188
+ var fileContent = File . ReadAllText ( file ) ;
189
+ //unity seem to use non valid yaml by added a "stripped" keyword on object that are linked to a prefab. Since the pareser itch on those, we remove them
190
+ fileContent = fileContent . Replace ( "stripped" , "" ) ;
191
+ var input = new StringReader ( fileContent ) ;
192
+
193
+ var yaml = new YamlStream ( ) ;
194
+ yaml . Load ( input ) ;
195
+
196
+ YamlVisitorEvent visitor = new YamlVisitorEvent ( ) ;
197
+ visitor . referenceFinder = this ;
198
+
199
+ //map gameobject id to a hashset of monobehaviour to check for type against the searched type
200
+ Dictionary < string , HashSet < string > > gameobjectToIdToCheck = new Dictionary < string , HashSet < string > > ( ) ;
201
+
202
+ //we store the anchor <-> node mapping, as there don't seem to be anyway to do that quickly through the YAml graph
203
+ Dictionary < string , YamlMappingNode > parsedNodes = new Dictionary < string , YamlMappingNode > ( ) ;
204
+
205
+ foreach ( var doc in yaml . Documents )
206
+ {
207
+ var root = ( YamlMappingNode ) doc . RootNode ;
208
+
209
+ parsedNodes [ root . Anchor ] = root ;
210
+
211
+ foreach ( var node in root . Children )
212
+ {
213
+ var scalarNode = ( YamlScalarNode ) node . Key ;
214
+ if ( scalarNode . Value == "MonoBehaviour" )
215
+ { //if it's a monobehaviour, it may have a list of event as child
216
+ YamlMappingNode sequenceNode = node . Value as YamlMappingNode ;
217
+
218
+ visitor . havePersistentCall = false ;
219
+ visitor . idToCheck . Clear ( ) ;
220
+ sequenceNode . Accept ( visitor ) ;
221
+
222
+ if ( visitor . havePersistentCall )
223
+ { //we found persistent call
224
+ string gameobjectID = ( ( YamlScalarNode ) node . Value [ "m_GameObject" ] [ "fileID" ] ) . Value ;
225
+
226
+ if ( ! gameobjectToIdToCheck . ContainsKey ( gameobjectID ) )
227
+ gameobjectToIdToCheck [ gameobjectID ] = new HashSet < string > ( ) ;
228
+
229
+ gameobjectToIdToCheck [ gameobjectID ] . UnionWith ( visitor . idToCheck ) ;
230
+ }
231
+ }
232
+ }
233
+ }
234
+
235
+ //now we go over all our gameobject to check, and if one of the monobehaviour they ahve been associated with are of the researched type, they are added to the result
236
+ foreach ( var pair in gameobjectToIdToCheck )
237
+ {
238
+ bool haveOneValidCall = false ;
239
+ if ( ! parsedNodes . ContainsKey ( pair . Key ) )
240
+ {
241
+ Debug . LogError ( "Tried to check an object id that don't exist : " + pair . Key ) ;
242
+ continue ;
243
+ }
244
+
245
+ foreach ( var id in pair . Value )
246
+ {
247
+ var targetNode = parsedNodes [ id ] ;
248
+ var guid = ( ( YamlScalarNode ) targetNode [ "MonoBehaviour" ] [ "m_Script" ] [ "guid" ] ) . Value ;
249
+
250
+ MonoScript script = AssetDatabase . LoadAssetAtPath < MonoScript > ( AssetDatabase . GUIDToAssetPath ( guid ) ) ;
251
+
252
+ if ( script . GetClass ( ) == typeSearched )
253
+ {
254
+ haveOneValidCall = true ;
255
+ }
256
+ }
257
+
258
+ if ( haveOneValidCall )
259
+ {
260
+ if ( ! filenameWrote )
261
+ {
262
+ filenameWrote = true ;
263
+ tempSearchResult += Path . GetFileName ( file ) + "\n " ;
264
+ }
265
+
266
+ if ( ( ( YamlScalarNode ) parsedNodes [ pair . Key ] [ "GameObject" ] [ "m_PrefabParentObject" ] [ "fileID" ] ) . Value != "0" )
267
+ { //this is a prefab instance, need to find the prefab value linked to it!!
268
+ tempSearchResult += "\t " + "A Prefab" ;
269
+ }
270
+ else
271
+ {
272
+ string fullPath = "" ;
273
+
274
+ //make an assumption here that the 1st component of every gameobject will always be its transform.
275
+ string currentGOId = pair . Key ;
276
+ while ( currentGOId != "0" )
277
+ {
278
+ fullPath = parsedNodes [ currentGOId ] [ "GameObject" ] [ "m_Name" ] + ( fullPath == "" ? "" : "/" + fullPath ) ;
279
+
280
+ string transformID = parsedNodes [ currentGOId ] [ "GameObject" ] [ "m_Component" ] [ 0 ] [ "component" ] [ "fileID" ] . ToString ( ) ;
281
+
282
+ Debug . Log ( "trasnofrmID " + transformID ) ;
283
+
284
+ string parentTransformID = parsedNodes [ transformID ] [ "Transform" ] [ "m_Father" ] [ "fileID" ] . ToString ( ) ;
285
+ if ( parentTransformID != "0" )
286
+ {
287
+ currentGOId = parsedNodes [ parentTransformID ] [ "Transform" ] [ "m_GameObject" ] [ "fileID" ] . ToString ( ) ;
288
+ }
289
+
290
+ Debug . Log ( "currentGOID " + currentGOId ) ;
291
+ }
292
+
293
+ tempSearchResult += "\t " + fullPath + "\n " ;
294
+ }
295
+ }
296
+ }
297
+ }
298
+
130
299
private void HandleFile ( string file )
131
300
{
132
301
bool nameWritten = false ;
133
302
303
+ string relativePath = file . Replace ( Application . dataPath , "Assets" ) ;
304
+ Debug . Log ( relativePath ) ;
305
+ Debug . Log ( AssetDatabase . AssetPathToGUID ( relativePath ) ) ;
306
+
134
307
string content = File . ReadAllText ( file ) ;
135
308
136
309
//we're doing rough simple parsing here, not robust but way faster than deserializing the YAML & going through object etc...
@@ -174,6 +347,9 @@ private string GetObjectName(string content, string gameobjectID, int startIndex
174
347
//if it wasn't found, search in the other direction (most of the time, it will be above, but in some case it can be inverted)
175
348
if ( parentGOIndex == - 1 ) parentGOIndex = content . IndexOf ( "&" + gameobjectID , startIndex ) ;
176
349
350
+ //we check if that object was "stripped". A stripped gameobject don't have any info, it is just a link to a prefab, so we have to go fetch the prefab data?
351
+
352
+
177
353
//if that object have a parent, we go fetch the name of it recursivly too
178
354
int fatherIDPlaceIndex = content . IndexOf ( gameObjectParentString , parentGOIndex ) ;
179
355
if ( fatherIDPlaceIndex != - 1 )
0 commit comments