Skip to content

Commit

Permalink
wip: show pp counter in ui
Browse files Browse the repository at this point in the history
  • Loading branch information
rushiiMachine committed Mar 27, 2024
1 parent da5a600 commit afb58d0
Show file tree
Hide file tree
Showing 18 changed files with 511 additions and 7 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using HarmonyLib;
using JetBrains.Annotations;
using Osu.Stubs;

namespace Osu.Patcher.Hook.Patches.LivePerformance;

/// <summary>
/// Hooks the constructor of <c>ScoreDisplay</c> to add our own <c>pTextSprite</c> for displaying
/// the performance counter to the ScoreDisplay's sprite manager.
/// </summary>
[HarmonyPatch]
[UsedImplicitly]
public class PatchAddPerformanceToScoreDisplay
{
[UsedImplicitly]
[HarmonyTargetMethod]
private static MethodBase Target() => ScoreDisplay.Constructor.Reference;

[UsedImplicitly]
[HarmonyPostfix]
[SuppressMessage("ReSharper", "InconsistentNaming")]
private static void After(
object __instance, // ScoreDisplay
[HarmonyArgument(0)] object spriteManager, // SpriteManager
[HarmonyArgument(1)] object position, // Vector2
[HarmonyArgument(2)] bool alignRight,
[HarmonyArgument(3)] float scale,
[HarmonyArgument(4)] bool showScore,
[HarmonyArgument(5)] bool showAccuracy
)
{
var positionX = Vector2.X.Get(position);

// TODO: use correct position
var startPosition = ((ConstructorInfo)Vector2.Constructor.Reference).Invoke([positionX, 0f]);

var performanceSprite = ((ConstructorInfo)pSpriteText.Constructor.Reference).Invoke(
[
/* text: */ "0000.0",
/* fontName: */ "", // TODO: use correct value
/* spacingOverlap: */ 0f, // TODO: use correct value
/* fieldType: */ alignRight ? Fields.TopRight : Fields.TopLeft,
/* origin: */ alignRight ? Origins.TopRight : Origins.TopLeft,
/* clock: */ Clocks.Game,
/* startPosition: */ startPosition,
/* drawDepth: */ 0.95f,
/* alwaysDraw: */ true,
/* color: */ Color.White, // TODO: try GhostWhite
/* precache: */ true,
/* source: */ SkinSource.All,
]);

SpriteManager.Add.Invoke(spriteManager, [performanceSprite]);
PerformanceDisplay.SetPerformanceCounter(performanceSprite);
}

[UsedImplicitly]
[HarmonyFinalizer]
[SuppressMessage("ReSharper", "InconsistentNaming")]
private static void Finalizer(Exception? __exception)
{
if (__exception != null)
{
Console.WriteLine($"Exception due to {nameof(PatchAddPerformanceToScoreDisplay)}: {__exception}");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
namespace Osu.Patcher.Hook.Patches.LivePerformance;

/// <summary>
/// Hooks <c>Ruleset::ResetScore()</c> to also reset our performance calculator.
/// Hooks <c>Ruleset::ResetScore()</c> to also reset our performance calculator and caches.
/// </summary>
[HarmonyPatch]
[UsedImplicitly]
Expand Down
42 changes: 42 additions & 0 deletions Osu.Patcher.Hook/Patches/LivePerformance/PerformanceDisplay.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using System;
using Osu.Stubs;

namespace Osu.Patcher.Hook.Patches.LivePerformance;

internal static class PerformanceDisplay
{
/// <summary>
/// The last known patched instance of our <c>pSpriteText</c> performance counter sprite.
/// </summary>
private static readonly WeakReference<object?> PerformanceCounter = new(null);

/// <summary>
/// Set a new active performance counter to update.
/// </summary>
/// <param name="sprite">The <c>pSpriteText</c> performance counter sprite.</param>
public static void SetPerformanceCounter(object sprite) =>
PerformanceCounter.SetTarget(sprite);

/// <summary>
/// Change the pp value for the currently active performance counter.
/// </summary>
public static void UpdatePerformanceCounter(double pp)
{
try
{
var ruleset = Ruleset.Instance.Get();

var scoreDisplay = Ruleset.ScoreDisplay.Get(ruleset);
if (scoreDisplay == null) return;

if (!PerformanceCounter.TryGetTarget(out var sprite))
return;

pText.SetText.Invoke(sprite, [$"{pp:00.0}pp"]);
}
catch (Exception e)
{
Console.WriteLine($"Failed to set performance counter sprite text: {e}");
}
}
}
16 changes: 16 additions & 0 deletions Osu.Stubs/Clocks.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System.Diagnostics.CodeAnalysis;
using JetBrains.Annotations;

namespace Osu.Stubs;

/// <summary>
/// Original: <c>osu.Graphics.Sprites.Clocks</c>
/// </summary>
[UsedImplicitly]
[SuppressMessage("ReSharper", "UnusedMember.Global")]
public class Clocks
{
public const int Game = 0;
public const int Audio = 0;
public const int AudioOnce = 0;
}
1 change: 1 addition & 0 deletions Osu.Stubs/Color.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ public static class Color
{
public static readonly XnaColor Red = GetColor("Red");
public static readonly XnaColor Orange = GetColor("Orange");
public static readonly XnaColor White = GetColor("White");
public static readonly XnaColor GhostWhite = GetColor("GhostWhite");

private static XnaColor GetColor(string name)
Expand Down
32 changes: 32 additions & 0 deletions Osu.Stubs/Fields.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using System.Diagnostics.CodeAnalysis;
using JetBrains.Annotations;

namespace Osu.Stubs;

/// <summary>
/// Original: <c>osu.Graphics.Sprites.Fields</c>
/// </summary>
[UsedImplicitly]
[SuppressMessage("ReSharper", "UnusedMember.Global")]
public class Fields
{
public const int GameField = 1;
public const int GameFieldWide = 2;
public const int Storyboard = 3;
public const int StoryboardCentre = 4;
public const int Native = 5;
public const int TopLeft = 6;
public const int TopCentre = 7;
public const int TopRight = 8;
public const int CentreLeft = 9;
public const int Centre = 10;
public const int CentreRight = 11;
public const int BottomLeft = 12;
public const int BottomCentre = 13;
public const int BottomRight = 14;
public const int StandardGameFieldScale = 15;
public const int NativeStandardScale = 16;
public const int NativeRight = 17;
public const int NativeBottomRight = 18;
public const int NativeBottomCentre = 19;
}
1 change: 1 addition & 0 deletions Osu.Stubs/Logger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public class Logger
Leave_S,
Ret,
},
false,
true
);
}
15 changes: 11 additions & 4 deletions Osu.Stubs/Opcode/LazyMethod.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,16 @@ public class LazyMethod
/// </summary>
/// <param name="name"><c>Class#Method</c> name of what this signature is matching.</param>
/// <param name="signature">Sequential opcodes to search the target method with.</param>
/// <param name="isConstructor">Whether this method is a constructor.</param>
/// <param name="entireMethod">Whether the signature is the entire method.</param>
internal LazyMethod(string name, IReadOnlyList<OpCode> signature, bool entireMethod = false)
internal LazyMethod(string name, IReadOnlyList<OpCode> signature,
bool isConstructor = false,
bool entireMethod = false)
{
_name = name;
_lazy = new Lazy<MethodBase?>(() => OpCodeMatcher.FindMethodBySignature(signature, entireMethod));
_lazy = new Lazy<MethodBase?>(() => isConstructor
? OpCodeMatcher.FindConstructorBySignature(signature, entireMethod)
: OpCodeMatcher.FindMethodBySignature(signature, entireMethod));
}

/// <summary>
Expand Down Expand Up @@ -66,8 +71,10 @@ public void Invoke(object? instance = null, object?[]? parameters = null) =>
public class LazyMethod<R> : LazyMethod
{
/// <inheritdoc />
internal LazyMethod(string name, IReadOnlyList<OpCode> signature)
: base(name, signature)
internal LazyMethod(string name, IReadOnlyList<OpCode> signature,
bool isConstructor = false,
bool entireMethod = false)
: base(name, signature, isConstructor, entireMethod)
{
}

Expand Down
6 changes: 4 additions & 2 deletions Osu.Stubs/Opcode/OpCodeMatcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,10 @@ public static class OpCodeMatcher
/// Search for a constructor inside the osu! assembly by an IL OpCode signature.
/// </summary>
/// <param name="signature">A set of sequential OpCodes to match.</param>
/// <param name="entireMethod">Whether the signature is the entire method to search for.</param>
/// <returns>The found constructor (method) or null if none found.</returns>
public static ConstructorInfo? FindConstructorBySignature(IReadOnlyList<OpCode> signature)
public static ConstructorInfo? FindConstructorBySignature(IReadOnlyList<OpCode> signature,
bool entireMethod = false)
{
if (signature.Count <= 0) return null;

Expand All @@ -50,7 +52,7 @@ public static class OpCodeMatcher
var instructions = method.GetMethodBody()?.GetILAsByteArray();
if (instructions == null) continue;

if (InstructionsMatchesSignature(instructions, signature, false))
if (InstructionsMatchesSignature(instructions, signature, entireMethod))
return method;
}

Expand Down
23 changes: 23 additions & 0 deletions Osu.Stubs/Origins.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System.Diagnostics.CodeAnalysis;
using JetBrains.Annotations;

namespace Osu.Stubs;

/// <summary>
/// Original: <c>osu.Graphics.Sprites.Origins</c>
/// </summary>
[UsedImplicitly]
[SuppressMessage("ReSharper", "UnusedMember.Global")]
public class Origins
{
public const int TopLeft = 0;
public const int Centre = 1;
public const int CentreLeft = 2;
public const int TopRight = 3;
public const int BottomCentre = 4;
public const int TopCentre = 5;
public const int Custom = 6;
public const int CentreRight = 7;
public const int BottomLeft = 8;
public const int BottomRight = 9;
}
37 changes: 37 additions & 0 deletions Osu.Stubs/Ruleset.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Reflection;
using JetBrains.Annotations;
using Osu.Stubs.Opcode;
using Osu.Stubs.Utils;
using static System.Reflection.Emit.OpCodes;

namespace Osu.Stubs;
Expand Down Expand Up @@ -78,6 +79,31 @@ public static class Ruleset
}
);

