public override PPTuple GetPerformance() { int pos = Beatmap.GetPosition(Time, out int nobject); if (_cleared == true) { _maxPpResult = SendCalculateCtb(new ArraySegment <byte>(Beatmap.RawData), Mods); if (_maxPpResult != null) { _ppTuple.MaxPP = CalculatePp(_maxPpResult, Mods, 100, _maxPpResult.FullCombo, 0); _ppTuple.MaxAccuracyPP = 0; _ppTuple.MaxSpeedPP = 0; _ppTuple.MaxAimPP = 0; } FullCombo = _maxPpResult.FullCombo; _cleared = false; } if (_lastAcc != Accuracy) { if (_maxPpResult != null) { double fcpp = CalculatePp(_maxPpResult, Mods, Accuracy, _maxPpResult.FullCombo, 0); _ppTuple.FullComboPP = fcpp; _ppTuple.FullComboAccuracyPP = 0; _ppTuple.FullComboSpeedPP = 0; _ppTuple.FullComboAimPP = 0; } } _lastAcc = Accuracy; bool needUpdate = _last_max_combo != MaxCombo; needUpdate |= _last_nmiss != CountMiss; if (needUpdate) { if (nobject > 0) { CtbServerResult ctbServerResult; ctbServerResult = SendCalculateCtb(new ArraySegment <byte>(Beatmap.RawData, 0, pos), Mods); if (ctbServerResult != null) { _ppTuple.RealTimePP = CalculatePp(ctbServerResult, Mods, Accuracy, MaxCombo, CountMiss); _ppTuple.RealTimeAccuracyPP = 0; _ppTuple.RealTimeSpeedPP = 0; _ppTuple.RealTimeAimPP = 0; RealTimeMaxCombo = ctbServerResult.FullCombo; } } } return(_ppTuple); }
/// <summary> /// Calculates the pp. /// </summary> /// <param name="serverResult">The server result.</param> /// <param name="ar">The ar.</param> /// <param name="mods">The mods.</param> /// <param name="acc">The acc (0-100).</param> /// <param name="maxCombo">The maximum combo.</param> /// <param name="nmiss">The nmiss.</param> /// <returns></returns> public static double CalculatePp(CtbServerResult serverResult, uint mods, double acc, int maxCombo, int nmiss) { acc /= 100.0; double pp = Math.Pow(((5 * serverResult.Stars / 0.0049) - 4), 2) / 100000; double length_bonus = 0.95 + 0.4 * Math.Min(1, maxCombo / 3000.0); if (maxCombo > 3000) { length_bonus += Math.Log10(maxCombo / 3000.0) * 0.5; } pp *= length_bonus; pp *= Math.Pow(0.97, nmiss); pp *= Math.Min(Math.Pow(maxCombo, 0.8) / Math.Pow(serverResult.FullCombo, 0.8), 1); if (serverResult.ApproachRate > 9) { pp *= 1 + 0.1 * (serverResult.ApproachRate - 9); } if (serverResult.ApproachRate < 8) { pp *= 1 + 0.025 * (8 - serverResult.ApproachRate); } if (mods.HasMod(ModsInfo.Mods.Hidden)) { pp *= 1.05 + 0.075 * (10 - Math.Min(10, serverResult.ApproachRate)); } if (mods.HasMod(ModsInfo.Mods.Flashlight)) { pp *= 1.35 * length_bonus; } pp *= Math.Pow(acc, 5.5); if (mods.HasMod(ModsInfo.Mods.NoFail)) { pp *= 0.9; } if (mods.HasMod(ModsInfo.Mods.SpunOut)) { pp *= 0.95; } return(pp); }
public CtbServerResult SendCalculateCtb(ArraySegment <byte> content, uint mods) { if (!CtbServerRunning) { RestartCtbServer(); return(new CtbServerResult()); } if (content.Count == 0) { return(new CtbServerResult()); } try { lock (_tcpClient) { var stream = _tcpClient.GetStream(); using (var sw = new BinaryWriter(stream, Encoding.UTF8, true)) { sw.Write(CALCULATE_CTB_PP); sw.Write(content.Count); stream.Write(content.Array, content.Offset, content.Count); sw.Write(mods); //mods } using (var br = new BinaryReader(stream, Encoding.UTF8, true)) { var ret = new CtbServerResult(); ret.Stars = br.ReadDouble(); ret.FullCombo = br.ReadInt32(); ret.ApproachRate = br.ReadDouble(); return(ret); } } } catch (Exception e) { #if DEBUG Sync.Tools.IO.CurrentIO.WriteColor($"[RTPPD::CTB]:{e.Message}", ConsoleColor.Yellow); #endif _tcpClient.Close(); ConnectCtbServer(); return(null); } }
public override void HandleExtraData(Dictionary <string, object> extra, Dictionary <string, string> map_info) { calc_cache = null; SetBeatmap(extra["ortdp_beatmap"] as OsuRTDataProvider.BeatmapInfo.Beatmap); SetMod((Mods.ModsInfo)extra["Mods"]); var list = extra["AccuracyList"] as List <float>; foreach (var acc in list) { var pp = GetData(acc, RequireType.PP); map_info[$"pp:{acc:F2}%"] = pp?.ToString("F2") ?? "0"; } map_info["ar"] = GetData(100, RequireType.AR)?.ToString("F2") ?? map_info["ar"]; var star = GetData(100, RequireType.Star)?.ToString("F2"); if (!string.IsNullOrWhiteSpace(star)) { map_info["stars"] = star; } }
private double?GetData(float acc, RequireType require) { int retry = 15; while (retry != 0) { try { if (calc_cache == null) { calc_cache = ctb_pp_calc.SendCalculateCtb(new ArraySegment <byte>(this.ctb_pp_calc.Beatmap.RawData), ctb_pp_calc.Mods); } switch (require) { case RequireType.PP: return(CalculatePp(calc_cache, ctb_pp_calc.Mods, acc, calc_cache.FullCombo, 0)); case RequireType.Star: return(calc_cache.Stars); case RequireType.AR: return(calc_cache.ApproachRate); default: return(null); } } catch { Thread.Sleep(50); retry--; } } return(0); }