Skip to content

Commit 3c5a088

Browse files
authored
Merge pull request #180 from winnerspiros/fix/ci-code-quality-daily-challenge-17383618337664907011
Fix CI and Code Quality issues in Daily Challenge
2 parents 81c8857 + 4469927 commit 3c5a088

30 files changed

Lines changed: 769 additions & 83 deletions

debug_ids.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import os
2+
3+
test_path = 'osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs'
4+
with open(test_path, 'r') as f:
5+
lines = f.readlines()
6+
7+
for i, line in enumerate(lines):
8+
if 'assertItemInQueueListStep' in line or 'addItemStep' in line:
9+
print(f"{i+1}: {line.strip()}")

final_cleanup.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import re
2+
3+
def fix_file(path, pattern, replacement):
4+
with open(path, 'r') as f:
5+
content = f.read()
6+
new_content = re.sub(pattern, replacement, content, flags=re.MULTILINE | re.DOTALL)
7+
with open(path, 'w') as f:
8+
f.write(new_content)
9+
10+
# 1. Fix TestMultiplayerClient spacing and duplicates
11+
fix_file('osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs',
12+
r'\s+private T clone<T>\(T incoming\).*?return result;\s+\}',
13+
'\n\n private T clone<T>(T incoming)\n {\n byte[] serialized = MessagePackSerializer.Serialize(typeof(T), incoming, SignalRUnionWorkaroundResolver.OPTIONS);\n var result = MessagePackSerializer.Deserialize<T>(serialized, SignalRUnionWorkaroundResolver.OPTIONS);\n\n if (incoming is MultiplayerRoomUser sourceUser && result is MultiplayerRoomUser targetUser)\n targetUser.User = sourceUser.User;\n\n if (incoming is MultiplayerRoom sourceRoom && result is MultiplayerRoom targetRoom)\n {\n foreach (var user in targetRoom.Users)\n user.User = sourceRoom.Users.FirstOrDefault(u => u.UserID == user.UserID)?.User;\n\n if (targetRoom.Host != null)\n targetRoom.Host.User = sourceRoom.Host?.User;\n }\n else if (incoming is MultiplayerRoomUser sourceSingleUser && result is MultiplayerRoomUser targetSingleUser)\n {\n targetSingleUser.User = sourceSingleUser.User;\n }\n\n return result;\n }')
14+
15+
# 2. Fix updatePlaylistOrder indentation
16+
fix_file('osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs',
17+
r'orderedActiveItems = itemsByPriority\s+\.OrderBy',
18+
'orderedActiveItems = itemsByPriority\n .OrderBy')
19+
20+
# 3. Fix GameplayWarmupScreen unnecessary using
21+
fix_file('osu.Game/Screens/OnlinePlay/Matchmaking/RankedPlay/GameplayWarmupScreen.cs',
22+
r'using osu\.Framework\.Logging;\s+',
23+
'')
24+
25+
# 4. Fix PlayerPanelOverlay null check simplification
26+
fix_file('osu.Game/Screens/OnlinePlay/Matchmaking/Match/PlayerPanelOverlay.cs',
27+
r'if \(panels\.FirstOrDefault\(p => p\.RoomUser\.Equals\(user\)\) is PlayerPanel panel\) panel\.HasQuit = true;',
28+
'var panel = panels.FirstOrDefault(p => p.RoomUser.Equals(user));\n if (panel != null) panel.HasQuit = true;')