/// <summary>
/// Original: <c>Initialize()</c>
/// b20240123: <c></c>
/// </summary>
[UsedImplicitly]
public static readonly LazyMethod Initialize = new(
"Ruleset#Initialize()",
new[]
{
Ldarg_0,
Callvirt,
}.Duplicate(8)
);

/// <summary>
/// Original: <c>CurrentScore</c>
/// b20240123: <c>Instance</c>
/// </summary>
[UsedImplicitly]
public static readonly LazyField<object> Instance = new(
"Ruleset#Instance",
() => RuntimeType.GetRuntimeFields()
.Single(field => field.FieldType == RuntimeType)
);

/// <summary>
/// Original: <c>CurrentScore</c>
/// b20240123: <c>#=zk4sdboE=</c>
Expand All @@ -89,6 +115,17 @@ public static class Ruleset
.Single(field => field.FieldType == Score.RuntimeType)
);

/// <summary>
/// Original: <c>ScoreDisplay</c>
/// b20240123: <c></c> TODO: find this
/// </summary>
[UsedImplicitly]
public static readonly LazyField<object?> ScoreDisplay = new(
"Ruleset#ScoreDisplay",
() => RuntimeType.GetRuntimeFields()
.Single(field => field.FieldType == ScoreDisplay.Reference)

Check warning on line 126 in Osu.Stubs/Ruleset.cs

View workflow job for this annotation

GitHub Actions / build

Dereference of a possibly null reference.

Check warning on line 126 in Osu.Stubs/Ruleset.cs

View workflow job for this annotation

GitHub Actions / build

Dereference of a possibly null reference.

Check warning on line 126 in Osu.Stubs/Ruleset.cs

View workflow job for this annotation

GitHub Actions / build

Dereference of a possibly null reference.

Check warning on line 126 in Osu.Stubs/Ruleset.cs

View workflow job for this annotation

GitHub Actions / build

Dereference of a possibly null reference.
);

[UsedImplicitly]
public static Type RuntimeType => OnIncreaseScoreHit.Reference.DeclaringType!;
}
Loading

0 comments on commit afb58d0

Please sign in to comment.