Skip to content

Commit

Permalink
Merge pull request #310 from bdach/daily-challenge-medal-amendments
Browse files Browse the repository at this point in the history
Disallow daily challenge-related medals from being awarded on non-daily-challenge score submissions
  • Loading branch information
peppy authored Dec 31, 2024
2 parents 9ebca62 + 10626f6 commit 5abef77
Show file tree
Hide file tree
Showing 7 changed files with 162 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,25 @@ public void MedalNotAwardedIfNoDailyChallengesOnRecord()
}

[Fact]
public void MedalAwardedIfAtLeastOneDailyChallengeOnRecord()
public void MedalAwardedOnDailyChallengeCompletion()
{
using (var db = Processor.GetDatabaseConnection())
db.Execute("INSERT INTO `daily_challenge_user_stats` (`user_id`, `daily_streak_best`) VALUES (2, 1)");
SetScoreForBeatmap(beatmap.beatmap_id);

ulong roomId = CreateMultiplayerRoom("daily challenge", "playlists", "daily_challenge");
ulong playlistItemId = CreatePlaylistItem(beatmap, roomId);

SetMultiplayerScoreForBeatmap(beatmap.beatmap_id, playlistItemId);
AssertSingleMedalAwarded(336);
}

[Fact]
public void MedalNotAwardedOnRandomBeatmapCompletionWithPastDailyStreakOnRecord()
{
using (var db = Processor.GetDatabaseConnection())
db.Execute("INSERT INTO `daily_challenge_user_stats` (`user_id`, `daily_streak_best`) VALUES (2, 1)");
SetScoreForBeatmap(beatmap.beatmap_id);
AssertNoMedalsAwarded();
}
}
}
61 changes: 33 additions & 28 deletions osu.Server.Queues.ScoreStatisticsProcessor.Tests/DatabaseTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -99,40 +99,45 @@ protected ScoreItem SetScoreForBeatmap(uint beatmapId, Action<ScoreItem>? scoreS

scoreSetup?.Invoke(score);

conn.Execute("INSERT INTO `scores` (`id`, `user_id`, `ruleset_id`, `beatmap_id`, `has_replay`, `preserve`, `ranked`, "
+ "`rank`, `passed`, `accuracy`, `max_combo`, `total_score`, `data`, `pp`, `legacy_score_id`, `legacy_total_score`, "
+ "`started_at`, `ended_at`, `build_id`) "
+ "VALUES (@id, @user_id, @ruleset_id, @beatmap_id, @has_replay, @preserve, @ranked, "
+ "@rank, @passed, @accuracy, @max_combo, @total_score, @data, @pp, @legacy_score_id, @legacy_total_score,"
+ "@started_at, @ended_at, @build_id)",
new
{
score.Score.id,
score.Score.user_id,
score.Score.ruleset_id,
score.Score.beatmap_id,
score.Score.has_replay,
score.Score.preserve,
score.Score.ranked,
rank = score.Score.rank.ToString(),
score.Score.passed,
score.Score.accuracy,
score.Score.max_combo,
score.Score.total_score,
score.Score.data,
score.Score.pp,
score.Score.legacy_score_id,
score.Score.legacy_total_score,
score.Score.started_at,
score.Score.ended_at,
score.Score.build_id,
});
InsertScore(conn, score);
PushToQueueAndWaitForProcess(score);

return score;
}
}

protected static void InsertScore(MySqlConnection conn, ScoreItem score)
{
conn.Execute("INSERT INTO `scores` (`id`, `user_id`, `ruleset_id`, `beatmap_id`, `has_replay`, `preserve`, `ranked`, "
+ "`rank`, `passed`, `accuracy`, `max_combo`, `total_score`, `data`, `pp`, `legacy_score_id`, `legacy_total_score`, "
+ "`started_at`, `ended_at`, `build_id`) "
+ "VALUES (@id, @user_id, @ruleset_id, @beatmap_id, @has_replay, @preserve, @ranked, "
+ "@rank, @passed, @accuracy, @max_combo, @total_score, @data, @pp, @legacy_score_id, @legacy_total_score,"
+ "@started_at, @ended_at, @build_id)",
new
{
score.Score.id,
score.Score.user_id,
score.Score.ruleset_id,
score.Score.beatmap_id,
score.Score.has_replay,
score.Score.preserve,
score.Score.ranked,
rank = score.Score.rank.ToString(),
score.Score.passed,
score.Score.accuracy,
score.Score.max_combo,
score.Score.total_score,
score.Score.data,
score.Score.pp,
score.Score.legacy_score_id,
score.Score.legacy_total_score,
score.Score.started_at,
score.Score.ended_at,
score.Score.build_id,
});
}