final_cleanup_v2.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import re
2+
3+
def fix_file(path, pattern, replacement):
4+
with open(path, 'r') as f:
5+
content = f.read()
6+
new_content = re.sub(pattern, replacement, content, flags=re.MULTILINE | re.DOTALL)
7+
if new_content == content:
8+
print(f"Warning: No change to {path}")
9+
with open(path, 'w') as f:
10+
f.write(new_content)
11+
12+
# DailyChallenge.cs cleanup
13+
# presentScore
14+
fix_file('osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallenge.cs',
15+
r'private void presentScore\(long id\).*?\{.*?if \(this\.IsCurrentScreen\(\) && playlistItem != null\).*?this\.Push\(new PlaylistItemScoreResultsScreen\(id, room\.RoomID \?\? 0, playlistItem\)\);.*?\}',
16+
''' private void presentScore(long id)
17+
{
18+
if (this.IsCurrentScreen() && playlistItem != null)
19+
this.Push(new PlaylistItemScoreResultsScreen(id, room.RoomID ?? 0, playlistItem));
20+
}''')
21+
22+
# updateMods
23+
fix_file('osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallenge.cs',
24+
r'private void updateMods\(\).*?\{.*?if \(!this\.IsCurrentScreen\(\) \|\| playlistItem == null\).*?return;.*?Mods\.Value = userMods\.Value\.Concat\(playlistItem\.RequiredMods\.Select\(m => m\.ToMod\(Ruleset\.Value\.CreateInstance\(\)\)\)\)\.ToList\(\);.*?\}',
25+
''' private void updateMods()
26+
{
27+
if (!this.IsCurrentScreen() || playlistItem == null)
28+
return;
29+
30+
Mods.Value = userMods.Value.Concat(playlistItem.RequiredMods.Select(m => m.ToMod(Ruleset.Value.CreateInstance()))).ToList();
31+
}''')
32+
33+
# startPlay
34+
fix_file('osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallenge.cs',
35+
r'private void startPlay\(\).*?\{.*?sampleStart\?\.Play\(\);.*?var item = playlistItem;.*?if \(item == null\) return;.*?this\.Push\(new PlayerLoader\(\(\) => new DailyChallengePlayer\(room, item\).*?\{.*?Exited = \(\) => Scheduler\.AddOnce\(\(\) => leaderboard\.RefetchScores\(\)\).*?\}\)\);.*?\}',
36+
''' private void startPlay()
37+
{
38+
sampleStart?.Play();
39+
40+
if (playlistItem == null)
41+
return;
42+
43+
this.Push(new PlayerLoader(() => new DailyChallengePlayer(room, playlistItem)
44+
{
45+
Exited = () => Scheduler.AddOnce(() => leaderboard.RefetchScores())
46+
}));
47+
}''')
48+
49+
# PresentBeatmap
50+
fix_file('osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallenge.cs',
51+
r'public void PresentBeatmap\(WorkingBeatmap beatmap, RulesetInfo ruleset\).*?\{.*?if \(!this\.IsCurrentScreen\(\)\).*?return;.*?var item = playlistItem;.*?if \(item == null\) return;.*?if \(item\.Beatmap\.BeatmapSet != null && beatmap\.BeatmapSetInfo\.OnlineID != item\.Beatmap\.BeatmapSet\.OnlineID\).*?\{.*?this\.Exit\(\);.*?game\?\.PresentBeatmap\(beatmap\.BeatmapSetInfo, b => b\.ID == beatmap\.BeatmapInfo\.ID\);.*?\}.*?\}',
52+
''' public void PresentBeatmap(WorkingBeatmap beatmap, RulesetInfo ruleset)
53+
{
54+
if (!this.IsCurrentScreen() || playlistItem == null)
55+
return;
56+
57+
// We can only handle the current daily challenge beatmap.
58+
// If the import was for a different beatmap, pass the duty off to global handling.
59+
if (playlistItem.Beatmap.BeatmapSet != null && beatmap.BeatmapSetInfo.OnlineID != playlistItem.Beatmap.BeatmapSet.OnlineID)
60+
{
61+
this.Exit();
62+
game?.PresentBeatmap(beatmap.BeatmapSetInfo, b => b.ID == beatmap.BeatmapInfo.ID);
63+
}
64+
65+
// And if we're handling, we don't really have much to do here.
66+
}''')

