Skip to content

Commit c61651d

Browse files
authored
Merge pull request #25 from Piotrekol/dev
2 parents c5bf2f0 + 332466b commit c61651d

44 files changed

Lines changed: 1934 additions & 603 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net8.0</TargetFramework>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<Nullable>enable</Nullable>
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<PackageReference Include="CommandLineParser" Version="2.9.1" />
12+
</ItemGroup>
13+
14+
<ItemGroup>
15+
<ProjectReference Include="..\OsuMemoryDataProvider\OsuMemoryDataProvider.csproj" />
16+
</ItemGroup>
17+
18+
</Project>
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
using CommandLine;
2+
using OsuMemoryDataProvider;
3+
using System.Diagnostics;
4+
using System.Text.Json;
5+
using System.Text.Json.Serialization;
6+
7+
var parsedArgs = Parser.Default.ParseArguments<CommandLineOptions>(args).MapResult((o) => o, o => null);
8+
if (parsedArgs == null)
9+
return;
10+
11+
Console.WriteLine(JsonSerializer.Serialize(parsedArgs));
12+
13+
var reader = StructuredOsuMemoryReader.Instance;
14+
reader.InvalidRead += readerOnInvalidRead;
15+
var baseAddresses = StructuredOsuMemoryReader.Instance.OsuMemoryAddresses;
16+
17+
JsonSerializerOptions jsonSerializerOptions = new()
18+
{
19+
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
20+
WriteIndented = parsedArgs.Indented,
21+
};
22+
var stopwatch = Stopwatch.StartNew();
23+
double readTimeMs, readTimeMsMin, readTimeMsMax;
24+
double _memoryReadTimeMin = double.PositiveInfinity;
25+
double _memoryReadTimeMax = double.NegativeInfinity;
26+
while (true)
27+
{
28+
while (!reader.CanRead)
29+
{
30+
Console.WriteLine("Waiting for osu! process...");
31+
await Task.Delay(200);
32+
};
33+
34+
stopwatch = Stopwatch.StartNew();
35+
36+
reader.TryRead(baseAddresses.Beatmap);
37+
reader.TryRead(baseAddresses.Skin);
38+
reader.TryRead(baseAddresses.GeneralData);
39+
reader.TryRead(baseAddresses.BanchoUser);
40+
41+
if (baseAddresses.GeneralData.OsuStatus == OsuMemoryStatus.SongSelect)
42+
reader.TryRead(baseAddresses.SongSelectionScores);
43+
else
44+
baseAddresses.SongSelectionScores.Scores.Clear();
45+
46+
if (baseAddresses.GeneralData.OsuStatus == OsuMemoryStatus.ResultsScreen)
47+
reader.TryRead(baseAddresses.ResultsScreen);
48+
49+
if (baseAddresses.GeneralData.OsuStatus == OsuMemoryStatus.Playing)
50+
{
51+
reader.TryRead(baseAddresses.Player);
52+
reader.TryRead(baseAddresses.LeaderBoard);
53+
reader.TryRead(baseAddresses.KeyOverlay);
54+
}
55+
else
56+
{
57+
baseAddresses.LeaderBoard.Players.Clear();
58+
}
59+
60+
var hitErrors = baseAddresses.Player?.HitErrors;
61+
if (hitErrors != null)
62+
{
63+
var hitErrorsCount = hitErrors.Count;
64+
hitErrors.Clear();
65+
hitErrors.Add(hitErrorsCount);
66+
}
67+
68+
stopwatch.Stop();
69+
readTimeMs = stopwatch.ElapsedTicks / (double)TimeSpan.TicksPerMillisecond;
70+
if (readTimeMs < _memoryReadTimeMin) _memoryReadTimeMin = readTimeMs;
71+
if (readTimeMs > _memoryReadTimeMax) _memoryReadTimeMax = readTimeMs;
72+
73+
readTimeMsMin = _memoryReadTimeMin;
74+
readTimeMsMax = _memoryReadTimeMax;
75+
76+
if (parsedArgs.Output)
77+
Console.WriteLine(JsonSerializer.Serialize(baseAddresses, jsonSerializerOptions));
78+
79+
Console.WriteLine($"ReadTimeMS: {readTimeMs}{Environment.NewLine}" +
80+
$"Min ReadTimeMS: {readTimeMsMin}{Environment.NewLine}" +
81+
$"Max ReadTimeMS: {readTimeMsMax}{Environment.NewLine}" +
82+
$"Press any key to reset min/max values{Environment.NewLine}");
83+
if (parsedArgs.ExitAfter)
84+
break;
85+
86+
if (Console.KeyAvailable)
87+
{
88+
Console.ReadKey(true);
89+
while (Console.KeyAvailable)
90+
Console.ReadKey(true);
91+
92+
_memoryReadTimeMin = double.PositiveInfinity;
93+
_memoryReadTimeMax = double.NegativeInfinity;
94+
95+
}
96+
97+
await Task.Delay(parsedArgs.Delay);
98+
}
99+
100+
void readerOnInvalidRead(object sender, (object readObject, string propPath) e)
101+
{
102+
Console.WriteLine($"{DateTime.Now:T} Error reading {e.propPath}{Environment.NewLine}");
103+
}
104+
105+
class CommandLineOptions
106+
{
107+
[Option('o', "output", Required = false, Default = false, HelpText = "Send read memory values to stdout")]
108+
public bool Output { get; set; }
109+
[Option('i', "indentedOutput", Required = false, Default = false, HelpText = "Format memory values sent to stdout")]
110+
public bool Indented { get; set; }
111+
[Option('e', "exitAfter", Required = false, Default = false, HelpText = "exit after reading memory values once")]
112+
public bool ExitAfter { get; set; }
113+
[Option('d', "delay", Required = false, Default = 200, HelpText = "delay between reads when continuous is enabled")]
114+
public int Delay { get; set; }
115+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<TargetFrameworks>net8.0</TargetFrameworks>
4+
<Platforms>x86;x64;AnyCPU</Platforms>
5+
<OutputType>Library</OutputType>
6+
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
7+
<LangVersion>latest</LangVersion>
8+
<IsPackable>false</IsPackable>
9+
</PropertyGroup>
10+
<ItemGroup>
11+
<PackageReference Include="xunit" Version="2.9.3" />
12+
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5">
13+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
14+
<PrivateAssets>all</PrivateAssets>
15+
</PackageReference>
16+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.3.0" />
17+
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
18+
<PackageReference Include="AwesomeAssertions" Version="9.4.0" />
19+
</ItemGroup>
20+
<ItemGroup>
21+
<ProjectReference Include="..\ProcessMemoryDataFinder\ProcessMemoryDataFinder.csproj" />
22+
<ProjectReference Include="..\OsuMemoryDataProvider\OsuMemoryDataProvider.csproj" />
23+
</ItemGroup>
24+
<ItemGroup>
25+
<None Update="Snapshots\osu_snapshot_baseline.json">
26+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
27+
</None>
28+
</ItemGroup>
29+
</Project>
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
using OsuMemoryDataProvider.IntegrationTests.TestHelpers;
2+
using OsuMemoryDataProvider.OsuMemoryModels;
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Diagnostics;
6+
using System.Linq;
7+
using System.Threading.Tasks;
8+
using Xunit;
9+
using Xunit.Abstractions;
10+
11+
namespace OsuMemoryDataProvider.IntegrationTests;
12+
13+
[Collection("Integration Tests")]
14+
public class PerformanceTests
15+
{
16+
private readonly ITestOutputHelper _output;
17+
private readonly StructuredOsuMemoryReader _reader;
18+
private readonly TimeSpan _testDuration = TimeSpan.FromSeconds(2);
19+
private readonly TimeSpan _readDelay = TimeSpan.FromMilliseconds(33);
20+
21+
public PerformanceTests(ITestOutputHelper output, TestFixture fixture)
22+
{
23+
_output = output;
24+
_reader = fixture.Reader;
25+
}
26+
27+
[Fact]
28+
public async Task Performance_ReadBaseAddresses_MeasuresTiming()
29+
{
30+
if (!_reader.OsuAvaliable(_output))
31+
{
32+
return;
33+
}
34+
35+
// Warmup
36+
OsuBaseAddresses baseAddresses = new();
37+
const int warmupCalls = 100;
38+
for (int i = 0; i < warmupCalls; i++)
39+
{
40+
_reader.ReadAll(baseAddresses);
41+
42+
await Task.Delay(_readDelay);
43+
}
44+
45+
// Measured runs
46+
Stopwatch stopwatch = new();
47+
List<double> readTimes = [];
48+
49+
int iterations = 0;
50+
DateTimeOffset startedAt = DateTimeOffset.UtcNow;
51+
DateTimeOffset endsAt = startedAt.Add(_testDuration);
52+
stopwatch.Start();
53+
54+
while (DateTimeOffset.UtcNow < endsAt)
55+
{
56+
stopwatch.Restart();
57+
58+
_reader.ReadAll(baseAddresses);
59+
60+
double readTimeMs = stopwatch.ElapsedTicks / (double)TimeSpan.TicksPerMillisecond;
61+
readTimes.Add(readTimeMs);
62+
iterations++;
63+
await Task.Delay(_readDelay);
64+
}
65+
66+
stopwatch.Stop();
67+
68+
double minTime = readTimes.Min();
69+
double maxTime = readTimes.Max();
70+
double avgTime = readTimes.Average();
71+
72+
_output.WriteLine($"Performance Test Results:");
73+
_output.WriteLine($" Total Duration: {_testDuration.TotalSeconds:F2}s");
74+
_output.WriteLine($" Warmup iterations: {warmupCalls}");
75+
_output.WriteLine($" Measured iterations: {iterations}");
76+
_output.WriteLine($" Delay between reads: {_readDelay.TotalMilliseconds}ms");
77+
_output.WriteLine($" Min Read Time: {minTime:F2}ms");
78+
_output.WriteLine($" Max Read Time: {maxTime:F2}ms");
79+
_output.WriteLine($" Avg Read Time: {avgTime:F2}ms");
80+
81+
}
82+
}
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
using AwesomeAssertions;
2+
using OsuMemoryDataProvider.IntegrationTests.TestHelpers;
3+
using OsuMemoryDataProvider.OsuMemoryModels.Abstract;
4+
using OsuMemoryDataProvider.OsuMemoryModels.Direct;
5+
using System.Collections.Generic;
6+
using Xunit;
7+
8+
namespace OsuMemoryDataProvider.IntegrationTests;
9+
10+
[Collection(TestCollection.Name)]
11+
public class PropertyTypeTests
12+
{
13+
private readonly TestFixture _fixture;
14+
15+
public PropertyTypeTests(TestFixture fixture)
16+
{
17+
_fixture = fixture;
18+
}
19+
20+
[Fact]
21+
public void ReadInt_Succeeds()
22+
{
23+
CurrentBeatmap beatmap = new();
24+
bool result = _fixture.Reader.TryReadProperty(beatmap, nameof(CurrentBeatmap.Id), out object value);
25+
26+
Assert.True(result);
27+
_ = Assert.IsType<int>(value);
28+
Assert.True((int)value > 0);
29+
}
30+
31+
[Fact]
32+
public void ReadShort_Succeeds()
33+
{
34+
CurrentBeatmap beatmap = new();
35+
bool result = _fixture.Reader.TryReadProperty(beatmap, nameof(CurrentBeatmap.Status), out object value);
36+
37+
Assert.True(result);
38+
_ = Assert.IsType<short>(value);
39+
}
40+
41+
[Fact]
42+
public void ReadFloat_Succeeds()
43+
{
44+
CurrentBeatmap beatmap = new();
45+
bool result = _fixture.Reader.TryReadProperty(beatmap, nameof(CurrentBeatmap.Ar), out object value);
46+
47+
Assert.True(result);
48+
_ = Assert.IsType<float>(value);
49+
Assert.True((float)value is >= 0 and <= 10);
50+
}
51+
52+
[Fact]
53+
public void ReadDouble_Succeeds()
54+
{
55+
GeneralData generalData = new();
56+
bool result = _fixture.Reader.TryReadProperty(generalData, nameof(GeneralData.TotalAudioTime), out object value);
57+
58+
Assert.True(result);
59+
double doubleValue = Assert.IsType<double>(value);
60+
_ = doubleValue.Should().BeInRange(0, 500_000);
61+
}
62+
63+
[Fact]
64+
public void ReadBool_Succeeds()
65+
{
66+
GeneralData generalData = new();
67+
bool result = _fixture.Reader.TryReadProperty(generalData, nameof(GeneralData.ChatIsExpanded), out object value);
68+
69+
Assert.True(result);
70+
_ = Assert.IsType<bool>(value);
71+
}
72+
73+
[Fact]
74+
public void ReadString_Succeeds()
75+
{
76+
CurrentBeatmap beatmap = new();
77+
bool result = _fixture.Reader.TryReadProperty(beatmap, nameof(CurrentBeatmap.MapString), out object value);
78+
79+
Assert.True(result);
80+
_ = Assert.IsType<string>(value);
81+
Assert.NotNull(value);
82+
Assert.NotEmpty((string)value);
83+
}
84+
85+
[Fact]
86+
public void ReadNullableInt_Succeeds()
87+
{
88+
SongSelectionScores songSelectionScores = new();
89+
bool result = _fixture.Reader.TryReadProperty(songSelectionScores, nameof(SongSelectionScores.AmountOfScores), out object value);
90+
91+
Assert.True(result);
92+
_ = Assert.IsType<int>(value);
93+
}
94+
95+
[Fact]
96+
public void ReadCustomObject_Succeeds()
97+
{
98+
Player player = new();
99+
_ = _fixture.Reader.TryRead(player);
100+
101+
Assert.NotNull(player.Mods);
102+
_ = Assert.IsType<Mods>(player.Mods);
103+
}
104+
105+
[Fact]
106+
public void ReadListInt_Succeeds()
107+
{
108+
Player player = new();
109+
_ = _fixture.Reader.TryRead(player);
110+
111+
if (player.HitErrors != null)
112+
{
113+
_ = Assert.IsType<System.Collections.Generic.List<int>>(player.HitErrors);
114+
Assert.Empty(player.HitErrors);
115+
}
116+
}
117+
118+
[Fact]
119+
public void ReadListPlayerScore_Succeeds()
120+
{
121+
SongSelectionScores songSelectionScores = new();
122+
_ = _fixture.Reader.TryRead(songSelectionScores);
123+
List<PlayerScore> scores = songSelectionScores.Scores;
124+
125+
Assert.NotNull(scores);
126+
_ = Assert.IsType<System.Collections.Generic.List<PlayerScore>>(scores);
127+
Assert.True(scores.Count > 0);
128+
}
129+
130+
[Fact]
131+
public void ReadListMultiplayerPlayer_Succeeds()
132+
{
133+
LeaderBoard leaderboard = new();
134+
_ = _fixture.Reader.TryRead(leaderboard);
135+
List<MultiplayerPlayer> players = leaderboard.Players;
136+
137+
Assert.NotNull(players);
138+
_ = Assert.IsType<System.Collections.Generic.List<MultiplayerPlayer>>(players);
139+
}
140+
}

0 commit comments

Comments
 (0)