@@ -12,23 +12,49 @@ public static class ReaderWriterProcessor
1212 {
1313 public static bool Process ( AssemblyDefinition CurrentAssembly , IAssemblyResolver resolver , Logger Log , Writers writers , Readers readers , ref bool WeavingFailed )
1414 {
15- // find NetworkReader/Writer extensions from Mirror.dll first.
16- // and NetworkMessage custom writer/reader extensions.
17- // NOTE: do not include this result in our 'modified' return value,
15+ // Process in two passes so that custom reader/writer registration
16+ // always completes before any NetworkMessage reader/writer is generated.
17+ //
18+ // With a single pass per assembly, a message assembly could be visited
19+ // before the assembly that declares a custom reader/writer for one of
20+ // the message's field types. The weaver would then generate a fallback
21+ // reader/writer for that type and cache it; the later custom registration
22+ // is ignored because a generated function is already registered. For a
23+ // reference type without public fields the fallback writer only emits the
24+ // null-flag byte, silently dropping the value. Registering all custom
25+ // readers/writers first makes the result independent of visit order.
26+
27+ // Mirror.dll is excluded from FindProcessTargetAssemblies, so resolve it
28+ // separately and feed it into both passes.
29+ AssemblyDefinition mirrorAssembly = ResolveMirrorAssembly ( CurrentAssembly , resolver , Log ) ;
30+
31+ // process dependencies, this way weaver can process types of other assemblies properly.
32+ // fixes: https://github.com/MirrorNetworking/Mirror/issues/2503
33+ List < AssemblyDefinition > assemblyReferences = FindProcessTargetAssemblies ( CurrentAssembly , resolver )
34+ . Where ( assembly => assembly != null && assembly != CurrentAssembly )
35+ . ToList ( ) ;
36+
37+ // pass 1: register all declared custom readers/writers.
38+ // NOTE: do not include Mirror/referenced results in 'modified',
1839 // otherwise Unity crashes when running tests
19- ProcessMirrorAssemblyClasses ( CurrentAssembly , resolver , Log , writers , readers , ref WeavingFailed ) ;
40+ if ( mirrorAssembly != null )
41+ RegisterDeclaredReadersWriters ( CurrentAssembly , mirrorAssembly , writers , readers ) ;
2042
21- // process dependencies first, this way weaver can process types of other assemblies properly.
22- // fixes: https://github.com/MirrorNetworking/Mirror/issues/2503
23- //
24- // find NetworkReader/Writer extensions in referenced assemblies
25- IEnumerable < AssemblyDefinition > assemblyReferences = FindProcessTargetAssemblies ( CurrentAssembly , resolver )
26- . Where ( assembly => assembly != null && assembly != CurrentAssembly ) ;
43+ foreach ( AssemblyDefinition referencedAssembly in assemblyReferences )
44+ RegisterDeclaredReadersWriters ( CurrentAssembly , referencedAssembly , writers , readers ) ;
45+
46+ bool modified = RegisterDeclaredReadersWriters ( CurrentAssembly , CurrentAssembly , writers , readers ) ;
47+
48+ // pass 2: generate readers/writers for all NetworkMessages.
49+ if ( mirrorAssembly != null )
50+ GenerateMessageReadersWriters ( CurrentAssembly , mirrorAssembly , writers , readers , ref WeavingFailed ) ;
2751
2852 foreach ( AssemblyDefinition referencedAssembly in assemblyReferences )
29- ProcessAssemblyClasses ( CurrentAssembly , referencedAssembly , writers , readers , ref WeavingFailed ) ;
53+ GenerateMessageReadersWriters ( CurrentAssembly , referencedAssembly , writers , readers , ref WeavingFailed ) ;
54+
55+ modified |= GenerateMessageReadersWriters ( CurrentAssembly , CurrentAssembly , writers , readers , ref WeavingFailed ) ;
3056
31- return ProcessAssemblyClasses ( CurrentAssembly , CurrentAssembly , writers , readers , ref WeavingFailed ) ;
57+ return modified ;
3258 }
3359
3460 // look for assembly instead of relying on CurrentAssembly.MainModule.
@@ -65,7 +91,10 @@ void ProcessAssembly(AssemblyDefinition current)
6591 }
6692 }
6793
68- static void ProcessMirrorAssemblyClasses ( AssemblyDefinition CurrentAssembly , IAssemblyResolver resolver , Logger Log , Writers writers , Readers readers , ref bool WeavingFailed )
94+ // resolve Mirror.dll's AssemblyDefinition. it is skipped by
95+ // FindProcessTargetAssemblies, so it has to be resolved separately
96+ // and fed into both passes.
97+ static AssemblyDefinition ResolveMirrorAssembly ( AssemblyDefinition CurrentAssembly , IAssemblyResolver resolver , Logger Log )
6998 {
7099 // find Mirror.dll in assembly's references.
71100 // those are guaranteed to be resolvable and correct.
@@ -78,16 +107,17 @@ static void ProcessMirrorAssemblyClasses(AssemblyDefinition CurrentAssembly, IAs
78107 // if we only were to resolve one known type like in WeaverTypes,
79108 // then we wouldn't need it.
80109 AssemblyDefinition mirrorAssembly = resolver . Resolve ( mirrorAssemblyReference ) ;
81- if ( mirrorAssembly != null )
82- {
83- ProcessAssemblyClasses ( CurrentAssembly , mirrorAssembly , writers , readers , ref WeavingFailed ) ;
84- }
85- else Log . Error ( $ "Failed to resolve { mirrorAssemblyReference } ") ;
110+ if ( mirrorAssembly == null )
111+ Log . Error ( $ "Failed to resolve { mirrorAssemblyReference } ") ;
112+ return mirrorAssembly ;
86113 }
87- else Log . Error ( "Failed to find Mirror AssemblyNameReference. Can't register Mirror.dll readers/writers." ) ;
114+
115+ Log . Error ( "Failed to find Mirror AssemblyNameReference. Can't register Mirror.dll readers/writers." ) ;
116+ return null ;
88117 }
89118
90- static bool ProcessAssemblyClasses ( AssemblyDefinition CurrentAssembly , AssemblyDefinition assembly , Writers writers , Readers readers , ref bool WeavingFailed )
119+ // pass 1: register all declared custom reader/writer extension methods in 'assembly'.
120+ static bool RegisterDeclaredReadersWriters ( AssemblyDefinition CurrentAssembly , AssemblyDefinition assembly , Writers writers , Readers readers )
91121 {
92122 bool modified = false ;
93123 foreach ( TypeDefinition klass in assembly . MainModule . Types )
@@ -101,7 +131,14 @@ static bool ProcessAssemblyClasses(AssemblyDefinition CurrentAssembly, AssemblyD
101131 modified |= LoadDeclaredReaders ( CurrentAssembly , klass , readers ) ;
102132 }
103133 }
134+ return modified ;
135+ }
104136
137+ // pass 2: generate readers/writers for all NetworkMessages in 'assembly'.
138+ // must run after every custom reader/writer has been registered.
139+ static bool GenerateMessageReadersWriters ( AssemblyDefinition CurrentAssembly , AssemblyDefinition assembly , Writers writers , Readers readers , ref bool WeavingFailed )
140+ {
141+ bool modified = false ;
105142 foreach ( TypeDefinition klass in assembly . MainModule . Types )
106143 {
107144 // if assembly has any network message then it is modified
0 commit comments