Esempio n. 1
0
        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)
        {
            try
            {
                var dbUser = await DbUser.GetDbUser(_ctx, us);
                var user = dbUser?.ToUser();
                if (dbUser?.IsPassword(pa) != true)
                    return Ok("error: pass");

                var cacheHash =
                    Hex.ToHex(
                        Crypto.GetMd5(
                            $"{fileMd5}{playMode}{mods}{type}{user.Id}{user.UserName}"
                        )
                    );

                if (_cache.TryGet($"sora:Scoreboards:{cacheHash}", out string cachedData))
                    return Ok(cachedData);

                var scores = await DbScore.GetScores(_ctx, fileMd5, dbUser, playMode,
                                                     type == ScoreboardType.Friends,
                                                     type == ScoreboardType.Country,
                                                     type == ScoreboardType.Mods,
                                                     mods);

                var beatmap = DbBeatmap.GetBeatmap(_ctx, fileMd5);
                BeatmapSet apiSet;
                if (beatmap == null)
                {
                    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 concurrentLock = new object();
                    var pool = serviceProvider.GetRequiredService<DbContextPool<SoraDbContext>>();
                    Task.WaitAll(beatmaps.Select(rawBeatmap => Task.Run(async () =>
                    {
                        var context = pool.Rent();
    
                        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();

                        pool.Return(context);
                    })).ToArray());
                    
                    beatmap = beatmaps.FirstOrDefault(s => s.FileMd5 == fileMd5);
                }

                JustContinue:
                var ownScore = await DbScore.GetLatestScore(_ctx, new DbScore
                {
                    FileMd5 = fileMd5,
                    UserId = user.Id,
                    PlayMode = playMode,
                    TotalScore = 0
                });

                var sScores = scores.Select(s => new Score
                {
                    Count100 = s.Count100,
                    Count50 = s.Count50,
                    Count300 = s.Count300,
                    Date = s.Date,
                    Mods = s.Mods,
                    CountGeki = s.CountGeki,
                    CountKatu = s.CountKatu,
                    CountMiss = s.CountMiss,
                    FileMd5 = s.FileMd5,
                    MaxCombo = s.MaxCombo,
                    PlayMode = s.PlayMode,
                    TotalScore = s.TotalScore,
                    UserId = s.UserId,
                    UserName = s.ScoreOwner.UserName,
                }).ToList();

                // Fetch the correct position for sScore
                for (var j = 0; j < scores.Count; j++)
                {
                    sScores[j].Position = await scores[j].Position(_ctx);
                }

                Score ownsScore = null;
                if (ownScore != null)
                    ownsScore = new Score
                    {
                        Count100 = ownScore.Count100,
                        Count50 = ownScore.Count50,
                        Count300 = ownScore.Count300,
                        Date = ownScore.Date,
                        Mods = ownScore.Mods,
                        CountGeki = ownScore.CountGeki,
                        CountKatu = ownScore.CountKatu,
                        CountMiss = ownScore.CountMiss,
                        FileMd5 = ownScore.FileMd5,
                        MaxCombo = ownScore.MaxCombo,
                        PlayMode = ownScore.PlayMode,
                        TotalScore = ownScore.TotalScore,
                        UserId = dbUser.Id,
                        UserName = dbUser.UserName,
                        Position = await ownScore.Position(_ctx)
                    };

                BeatmapSet set = null;
                
                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
                            }
                        }
                    };
                
                var sboard = new Scoreboard(set?.ChildrenBeatmaps.FirstOrDefault(bm => bm.FileMD5 == fileMd5),
                                            set, sScores, ownsScore);

                _cache.Set($"sora:Scoreboards:{cacheHash}", cachedData = sboard.ToOsuString(), TimeSpan.FromSeconds(30));
                return Ok(cachedData);
            }
            catch (Exception ex)
            {
                Logger.Err(ex);
                return Ok("Failed");
            }
        }
