def asyncGet(self): statusCode = 400 data = {"message": "unknown error"} try: # Check arguments if not requestsManager.checkArguments(self.request.arguments, ["b"]): raise exceptions.invalidArgumentsException(self.MODULE_NAME) # Get beatmap ID and make sure it's a valid number beatmapId = self.get_argument("b") if not beatmapId.isdigit(): raise exceptions.invalidArgumentsException(self.MODULE_NAME) # Get mods if "m" in self.request.arguments: modsEnum = self.get_argument("m") if not modsEnum.isdigit(): raise exceptions.invalidArgumentsException( self.MODULE_NAME) modsEnum = int(modsEnum) else: modsEnum = 0 # Get game mode if "g" in self.request.arguments: gameMode = self.get_argument("g") if not gameMode.isdigit(): raise exceptions.invalidArgumentsException( self.MODULE_NAME) gameMode = int(gameMode) else: gameMode = 0 # Get acc if "a" in self.request.arguments: accuracy = self.get_argument("a") try: accuracy = float(accuracy) except ValueError: raise exceptions.invalidArgumentsException( self.MODULE_NAME) else: accuracy = None # Print message log.info("Requested pp for beatmap {}".format(beatmapId)) # Get beatmap md5 from osuapi # TODO: Move this to beatmap object osuapiData = osuapiHelper.osuApiRequest("get_beatmaps", "b={}".format(beatmapId)) if osuapiData is None or "file_md5" not in osuapiData or "beatmapset_id" not in osuapiData: raise exceptions.invalidBeatmapException(self.MODULE_NAME) beatmapMd5 = osuapiData["file_md5"] beatmapSetID = osuapiData["beatmapset_id"] # Create beatmap object bmap = beatmap.beatmap(beatmapMd5, beatmapSetID) # Check beatmap length # TODO: Why do we do this? if bmap.hit_length > 900: raise exceptions.beatmapTooLongException(self.MODULE_NAME) if gameMode == gameModes.STD and bmap.starsStd == 0: # Mode Specific beatmap, auto detect game mode if bmap.starsTaiko > 0: gameMode = gameModes.TAIKO if bmap.starsCtb > 0: gameMode = gameModes.CTB if bmap.starsMania > 0: gameMode = gameModes.MANIA # Calculate pp if gameMode in (gameModes.STD, gameModes.TAIKO): # osu!standard and osu!taiko oppai = ez.Ez(bmap, mods_=modsEnum, tillerino=accuracy is None, acc=accuracy, gameMode=gameMode) bmap.starsStd = oppai.stars returnPP = oppai.pp elif gameMode == gameModes.CTB: # osu!catch ciccio = cicciobello.Cicciobello(bmap, mods_=modsEnum, tillerino=accuracy is None, accuracy=accuracy) bmap.starsStd = ciccio.stars returnPP = ciccio.pp else: raise exceptions.unsupportedGameModeException() # Data to return data = { "song_name": bmap.songName, "pp": [x for x in returnPP] if type(returnPP) is list else returnPP, "game_mode": gameMode, "length": bmap.hit_length, "stars": bmap.starsStd if bmap.starsStd is not None else bmap.difficultyrating, "ar": bmap.diff_approach, "bpm": bmap.bpm, } # Set status code and message statusCode = 200 data["message"] = "ok" except exceptions.invalidArgumentsException: # Set error and message statusCode = 400 data["message"] = "missing required arguments" except exceptions.invalidBeatmapException: statusCode = 400 data["message"] = "beatmap not found" except exceptions.beatmapTooLongException: statusCode = 400 data["message"] = "requested beatmap is too long" except exceptions.unsupportedGameModeException: statusCode = 400 data["message"] = "Unsupported gamemode" finally: # Add status code to data data["status"] = statusCode # Debug output log.debug(str(data)) # Send response self.write(json.dumps(data)) self.set_header("Content-Type", "application/json") self.set_status(statusCode)
def asyncGet(self): statusCode = 400 data = {"message": "unknown error"} try: # Check arguments if not requestsManager.checkArguments(self.request.arguments, ["b"]): raise exceptions.invalidArgumentsException(MODULE_NAME) # Get beatmap ID and make sure it's a valid number beatmapID = self.get_argument("b") if not beatmapID.isdigit(): raise exceptions.invalidArgumentsException(MODULE_NAME) # Get mods if "m" in self.request.arguments: modsEnum = self.get_argument("m") if not modsEnum.isdigit(): raise exceptions.invalidArgumentsException(MODULE_NAME) modsEnum = int(modsEnum) else: modsEnum = 0 # Get game mode if "g" in self.request.arguments: gameMode = self.get_argument("g") if not gameMode.isdigit(): raise exceptions.invalidArgumentsException(MODULE_NAME) gameMode = int(gameMode) else: gameMode = 0 # Get acc if "a" in self.request.arguments: accuracy = self.get_argument("a") try: accuracy = float(accuracy) except ValueError: raise exceptions.invalidArgumentsException(MODULE_NAME) else: accuracy = None # Print message log.info("Requested pp for beatmap {}".format(beatmapID)) # Get beatmap md5 from osuapi # TODO: Move this to beatmap object osuapiData = osuapiHelper.osuApiRequest("get_beatmaps", "b={}".format(beatmapID)) if int(beatmapID) > 100000000: raise exceptions.ppCustomBeatmap(MODULE_NAME) if osuapiData is None or "file_md5" not in osuapiData or "beatmapset_id" not in osuapiData: raise exceptions.invalidBeatmapException(MODULE_NAME) beatmapMd5 = osuapiData["file_md5"] beatmapSetID = osuapiData["beatmapset_id"] # Create beatmap object bmap = beatmap.beatmap(beatmapMd5, beatmapSetID) # Check beatmap length if bmap.hitLength > 900: raise exceptions.beatmapTooLongException(MODULE_NAME) returnPP = [] if gameMode == gameModes.STD and bmap.starsStd == 0: # Mode Specific beatmap, auto detect game mode if bmap.starsTaiko > 0: gameMode = gameModes.TAIKO if bmap.starsCtb > 0: gameMode = gameModes.CTB if bmap.starsMania > 0: gameMode = gameModes.MANIA # Calculate pp if gameMode in (gameModes.STD, gameModes.TAIKO): # Std pp if accuracy is None and modsEnum == 0: # Generic acc/nomod # Get cached pp values cachedPP = bmap.getCachedTillerinoPP() if (modsEnum & mods.RELAX): cachedPP = [0, 0, 0, 0] elif (modsEnum & mods.RELAX2): cachedPP = [0, 0, 0, 0] if cachedPP != [0, 0, 0, 0]: log.debug("Got cached pp.") returnPP = cachedPP else: log.debug( "Cached pp not found. Calculating pp with oppai..." ) # Cached pp not found, calculate them if gameMode == gameModes.STD and (modsEnum & mods.RELAX): oppai = relaxoppai.oppai(bmap, mods=modsEnum, tillerino=True) elif gameMode == gameModes.STD and (modsEnum & mods.RELAX2): oppai = relax2oppai.oppai(bmap, mods=modsEnum, tillerino=True) else: oppai = rippoppai.oppai(bmap, mods=modsEnum, tillerino=True) returnPP = oppai.pp bmap.starsStd = oppai.stars if not (modsEnum & mods.RELAX) or (modsEnum & mods.RELAX2): # Cache values in DB log.debug("Saving cached pp...") if type(returnPP) == list and len(returnPP) == 4: bmap.saveCachedTillerinoPP(returnPP) else: # Specific accuracy/mods, calculate pp # Create oppai instance log.debug( "Specific request ({}%/{}). Calculating pp with oppai..." .format(accuracy, modsEnum)) if gameMode == gameModes.STD and (modsEnum & mods.RELAX): oppai = relaxoppai.oppai(bmap, mods=modsEnum, tillerino=True) elif gameMode == gameModes.STD and (modsEnum & mods.RELAX2): oppai = relax2oppai.oppai(bmap, mods=modsEnum, tillerino=True) else: oppai = rippoppai.oppai(bmap, mods=modsEnum, tillerino=True) bmap.starsStd = oppai.stars if accuracy is not None: returnPP = calculatePPFromAcc(oppai, accuracy) else: returnPP = oppai.pp else: raise exceptions.unsupportedGameModeException() # Data to return data = { "song_name": bmap.songName, "pp": [x for x in returnPP] if type(returnPP) is list else returnPP, "length": bmap.hitLength, "stars": bmap.starsStd, "ar": bmap.AR, "bpm": bmap.bpm, } # Set status code and message statusCode = 200 data["message"] = "ok" except exceptions.invalidArgumentsException: # Set error and message statusCode = 400 data["message"] = "missing required arguments" except exceptions.ppCustomBeatmap: statusCode = 400 data[ "message"] = "Custom map does not supported pp calculating yet" except exceptions.invalidBeatmapException: statusCode = 400 data["message"] = "beatmap not found" except exceptions.beatmapTooLongException: statusCode = 400 data["message"] = "requested beatmap is too long" except exceptions.unsupportedGameModeException: statusCode = 400 data["message"] = "Unsupported gamemode" finally: # Add status code to data data["status"] = statusCode # Debug output log.debug(str(data)) # Send response #self.clear() self.write(json.dumps(data)) self.set_header("Content-Type", "application/json") self.set_status(statusCode)
def asyncGet(self): statusCode = 400 data = {"message": "unknown error"} try: # Check arguments if not requestsManager.checkArguments(self.request.arguments, ["b"]): raise exceptions.invalidArgumentsException(MODULE_NAME) # Get beatmap ID and make sure it's a valid number beatmapID = self.get_argument("b") if not beatmapID.isdigit(): raise exceptions.invalidArgumentsException(MODULE_NAME) # Get mods if "m" in self.request.arguments: modsEnum = self.get_argument("m") if not modsEnum.isdigit(): raise exceptions.invalidArgumentsException(MODULE_NAME) modsEnum = int(modsEnum) else: modsEnum = 0 # Get game mode if "g" in self.request.arguments: gameMode = self.get_argument("g") if not gameMode.isdigit(): raise exceptions.invalidArgumentsException(MODULE_NAME) gameMode = int(gameMode) else: gameMode = 0 # Get acc if "a" in self.request.arguments: accuracy = self.get_argument("a") try: accuracy = float(accuracy) if math.isnan(accuracy): accuracy = -1.0 except ValueError: raise exceptions.invalidArgumentsException(MODULE_NAME) else: accuracy = -1.0 if "x" in self.request.arguments: misses = self.get_argument("x") try: misses = int(misses) except ValueError: raise exceptions.invalidArgumentsException(MODULE_NAME) else: misses = 0 if "c" in self.request.arguments: combo = self.get_argument("c") if not combo.isdigit(): raise exceptions.invalidArgumentsException(MODULE_NAME) combo = int(combo) else: combo = 0 # Print message log.info("Requested pp for beatmap {}".format(beatmapID)) # Get beatmap md5 from osuapi # TODO: Move this to beatmap object """osuapiData = osuapiHelper.osuApiRequest("get_beatmaps", "b={}".format(beatmapID)) if osuapiData is None or "file_md5" not in osuapiData or "beatmapset_id" not in osuapiData: raise exceptions.invalidBeatmapException(MODULE_NAME) beatmapMd5 = osuapiData["file_md5"] beatmapSetID = osuapiData["beatmapset_id"]""" dbData = glob.db.fetch("SELECT beatmap_md5, beatmapset_id, mode FROM beatmaps WHERE beatmap_id = {}".format(beatmapID)) if dbData is None: raise exceptions.invalidBeatmapException(MODULE_NAME) beatmapMd5 = dbData["beatmap_md5"] beatmapSetID = dbData["beatmapset_id"] # Create beatmap object bmap = beatmap.beatmap(beatmapMd5, beatmapSetID) stars = 0 # Check beatmap length if bmap.hitLength > 900: raise exceptions.beatmapTooLongException(MODULE_NAME) returnPP = [] if gameMode == gameModes.STD and bmap.starsStd == 0: gameMode = dbData["mode"] if(gameMode == 2): raise exceptions.unsupportedGameModeException if accuracy > 100 or combo > bmap.maxCombo or misses < 0 or math.isnan(accuracy): raise exceptions.invalidArgumentsException(MODULE_NAME) # Calculate pp if gameMode != 2: # Std pp if accuracy < 0 and combo == 0 and misses == 0 and gameMode == dbData["mode"]: # Generic acc # Get cached pp values cachedPP = bmap.getCachedTillerinoPP() if(modsEnum == 0 and cachedPP != [0,0,0,0]): log.debug("Got cached pp.") returnPP = cachedPP stars = bmap.starsStd else: log.debug("Cached pp not found. Calculating pp with oppai...") # Cached pp not found, calculate them if(gameMode == 1 or gameMode == 0): oppai = rippoppai.oppai(bmap, mods=modsEnum, tillerino=True, stars=True) returnPP = oppai.pp stars = oppai.stars else: xeno = omppcPy.piano(bmap, mods=modsEnum, tillerino=True, stars=True) returnPP = xeno.pp stars = xeno.stars # Cache values in DB log.debug("Saving cached pp...") if(modsEnum == 0 and type(returnPP) != int and len(returnPP) == 4): bmap.saveCachedTillerinoPP(returnPP) else: # Specific accuracy, calculate # Create oppai instance if(gameMode == 3): log.info("Specific request ({}%/{}). Calculating pp with omppc...".format(accuracy, modsEnum)) xeno = omppcPy.piano(bmap, acc=accuracy, mods=modsEnum, tillerino=False, stars=True) returnPP.append(xeno.pp) stars = xeno.stars else: if(gameMode != 0 and gameMode != 1): raise exceptions.unsupportedGameModeException log.debug("Specific request ({}%/{}). Calculating pp with oppai...".format(accuracy, modsEnum)) oppai = rippoppai.oppai(bmap, mods=modsEnum, tillerino=False, stars=True) stars = oppai.stars if accuracy > 0: oppai.acc = accuracy if combo > 0: oppai.combo = combo if misses > 0: oppai.misses = misses oppai.getPP() returnPP.append(oppai.pp) else: raise exceptions.unsupportedGameModeException # Data to return data = { "song_name": bmap.songName, "pp": returnPP, "length": round(bmap.hitLength*0.75) if modsEnum & mods.DOUBLETIME > 0 else bmap.hitLength if modsEnum & mods.HALFTIME < 1 else round(bmap.hitLength*1.50), "stars": stars, "ar": round(min(10, bmap.AR * 1.4),1) if modsEnum & mods.HARDROCK > 0 else bmap.AR if modsEnum & mods.EASY < 1 else round(max(0, bmap.AR / 2),1), "od": round(min(10, bmap.OD * 1.4),1) if modsEnum & mods.HARDROCK > 0 else bmap.OD if modsEnum & mods.EASY < 1 else round(max(0, bmap.OD / 2),1), "bpm": round(bmap.bpm*1.5) if modsEnum & mods.DOUBLETIME > 0 else bmap.bpm if modsEnum & mods.HALFTIME < 1 else round(bmap.bpm*0.75), } # Set status code and message statusCode = 200 data["message"] = "ok" except exceptions.invalidArgumentsException: # Set error and message statusCode = 400 data["message"] = "missing required arguments" except exceptions.invalidBeatmapException: statusCode = 400 data["message"] = "beatmap not found" except exceptions.beatmapTooLongException: statusCode = 400 data["message"] = "requested beatmap is too long" except exceptions.unsupportedGameModeException: statusCode = 400 data["message"] = "Unsupported gamemode" finally: # Add status code to data data["status"] = statusCode # Debug output log.debug(str(data)) # Send response #self.clear() self.write(json.dumps(data)) self.set_header("Content-Type", "application/json") self.set_status(statusCode)
def getPP(self): try: stars = self.beatmap.starsMania if stars == 0: # This beatmap can't be converted to mania raise exceptions.invalidBeatmapException() # Cache beatmap for cono mapFile = mapsHelper.cachedMapPath(self.beatmap.beatmapID) mapsHelper.cacheMap(mapFile, self.beatmap) od = self.beatmap.OD objects = self.score.c50 + self.score.c100 + self.score.c300 + self.score.cKatu + self.score.cGeki + self.score.cMiss score = self.score.score accuracy = self.score.accuracy scoreMods = self.score.mods log.debug( "[WIFIPIANO2] SCORE DATA: Stars: {stars}, OD: {od}, obj: {objects}, score: {score}, acc: {acc}, mods: {mods}" .format(stars=stars, od=od, objects=objects, score=score, acc=accuracy, mods=scoreMods)) # ---------- STRAIN PP # Scale score to mods multiplier scoreMultiplier = 1.0 # Doubles score if EZ/HT if scoreMods & mods.EASY != 0: scoreMultiplier *= 0.50 #if scoreMods & mods.HALFTIME != 0: # scoreMultiplier *= 0.50 # Calculate strain PP if scoreMultiplier <= 0: strainPP = 0 else: score *= int(1.0 / scoreMultiplier) strainPP = pow(5.0 * max(1.0, stars / 0.0825) - 4.0, 3.0) / 110000.0 strainPP *= 1 + 0.1 * min(1.0, float(objects) / 1500.0) if score <= 500000: strainPP *= (float(score) / 500000.0) * 0.1 elif score <= 600000: strainPP *= 0.1 + float(score - 500000) / 100000.0 * 0.2 elif score <= 700000: strainPP *= 0.3 + float(score - 600000) / 100000.0 * 0.35 elif score <= 800000: strainPP *= 0.65 + float(score - 700000) / 100000.0 * 0.20 elif score <= 900000: strainPP *= 0.85 + float(score - 800000) / 100000.0 * 0.1 else: strainPP *= 0.95 + float(score - 900000) / 100000.0 * 0.05 # ---------- ACC PP # Makes sure OD is in range 0-10. If this is done elsewhere, remove this. scrubbedOD = min(10.0, max(0, 10.0 - od)) # Old formula but done backwards. hitWindow300 = (34 + 3 * scrubbedOD) # Increases hitWindow if EZ is on if scoreMods & mods.EASY != 0: hitWindow300 *= 1.4 # Fiddles with DT and HT to make them match hitWindow300's ingame. if scoreMods & mods.DOUBLETIME != 0: hitWindow300 *= 1.5 elif scoreMods & mods.HALFTIME != 0: hitWindow300 *= 0.75 # makes hit window match what it is ingame. hitWindow300 = int(hitWindow300) + 0.5 if scoreMods & mods.DOUBLETIME != 0: hitWindow300 /= 1.5 elif scoreMods & mods.HALFTIME != 0: hitWindow300 /= 0.75 # Calculate accuracy PP accPP = pow((150.0 / hitWindow300) * pow(accuracy, 16), 1.8) * 2.5 accPP *= min(1.15, pow(float(objects) / 1500.0, 0.3)) # ---------- TOTAL PP multiplier = 1.1 if scoreMods & mods.NOFAIL != 0: multiplier *= 0.90 if scoreMods & mods.SPUNOUT != 0: multiplier *= 0.95 if scoreMods & mods.EASY != 0: multiplier *= 0.50 if scoreMods & mods.HARDROCK != 0: multiplier *= 1.20 if scoreMods & mods.DOUBLETIME != 0: multiplier *= 1.45 if scoreMods & mods.NIGHTCORE != 0: multiplier *= 1.45 pp = pow(pow(strainPP, 1.1) + pow(accPP, 1.1), 1.0 / 1.1) * multiplier log.debug("[WIFIPIANO2] Calculated PP: {}".format(pp)) self.pp = pp except exceptions.invalidBeatmapException: log.warning("Invalid beatmap {}".format(self.beatmap.beatmapID)) self.pp = 0 finally: return self.pp
def getPP(self): try: stars = self.beatmap.starsMania if stars == 0: # This beatmap can't be converted to mania raise exceptions.invalidBeatmapException() od = self.beatmap.OD objects = self.score.c50 + self.score.c100 + self.score.c300 + self.score.cKatu + self.score.cGeki + self.score.cMiss score = self.score.score accuracy = self.score.accuracy scoreMods = self.score.mods log.debug( "[WIFIPIANO2] SCORE DATA: Stars: {stars}, OD: {od}, obj: {objects}, score: {score}, acc: {acc}, mods: {mods}" .format(stars=stars, od=od, objects=objects, score=score, acc=accuracy, mods=scoreMods)) # ---------- STRAIN PP # Scale score to mods multiplier scoreMultiplier = 1.0 if scoreMods & mods.EASY > 0: scoreMultiplier *= 2.00 if scoreMods & mods.NOFAIL > 0: scoreMultiplier *= 2.00 # NOTE: HT gives less pp tho #if scoreMods & mods.HALFTIME > 0: # scoreMultiplier *= 2.00 if scoreMultiplier <= 0: strainPP = 0 else: score *= int(1.0 / scoreMultiplier) strainPP = pow(5.0 * max(1.0, stars / 0.0825) - 4.0, 3.0) / 110000.0 strainPP *= 1 + 0.1 * min(1.0, float(objects) / 1500.0) if score <= 500000: strainPP *= (float(score) / 500000.0) * 0.1 elif score <= 600000: strainPP *= 0.1 + float(score - 500000) / 100000.0 * 0.2 elif score <= 700000: strainPP *= 0.3 + float(score - 600000) / 100000.0 * 0.35 elif score <= 800000: strainPP *= 0.65 + float(score - 700000) / 100000.0 * 0.20 elif score <= 900000: strainPP *= 0.85 + float(score - 800000) / 100000.0 * 0.1 else: strainPP *= 0.95 + float(score - 900000) / 100000.0 * 0.05 # ---------- ACC PP hitWindow300 = 64 - 3 * float(od) accPP = pow((150.0 / hitWindow300) * pow(accuracy, 16), 1.8) * 2.5 accPP *= min(1.15, pow(float(objects) / 1500.0, 0.3)) # ---------- TOTAL PP multiplier = 1.1 if scoreMods & mods.NOFAIL > 0: multiplier *= 0.90 if scoreMods & mods.SPUNOUT > 0: multiplier *= 0.95 if scoreMods & mods.EASY > 0: multiplier *= 0.50 pp = pow(pow(strainPP, 1.1) + pow(accPP, 1.1), 1.0 / 1.1) * multiplier log.debug("[WIFIPIANO2] Calculated PP: {}".format(pp)) self.pp = pp except exceptions.invalidBeatmapException: log.warning("Invalid beatmap {}".format(self.beatmap.beatmapID)) self.pp = 0 finally: return self.pp