feat(custom-rulesets): support custom rulesets (#23)
* feat(custom_ruleset): add custom rulesets support * feat(custom-ruleset): add version check * feat(custom-ruleset): add LegacyIO API to get ruleset hashes * feat(pp): add check for rulesets whose pp cannot be calculated * docs(readme): update README to include support for custom rulesets * fix(custom-ruleset): make `rulesets` empty instead of throw a error when version check is disabled Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * chore(custom-ruleset): apply the latest changes of generatorc891bcd159ande25041ad3b* feat(calculator): add fallback performance calculation for unsupported modes * fix(calculator): remove debug print * fix: resolve reviews * feat(calculator): add difficulty calculation checks --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -25,11 +25,13 @@ logger = log("Calculator")
|
||||
CALCULATOR: PerformanceCalculator | None = None
|
||||
|
||||
|
||||
def init_calculator():
|
||||
async def init_calculator():
|
||||
global CALCULATOR
|
||||
try:
|
||||
module = importlib.import_module(f"app.calculators.performance.{settings.calculator}")
|
||||
CALCULATOR = module.PerformanceCalculator(**settings.calculator_config)
|
||||
if CALCULATOR is not None:
|
||||
await CALCULATOR.init()
|
||||
except (ImportError, AttributeError) as e:
|
||||
raise ImportError(f"Failed to import performance calculator for {settings.calculator}") from e
|
||||
return CALCULATOR
|
||||
@@ -50,6 +52,26 @@ def clamp[T: int | float](n: T, min_value: T, max_value: T) -> T:
|
||||
return n
|
||||
|
||||
|
||||
def calculate_pp_for_no_calculator(score: "Score", star_rating: float) -> float:
|
||||
# TODO: Improve this algorithm
|
||||
# https://www.desmos.com/calculator/i2aa7qm3o6
|
||||
k = 4.0
|
||||
|
||||
pmax = 1.4 * (star_rating**2.8)
|
||||
b = 0.95 - 0.33 * ((clamp(star_rating, 1, 8) - 1) / 7)
|
||||
|
||||
x = score.total_score / 1000000
|
||||
|
||||
if x < b:
|
||||
# Linear section
|
||||
return pmax * x
|
||||
else:
|
||||
# Exponential reward section
|
||||
x = (x - b) / (1 - b)
|
||||
exp_part = (math.exp(k * x) - 1) / (math.exp(k) - 1)
|
||||
return pmax * (b + (1 - b) * exp_part)
|
||||
|
||||
|
||||
async def calculate_pp(score: "Score", beatmap: str, session: AsyncSession) -> float:
|
||||
from app.database.beatmap import BannedBeatmaps
|
||||
|
||||
@@ -68,8 +90,18 @@ async def calculate_pp(score: "Score", beatmap: str, session: AsyncSession) -> f
|
||||
except Exception:
|
||||
logger.exception(f"Error checking if beatmap {score.beatmap_id} is suspicious")
|
||||
|
||||
attrs = await get_calculator().calculate_performance(beatmap, score)
|
||||
pp = attrs.pp
|
||||
if not (await get_calculator().can_calculate_performance(score.gamemode)):
|
||||
if not settings.fallback_no_calculator_pp:
|
||||
return 0
|
||||
star_rating = -1
|
||||
if await get_calculator().can_calculate_difficulty(score.gamemode):
|
||||
star_rating = (await get_calculator().calculate_difficulty(beatmap, score.mods, score.gamemode)).star_rating
|
||||
if star_rating < 0:
|
||||
star_rating = (await score.awaitable_attrs.beatmap).difficulty_rating
|
||||
pp = calculate_pp_for_no_calculator(score, star_rating)
|
||||
else:
|
||||
attrs = await get_calculator().calculate_performance(beatmap, score)
|
||||
pp = attrs.pp
|
||||
|
||||
if settings.suspicious_score_check and (pp > 3000):
|
||||
logger.warning(
|
||||
|
||||
Reference in New Issue
Block a user