@@ -23,22 +23,39 @@ internal static void TryProcessType(
2323 Type t )
2424 {
2525 var modInterop = t . GetCustomAttribute < ModInteropAttribute > ( ) ;
26- if ( modInterop is null )
26+ var assemblyInterop = t . GetCustomAttribute < AssemblyInteropAttribute > ( ) ;
27+ if ( modInterop != null && assemblyInterop != null )
28+ {
29+ RitsuLibFramework . Logger . Warn (
30+ $ "[Interop] Type { t . FullName } declares both ModInterop and AssemblyInterop; skipping.") ;
2731 return ;
32+ }
33+
34+ if ( modInterop != null )
35+ {
36+ if ( ! loadedAssembliesByModId . TryGetValue ( modInterop . ModId , out var assembly ) )
37+ return ;
2838
29- if ( ! loadedAssembliesByModId . TryGetValue ( modInterop . ModId , out var assembly ) )
39+ RitsuLibFramework . Logger . Info ( $ "[ModInterop] Processing type { t . FullName } -> mod { modInterop . ModId } ") ;
40+
41+ var members = t . GetMembers ( ValidMemberFlags ) ;
42+ GenInteropMembers ( members , harmony , TargetResolutionContext . ForModAssembly ( assembly ) ,
43+ modInterop . Type , true ) ;
3044 return ;
45+ }
3146
32- RitsuLibFramework . Logger . Info ( $ "[ModInterop] Processing type { t . FullName } -> mod { modInterop . ModId } ") ;
47+ if ( assemblyInterop == null )
48+ return ;
3349
34- var members = t . GetMembers ( ValidMemberFlags ) ;
35- GenInteropMembers ( members , harmony , assembly , modInterop . Type , true ) ;
50+ RitsuLibFramework . Logger . Info ( $ "[AssemblyInterop] Processing type { t . FullName } ") ;
51+ GenInteropMembers ( t . GetMembers ( ValidMemberFlags ) , harmony ,
52+ TargetResolutionContext . ForAssemblyQualifiedTypes ( ) , assemblyInterop . Type , true ) ;
3653 }
3754
3855 private static bool GenInteropMembers (
3956 MemberInfo [ ] members ,
4057 Harmony harmony ,
41- Assembly assembly ,
58+ TargetResolutionContext targetContext ,
4259 string ? contextTargetType ,
4360 bool requireStatic )
4461 {
@@ -48,21 +65,21 @@ private static bool GenInteropMembers(
4865 case PropertyInfo property :
4966 if ( requireStatic && ! ( property . SetMethod ? . IsStatic ?? true ) )
5067 continue ;
51- if ( ! GenInteropPropertyOrField ( harmony , assembly , contextTargetType , property ) )
68+ if ( ! GenInteropPropertyOrField ( harmony , targetContext , contextTargetType , property ) )
5269 return false ;
5370 break ;
5471 case MethodInfo method :
5572 if ( requireStatic && ! method . IsStatic )
5673 continue ;
5774 if ( method . IsConstructor || method . GetCustomAttribute < CompilerGeneratedAttribute > ( ) != null )
5875 continue ;
59- if ( ! GenInteropMethod ( harmony , assembly , contextTargetType , method ) )
76+ if ( ! GenInteropMethod ( harmony , targetContext , contextTargetType , method ) )
6077 return false ;
6178 break ;
6279 case TypeInfo nested :
6380 if ( ! nested . IsAssignableTo ( typeof ( InteropClassWrapper ) ) )
6481 continue ;
65- if ( ! GenInteropType ( harmony , assembly , contextTargetType , nested ) )
82+ if ( ! GenInteropType ( harmony , targetContext , contextTargetType , nested ) )
6683 return false ;
6784 break ;
6885 }
@@ -72,7 +89,7 @@ private static bool GenInteropMembers(
7289
7390 private static bool GenInteropType (
7491 Harmony harmony ,
75- Assembly targetAssembly ,
92+ TargetResolutionContext targetContext ,
7693 string ? contextTargetType ,
7794 TypeInfo type )
7895 {
@@ -86,9 +103,7 @@ private static bool GenInteropType(
86103
87104 try
88105 {
89- var targetType = Type . GetType ( $ "{ targetName } , { targetAssembly } ")
90- ?? throw new InvalidOperationException (
91- $ "Type { targetName } not found in assembly { targetAssembly } ") ;
106+ var targetType = ResolveTargetType ( targetName , targetContext ) ;
92107
93108 foreach ( var constructor in constructors )
94109 {
@@ -111,7 +126,7 @@ private static bool GenInteropType(
111126 }
112127
113128 RitsuLibFramework . Logger . Info ( $ "[ModInterop] Generated interop type { type . FullName } ") ;
114- return GenInteropMembers ( type . GetMembers ( ValidMemberFlags ) , harmony , targetAssembly , targetName , false ) ;
129+ return GenInteropMembers ( type . GetMembers ( ValidMemberFlags ) , harmony , targetContext , targetName , false ) ;
115130 }
116131 catch ( Exception e )
117132 {
@@ -122,7 +137,7 @@ private static bool GenInteropType(
122137
123138 private static bool GenInteropMethod (
124139 Harmony harmony ,
125- Assembly targetAssembly ,
140+ TargetResolutionContext targetContext ,
126141 string ? contextTargetType ,
127142 MethodInfo method )
128143 {
@@ -134,9 +149,7 @@ private static bool GenInteropMethod(
134149
135150 try
136151 {
137- var targetType = Type . GetType ( $ "{ typeName } , { targetAssembly } ")
138- ?? throw new InvalidOperationException (
139- $ "Type { typeName } not found in assembly { targetAssembly } ") ;
152+ var targetType = ResolveTargetType ( typeName , targetContext ) ;
140153
141154 var methodParams = method . GetParameters ( ) . Select ( p => p . ParameterType ) . ToArray ( ) ;
142155 var nonStaticParams = method . IsStatic ? methodParams . Skip ( 1 ) . ToArray ( ) : methodParams ;
@@ -210,7 +223,7 @@ private static bool GenInteropMethod(
210223
211224 private static bool GenInteropPropertyOrField (
212225 Harmony harmony ,
213- Assembly targetAssembly ,
226+ TargetResolutionContext targetContext ,
214227 string ? contextTargetType ,
215228 PropertyInfo property )
216229 {
@@ -221,9 +234,7 @@ private static bool GenInteropPropertyOrField(
221234
222235 try
223236 {
224- var targetType = Type . GetType ( $ "{ typeName } , { targetAssembly } ")
225- ?? throw new InvalidOperationException (
226- $ "Type { typeName } not found in assembly { targetAssembly } ") ;
237+ var targetType = ResolveTargetType ( typeName , targetContext ) ;
227238
228239 var targetProperty = AccessTools . DeclaredProperty ( targetType , name ) ;
229240 if ( targetProperty is not null && targetProperty . PropertyType == property . PropertyType )
@@ -357,6 +368,40 @@ private static bool GenInteropPropertyOrField(
357368 }
358369 }
359370
371+ private static Type ResolveTargetType ( string targetName , TargetResolutionContext targetContext )
372+ {
373+ if ( targetContext . AssemblyQualifiedOnly )
374+ {
375+ _ = TryResolveAssemblyQualifiedType ( targetName , out var resolved ) ;
376+ return resolved ?? throw new InvalidOperationException (
377+ $ "AssemblyInterop target type '{ targetName } ' must be an assembly-qualified CLR type name that can be resolved.") ;
378+ }
379+
380+ if ( IsAssemblyQualifiedTypeName ( targetName ) )
381+ throw new InvalidOperationException (
382+ $ "ModInterop target type '{ targetName } ' must be a full type name inside the target mod assembly. Use AssemblyInterop for assembly-qualified CLR type names.") ;
383+
384+ return targetContext . ModAssembly ! . GetType ( targetName , false )
385+ ?? Type . GetType ( $ "{ targetName } , { targetContext . ModAssembly . FullName } ", false )
386+ ?? throw new InvalidOperationException (
387+ $ "Type { targetName } not found in assembly { targetContext . ModAssembly . FullName } ") ;
388+ }
389+
390+ private static bool IsAssemblyQualifiedTypeName ( string ? targetName )
391+ {
392+ return ! string . IsNullOrWhiteSpace ( targetName ) && targetName . Contains ( ',' , StringComparison . Ordinal ) ;
393+ }
394+
395+ private static bool TryResolveAssemblyQualifiedType ( string ? targetName , out Type ? type )
396+ {
397+ type = null ;
398+ if ( ! IsAssemblyQualifiedTypeName ( targetName ) )
399+ return false ;
400+
401+ type = Type . GetType ( targetName ! , false ) ;
402+ return type != null ;
403+ }
404+
360405 private static bool CheckParamMatch ( ParameterInfo [ ] targetParams , Type [ ] checkParams )
361406 {
362407 if ( targetParams . Length != checkParams . Length )
@@ -375,5 +420,18 @@ private static string FormatConstructor(ConstructorInfo c)
375420 return
376421 $ "{ c . DeclaringType ? . FullName } .ctor({ string . Join ( ", " , c . GetParameters ( ) . Select ( p => p . ParameterType . Name ) ) } )";
377422 }
423+
424+ private readonly record struct TargetResolutionContext ( Assembly ? ModAssembly , bool AssemblyQualifiedOnly )
425+ {
426+ public static TargetResolutionContext ForModAssembly ( Assembly assembly )
427+ {
428+ return new ( assembly , false ) ;
429+ }
430+
431+ public static TargetResolutionContext ForAssemblyQualifiedTypes ( )
432+ {
433+ return new ( null , true ) ;
434+ }
435+ }
378436 }
379437}
0 commit comments