Esempio n. 2
0
        public async Task<IActionResult> PostSubmitModular()
        {
            if (!Directory.Exists("data/replays"))
                Directory.CreateDirectory("data/replays");
            
            string encScore = Request.Form["score"];
            string iv = Request.Form["iv"];
            string osuver = Request.Form["osuver"];
            string passwd = Request.Form["pass"];

            var (pass, score) = ScoreSubmissionParser.ParseScore(encScore, iv, osuver);
            var dbUser = await DbUser.GetDbUser(_ctx, score.UserName);

            if (dbUser == null)
                return Ok("error: pass");
            
            if (!dbUser.IsPassword(passwd))
                return Ok("error: pass");

            if (!_ps.TryGet(dbUser.Id, out var pr))
                return Ok("error: pass"); // User not logged in in Bancho!

            if (!pass || !RankedMods.IsRanked(score.Mods))
            {
                var lb = await DbLeaderboard.GetLeaderboardAsync(_ctx, dbUser);

                lb.IncreasePlaycount(score.PlayMode);
                lb.IncreaseScore((ulong) score.TotalScore, false, score.PlayMode);

                await lb.SaveChanges(_ctx);
                
                // Send to other People
                await _ev.RunEvent(
                    EventType.BanchoUserStatsRequest,
                    new BanchoUserStatsRequestArgs {UserIds = new List<int> {score.Id}, Pr = pr}
                );

                // Send to self
                await _ev.RunEvent(
                    EventType.BanchoSendUserStatus,
                    new BanchoSendUserStatusArgs {Status = pr.Status, Pr = pr}
                );

                return Ok("Thanks for your hard work! onii-chyan~"); // even though, we're Sora, we can still be cute!
            }

            var replayFileData = Request.Form.Files.GetFile("score");

            var dbScore = new DbScore
            {
                Accuracy = score.ComputeAccuracy(),
                Count100 = score.Count100,
                Count50 = score.Count50,
                Count300 = score.Count300,
                Date = score.Date,
                Mods = score.Mods,
                CountGeki = score.CountGeki,
                CountKatu = score.CountKatu,
                CountMiss = score.CountMiss,
                FileMd5 = score.FileMd5,
                MaxCombo = score.MaxCombo,
                PlayMode = score.PlayMode,
                ScoreOwner = dbUser,
                TotalScore = score.TotalScore,
                UserId = dbUser.Id
            };

            await _pisstaube.DownloadBeatmapAsync(dbScore.FileMd5);

            await using (var m = new MemoryStream())
            {
                replayFileData.CopyTo(m);
                m.Position = 0;
                dbScore.ReplayMd5 = Hex.ToHex(Crypto.GetMd5(m)) ?? string.Empty;
                if (!string.IsNullOrEmpty(dbScore.ReplayMd5))
                {
                    await using var replayFile = System.IO.File.Create($"data/replays/{dbScore.ReplayMd5}");
                    m.Position = 0;
                    m.WriteTo(replayFile);
                    m.Close();
                    replayFile.Close();
                }
            }

            dbScore.PerformancePoints = dbScore.ComputePerformancePoints();

            var oldScore = await DbScore.GetLatestScore(_ctx, dbScore);

            var oldLb = await DbLeaderboard.GetLeaderboardAsync(_ctx, dbScore.ScoreOwner);
            var oldStdPos = oldLb.GetPosition(_ctx, dbScore.PlayMode);
            
            var oldAcc = oldLb.GetAccuracy(_ctx, dbScore.PlayMode);
            double newAcc;

            if (oldScore != null && oldScore.TotalScore <= dbScore.TotalScore)
            {
                _ctx.Remove(oldScore);
                System.IO.File.Delete($"data/replays/{oldScore.ReplayMd5}");

                await DbScore.InsertScore(_ctx, dbScore);
            }
            else if (oldScore == null)
            {
                await DbScore.InsertScore(_ctx, dbScore);
            }
            else
            {
                System.IO.File.Delete($"data/replays/{oldScore.ReplayMd5}");
            }

            var newlb = await DbLeaderboard.GetLeaderboardAsync(_ctx, dbScore.ScoreOwner);

            newlb.IncreasePlaycount(dbScore.PlayMode);
            newlb.IncreaseScore((ulong) dbScore.TotalScore, true, dbScore.PlayMode);
            newlb.IncreaseScore((ulong) dbScore.TotalScore, false, dbScore.PlayMode);

            newlb.UpdatePp(_ctx, dbScore.PlayMode);

            await newlb.SaveChanges(_ctx);

            var newStdPos = newlb.GetPosition(_ctx, dbScore.PlayMode);
            newAcc = newlb.GetAccuracy(_ctx, dbScore.PlayMode);

            var newScore = await DbScore.GetLatestScore(_ctx, dbScore);

            var set = await _pisstaube.FetchBeatmapSetAsync(dbScore.FileMd5);

            var bm = set?.ChildrenBeatmaps.First(x => x.FileMD5 == dbScore.FileMd5) ?? new Beatmap();

            ulong oldRankedScore;
            ulong newRankedScore;

            double oldPp;
            double newPp;

            switch (dbScore.PlayMode)
            {
                case PlayMode.Osu:
                    oldRankedScore = oldLb.RankedScoreOsu;
                    newRankedScore = newlb.RankedScoreOsu;

                    oldPp = oldLb.PerformancePointsOsu;
                    newPp = newlb.PerformancePointsOsu;
                    break;
                case PlayMode.Taiko:
                    oldRankedScore = oldLb.RankedScoreTaiko;
                    newRankedScore = newlb.RankedScoreTaiko;

                    oldPp = oldLb.PerformancePointsTaiko;
                    newPp = newlb.PerformancePointsTaiko;
                    break;
                case PlayMode.Ctb:
                    oldRankedScore = oldLb.RankedScoreCtb;
                    newRankedScore = newlb.RankedScoreCtb;

                    oldPp = oldLb.PerformancePointsCtb;
                    newPp = newlb.PerformancePointsCtb;
                    break;
                case PlayMode.Mania:
                    oldRankedScore = oldLb.RankedScoreMania;
                    newRankedScore = newlb.RankedScoreMania;

                    oldPp = oldLb.PerformancePointsMania;
                    newPp = newlb.PerformancePointsMania;
                    break;
                default:
                    return Ok("");
            }

            var newScorePosition = newScore != null ? await newScore.Position(_ctx) : 0;
            var oldScorePosition = oldScore != null ? await oldScore.Position(_ctx) : 0;
            
            if (newScorePosition == 1)
                _sora.SendMessage(
                    $"[http://{_config.Server.ScreenShotHostname}/{dbScore.ScoreOwner.Id} {dbScore.ScoreOwner.UserName}] " +
                    $"has reached #1 on [https://osu.ppy.sh/b/{bm.BeatmapID} {set?.Title} [{bm.DiffName}]] " +
                    $"using {ModUtil.ToString(newScore.Mods)} " +
                    $"Good job! +{newScore.PerformancePoints:F}PP",
                    "#announce",
                    false
                );

            Logger.Info(
                $"{LCol.RED}{dbScore.ScoreOwner.UserName}",
                $"{LCol.PURPLE}( {dbScore.ScoreOwner.Id} ){LCol.WHITE}",
                $"has just submitted a Score! he earned {LCol.BLUE}{newScore?.PerformancePoints:F}PP",
                $"{LCol.WHITE}with an Accuracy of {LCol.RED}{newScore?.Accuracy * 100:F}",
                $"{LCol.WHITE}on {LCol.YELLOW}{set?.Title} [{bm.DiffName}]",
                $"{LCol.WHITE}using {LCol.BLUE}{ModUtil.ToString(newScore?.Mods ?? Mod.None)}"
            );

            var bmChart = new Chart(
                "beatmap",
                "Beatmap Ranking",
                $"https://osu.ppy.sh/b/{bm.BeatmapID}",
                oldScorePosition,
                newScorePosition,
                oldScore?.MaxCombo ?? 0,
                newScore?.MaxCombo ?? 0,
                oldScore?.Accuracy * 100 ?? 0,
                newScore?.Accuracy * 100 ?? 0,
                (ulong) (oldScore?.TotalScore ?? 0),
                (ulong) (newScore?.TotalScore ?? 0),
                oldScore?.PerformancePoints ?? 0,
                newScore?.PerformancePoints ?? 0,
                newScore?.Id ?? 0
            );

            var overallChart = new Chart(
                "overall",
                "Global Ranking",
                $"https://osu.ppy.sh/u/{dbUser.Id}",
                (int) oldStdPos,
                (int) newStdPos,
                0,
                0,
                oldAcc * 100,
                newAcc * 100,
                oldRankedScore,
                newRankedScore,
                oldPp,
                newPp,
                newScore?.Id ?? 0,
                AchievementProcessor.ProcessAchievements(
                    _ctx, dbScore.ScoreOwner, score, bm, set, oldLb, newlb
                )
            );

            pr["LB"] = newlb;
            pr.Stats.Accuracy = (float) newlb.GetAccuracy(_ctx, score.PlayMode);
            pr.Stats.Position = newlb.GetPosition(_ctx, score.PlayMode);
            switch (score.PlayMode)
            {
                case PlayMode.Osu:
                    pr.Stats.PerformancePoints = (ushort) newlb.PerformancePointsOsu;
                    pr.Stats.TotalScore = (ushort) newlb.TotalScoreOsu;
                    pr.Stats.RankedScore = (ushort) newlb.RankedScoreOsu;
                    pr.Stats.PlayCount = (ushort) newlb.PlayCountOsu;
                    break;
                case PlayMode.Taiko:
                    pr.Stats.PerformancePoints = (ushort) newlb.PerformancePointsTaiko;
                    pr.Stats.TotalScore = (ushort) newlb.TotalScoreTaiko;
                    pr.Stats.RankedScore = (ushort) newlb.RankedScoreTaiko;
                    pr.Stats.PlayCount = (ushort) newlb.PlayCountTaiko;
                    break;
                case PlayMode.Ctb:
                    pr.Stats.PerformancePoints = (ushort) newlb.PerformancePointsCtb;
                    pr.Stats.TotalScore = (ushort) newlb.TotalScoreCtb;
                    pr.Stats.RankedScore = (ushort) newlb.RankedScoreCtb;
                    pr.Stats.PlayCount = (ushort) newlb.PlayCountCtb;
                    break;
                case PlayMode.Mania:
                    pr.Stats.PerformancePoints = (ushort) newlb.PerformancePointsMania;
                    pr.Stats.TotalScore = (ushort) newlb.TotalScoreMania;
                    pr.Stats.RankedScore = (ushort) newlb.RankedScoreMania;
                    pr.Stats.PlayCount = (ushort) newlb.PlayCountMania;
                    break;
            }
            
            // Send to other People
            await _ev.RunEvent(
                EventType.BanchoUserStatsRequest,
                new BanchoUserStatsRequestArgs {UserIds = new List<int> {score.Id}, Pr = pr}
            );

            // Send to self
            await _ev.RunEvent(
                EventType.BanchoSendUserStatus,
                new BanchoSendUserStatusArgs {Status = pr.Status, Pr = pr}
            );

            return Ok(
                $"beatmapId:{bm.BeatmapID}|beatmapSetId:{bm.ParentSetID}|beatmapPlaycount:0|beatmapPasscount:0|approvedDate:\n\n" +
                bmChart.ToOsuString() + "\n" + overallChart.ToOsuString()
            );
        }