final_fix.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import os
2+
import re
3+
4+
def patch_file(path, old, new):
5+
if not os.path.exists(path):
6+
return
7+
with open(path, 'r') as f:
8+
content = f.read()
9+
if old in content:
10+
with open(path, 'w') as f:
11+
f.write(content.replace(old, new))
12+
else:
13+
# Try regex if literal fails
14+
new_content = re.sub(re.escape(old).replace(r'\ ', r'\s+'), new, content, flags=re.MULTILINE | re.DOTALL)
15+
if new_content != content:
16+
with open(path, 'w') as f:
17+
f.write(new_content)
18+
else:
19+
print(f"Warning: '{old}' not found in {path}")
20+
21+
# 1. Fix GameplayWarmupScreen IDE0074 (compound assignment)
22+
gw_path = 'osu.Game/Screens/OnlinePlay/Matchmaking/RankedPlay/GameplayWarmupScreen.cs'
23+
old_gw = """ if (card == null)
24+
{
25+
// Played card was not on the screen.
26+
27+
card = new RankedPlayCard(matchInfo.LastPlayedCard)"""
28+
new_gw = """ card ??= new RankedPlayCard(matchInfo.LastPlayedCard)
29+
{
30+
// Played card was not on the screen."""
31+
# Wait, the braces are different. Let's look at the original code.

fix_bot_feedback.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import sys
2+
3+
# 1. Fix DailyChallenge.cs
4+
with open('osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallenge.cs', 'r') as f:
5+
content = f.read()
6+
7+
# Fix redundant conditional access
8+
content = content.replace('if (item?.AllowedMods.Any() == true)', 'if (item.AllowedMods.Any())')
9+
10+
# 2. Fix GameplayWarmupScreen.cs line breaks
11+
with open('osu.Game/Screens/OnlinePlay/Matchmaking/RankedPlay/GameplayWarmupScreen.cs', 'r') as f:
12+
gw_content = f.read()
13+
14+
old_ternary = 'Children = beatmap == null ? System.Array.Empty<Drawable>() : ['
15+
new_ternary = 'Children = beatmap == null\n ? System.Array.Empty<Drawable>()\n : ['
16+
17+
gw_content = gw_content.replace(old_ternary, new_ternary)
18+
19+
with open('osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallenge.cs', 'w') as f:
20+
f.write(content)
21+
22+
with open('osu.Game/Screens/OnlinePlay/Matchmaking/RankedPlay/GameplayWarmupScreen.cs', 'w') as f:
23+
f.write(gw_content)

fix_client_and_tests.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import re
2+
3+
# 1. Update MultiplayerClient.cs to use UserID for LocalUser identification
4+
# Also improve null safety in room setup.
5+
with open('osu.Game/Online/Multiplayer/MultiplayerClient.cs', 'r') as f:
6+
content = f.read()
7+
8+
content = content.replace('public virtual MultiplayerRoomUser? LocalUser => Room?.Users.FirstOrDefault(u => u.UserID == API.LocalUser.Value.Id);',
9+
'public virtual MultiplayerRoomUser? LocalUser => Room?.Users.FirstOrDefault(u => u.UserID == API.LocalUser.Value.OnlineID);')
10+
11+
with open('osu.Game/Online/Multiplayer/MultiplayerClient.cs', 'w') as f:
12+
f.write(content)
13+
14+
# 2. Update TestRoomRequestsHandler.cs to preserve RoomID, StartDate, and EndDate
15+
with open('osu.Game/Tests/Visual/OnlinePlay/TestRoomRequestsHandler.cs', 'r') as f:
16+
handler_content = f.read()
17+
18+
old_clone_room = """ private Room cloneRoom(Room source)
19+
{
20+
var result = new Room();
21+
result.CopyFrom(source);
22+
result.RoomID = source.RoomID;
23+
result.StartDate = source.StartDate;
24+
result.EndDate = source.EndDate;
25+
result.Playlist = source.Playlist.Select(p => p.With()).ToList();
26+
return result;
27+
}"""
28+
29+
new_clone_room = """ private Room cloneRoom(Room source)
30+
{
31+
var result = new Room();
32+
result.CopyFrom(source);
33+
result.RoomID = source.RoomID;
34+
result.StartDate = source.StartDate;
35+
result.EndDate = source.EndDate;
36+
result.Host = source.Host;
37+
result.Playlist = source.Playlist.Select(p => p.With()).ToList();
38+
return result;
39+
}"""
40+
41+
handler_content = handler_content.replace(old_clone_room, new_clone_room)
42+
43+
with open('osu.Game/Tests/Visual/OnlinePlay/TestRoomRequestsHandler.cs', 'w') as f:
44+
f.write(handler_content)
45+
46+
# 3. Update TestScenePlayerPanelOverlay.cs assertions
47+
with open('osu.Game.Tests/Visual/Matchmaking/TestScenePlayerPanelOverlay.cs', 'r') as f:
48+
test_overlay_content = f.read()
49+
50+
test_overlay_content = test_overlay_content.replace('AddAssert("no panels quit", () => this.ChildrenOfType<PlayerPanel>().Count(p => p.HasQuit), () => Is.EqualTo(0));',
51+
'AddAssert("no panels quit", () => list.Panels.Count(p => p.HasQuit), () => Is.EqualTo(0));')
52+
53+
with open('osu.Game.Tests/Visual/Matchmaking/TestScenePlayerPanelOverlay.cs', 'w') as f:
54+
f.write(test_overlay_content)

