Closed DanielWillett closed 10 months ago
Maybe mark type with [Browsable(false)]
(should be displayed in a Properties window like the one used in Visual Studio's designer environment, used in Windows Forms), to not add any extra API and make it a global standard in Unturned, for example, I see it useful in RocketMod and OpenMod also
Or instead if its not a problem to add a new API then:
/// <summary>
/// Specifies that a member with this attribute should be ignored when performing reflection activities.
/// </summary>
/// <remarks>
/// This attribute can be applied to members in order to exclude them from reflection-related operations, such as searching types in an assembly or invoking methods at runtime.
/// Refer to the example below for usage:
///
/// ```csharp
/// [ReflectionIgnore]
/// public class IgnoredClass
/// { }
///
/// // In another part of your code
/// var types = assembly.GetTypes().Where(type => !type.GetCustomAttributes(typeof(ReflectionIgnoreAttribute), false).Any());
/// ```
/// </remarks>
[AttributeUsage(AttributeTargets.All)]
public sealed class ReflectionIgnoreAttribute : Attribute
{
}
Attribute.IsDefined(type, typeof(ReflectionIgnoreAttribute))
also works, but yeah something like this would also be very nice. Imo a new attribute is a bit cleaner than using Browsable.
Update: it seems GetCustomAttribute and IsDefined have the same problem.
Seems like this is because all attributes are gotten when you call GetCustomAttribute (not just the type you asked for), and if one is in a library that isn't referenced it will throw an exception (this appears to be the same in .NET Framework and mono).
These could be completely unnecessary attributes like XML, JSON, Entity Framework attributes, that you may not need to use the class.
GetCustomAttribute:
FileNotFoundException: Could not load file or assembly 'System.Runtime.Serialization, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' or one of its dependencies.
System.MonoCustomAttrs.GetCustomAttributesBase (System.Reflection.ICustomAttributeProvider obj, System.Type attributeType, System.Boolean inheritedOnly) (at <47fc8c70fa834cbf8141d7c1a7589125>:0)
System.MonoCustomAttrs.GetCustomAttributes (System.Reflection.ICustomAttributeProvider obj, System.Type attributeType, System.Boolean inherit) (at <47fc8c70fa834cbf8141d7c1a7589125>:0)
System.RuntimeType.GetCustomAttributes (System.Type attributeType, System.Boolean inherit) (at <47fc8c70fa834cbf8141d7c1a7589125>:0)
System.Attribute.GetCustomAttributes (System.Reflection.MemberInfo element, System.Type type, System.Boolean inherit) (at <47fc8c70fa834cbf8141d7c1a7589125>:0)
System.Attribute.GetCustomAttribute (System.Reflection.MemberInfo element, System.Type attributeType, System.Boolean inherit) (at <47fc8c70fa834cbf8141d7c1a7589125>:0)
System.Attribute.GetCustomAttribute (System.Reflection.MemberInfo element, System.Type attributeType) (at <47fc8c70fa834cbf8141d7c1a7589125>:0)
DevkitServer.API.Accessor.GetPriority (System.Type type) (at <c80404aebfcb4c838ecb4ee88a3e66d2>:0)
DevkitServer.API.Accessor.SortTypesByPriorityHandler (System.Type a, System.Type b) (at <c80404aebfcb4c838ecb4ee88a3e66d2>:0)
(shortened a bit)
IsDefined:
FileNotFoundException: Could not load file or assembly 'System.Runtime.Serialization, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' or one of its dependencies.
System.MonoCustomAttrs.IsDefined (System.Reflection.ICustomAttributeProvider obj, System.Type attributeType, System.Boolean inherit) (at <47fc8c70fa834cbf8141d7c1a7589125>:0)
System.RuntimeType.IsDefined (System.Type attributeType, System.Boolean inherit) (at <47fc8c70fa834cbf8141d7c1a7589125>:0)
System.Attribute.IsDefined (System.Reflection.MemberInfo element, System.Type attributeType, System.Boolean inherit) (at <47fc8c70fa834cbf8141d7c1a7589125>:0)
System.Attribute.IsDefined (System.Reflection.MemberInfo element, System.Type attributeType) (at <47fc8c70fa834cbf8141d7c1a7589125>:0)
DevkitServer.API.Accessor.GetPriority (System.Type type) (at <248d0303599945b985516323f1e237e3>:0)
DevkitServer.API.Accessor.SortTypesByPriorityHandler (System.Type a, System.Type b) (at <248d0303599945b985516323f1e237e3>:0)
A Solution:
public static bool IsIgnored(this Type type)
{
try
{
return Attribute.IsDefined(type, typeof(ReflectionIgnoreAttribute));
}
catch (TypeLoadException)
{
return false;
}
catch (FileNotFoundException)
{
return false;
}
}
public static List<Type> GetTypesSafe(IEnumerable<Assembly> assemblies, bool removeIgnored = true)
{
List<Type?> types = new List<Type?>();
foreach (Assembly assembly in assmeblies)
{
try
{
types.AddRange(assembly.GetTypes());
}
catch (FileNotFoundException ex)
{
CommandWindow.Log($"Unable to get any types from assembly {assembly.FullName}. Missing dependency: {ex.FileName}.");
}
catch (ReflectionTypeLoadException ex)
{
types.AddRange(ex.Types);
}
}
types.RemoveAll(x => x == null || removeIgnored && x.IsIgnored());
return types!;
}
public static bool IsAssignableFromSafe(this Type type, Type subType)
{
try
{
return type.IsAssignableFrom(subType);
}
catch (TypeLoadException)
{
return false;
}
catch (FileNotFoundException)
{
return false;
}
}
Sounds like the easiest approach is the IsAssignableFrom adjustment. Do you think there's anywhere else the game should catch IsAssignableFrom exceptions?
Thank you
Do you think there's anywhere else the game should catch IsAssignableFrom exceptions?
I don't believe so.
Since the new Unity update, dealing with 'optional' features in modules is so much more difficult now because of the
IsAssignableFrom
call inModule.initialize
, which appears to now try to load the type completely (which it didn't do in Unity 2019).This also applies to any other non-nexus assemblies you have listed (although this is less of a problem with the auto-assembly searching now), all classes in each library is loaded which means almost any reference those libraries make also has to be present, even if you're not using the feature.
While, yes I know what this error means and I should just add the library, there's no reason that library should have to be there for the module to load, just that type will not be able to load (which in this case is fine).
All I propose is making your own method for checking types:
and replacing it in here
Alternatively maybe we could get some kind of ignore attribute for types but I think the other option is more fullproof.