private static ulong scoreIDSource;

protected void PushToQueueAndWaitForProcess(ScoreItem item)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Dapper;
using MySqlConnector;
using osu.Server.Queues.ScoreStatisticsProcessor.Models;
using osu.Server.Queues.ScoreStatisticsProcessor.Processors;
using Xunit;
Expand All @@ -29,6 +31,10 @@ protected MedalAwarderTest(AssemblyName[]? externalProcessorAssemblies = null)
db.Execute("TRUNCATE TABLE osu_beatmappacks_items");

db.Execute("TRUNCATE TABLE daily_challenge_user_stats");

db.Execute("TRUNCATE TABLE multiplayer_score_links");
db.Execute("TRUNCATE TABLE multiplayer_playlist_items");
db.Execute("TRUNCATE TABLE multiplayer_rooms");
}
}

Expand Down Expand Up @@ -89,6 +95,54 @@ private void onMedalAwarded(MedalProcessor.AwardedMedal awarded)
}
}

protected ulong CreateMultiplayerRoom(string roomName, string roomType, string roomCategory = "normal")
{
using var conn = Processor.GetDatabaseConnection();
return conn.QuerySingle<ulong>(
"INSERT INTO `multiplayer_rooms` (`name`, `type`, `category`) VALUES (@name, @type, @category); SELECT LAST_INSERT_ID();",
new
{
name = roomName,
type = roomType,
category = roomCategory,
});
}

protected ulong CreatePlaylistItem(Beatmap beatmap, ulong roomId)
{
using var conn = Processor.GetDatabaseConnection();
return conn.QuerySingle<ulong>(
"INSERT INTO `multiplayer_playlist_items` (`room_id`, `owner_id`, `beatmap_id`, `ruleset_id`) VALUES (@room_id, 1, @beatmap_id, @ruleset_id); SELECT LAST_INSERT_ID();",
new
{
beatmap_id = beatmap.beatmap_id,
ruleset_id = beatmap.playmode,
room_id = roomId,
});
}

protected ScoreItem SetMultiplayerScoreForBeatmap(uint beatmapId, ulong playlistItemId, Action<ScoreItem>? scoreSetup = null)
{
using (MySqlConnection conn = Processor.GetDatabaseConnection())
{
var score = CreateTestScore(beatmapId: beatmapId);

scoreSetup?.Invoke(score);

InsertScore(conn, score);
conn.Execute("INSERT INTO `multiplayer_score_links` (`user_id`, `playlist_item_id`, `score_id`) VALUES (@user_id, @playlist_item_id, @score_id)",
new
{
user_id = score.Score.user_id,
playlist_item_id = playlistItemId,
score_id = score.Score.id,
});
PushToQueueAndWaitForProcess(score);

return score;
}
}