fix_daily_challenge.py

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
import sys
2+
3+
with open('osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallenge.cs', 'r') as f:
4+
content = f.read()
5+
6+
# Fix presentScore
7+
old_present_score = """ private void presentScore(long id)
8+
{
9+
if (!this.IsCurrentScreen())
10+
var item = playlistItem;
11+
if (item == null) return;
12+
13+
var item = playlistItem;
14+
if (item != null)
15+
this.Push(new PlaylistItemScoreResultsScreen(id, (room.RoomID ?? 0), item));
16+
}"""
17+
18+
new_present_score = """ private void presentScore(long id)
19+
{
20+
if (!this.IsCurrentScreen())
21+
return;
22+
23+
var item = playlistItem;
24+
if (item == null) return;
25+
26+
this.Push(new PlaylistItemScoreResultsScreen(id, (room.RoomID ?? 0), item));
27+
}"""
28+
29+
# Fix updateMods
30+
old_update_mods = """ private void updateMods()
31+
{
32+
var item = playlistItem;
33+
if (item == null) return;
34+
return;
35+
36+
var item = playlistItem;
37+
if (item != null) Mods.Value = userMods.Value.Concat(item.RequiredMods.Select(m => m.ToMod(Ruleset.Value.CreateInstance()))).ToList();
38+
}"""
39+
40+
new_update_mods = """ private void updateMods()
41+
{
42+
if (!this.IsCurrentScreen())
43+
return;
44+
45+
var item = playlistItem;
46+
if (item == null) return;
47+
48+
Mods.Value = userMods.Value.Concat(item.RequiredMods.Select(m => m.ToMod(Ruleset.Value.CreateInstance()))).ToList();
49+
}"""
50+
51+
# Fix startPlay
52+
old_start_play = """ private void startPlay()
53+
{
54+
sampleStart?.Play();
55+
var item = playlistItem; if (item != null) this.Push(new PlayerLoader(() => new DailyChallengePlayer(room, item)
56+
{
57+
Exited = () => Scheduler.AddOnce(() => leaderboard.RefetchScores())
58+
}));
59+
}"""
60+
61+
new_start_play = """ private void startPlay()
62+
{
63+
sampleStart?.Play();
64+
65+
var item = playlistItem;
66+
if (item == null) return;
67+
68+
this.Push(new PlayerLoader(() => new DailyChallengePlayer(room, item)
69+
{
70+
Exited = () => Scheduler.AddOnce(() => leaderboard.RefetchScores())
71+
}));
72+
}"""
73+
74+
# Fix PresentBeatmap
75+
old_present_beatmap = """ public void PresentBeatmap(WorkingBeatmap beatmap, RulesetInfo ruleset)
76+
{
77+
var item = playlistItem;
78+
if (item == null) return;
79+
if (!this.IsCurrentScreen())
80+
return;
81+
82+
var item = playlistItem;
83+
84+
// We can only handle the current daily challenge beatmap.
85+
// If the import was for a different beatmap, pass the duty off to global handling.
86+
if (item?.Beatmap.BeatmapSet != null && beatmap.BeatmapSetInfo.OnlineID != item.Beatmap.BeatmapSet.OnlineID)
87+
{
88+
this.Exit();
89+
game?.PresentBeatmap(beatmap.BeatmapSetInfo, b => b.ID == beatmap.BeatmapInfo.ID);
90+
}
91+
92+
// And if we're handling, we don't really have much to do here.
93+
}"""
94+
95+
new_present_beatmap = """ public void PresentBeatmap(WorkingBeatmap beatmap, RulesetInfo ruleset)
96+
{
97+
if (!this.IsCurrentScreen())
98+
return;
99+
100+
var item = playlistItem;
101+
if (item == null) return;
102+
103+
// We can only handle the current daily challenge beatmap.
104+
// If the import was for a different beatmap, pass the duty off to global handling.
105+
if (item.Beatmap.BeatmapSet != null && beatmap.BeatmapSetInfo.OnlineID == item.Beatmap.BeatmapSet.OnlineID)
106+
return;
107+
108+
this.Exit();
109+
game?.PresentBeatmap(beatmap.BeatmapSetInfo, b => b.ID == beatmap.BeatmapInfo.ID);
110+
111+
// And if we're handling, we don't really have much to do here.
112+
}"""
113+
114+
content = content.replace(old_present_score, new_present_score)
115+
content = content.replace(old_update_mods, new_update_mods)
116+
content = content.replace(old_start_play, new_start_play)
117+
content = content.replace(old_present_beatmap, new_present_beatmap)
118+
119+
with open('osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallenge.cs', 'w') as f:
120+
f.write(content)

