diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 382144c..98dccfc 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,4 +1,4 @@ -name: Build +name: Build & Test on: push: @@ -34,9 +34,12 @@ jobs: - name: Build run: dotnet build Osu.Patcher.Injector - - name: Upload artifacts + - name: Upload Injector artifact uses: actions/upload-artifact@v4 with: name: osu!patcher-debug if-no-files-found: error path: .\Osu.Patcher.Injector\bin\Debug\net8.0\** + + - name: Run stub tests + run: dotnet run --project Osu.Stubs.Tests \ No newline at end of file diff --git a/.gitignore b/.gitignore index 604086d..962b066 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ target/ *.idea *.iml *DotSettings.user +**/TestResult.xml diff --git a/Osu.Patcher.Hook/Patches/BeatmapMirror/EnableOsuDirect.cs b/Osu.Patcher.Hook/Patches/BeatmapMirror/EnableOsuDirect.cs index 9e3c251..6bc73a2 100644 --- a/Osu.Patcher.Hook/Patches/BeatmapMirror/EnableOsuDirect.cs +++ b/Osu.Patcher.Hook/Patches/BeatmapMirror/EnableOsuDirect.cs @@ -2,7 +2,6 @@ using System.Reflection; using HarmonyLib; using JetBrains.Annotations; -using Osu.Stubs; using Osu.Stubs.Other; using Osu.Utils.Extensions; using static System.Reflection.Emit.OpCodes; diff --git a/Osu.Patcher.Hook/Patches/LivePerformance/AddPerformanceToUi.cs b/Osu.Patcher.Hook/Patches/LivePerformance/AddPerformanceToUi.cs index c00d71a..478a0e7 100644 --- a/Osu.Patcher.Hook/Patches/LivePerformance/AddPerformanceToUi.cs +++ b/Osu.Patcher.Hook/Patches/LivePerformance/AddPerformanceToUi.cs @@ -4,7 +4,6 @@ using System.Reflection; using HarmonyLib; using JetBrains.Annotations; -using Osu.Stubs; using Osu.Stubs.Framework; using Osu.Stubs.GameModes; using Osu.Stubs.Graphics; diff --git a/Osu.Patcher.Hook/Patches/LivePerformance/PerformanceCalculator.cs b/Osu.Patcher.Hook/Patches/LivePerformance/PerformanceCalculator.cs index 24e9597..97d1c61 100644 --- a/Osu.Patcher.Hook/Patches/LivePerformance/PerformanceCalculator.cs +++ b/Osu.Patcher.Hook/Patches/LivePerformance/PerformanceCalculator.cs @@ -1,7 +1,6 @@ using System; using System.Diagnostics; using Osu.Performance; -using Osu.Stubs; using Osu.Stubs.GameModes; using Osu.Stubs.Other; using Osu.Stubs.Scoring; diff --git a/Osu.Patcher.Hook/Patches/LivePerformance/PerformanceDisplay.cs b/Osu.Patcher.Hook/Patches/LivePerformance/PerformanceDisplay.cs index 96ebb0f..0306061 100644 --- a/Osu.Patcher.Hook/Patches/LivePerformance/PerformanceDisplay.cs +++ b/Osu.Patcher.Hook/Patches/LivePerformance/PerformanceDisplay.cs @@ -1,5 +1,4 @@ using System; -using Osu.Stubs; using Osu.Stubs.Framework; namespace Osu.Patcher.Hook.Patches.LivePerformance; diff --git a/Osu.Patcher.Hook/Patches/LivePerformance/TrackOnScoreHit.cs b/Osu.Patcher.Hook/Patches/LivePerformance/TrackOnScoreHit.cs index 75b9fa2..eed9ded 100644 --- a/Osu.Patcher.Hook/Patches/LivePerformance/TrackOnScoreHit.cs +++ b/Osu.Patcher.Hook/Patches/LivePerformance/TrackOnScoreHit.cs @@ -5,7 +5,6 @@ using HarmonyLib; using JetBrains.Annotations; using Osu.Performance; -using Osu.Stubs; using Osu.Stubs.Rulesets; using Osu.Stubs.Scoring; diff --git a/Osu.Patcher.Hook/Patches/LivePerformance/TrackResetScore.cs b/Osu.Patcher.Hook/Patches/LivePerformance/TrackResetScore.cs index 6b86fa7..97543f1 100644 --- a/Osu.Patcher.Hook/Patches/LivePerformance/TrackResetScore.cs +++ b/Osu.Patcher.Hook/Patches/LivePerformance/TrackResetScore.cs @@ -1,7 +1,6 @@ using System.Reflection; using HarmonyLib; using JetBrains.Annotations; -using Osu.Stubs; using Osu.Stubs.Rulesets; namespace Osu.Patcher.Hook.Patches.LivePerformance; diff --git a/Osu.Patcher.Hook/Patches/Misc/AllowPlayModeReload.cs b/Osu.Patcher.Hook/Patches/Misc/AllowPlayModeReload.cs index 5e312a4..cf632ff 100644 --- a/Osu.Patcher.Hook/Patches/Misc/AllowPlayModeReload.cs +++ b/Osu.Patcher.Hook/Patches/Misc/AllowPlayModeReload.cs @@ -3,7 +3,6 @@ using HarmonyLib; using JetBrains.Annotations; using Osu.Patcher.Hook.Patches.UI; -using Osu.Stubs; using Osu.Stubs.Other; using Osu.Utils.Extensions; using static System.Reflection.Emit.OpCodes; diff --git a/Osu.Patcher.Hook/Patches/Misc/DisableErrorReporting.cs b/Osu.Patcher.Hook/Patches/Misc/DisableErrorReporting.cs index 6b9206e..86ef251 100644 --- a/Osu.Patcher.Hook/Patches/Misc/DisableErrorReporting.cs +++ b/Osu.Patcher.Hook/Patches/Misc/DisableErrorReporting.cs @@ -1,7 +1,6 @@ using System.Reflection; using HarmonyLib; using JetBrains.Annotations; -using Osu.Stubs; using Osu.Stubs.Helpers; namespace Osu.Patcher.Hook.Patches.Misc; diff --git a/Osu.Patcher.Hook/Patches/Misc/FixDoubleSkipping.cs b/Osu.Patcher.Hook/Patches/Misc/FixDoubleSkipping.cs index a7766e7..312a6c9 100644 --- a/Osu.Patcher.Hook/Patches/Misc/FixDoubleSkipping.cs +++ b/Osu.Patcher.Hook/Patches/Misc/FixDoubleSkipping.cs @@ -3,7 +3,6 @@ using System.Reflection; using HarmonyLib; using JetBrains.Annotations; -using Osu.Stubs; using Osu.Stubs.GameModes; using Osu.Stubs.Other; diff --git a/Osu.Patcher.Hook/Patches/Misc/LogOsuLogger.cs b/Osu.Patcher.Hook/Patches/Misc/LogOsuLogger.cs index dec58ec..a6c2fe3 100644 --- a/Osu.Patcher.Hook/Patches/Misc/LogOsuLogger.cs +++ b/Osu.Patcher.Hook/Patches/Misc/LogOsuLogger.cs @@ -5,7 +5,6 @@ using System.Reflection; using HarmonyLib; using JetBrains.Annotations; -using Osu.Stubs; using Osu.Stubs.Helpers; namespace Osu.Patcher.Hook.Patches.Misc; diff --git a/Osu.Patcher.Hook/Patches/Misc/LogSoftErrors.cs b/Osu.Patcher.Hook/Patches/Misc/LogSoftErrors.cs index 5b06b88..918f8ec 100644 --- a/Osu.Patcher.Hook/Patches/Misc/LogSoftErrors.cs +++ b/Osu.Patcher.Hook/Patches/Misc/LogSoftErrors.cs @@ -2,7 +2,6 @@ using System.Reflection; using HarmonyLib; using JetBrains.Annotations; -using Osu.Stubs; using Osu.Stubs.Other; namespace Osu.Patcher.Hook.Patches.Misc; diff --git a/Osu.Patcher.Hook/Patches/Mods/PatchSuddenDeathAutoRetry.cs b/Osu.Patcher.Hook/Patches/Mods/PatchSuddenDeathAutoRetry.cs index f3eb5ff..bf0b27e 100644 --- a/Osu.Patcher.Hook/Patches/Mods/PatchSuddenDeathAutoRetry.cs +++ b/Osu.Patcher.Hook/Patches/Mods/PatchSuddenDeathAutoRetry.cs @@ -2,7 +2,6 @@ using System.Reflection; using HarmonyLib; using JetBrains.Annotations; -using Osu.Stubs; using Osu.Stubs.Rulesets; using static System.Reflection.Emit.OpCodes; diff --git a/Osu.Patcher.Hook/Patches/UI/AllowOpenOptionsInGameplay.cs b/Osu.Patcher.Hook/Patches/UI/AllowOpenOptionsInGameplay.cs index 4d5d224..124b814 100644 --- a/Osu.Patcher.Hook/Patches/UI/AllowOpenOptionsInGameplay.cs +++ b/Osu.Patcher.Hook/Patches/UI/AllowOpenOptionsInGameplay.cs @@ -2,7 +2,6 @@ using System.Reflection; using HarmonyLib; using JetBrains.Annotations; -using Osu.Stubs; using Osu.Stubs.Other; using Osu.Utils.Extensions; using static System.Reflection.Emit.OpCodes; diff --git a/Osu.Patcher.Hook/Patches/UI/CustomSongSelectThumbnailAlpha.cs b/Osu.Patcher.Hook/Patches/UI/CustomSongSelectThumbnailAlpha.cs index 65b923d..70e0d1e 100644 --- a/Osu.Patcher.Hook/Patches/UI/CustomSongSelectThumbnailAlpha.cs +++ b/Osu.Patcher.Hook/Patches/UI/CustomSongSelectThumbnailAlpha.cs @@ -3,7 +3,6 @@ using System.Reflection; using HarmonyLib; using JetBrains.Annotations; -using Osu.Stubs; using Osu.Stubs.SongSelect; using Osu.Utils.Extensions; using static System.Reflection.Emit.OpCodes; diff --git a/Osu.Patcher.Hook/Patches/UI/RevertSortWhenNoGroup.cs b/Osu.Patcher.Hook/Patches/UI/RevertSortWhenNoGroup.cs index 70273a9..4cc34de 100644 --- a/Osu.Patcher.Hook/Patches/UI/RevertSortWhenNoGroup.cs +++ b/Osu.Patcher.Hook/Patches/UI/RevertSortWhenNoGroup.cs @@ -3,7 +3,6 @@ using System.Reflection.Emit; using HarmonyLib; using JetBrains.Annotations; -using Osu.Stubs; using Osu.Stubs.Helpers; using Osu.Stubs.SongSelect; using Osu.Utils.Extensions; diff --git a/Osu.Patcher.Hook/Patches/UI/ShowModsInGameplay.cs b/Osu.Patcher.Hook/Patches/UI/ShowModsInGameplay.cs index 5b2b0d1..ecd48b7 100644 --- a/Osu.Patcher.Hook/Patches/UI/ShowModsInGameplay.cs +++ b/Osu.Patcher.Hook/Patches/UI/ShowModsInGameplay.cs @@ -3,7 +3,6 @@ using System.Reflection; using HarmonyLib; using JetBrains.Annotations; -using Osu.Stubs; using Osu.Stubs.GameModes; using static System.Reflection.Emit.OpCodes; diff --git a/Osu.Stubs.Tests/Osu.Stubs.Tests.csproj b/Osu.Stubs.Tests/Osu.Stubs.Tests.csproj new file mode 100644 index 0000000..1ce1e03 --- /dev/null +++ b/Osu.Stubs.Tests/Osu.Stubs.Tests.csproj @@ -0,0 +1,45 @@ + + + + Exe + Osu.Stubs.Tests + net462 + x86 + 12 + enable + + + true + full + false + DEBUG;TRACE + prompt + 4 + + + none + true + TRACE + prompt + 4 + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + diff --git a/Osu.Stubs.Tests/OsuApi.cs b/Osu.Stubs.Tests/OsuApi.cs new file mode 100644 index 0000000..178cb83 --- /dev/null +++ b/Osu.Stubs.Tests/OsuApi.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Net.Http; +using System.Threading.Tasks; +using JetBrains.Annotations; +using Newtonsoft.Json; + +namespace Osu.Stubs.Tests; + +[PublicAPI] +public static class OsuApi +{ + [PublicAPI] + public enum ReleaseStream + { + CuttingEdge, + Stable40, + Beta40, + } + + private static readonly HttpClient Http = new(); + + /// + /// Gets the latest release files for a specific release stream. + /// + public static async Task> GetReleaseFiles(ReleaseStream stream) + { + Console.WriteLine("Fetching latest osu! update info"); + + var url = $"https://osu.ppy.sh/web/check-updates.php" + + $"?action=check" + + $"&stream={stream.ToString().ToLower()}" + + $"&time={DateTime.Now.Ticks}"; + + using var response = await Http.GetAsync(url); + response.EnsureSuccessStatusCode(); + + var bodyText = await response.Content.ReadAsStringAsync(); + if (bodyText == null) throw new Exception("Response returned no body"); + + var releaseFiles = JsonConvert.DeserializeObject>(bodyText); + if (releaseFiles == null) throw new Exception("Failed to deserialize update files"); + + return releaseFiles; + } + + /// + /// Downloads the full osu! update file list to a specific directory. + /// + /// An empty directory. + /// The release stream to download. + public static async Task DownloadOsu(string dir, ReleaseStream stream = ReleaseStream.Stable40) + { + var updateFiles = await GetReleaseFiles(ReleaseStream.Stable40); + + Parallel.ForEach(updateFiles, updateFile => + { + Console.WriteLine($"Downloading {updateFile.FileName}"); + DownloadFile(updateFile.DownloadUrl, Path.Combine(dir, updateFile.FileName)).Wait(); + }); + + Console.WriteLine("Finished downloading osu!"); + } + + private static async Task DownloadFile(string url, string path) + { + using var response = await Http.GetAsync(url); + response.EnsureSuccessStatusCode(); + + var bodyStream = await response.Content.ReadAsStreamAsync(); + if (bodyStream == null) throw new Exception("Response returned no body"); + + using var file = File.Create(path); + await bodyStream.CopyToAsync(file); + } +} \ No newline at end of file diff --git a/Osu.Stubs.Tests/OsuUpdateFile.cs b/Osu.Stubs.Tests/OsuUpdateFile.cs new file mode 100644 index 0000000..6804b17 --- /dev/null +++ b/Osu.Stubs.Tests/OsuUpdateFile.cs @@ -0,0 +1,30 @@ +using Newtonsoft.Json; + +namespace Osu.Stubs.Tests; + +public class OsuUpdateFile +{ + [JsonRequired] + [JsonProperty("file_version")] + public int FileVersion { get; set; } + + [JsonRequired] + [JsonProperty("filesize")] + public int FileSize { get; set; } + + [JsonRequired] + [JsonProperty("filename")] + public string FileName { get; set; } = null!; + + [JsonRequired] + [JsonProperty("file_hash")] + public string FileHash { get; set; } = null!; + + [JsonRequired] + [JsonProperty("timestamp")] + public string Timestamp { get; set; } = null!; + + [JsonRequired] + [JsonProperty("url_full")] + public string DownloadUrl { get; set; } = null!; +} \ No newline at end of file diff --git a/Osu.Stubs.Tests/Program.cs b/Osu.Stubs.Tests/Program.cs new file mode 100644 index 0000000..8e470e7 --- /dev/null +++ b/Osu.Stubs.Tests/Program.cs @@ -0,0 +1,8 @@ +using NUnitLite; + +namespace Osu.Stubs.Tests; + +public static class Program +{ + public static int Main(string[] args) => new AutoRun().Execute(args); +} \ No newline at end of file diff --git a/Osu.Stubs.Tests/TestStubs.cs b/Osu.Stubs.Tests/TestStubs.cs new file mode 100644 index 0000000..c229428 --- /dev/null +++ b/Osu.Stubs.Tests/TestStubs.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using NUnit.Framework; +using Osu.Utils.Extensions; +using Osu.Utils.Lazy; + +#pragma warning disable CS0618 // Type or member is obsolete + +namespace Osu.Stubs.Tests; + +[TestFixture] +[Parallelizable] +public class TestStubs +{ + [TestCaseSource(nameof(LoadStubs))] + public void TestStub(ILazy lazy) => Assert.DoesNotThrow( + () => + { + try + { + lazy.Fill(); + } + catch (AggregateException e) + { + if (e.InnerException is ReflectionTypeLoadException typeLoadException) + { + throw new AggregateException(typeLoadException.LoaderExceptions); + } + + throw e.InnerException!; + } + } + ); + + [Test(Description = "Tests cannot be run in 64bit mode!", ExpectedResult = true)] + public static bool CheckIs32Bit() => !Environment.Is64BitProcess; + + private static IEnumerable> LoadStubs() + { + if (!CheckIs32Bit()) return []; + + var osuDir = Path.Combine(Assembly.GetExecutingAssembly().Location, "../osu!"); + var osuExe = Path.Combine(osuDir, "osu!.exe"); + + if (!Directory.Exists(osuDir)) // TODO: check file hashes to force re-download + { + Directory.CreateDirectory(osuDir); + OsuApi.DownloadOsu(osuDir).Wait(); + } + + // Add osu! directory to assembly search path + AppDomain.CurrentDomain.AppendPrivatePath(osuDir); + + // Load the osu! executable and it's dependencies as assemblies without executing + Assembly.LoadFile(osuExe); + + var stubs = new List>(300); + + foreach (var type in Assembly.GetAssembly(typeof(Stub)).GetTypes()) + { + foreach (var field in type.GetFields(BindingFlags.Public | BindingFlags.Static)) + { + if (field.GetCustomAttribute(typeof(Stub)) == null) + continue; + + var stub = field.GetValue>(null); + if (stub == null) throw new Exception(); + + stubs.Add(stub); + } + } + + stubs.Sort((a, b) => + string.Compare(a.Name, b.Name, StringComparison.OrdinalIgnoreCase)); + + return stubs; + } +} \ No newline at end of file diff --git a/Osu.Stubs/Graphics/SkinManager.cs b/Osu.Stubs/Graphics/SkinManager.cs index 68c9d8c..a7964db 100644 --- a/Osu.Stubs/Graphics/SkinManager.cs +++ b/Osu.Stubs/Graphics/SkinManager.cs @@ -44,7 +44,7 @@ public static class SkinManager /// [Stub] public static readonly LazyField Current = new( - "SkinManager#Current", + "osu.Graphics.Skinning.SkinManager::Current", // There is two fields with type SkinOsu; Current and CurrentUserSkin in that order () => Class.Reference.GetDeclaredFields() .First(f => f.FieldType == SkinOsu.Class.Reference) diff --git a/Osu.Stubs/Helpers/Obfuscated.cs b/Osu.Stubs/Helpers/Obfuscated.cs index 0797e66..432e4f4 100644 --- a/Osu.Stubs/Helpers/Obfuscated.cs +++ b/Osu.Stubs/Helpers/Obfuscated.cs @@ -16,7 +16,7 @@ public static class Obfuscated /// [Stub] public static readonly LazyType Class = new( - "osu.Helpers.Obfuscated{T}", + "osu.Helpers.Obfuscated", () => Finalize!.Reference.DeclaringType! ); @@ -49,7 +49,7 @@ public static class Obfuscated /// [Stub] public static readonly LazyMethod GetValue = new( - "osu.Helpers.Obfuscated{T}::get_Value()", + "osu.Helpers.Obfuscated::get_Value()", () => Class.Reference .GetRuntimeMethods() .First(mtd => diff --git a/Osu.Stubs/Scoring/Score.cs b/Osu.Stubs/Scoring/Score.cs index 8628c76..d243140 100644 --- a/Osu.Stubs/Scoring/Score.cs +++ b/Osu.Stubs/Scoring/Score.cs @@ -104,7 +104,7 @@ public static class Score /// [Stub] public static readonly LazyField EnabledMods = new( - "Score#EnabledMods", + "osu.GameplayElements.Scoring.Score::EnabledMods", () => Class.Reference .GetDeclaredFields() .Single(field => field.FieldType.IsGenericType && diff --git a/Osu.Stubs/Stub.cs b/Osu.Stubs/Stub.cs index 331f53a..e7938db 100644 --- a/Osu.Stubs/Stub.cs +++ b/Osu.Stubs/Stub.cs @@ -4,7 +4,7 @@ namespace Osu.Stubs; /// -/// Stub marker for a type extending . +/// Stub marker for a type extending . /// Used for testing stubs on new versions of osu!. /// [AttributeUsage(AttributeTargets.Field)] diff --git a/Osu.Utils/Lazy/LazyInfo.cs b/Osu.Utils/Lazy/ILazy.cs similarity index 59% rename from Osu.Utils/Lazy/LazyInfo.cs rename to Osu.Utils/Lazy/ILazy.cs index 8b200ca..89d59bc 100644 --- a/Osu.Utils/Lazy/LazyInfo.cs +++ b/Osu.Utils/Lazy/ILazy.cs @@ -8,7 +8,7 @@ namespace Osu.Utils.Lazy; /// A base type for finding stuff with the use of reflection lazily. /// [PublicAPI] -public abstract class LazyInfo where T : MemberInfo +public interface ILazy where T : MemberInfo { /// /// The assumed original human readable fully qualified name. @@ -16,40 +16,45 @@ public abstract class LazyInfo where T : MemberInfo /// public abstract string Name { get; } - /// - /// The runtime type name of the resolved reflective type. - /// - public string RuntimeName => Reference.Name; - /// /// Reference to the reflective target type. /// This retrieves or finds the type when called. /// public abstract T Reference { get; } +} + +[PublicAPI] +public static class LazyExtensions +{ + /// + /// Execute the lazy action to fill in the lazy value if not already done so. + /// + public static void Fill(this ILazy lazy) => _ = lazy.Reference; /// /// Gets the result of a otherwise throws appropriate exceptions. /// + /// The ILazy instance, used for obtaining the ILazy implementor's name. /// - /// + /// /// - /// The lazy to invoke. - /// Subtype of LazyInfo. + /// The lazy to invoke. + /// The result type of ILazy /// The Lazy's result value - protected static T GetReference(string name, Lazy lazy) - where TL : LazyInfo + internal static T GetReference(this ILazy lazy, string name, Lazy realLazy) + where T : MemberInfo { try { - var value = lazy.Value; + var value = realLazy.Value; if (value != null) return value; - var lazyName = typeof(TL).Name; + var lazyName = lazy.GetType().Name; throw new Exception($"{lazyName} result cannot be null!"); } catch (Exception e) { - var lazyName = typeof(TL).Name; + var lazyName = lazy.GetType().Name; throw new AggregateException($"Failed to run {lazyName} {name}", e); } } diff --git a/Osu.Utils/Lazy/LazyConstructor.cs b/Osu.Utils/Lazy/LazyConstructor.cs index c239aba..b80aa2c 100644 --- a/Osu.Utils/Lazy/LazyConstructor.cs +++ b/Osu.Utils/Lazy/LazyConstructor.cs @@ -11,14 +11,14 @@ namespace Osu.Utils.Lazy; /// A reference to a constructor that gets located at runtime and invoked reflectively. /// [PublicAPI] -public class LazyConstructor : LazyInfo +public class LazyConstructor : ILazy { private readonly Lazy _lazy; /// /// Make a wrapper around Lazy for constructor. /// - /// of type what this is returning. + /// of type what this is returning. /// The lazy action to run when the value is needed. public LazyConstructor(string name, Func action) { @@ -26,9 +26,11 @@ public LazyConstructor(string name, Func action) _lazy = new Lazy(action); } - public override string Name { get; } + public string Name { get; } - public override ConstructorInfo Reference => GetReference(Name, _lazy); + public ConstructorInfo Reference => this.GetReference(Name, _lazy); + + public override string ToString() => $"{nameof(LazyConstructor)}({Name})"; /// /// Find if not already cached and reflectively invoke this constructor to create a new instance of a class. diff --git a/Osu.Utils/Lazy/LazyField.cs b/Osu.Utils/Lazy/LazyField.cs index 358a883..7911288 100644 --- a/Osu.Utils/Lazy/LazyField.cs +++ b/Osu.Utils/Lazy/LazyField.cs @@ -9,7 +9,7 @@ namespace Osu.Utils.Lazy; /// /// The type this field should be treated as. [PublicAPI] -public class LazyField : LazyInfo +public class LazyField : ILazy { private readonly Lazy _lazy; @@ -24,9 +24,11 @@ public LazyField(string name, Func action) _lazy = new Lazy(action); } - public override string Name { get; } + public string Name { get; } - public override FieldInfo Reference => GetReference>(Name, _lazy); + public FieldInfo Reference => this.GetReference(Name, _lazy); + + public override string ToString() => $"{nameof(LazyField)}({Name})"; /// /// Gets the current value of this field. diff --git a/Osu.Utils/Lazy/LazyMethod.cs b/Osu.Utils/Lazy/LazyMethod.cs index cc54f81..3895007 100644 --- a/Osu.Utils/Lazy/LazyMethod.cs +++ b/Osu.Utils/Lazy/LazyMethod.cs @@ -11,14 +11,14 @@ namespace Osu.Utils.Lazy; /// A reference to a method that gets located at runtime and invoked reflectively. /// [PublicAPI] -public class LazyMethod : LazyInfo +public class LazyMethod : ILazy { private readonly Lazy _lazy; /// /// Make a wrapper around Lazy for methods. /// - /// of what type this is returning. + /// of what type this is returning. /// The lazy action to run when the value is needed. public LazyMethod(string name, Func action) { @@ -26,9 +26,11 @@ public LazyMethod(string name, Func action) _lazy = new Lazy(action); } - public override string Name { get; } + public string Name { get; } - public override MethodInfo Reference => GetReference(Name, _lazy); + public MethodInfo Reference => this.GetReference(Name, _lazy); + + public override string ToString() => $"{nameof(LazyMethod)}({Name})"; /// /// Find if not already cached and reflectively invoke this method. Does not return any value. @@ -43,7 +45,7 @@ public void Invoke(object? instance = null, object?[]? parameters = null) => /// Searches every method to see if this is the entire method's bytecode. /// /// - /// + /// /// /// Sequential opcodes to compare the target method's bytecode with. public static LazyMethod BySignature(string name, IReadOnlyList signature) => @@ -54,7 +56,7 @@ public static LazyMethod BySignature(string name, IReadOnlyList signatur /// Searches every method to see if this is located in the method's bytecode. /// /// - /// + /// /// /// Sequential opcodes to search the target method with. public static LazyMethod ByPartialSignature(string name, IReadOnlyList signature) => diff --git a/Osu.Utils/Lazy/LazyType.cs b/Osu.Utils/Lazy/LazyType.cs index 6b1e40e..2b4a0db 100644 --- a/Osu.Utils/Lazy/LazyType.cs +++ b/Osu.Utils/Lazy/LazyType.cs @@ -7,7 +7,7 @@ namespace Osu.Utils.Lazy; /// A reference to a Type that gets located at runtime and interacted with reflectively. /// [PublicAPI] -public class LazyType : LazyInfo +public class LazyType : ILazy { private readonly Lazy _lazy; @@ -22,15 +22,17 @@ public LazyType(string name, Func action) _lazy = new Lazy(action); } - public override string Name { get; } + public string Name { get; } - public override Type Reference => GetReference(Name, _lazy); + public Type Reference => this.GetReference(Name, _lazy); + + public override string ToString() => $"{nameof(LazyType)}({Name})"; /// /// Finds a class based on it's full name including namespace. /// /// - /// + /// /// /// /// The full runtime name to search for, including any namespaces. If this isn't different from diff --git a/osu-patcher.sln b/osu-patcher.sln index 11e495b..eb357b5 100644 --- a/osu-patcher.sln +++ b/osu-patcher.sln @@ -9,6 +9,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Osu.Stubs", "Osu.Stubs\Osu. EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Osu.Utils", "Osu.Utils\Osu.Utils.csproj", "{DBE8C9EB-4AC9-45A3-AFFF-A899A7A99823}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Osu.Stubs.Tests", "Osu.Stubs.Tests\Osu.Stubs.Tests.csproj", "{F60A9670-BD12-4FF9-A56B-AF564CFD8CCD}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x86 = Debug|x86 @@ -35,5 +37,9 @@ Global {DBE8C9EB-4AC9-45A3-AFFF-A899A7A99823}.Debug|x86.Build.0 = Debug|Any CPU {DBE8C9EB-4AC9-45A3-AFFF-A899A7A99823}.Release|x86.ActiveCfg = Release|Any CPU {DBE8C9EB-4AC9-45A3-AFFF-A899A7A99823}.Release|x86.Build.0 = Release|Any CPU + {F60A9670-BD12-4FF9-A56B-AF564CFD8CCD}.Debug|x86.ActiveCfg = Debug|Any CPU + {F60A9670-BD12-4FF9-A56B-AF564CFD8CCD}.Debug|x86.Build.0 = Debug|Any CPU + {F60A9670-BD12-4FF9-A56B-AF564CFD8CCD}.Release|x86.ActiveCfg = Release|Any CPU + {F60A9670-BD12-4FF9-A56B-AF564CFD8CCD}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection EndGlobal