Added trimming and AOT annotations and made MLEM trimmable

This commit is contained in:
Ell 2022-10-31 18:33:53 +01:00
parent f58e3c94d5
commit d138577285
26 changed files with 116 additions and 20 deletions

View File

@ -23,6 +23,7 @@ Improvements
- Discard old data when updating a StaticSpriteBatch
- Multi-target net452, making MLEM compatible with MonoGame for consoles
- Allow retrieving the cost of a calculated path when using AStar
- Added trimming and AOT annotations and made MLEM trimmable
- **Drastically improved StaticSpriteBatch batching performance**
- **Made GenericFont and TokenizedString support UTF-32 characters like emoji**
@ -48,6 +49,7 @@ Improvements
- Close other dropdowns when opening a dropdown
- Generified UiMarkdownParser by adding abstract UiParser
- Multi-target net452, making MLEM compatible with MonoGame for consoles
- Added trimming and AOT annotations and made MLEM.Ui trimmable
Fixes
- Fixed parents of elements that prevent spill not being notified properly
@ -69,6 +71,7 @@ Improvements
- Allow data texture atlas pivots and offsets to be negative
- Made RuntimeTexturePacker restore texture region name and pivot when packing
- Multi-target net452, making MLEM compatible with MonoGame for consoles
- Added trimming and AOT annotations and made MLEM.Data trimmable
Fixes
- Fixed data texture atlases not allowing most characters in their region names
@ -79,11 +82,13 @@ Removals
## MLEM.Extended
Improvements
- Multi-target net452, making MLEM compatible with MonoGame for consoles
- Added trimming and AOT annotations and made MLEM.Extended trimmable
- **Made GenericBitmapFont and GenericStashFont support UTF-32 characters like emoji**
## MLEM.Startup
Improvements
- Multi-target net452, making MLEM compatible with MonoGame for consoles
- Added trimming and AOT annotations and made MLEM.Startup trimmable
## 6.0.0

View File