fix_daily_challenge_final.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
with open('osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallenge.cs', 'r') as f:
2+
content = f.read()
3+
4+
# Bot wants null propagation for:
5+
# var item = playlistItem;
6+
# if (item == null) return;
7+
# This pattern is used in: presentScore, updateMods, startPlay, PresentBeatmap.
8+
9+
# Since playlistItem is a field, we can use null-propagation directly in most cases.
10+
# However, for startPlay and PresentBeatmap we need to perform actions.
11+
12+
content = content.replace(''' private void presentScore(long id)
13+
{
14+
if (!this.IsCurrentScreen())
15+
return;
16+
17+
var item = playlistItem;
18+
if (item == null) return;
19+
20+
this.Push(new PlaylistItemScoreResultsScreen(id, (room.RoomID ?? 0), item));
21+
}''', ''' private void presentScore(long id)
22+
{
23+
if (this.IsCurrentScreen() && playlistItem != null)
24+
this.Push(new PlaylistItemScoreResultsScreen(id, room.RoomID ?? 0, playlistItem));
25+
}''')
26+
27+
content = content.replace(''' private void updateMods()
28+
{
29+
if (!this.IsCurrentScreen())
30+
return;
31+
32+
var item = playlistItem;
33+
if (item == null) return;
34+
35+
Mods.Value = userMods.Value.Concat(item.RequiredMods.Select(m => m.ToMod(Ruleset.Value.CreateInstance()))).ToList();
36+
}''', ''' private void updateMods()
37+
{
38+
if (!this.IsCurrentScreen() || playlistItem == null)
39+
return;
40+
41+
Mods.Value = userMods.Value.Concat(playlistItem.RequiredMods.Select(m => m.ToMod(Ruleset.Value.CreateInstance()))).ToList();
42+
}''')
43+
44+
with open('osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallenge.cs', 'w') as f:
45+
f.write(content)

0 commit comments

Comments
 (0)