public override void Dispose()
{
base.Dispose();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,21 +21,39 @@ public MonthlyShrubTest()
[InlineData(0)]
[InlineData(9)]
[InlineData(26)]
public void MedalNotAwardedIfNotEnoughDailyChallengesOnRecord(int dailyChallengeCount)
public void MedalNotAwardedIfNotEnoughDailyChallengesOnRecord(int bestStreak)
{
using (var db = Processor.GetDatabaseConnection())
db.Execute($"INSERT INTO `daily_challenge_user_stats` (`user_id`, `daily_streak_best`) VALUES (2, {dailyChallengeCount})");
db.Execute($"INSERT INTO `daily_challenge_user_stats` (`user_id`, `daily_streak_best`) VALUES (2, {bestStreak})");

ulong roomId = CreateMultiplayerRoom("daily challenge", "playlists", "daily_challenge");
ulong playlistItemId = CreatePlaylistItem(beatmap, roomId);

SetMultiplayerScoreForBeatmap(beatmap.beatmap_id, playlistItemId);
SetScoreForBeatmap(beatmap.beatmap_id);
AssertNoMedalsAwarded();
}

[Fact]
public void MedalAwardedIfAtLeastThirtyDailyChallengesOnRecord()
public void MedalAwardedOnDailyChallengeIfLongestHistoricalStreakAtLeastThirtyDays()
{
using (var db = Processor.GetDatabaseConnection())
db.Execute("INSERT INTO `daily_challenge_user_stats` (`user_id`, `daily_streak_best`) VALUES (2, 30)");
SetScoreForBeatmap(beatmap.beatmap_id);

ulong roomId = CreateMultiplayerRoom("daily challenge", "playlists", "daily_challenge");
ulong playlistItemId = CreatePlaylistItem(beatmap, roomId);

SetMultiplayerScoreForBeatmap(beatmap.beatmap_id, playlistItemId);
AssertSingleMedalAwarded(338);
}

[Fact]
public void MedalNotAwardedOutsideOfDailyChallengeEvenWithLongEnoughHistoricalBestStreak()
{
using (var db = Processor.GetDatabaseConnection())
db.Execute("INSERT INTO `daily_challenge_user_stats` (`user_id`, `daily_streak_best`) VALUES (2, 33)");
SetScoreForBeatmap(beatmap.beatmap_id);
AssertNoMedalsAwarded();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,21 +21,38 @@ public WeeklySaplingTest()
[InlineData(0)]
[InlineData(4)]
[InlineData(6)]
public void MedalNotAwardedIfNotEnoughDailyChallengesOnRecord(int dailyChallengeCount)
public void MedalNotAwardedIfNotEnoughDailyChallengesOnRecord(int bestStreak)
{
using (var db = Processor.GetDatabaseConnection())
db.Execute($"INSERT INTO `daily_challenge_user_stats` (`user_id`, `daily_streak_best`) VALUES (2, {dailyChallengeCount})");
SetScoreForBeatmap(beatmap.beatmap_id);
db.Execute($"INSERT INTO `daily_challenge_user_stats` (`user_id`, `daily_streak_best`) VALUES (2, {bestStreak})");

ulong roomId = CreateMultiplayerRoom("daily challenge", "playlists", "daily_challenge");
ulong playlistItemId = CreatePlaylistItem(beatmap, roomId);

SetMultiplayerScoreForBeatmap(beatmap.beatmap_id, playlistItemId);
AssertNoMedalsAwarded();
}

[Fact]
public void MedalAwardedIfAtLeastSevenDailyChallengesOnRecord()
public void MedalAwardedOnDailyChallengeIfLongestHistoricalStreakAtLeastSevenDays()
{
using (var db = Processor.GetDatabaseConnection())
db.Execute("INSERT INTO `daily_challenge_user_stats` (`user_id`, `daily_streak_best`) VALUES (2, 7)");
SetScoreForBeatmap(beatmap.beatmap_id);

ulong roomId = CreateMultiplayerRoom("daily challenge", "playlists", "daily_challenge");
ulong playlistItemId = CreatePlaylistItem(beatmap, roomId);

SetMultiplayerScoreForBeatmap(beatmap.beatmap_id, playlistItemId);
AssertSingleMedalAwarded(337);
}

[Fact]
public void MedalNotAwardedOutsideOfDailyChallengeEvenWithLongEnoughHistoricalBestStreak()
{
using (var db = Processor.GetDatabaseConnection())
db.Execute("INSERT INTO `daily_challenge_user_stats` (`user_id`, `daily_streak_best`) VALUES (2, 9)");
SetScoreForBeatmap(beatmap.beatmap_id);
AssertNoMedalsAwarded();
}
}
}
12 changes: 12 additions & 0 deletions osu.Server.Queues.ScoreStatisticsProcessor/Helpers/MedalHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,5 +101,17 @@ public static bool UserPassedPack(MedalAwarderContext context, bool noReductionM

return completed >= countForPack;
}

public static bool IsDailyChallengeScore(MedalAwarderContext context)
{
return context.Connection.QuerySingleOrDefault<string?>(
"""
SELECT `multiplayer_rooms`.`category` FROM `scores`
JOIN `multiplayer_score_links` ON `multiplayer_score_links`.`score_id` = `scores`.`id`
JOIN `multiplayer_playlist_items` ON `multiplayer_playlist_items`.`id` = `multiplayer_score_links`.`playlist_item_id`
JOIN `multiplayer_rooms` ON `multiplayer_rooms`.`id` = `multiplayer_playlist_items`.`room_id`
""",
transaction: context.Transaction) == "daily_challenge";
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System.Collections.Generic;
using JetBrains.Annotations;
using osu.Server.Queues.ScoreStatisticsProcessor.Helpers;
using osu.Server.Queues.ScoreStatisticsProcessor.Models;

namespace osu.Server.Queues.ScoreStatisticsProcessor.Processors.MedalAwarders
Expand All @@ -15,6 +16,9 @@ public class DailyChallengeMedalAwarder : IMedalAwarder

public IEnumerable<Medal> Check(IEnumerable<Medal> medals, MedalAwarderContext context)
{
if (!MedalHelpers.IsDailyChallengeScore(context))
yield break;

foreach (var medal in medals)
{
switch (medal.achievement_id)
Expand Down

0 comments on commit 5abef77

Please sign in to comment.