@ -5,33 +5,52 @@ using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
#if NET6_0_OR_GREATER
using System.Diagnostics.CodeAnalysis;
#endif
namespace MLEM.Data.Content {
/// <summary>
/// Represents a version of <see cref="ContentManager"/> that doesn't load content binary <c>xnb</c> files, but rather as their regular formats.
/// </summary>
public class RawContentManager : ContentManager, IGameComponent {
private static List<RawContentReader> readers;
/// <summary>
/// The graphics device that this content manager uses
/// </summary>
public readonly GraphicsDevice GraphicsDevice;
private readonly List<IDisposable> disposableAssets = new List<IDisposable>();
private readonly List<RawContentReader> readers;
#if FNA
private Dictionary<string, object> LoadedAssets { get; } = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
#endif
/// <summary>
/// Creates a new content manager with an optionally specified root directory.
/// Each <see cref="RawContentReader"/> required for asset loading is gathered and instantiated automatically from the loaded assemblies.
/// </summary>
/// <param name="serviceProvider">The service provider of your game</param>
/// <param name="rootDirectory">The root directory. Defaults to "Content"</param>
#if NET6_0_OR_GREATER
[RequiresUnreferencedCode("Automatically gathered RawContentReader types might be removed, use other constructor to add readers manually")]
#endif
public RawContentManager(IServiceProvider serviceProvider, string rootDirectory = "Content") :
this(serviceProvider, RawContentManager.CollectContentReaders(), rootDirectory) {}
/// <summary>
/// Creates a new content manager with an optionally specified root directory.
/// Each <see cref="RawContentReader"/> required for asset loading has to be passed using <paramref name="readers"/>.
/// </summary>
/// <param name="serviceProvider">The service provider of your game</param>
/// <param name="readers">The raw content readers to use, which can be modified externally afterwards to add additional readers if desired.</param>
/// <param name="rootDirectory">The root directory. Defaults to "Content"</param>
public RawContentManager(IServiceProvider serviceProvider, List<RawContentReader> readers, string rootDirectory) :
base(serviceProvider, rootDirectory) {
if (serviceProvider.GetService(typeof(IGraphicsDeviceService)) is IGraphicsDeviceService s)
this.GraphicsDevice = s.GraphicsDevice;
this.readers = readers;
}
/// <summary>
@ -63,9 +82,7 @@ namespace MLEM.Data.Content {
private T Read<T>(string assetName, T existing) {
var triedFiles = new List<string>();
if (RawContentManager.readers == null)
RawContentManager.readers = RawContentManager.CollectContentReaders();
foreach (var reader in RawContentManager.readers) {
foreach (var reader in this.readers) {
if (!reader.CanRead(typeof(T)))
continue;
foreach (var ext in reader.GetFileExtensions()) {
@ -104,6 +121,9 @@ namespace MLEM.Data.Content {
/// </summary>
public void Initialize() {}
#if NET6_0_OR_GREATER
[RequiresUnreferencedCode("Automatically gathered RawContentReader types might be removed, use other constructor to add readers manually")]
#endif
private static List<RawContentReader> CollectContentReaders() {
var ret = new List<RawContentReader>();
var assemblyExceptions = new List<Exception>();

View File

@ -12,6 +12,10 @@ namespace MLEM.Data.Content {
}
/// <inheritdoc />
#if NET6_0_OR_GREATER
[System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026",
Justification = "RawContentManager does not support XmlReader in a trimmed or AOT context, so this method is not expected to be called.")]
#endif
public override object Read(RawContentManager manager, string assetPath, Stream stream, Type t, object existing) {
return new XmlSerializer(t).Deserialize(stream);
}

View File

@ -3,11 +3,18 @@ using System.Collections.Generic;
using System.Linq;
using System.Reflection;
#if NET6_0_OR_GREATER
using System.Diagnostics.CodeAnalysis;
#endif
namespace MLEM.Data {
/// <summary>
/// A set of extensions for dealing with copying objects.
/// </summary>
[Obsolete("CopyExtensions has major flaws and insufficient speed compared to other libraries specifically designed for copying objects.")]
#if NET6_0_OR_GREATER
[UnconditionalSuppressMessage("Aot", "IL3050"), UnconditionalSuppressMessage("Aot", "IL2070"), UnconditionalSuppressMessage("Aot", "IL2090")]
#endif
public static class CopyExtensions {
private const BindingFlags DefaultFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
@ -15,7 +22,7 @@ namespace MLEM.Data {
/// <summary>
/// Creates a shallow copy of the object and returns it.
/// Object creation occurs using a constructor with the <see cref="CopyConstructorAttribute"/> or, if none is present, the first constructor with the correct <see cref="BindingFlags"/>.
/// Object creation occurs using a constructor with the <see cref="CopyConstructorAttribute"/> or, if none is present, the first constructor with the correct <see cref="BindingFlags"/>.
/// </summary>
/// <param name="obj">The object to create a shallow copy of</param>
/// <param name="flags">The binding flags for field searching</param>
@ -31,7 +38,7 @@ namespace MLEM.Data {
/// <summary>
/// Creates a deep copy of the object and returns it.
/// Object creation occurs using a constructor with the <see cref="CopyConstructorAttribute"/> or, if none is present, the first constructor with the correct <see cref="BindingFlags"/>.
/// Object creation occurs using a constructor with the <see cref="CopyConstructorAttribute"/> or, if none is present, the first constructor with the correct <see cref="BindingFlags"/>.
/// </summary>
/// <param name="obj">The object to create a deep copy of</param>
/// <param name="flags">The binding flags for field searching</param>
@ -63,7 +70,7 @@ namespace MLEM.Data {
/// <summary>
/// Copies the given object <paramref name="obj"/> into the given object <paramref name="otherObj"/> in a deep manner.
/// Object creation occurs using a constructor with the <see cref="CopyConstructorAttribute"/> or, if none is present, the first constructor with the correct <see cref="BindingFlags"/>.
/// Object creation occurs using a constructor with the <see cref="CopyConstructorAttribute"/> or, if none is present, the first constructor with the correct <see cref="BindingFlags"/>.
/// </summary>
/// <param name="obj">The object to create a deep copy of</param>
/// <param name="otherObj">The object to copy into</param>

View File

@ -26,6 +26,9 @@ namespace MLEM.Data {
/// </code>
/// </remarks>
[Obsolete("DynamicEnum has been moved into the DynamicEnums library: https://www.nuget.org/packages/DynamicEnums"), JsonConverter(typeof(DynamicEnumConverter))]
#if NET6_0_OR_GREATER
[System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Aot", "IL2067")]
#endif
public abstract class DynamicEnum {
private static readonly Dictionary<Type, Storage> Storages = new Dictionary<Type, Storage>();

View File

@ -11,9 +11,16 @@ namespace MLEM.Data.Json {
/// <summary>
/// An array of all of the <see cref="JsonConverter"/>s that are part of MLEM.Data
/// </summary>
public static readonly JsonConverter[] Converters = typeof(JsonConverters).Assembly.GetExportedTypes()
.Where(t => t.IsSubclassOf(typeof(JsonConverter)) && !t.IsGenericType)
.Select(Activator.CreateInstance).Cast<JsonConverter>().ToArray();
public static readonly JsonConverter[] Converters = {
new Direction2Converter(),
#pragma warning disable CS0618
new DynamicEnumConverter(),
#pragma warning restore CS0618
new PointConverter(),
new RectangleConverter(),
new RectangleFConverter(),
new Vector2Converter()
};
/// <summary>
/// Adds all of the <see cref="JsonConverter"/> objects that are part of MLEM.Data to the given <see cref="JsonSerializer"/>

View File

@ -9,6 +9,9 @@ namespace MLEM.Data.Json {
/// This class uses <see cref="JsonTypeSafeWrapper"/> for each object stored to ensure that objects with a custom <see cref="JsonConverter"/> get deserialized as an instance of their original type if <see cref="JsonSerializer.TypeNameHandling"/> is not set to <see cref="TypeNameHandling.None"/>.
/// </summary>
[DataContract]
#if NET7_0_OR_GREATER
[System.Diagnostics.CodeAnalysis.RequiresDynamicCode("The native code for instantiation of JsonTypeSafeWrapper instances might not be available at runtime.")]
#endif
public class JsonTypeSafeGenericDataHolder : IGenericDataHolder {
private static readonly string[] EmptyStrings = new string[0];

View File

@ -34,6 +34,9 @@ namespace MLEM.Data.Json {
/// </summary>
/// <param name="value">The value to wrap</param>
/// <returns>A <see cref="JsonTypeSafeWrapper{T}"/> with a type matching the type of <paramref name="value"/></returns>
#if NET7_0_OR_GREATER
[System.Diagnostics.CodeAnalysis.RequiresDynamicCode("The native code for this instantiation might not be available at runtime.")]
#endif
public static JsonTypeSafeWrapper Of(object value) {
var type = typeof(JsonTypeSafeWrapper<>).MakeGenericType(value.GetType());
return (JsonTypeSafeWrapper) Activator.CreateInstance(type, value);

View File

@ -4,9 +4,13 @@ using System.Linq;
using System.Reflection;
using Newtonsoft.Json;
#if NET6_0_OR_GREATER
using System.Diagnostics.CodeAnalysis;
#endif
namespace MLEM.Data.Json {
/// <summary>
/// A <see cref="JsonConverter{T}"/> that doesn't actually serialize the object, but instead serializes the name given to it by the underlying <see cref="Dictionary{T,T}"/>.
/// A <see cref="JsonConverter{T}"/> that doesn't actually serialize the object, but instead serializes the name given to it by the underlying <see cref="Dictionary{T,T}"/>.
/// Optionally, the name of a <see cref="Dictionary{TKey,TValue}"/> can be passed to this converter when used in the <see cref="JsonConverterAttribute"/> by passing the arguments for the <see cref="StaticJsonConverter{T}(Type,string)"/> constructor as <see cref="JsonConverterAttribute.ConverterParameters"/>.
/// </summary>
/// <typeparam name="T">The type of the object to convert</typeparam>
@ -29,7 +33,11 @@ namespace MLEM.Data.Json {
/// </summary>
/// <param name="type">The type that the dictionary is declared in</param>
/// <param name="memberName">The name of the dictionary itself</param>
public StaticJsonConverter(Type type, string memberName) : this(StaticJsonConverter<T>.GetEntries(type, memberName)) {}
public StaticJsonConverter(
#if NET6_0_OR_GREATER
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)]
#endif
Type type, string memberName) : this(StaticJsonConverter<T>.GetEntries(type, memberName)) {}
/// <summary>Writes the JSON representation of the object.</summary>
/// <param name="writer">The <see cref="T:Newtonsoft.Json.JsonWriter" /> to write to.</param>
@ -56,7 +64,11 @@ namespace MLEM.Data.Json {
return ret;
}
private static Dictionary<string, T> GetEntries(Type type, string memberName) {
private static Dictionary<string, T> GetEntries(
#if NET6_0_OR_GREATER
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)]
#endif
Type type, string memberName) {
const BindingFlags flags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
var value = type.GetProperty(memberName, flags)?.GetValue(null) ?? type.GetField(memberName, flags)?.GetValue(null);
if (value == null)

View File

@ -3,6 +3,7 @@
<TargetFrameworks>net452;netstandard2.0;net6.0</TargetFrameworks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<ProduceReferenceAssembly>true</ProduceReferenceAssembly>
<IsTrimmable>true</IsTrimmable>
<RootNamespace>MLEM.Data</RootNamespace>
<DefineConstants>$(DefineConstants);FNA</DefineConstants>
<NoWarn>NU1701</NoWarn>

View File

@ -3,6 +3,7 @@
<TargetFrameworks>net452;netstandard2.0;net6.0</TargetFrameworks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<ProduceReferenceAssembly>true</ProduceReferenceAssembly>
<IsTrimmable>true</IsTrimmable>
<NoWarn>NU1701</NoWarn>
</PropertyGroup>

View File

@ -11,6 +11,9 @@ namespace MLEM.Data {
/// Before serializing and deserializing an object, each of the object's fields has to have a handler. New handlers can be added using <see cref="AddHandler{T}(System.Action{Lidgren.Network.NetBuffer,T},System.Func{Lidgren.Network.NetBuffer,T})"/> or <see cref="AddHandler{T}(Newtonsoft.Json.JsonSerializer)"/>.
/// </summary>
[Obsolete("Lidgren.Network support is deprecated. Consider using LiteNetLib or a custom implementation instead.")]
#if NET6_0_OR_GREATER
[System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Aot", "IL2070")]
#endif
public class NetBufferSerializer {
private readonly Dictionary<Type, Action<NetBuffer, object>> writeFunctions = new Dictionary<Type, Action<NetBuffer, object>>();

View File

@ -3,6 +3,7 @@
<TargetFrameworks>netstandard2.0;net6.0</TargetFrameworks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<ProduceReferenceAssembly>true</ProduceReferenceAssembly>
<IsTrimmable>true</IsTrimmable>
<RootNamespace>MLEM.Extended</RootNamespace>
<DefineConstants>$(DefineConstants);FNA</DefineConstants>
<NoWarn>NU1702</NoWarn>

View File

@ -3,6 +3,7 @@
<TargetFrameworks>netstandard2.0;net6.0</TargetFrameworks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<ProduceReferenceAssembly>true</ProduceReferenceAssembly>
<IsTrimmable>true</IsTrimmable>
</PropertyGroup>
<PropertyGroup>

View File

@ -4,6 +4,7 @@
<TargetFrameworks>net452;netstandard2.0;net6.0</TargetFrameworks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<ProduceReferenceAssembly>true</ProduceReferenceAssembly>
<IsTrimmable>true</IsTrimmable>
<RootNamespace>MLEM.Startup</RootNamespace>
<DefineConstants>$(DefineConstants);FNA</DefineConstants>
</PropertyGroup>

View File

@ -4,6 +4,7 @@
<TargetFrameworks>net452;netstandard2.0;net6.0</TargetFrameworks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<ProduceReferenceAssembly>true</ProduceReferenceAssembly>
<IsTrimmable>true</IsTrimmable>
</PropertyGroup>
<PropertyGroup>

View File

@ -6,6 +6,7 @@
<IncludeBuildOutput>false</IncludeBuildOutput>
<ContentTargetFolders>content</ContentTargetFolders>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<IsTrimmable>true</IsTrimmable>
<NoWarn>NU5128</NoWarn>
</PropertyGroup>

View File

@ -3,6 +3,7 @@
<TargetFrameworks>net452;netstandard2.0;net6.0</TargetFrameworks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<ProduceReferenceAssembly>true</ProduceReferenceAssembly>
<IsTrimmable>true</IsTrimmable>
<RootNamespace>MLEM.Ui</RootNamespace>
<DefineConstants>$(DefineConstants);FNA</DefineConstants>
</PropertyGroup>

View File

@ -3,6 +3,7 @@
<TargetFrameworks>net452;netstandard2.0;net6.0</TargetFrameworks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<ProduceReferenceAssembly>true</ProduceReferenceAssembly>
<IsTrimmable>true</IsTrimmable>
</PropertyGroup>
<PropertyGroup>

View File

@ -24,7 +24,12 @@ namespace MLEM.Ui.Parsers {
/// <summary>
/// An array containing all of the <see cref="ElementType"/> enum values.
/// </summary>
public static readonly ElementType[] ElementTypes = (ElementType[]) Enum.GetValues(typeof(ElementType));
public static readonly ElementType[] ElementTypes =
#if NET6_0_OR_GREATER
Enum.GetValues<ElementType>();
#else
(ElementType[]) Enum.GetValues(typeof(ElementType));
#endif
/// <summary>
/// The base path for images, which is prepended to the image link.

View File

@ -17,11 +17,21 @@ namespace MLEM.Input {
/// <summary>
/// All values of the <see cref="Buttons"/> enum.
/// </summary>
public static readonly Buttons[] AllButtons = (Buttons[]) Enum.GetValues(typeof(Buttons));
public static readonly Buttons[] AllButtons =
#if NET6_0_OR_GREATER
Enum.GetValues<Buttons>();
#else
(Buttons[]) Enum.GetValues(typeof(Buttons));
#endif
/// <summary>
/// All values of the <see cref="Keys"/> enum.
/// </summary>
public static readonly Keys[] AllKeys = (Keys[]) Enum.GetValues(typeof(Keys));
public static readonly Keys[] AllKeys =
#if NET6_0_OR_GREATER
Enum.GetValues<Keys>();
#else
(Keys[]) Enum.GetValues(typeof(Keys));
#endif
#if FNA
private const int MaximumGamePadCount = 4;

View File

@ -12,7 +12,7 @@ namespace MLEM.Input {
/// <summary>
/// All enum values of <see cref="ModifierKey"/>
/// </summary>
public static readonly ModifierKey[] ModifierKeys = (ModifierKey[]) Enum.GetValues(typeof(ModifierKey));
public static readonly ModifierKey[] ModifierKeys = {ModifierKey.None, ModifierKey.Shift, ModifierKey.Control, ModifierKey.Alt};
private static readonly Dictionary<ModifierKey, Keys[]> KeysLookup = new Dictionary<ModifierKey, Keys[]> {
{ModifierKey.Shift, new[] {Keys.LeftShift, Keys.RightShift}},
{ModifierKey.Control, new[] {Keys.LeftControl, Keys.RightControl}},

View File

@ -10,7 +10,7 @@ namespace MLEM.Input {
/// <summary>
/// All enum values of <see cref="MouseButton"/>
/// </summary>
public static readonly MouseButton[] MouseButtons = (MouseButton[]) Enum.GetValues(typeof(MouseButton));
public static readonly MouseButton[] MouseButtons = {MouseButton.Left, MouseButton.Middle, MouseButton.Right, MouseButton.Extra1, MouseButton.Extra2};
/// <summary>
/// Returns the <see cref="ButtonState"/> of the given mouse button.

View File

@ -3,6 +3,7 @@
<TargetFrameworks>net452;netstandard2.0;net6.0</TargetFrameworks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<ProduceReferenceAssembly>true</ProduceReferenceAssembly>
<IsTrimmable>true</IsTrimmable>
<RootNamespace>MLEM</RootNamespace>
<DefineConstants>$(DefineConstants);FNA</DefineConstants>
</PropertyGroup>

View File

@ -3,6 +3,7 @@
<TargetFrameworks>net452;netstandard2.0;net6.0</TargetFrameworks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<ProduceReferenceAssembly>true</ProduceReferenceAssembly>
<IsTrimmable>true</IsTrimmable>
</PropertyGroup>
<PropertyGroup>

View File

@ -71,7 +71,10 @@ namespace MLEM.Misc {
/// <summary>
/// All <see cref="Direction2"/> enum values
/// </summary>
public static readonly Direction2[] All = (Direction2[]) Enum.GetValues(typeof(Direction2));
public static readonly Direction2[] All = {
Direction2.None, Direction2.Up, Direction2.Right, Direction2.Down, Direction2.Left,
Direction2.UpRight, Direction2.DownRight, Direction2.UpLeft, Direction2.DownLeft
};
/// <summary>
/// The <see cref="Direction2.Up"/> through <see cref="Direction2.Left"/> directions
/// </summary>