public static async IAsyncEnumerable <DbScore> GetScores( SoraDbContext ctx, string fileMd5, DbUser user, PlayMode playMode = PlayMode.Osu, bool friendsOnly = false, bool countryOnly = false, bool modOnly = false, Mod mods = Mod.None, bool onlySelf = false ) { const CountryId cid = CountryId.XX; /* Legacy Code * if (countryOnly) * cid = UserStats.GetUserStats(factory, user.Id).CountryId; */ var query = ctx.Scores .Where(score => score.FileMd5 == fileMd5 && score.PlayMode == playMode) .Where( score => !friendsOnly || ctx.Friends .Where(f => f.UserId == user.Id) .Select(f => f.FriendId) .Contains(score.UserId) ) /* Legacy Code TODO: Implement * .Where( * score * => !countryOnly || factory.Get().UserStats * .Select(c => c.CountryId) * .Contains(cid) * ) */ .Where(score => !modOnly || score.Mods == mods) .Where(score => !onlySelf || score.UserId == user.Id) .OrderByDescending(score => score.TotalScore) .Include(x => x.ScoreOwner) .ToList() .GroupBy(s => s.UserId) // TODO: this is stupid. but we cant change it :c .Select(s => s.First()) .Take(50); foreach (var dbScore in query) { yield return(dbScore); } }
public async Task <IActionResult> GetScoreResult( [FromQuery(Name = "v")] ScoreboardType type, [FromQuery(Name = "c")] string fileMd5, [FromQuery(Name = "f")] string f, [FromQuery(Name = "m")] PlayMode playMode, [FromQuery(Name = "i")] int i, [FromQuery(Name = "mods")] Mod mods, [FromQuery(Name = "us")] string us, [FromQuery(Name = "ha")] string pa, [FromServices] IServiceProvider serviceProvider, [FromServices] SoraDbContext ctx, [FromServices] DbContextPool <SoraDbContext> ctxPool, [FromServices] Pisstaube pisstaube, [FromServices] Cache cache) { try { var dbUser = await DbUser.GetDbUser(ctx, us); if (dbUser?.IsPassword(pa) != true) { return(Ok("error: pass")); } var cacheHash = Hex.ToHex( Crypto.GetMd5( $"{fileMd5}{playMode}{mods}{type}{dbUser.Id}{dbUser.UserName}" ) ); if (cache.TryGet($"sora:Scoreboards:{cacheHash}", out string cachedData)) { return(Ok(cachedData)); } var scores = DbScore.GetScores(ctx, fileMd5, dbUser, playMode, type == ScoreboardType.Friends, type == ScoreboardType.Country, type == ScoreboardType.Mods, mods); BeatmapSet set = null; DbScore ownScore = null; var beatmap = DbBeatmap.GetBeatmap(ctx, fileMd5); if (beatmap == null) { var apiSet = await pisstaube.FetchBeatmapSetAsync(fileMd5); if (apiSet == null) { goto JustContinue; } var beatmaps = DbBeatmap.FromBeatmapSet(apiSet).ToList(); var beatmapChecksums = beatmaps.Select(s => s.FileMd5); var dbBeatmaps = ctx.Beatmaps.Where(rset => beatmapChecksums.Any(lFileMd5 => rset.FileMd5 == lFileMd5)) .ToList(); var pool = serviceProvider.GetRequiredService <DbContextPool <SoraDbContext> >(); Task.WaitAll(beatmaps.Select(rawBeatmap => Task.Run(async() => { var context = pool.Rent(); try { var dbBeatmap = dbBeatmaps.FirstOrDefault(s => s.FileMd5 == rawBeatmap.FileMd5); if (dbBeatmap != null && (dbBeatmap.Flags & DbBeatmapFlags.RankedFreeze) != 0) { rawBeatmap.RankedStatus = dbBeatmap.RankedStatus; rawBeatmap.Flags = dbBeatmap.Flags; } context.Beatmaps.AddOrUpdate(rawBeatmap); await context.SaveChangesAsync(); } finally { pool.Return(context); } })).ToArray()); beatmap = beatmaps.FirstOrDefault(s => s.FileMd5 == fileMd5); } await foreach (var score in DbScore.GetScores(ctx, fileMd5, dbUser, playMode, false, false, false, mods, true)) { ownScore = score; break; } if (beatmap != null) { set = new BeatmapSet { SetID = beatmap.Id, Artist = beatmap.Artist, Title = beatmap.Title, RankedStatus = beatmap.RankedStatus, ChildrenBeatmaps = new List <Beatmap> { new Beatmap { FileMD5 = beatmap.FileMd5, DiffName = beatmap.DiffName, ParentSetID = beatmap.SetId, BeatmapID = beatmap.Id, Mode = beatmap.PlayMode, }, }, } } ; JustContinue: var sboard = new Scoreboard(set?.ChildrenBeatmaps.FirstOrDefault(bm => bm.FileMD5 == fileMd5), set, scores, ownScore); cache.Set($"sora:Scoreboards:{cacheHash}", cachedData = await sboard.ToOsuString(ctxPool), TimeSpan.FromSeconds(30)); return(Ok(cachedData)); } catch (Exception ex) { Logger.Err(ex); return(Ok("Failed")); } }