Beispiel #1
0
    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)
Beispiel #2
0
    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)
Beispiel #3
0
	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)
Beispiel #4
0
    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
Beispiel #5
0
    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