Skip to content

Commit 76929de

Browse files
committed
Support request with ruleset name
1 parent b862fb1 commit 76929de

5 files changed

Lines changed: 76 additions & 27 deletions

File tree

PerformanceServer/BeatmapDifficultyController.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,13 @@
1111

1212
namespace PerformanceServer
1313
{
14-
public class DifficultyRequestBody
14+
public class DifficultyRequestBody : INeedsRuleset
1515
{
1616
[JsonProperty("beatmap_id")] public int BeatmapId { get; set; }
1717
[JsonProperty("checksum")] public string? Checksum { get; set; }
1818
[JsonProperty("mods")] public List<APIMod> Mods { get; set; } = [];
1919
[JsonProperty("ruleset_id")] public int? RulesetId { get; set; }
20+
[JsonProperty("ruleset")] public string? RulesetName { get; set; }
2021
[JsonProperty("beatmap_file")] public string? BeatmapFile { get; set; }
2122
}
2223

@@ -51,8 +52,7 @@ public async Task<ActionResult<DifficultyAttributes>> CalculateDifficulty([FromB
5152
}
5253
}
5354

54-
int rulesetId = body.RulesetId ?? workingBeatmap.BeatmapInfo.Ruleset.OnlineID;
55-
Ruleset ruleset = Helper.GetRuleset(rulesetId);
55+
Ruleset ruleset = Helper.GetRuleset(body, workingBeatmap.BeatmapInfo.Ruleset.OnlineID);
5656
Mod[] mods = body.Mods.Select(m => m.ToMod(ruleset)).ToArray();
5757
DifficultyAttributes? difficultyAttributes =
5858
ruleset.CreateDifficultyCalculator(workingBeatmap).Calculate(mods);
@@ -61,7 +61,7 @@ public async Task<ActionResult<DifficultyAttributes>> CalculateDifficulty([FromB
6161
JsonConvert.DeserializeObject<Dictionary<string, object>>(
6262
JsonConvert.SerializeObject(difficultyAttributes));
6363
if (attr != null)
64-
attr["ruleset_id"] = rulesetId;
64+
attr["ruleset"] = ruleset.ShortName;
6565

6666
return attr != null ? Ok(attr) : BadRequest("Failed to calculate difficulty.");
6767
}

PerformanceServer/Helper.cs

Lines changed: 41 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,27 +12,53 @@ namespace PerformanceServer
1212
{
1313
public static class Helper
1414
{
15-
public static Ruleset GetRuleset(int rulesetId)
15+
public static Ruleset GetRuleset(INeedsRuleset body, int defaultRulesetId = -1)
1616
{
17-
switch (rulesetId)
17+
Ruleset ruleset;
18+
if (!string.IsNullOrEmpty(body.RulesetName))
1819
{
19-
default:
20-
throw new ArgumentException("Invalid ruleset ID provided.");
21-
22-
case 0:
23-
return new OsuRuleset();
20+
ruleset = GetRuleset(body.RulesetName);
21+
}
22+
else if (body.RulesetId != null)
23+
{
24+
ruleset = GetRuleset(body.RulesetId.Value);
25+
}
26+
else if (defaultRulesetId >= -1)
27+
{
28+
ruleset = GetRuleset(defaultRulesetId);
29+
}
30+
else
31+
{
32+
throw new ArgumentException("No ruleset provided.");
33+
}
2434

25-
case 1:
26-
return new TaikoRuleset();
35+
return ruleset;
36+
}
2737

28-
case 2:
29-
return new CatchRuleset();
38+
public static Ruleset GetRuleset(int rulesetId)
39+
{
40+
return rulesetId switch
41+
{
42+
0 => GetRuleset("osu"),
43+
1 => GetRuleset("taiko"),
44+
2 => GetRuleset("fruits"),
45+
3 => GetRuleset("mania"),
46+
_ => throw new ArgumentException("Invalid ruleset ID provided.")
47+
};
48+
}
3049

31-
case 3:
32-
return new ManiaRuleset();
33-
}
50+
public static Ruleset GetRuleset(string shortName)
51+
{
52+
return shortName switch
53+
{
54+
"osu" => new OsuRuleset(),
55+
"taiko" => new TaikoRuleset(),
56+
"fruits" or "catch" => new CatchRuleset(),
57+
"mania" => new ManiaRuleset(),
58+
_ => throw new ArgumentException("Invalid ruleset name provided.")
59+
};
3460
}
35-
61+
3662
public static string ComputeMd5(string input)
3763
{
3864
byte[] inputBytes = System.Text.Encoding.UTF8.GetBytes(input);

PerformanceServer/INeedsRuleset.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Copyright (c) 2025 GooGuTeam
2+
// Licensed under the MIT Licence. See the LICENCE file in the repository root for full licence text.
3+
4+
using Newtonsoft.Json;
5+
6+
namespace PerformanceServer
7+
{
8+
public interface INeedsRuleset
9+
{
10+
[JsonProperty("ruleset_id")] int? RulesetId { get; set; }
11+
[JsonProperty("ruleset")] string? RulesetName { get; set; }
12+
}
13+
}

PerformanceServer/PerformanceController.cs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,15 @@
1313

1414
namespace PerformanceServer
1515
{
16-
public class PerformanceRequestBody
16+
public class PerformanceRequestBody : INeedsRuleset
1717
{
1818
[JsonProperty("beatmap_id")] public int BeatmapId { get; set; }
1919
[JsonProperty("checksum")] public string? Checksum { get; set; }
2020
[JsonProperty("mods")] public List<APIMod> Mods { get; set; } = [];
2121
[JsonProperty("is_legacy")] public bool IsLegacy { get; set; }
2222
[JsonProperty("accuracy")] public float Accuracy { get; set; }
23-
[JsonProperty("ruleset_id")] public int RulesetId { get; set; }
23+
[JsonProperty("ruleset_id")] public int? RulesetId { get; set; }
24+
[JsonProperty("ruleset")] public string? RulesetName { get; set; }
2425
[JsonProperty("combo")] public int Combo { get; set; }
2526
[JsonProperty("statistics")] public Dictionary<HitResult, int> Statistics { get; set; } = new();
2627
[JsonProperty("beatmap_file")] public string? BeatmapFile { get; set; }
@@ -39,7 +40,16 @@ public class PerformanceController : ControllerBase
3940
public async Task<ActionResult<PerformanceAttributes>> CalculatePerformance(
4041
[FromBody] PerformanceRequestBody body)
4142
{
42-
Ruleset ruleset = Helper.GetRuleset(body.RulesetId);
43+
Ruleset ruleset;
44+
try
45+
{
46+
ruleset = Helper.GetRuleset(body);
47+
}
48+
catch (ArgumentException e)
49+
{
50+
return BadRequest(e.Message);
51+
}
52+
4353
List<Mod> mods = body.Mods.Select(m => m.ToMod(ruleset)).ToList();
4454
if (body.IsLegacy && !mods.Any(m => m is ModClassic))
4555
{
@@ -51,7 +61,6 @@ public async Task<ActionResult<PerformanceAttributes>> CalculatePerformance(
5161
ScoreInfo scoreInfo = new()
5262
{
5363
IsLegacyScore = body.IsLegacy,
54-
Ruleset = new RulesetInfo { OnlineID = body.RulesetId },
5564
BeatmapInfo = new BeatmapInfo { OnlineID = body.BeatmapId },
5665
Statistics = body.Statistics,
5766
Mods = mods.ToArray(),

README.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ Request JSON fields:
4040
- `checksum` (string, optional) MD5 hash used to validate local cached file; if mismatch triggers re-download.
4141
- `mods` (array, optional) List of osu! API mod objects (typically at least `{ "acronym": "HD" }`). Unsupported or invalid acronyms will be ignored by the underlying conversion if not recognized.
4242
- `ruleset_id` (int, optional) Override ruleset; if omitted and beatmap file is provided/decoded it falls back to the beatmap's own ruleset.
43+
- `ruleset_name` (string, optional) Same as above but by name (e.g., `osu`, `taiko`, `fruits`, `mania`). If both ID and name are provided, Name takes precedence.
4344
- `beatmap_file` (string, optional) Raw `.osu` file content (entire file). If present, `beatmap_id` may still be supplied for caching, but content is authoritative.
4445

4546
Example request using inline file content:
@@ -66,7 +67,8 @@ Request JSON fields:
6667
- `mods` (array) Same structure as above.
6768
- `is_legacy` (bool) Whether to treat score as stable scores.
6869
- `accuracy` (float) Accuracy value (0.0–1.0). Provide either accurate `statistics` or a suitable accuracy.
69-
- `ruleset_id` (int) Explicit ruleset selection (required here because score construction needs it up-front unless you infer from map; server expects this field).
70+
- `ruleset_id` (int, optional) Explicit ruleset selection (Either `ruleset_id` or `ruleset_name` must be provided).
71+
- `ruleset_name` (string, optional) Same as above but by name (e.g., `osu`, `taiko`, `fruits`, `mania`). If both ID and name are provided, Name takes precedence.
7072
- `combo` (int) Achieved max combo for the score.
7173
- `statistics` (object) Mapping of hit result enum names to counts. Keys must match `HitResult` enumeration names from osu! (e.g., `great`, `ok`, `meh`, `miss`, `perfect`, `good`, etc. — varies by ruleset). Only relevant ones need to be present.
7274

@@ -88,10 +90,9 @@ Minimal example:
8890
}
8991
```
9092

91-
Successful Response: `200 OK` with a JSON `PerformanceAttributes` object with `ruleset_id` including (fields differ by ruleset):
92-
- `total` (float) Total pp value.
93+
Successful Response: `200 OK` with a JSON `PerformanceAttributes` object with `ruleset` (name) including (fields differ by ruleset):
94+
- `pp` (float) Total pp value.
9395
- Component breakdown fields (aim, speed, flashlight, accuracy, strain, etc.)
94-
- Embedded `DifficultyAttributes` subset depending on version
9596

9697
Error Cases:
9798
- `400 Bad Request` – Calculation failed (e.g., malformed beatmap or inconsistent inputs).

0 commit comments

Comments
 (0)