Exemplo n.º 1
0
    def recalc_score(score_data: Dict,
                     no_download=False) -> Optional[score.score]:
        """
        Recalculates pp for a score

        :param score_data: dict containing score and beatmap information about a score.
        :param no_download: if True, raise FileNotFoundError() if the map should be re-downloaded.
                            this ensures no requests are made to osu!
        :return: new `score` object, with `pp` attribute set to the new value
        """
        # Create score object and set its data
        s: score.score = score.score()
        s.setDataFromDict(score_data)
        s.passed = True

        # Create beatmap object and set its data
        b: beatmap.beatmap = beatmap.beatmap()
        b.setDataFromDict(score_data)

        # Abort if we are running in no_download mode and the map should be re-downloaded
        if no_download and mapsHelper.shouldDownloadMap(
                mapsHelper.cachedMapPath(b.beatmapID), b):
            raise FileNotFoundError("no_download mode and local map not found")

        # Calculate score pp
        s.calculatePP(b)
        del b
        return s
Exemplo n.º 2
0
    def calculate_pp(self):
        try:
            # Cache beatmap
            mapFile = mapsHelper.cachedMapPath(self.beatmap.beatmapID)
            mapsHelper.cacheMap(mapFile, self.beatmap)

            # TODO: Sanizite mods

            # Gamemode check
            if self.score and self.score.gameMode != gameModes.CTB:
                raise exceptions.unsupportedGameModeException()

            # Accuracy check
            if self.accuracy > 1:
                raise ValueError("Accuracy must be between 0 and 1")

            # Calculate difficulty
            calcBeatmap = CalcBeatmap(mapFile)
            difficulty = Difficulty(beatmap=calcBeatmap, mods=self.mods)

            # Calculate pp
            if self.tillerino:
                results = []
                for acc in [1, 0.99, 0.98, 0.95]:
                    results.append(ppCalc.calculate_pp(
                        diff=difficulty,
						accuracy=acc,
						combo=self.combo if self.combo >= 0 else calcBeatmap.max_combo,
						miss=self.misses
                    ))
                self.pp = results
            else:
                self.pp = ppCalc.calculate_pp(
                    diff=difficulty,
					accuracy=self.accuracy,
					combo=self.combo if self.combo >= 0 else calcBeatmap.max_combo,
					miss=self.misses
                )
        except exceptions.osuApiFailException:
            log.error("cicciobello ~> osu!api error!")
            self.pp = 0
        except exceptions.unsupportedGameModeException:
            log.error("cicciobello ~> Unsupported gamemode")
            self.pp = 0
        except Exception as e:
            log.error("cicciobello ~> Unhandled exception: {}".format(str(e)))
            self.pp = 0
            raise
        finally:
            log.debug("cicciobello ~> Shutting down, pp = {}".format(self.pp))
Exemplo n.º 3
0
	def calculatePP(self):
		"""
		Calculate total pp value with oppai and return it

		return -- total pp
		"""
		# Set variables
		self.pp = None
		try:
			# Build .osu map file path
			mapFile = mapsHelper.cachedMapPath(self.beatmap.beatmapID)
			log.debug("oppai ~> Map file: {}".format(mapFile))
			mapsHelper.cacheMap(mapFile, self.beatmap)

			# Use only mods supported by oppai
			modsFixed = self.mods & 5983

			# Check gamemode
			if self.gameMode != gameModes.STD and self.gameMode != gameModes.TAIKO:
				raise exceptions.unsupportedGameModeException()

			command = "./pp/oppai-ng/oppai {}".format(mapFile)
			if not self.tillerino:
				# force acc only for non-tillerino calculation
				# acc is set for each subprocess if calculating tillerino-like pp sets
				if self.acc > 0:
					command += " {acc:.2f}%".format(acc=self.acc)
			if self.mods > 0:
				command += " +{mods}".format(mods=scoreUtils.readableMods(modsFixed))
			if self.combo >= 0:
				command += " {combo}x".format(combo=self.combo)
			if self.misses > 0:
				command += " {misses}xm".format(misses=self.misses)
			if self.gameMode == gameModes.TAIKO:
				command += " -taiko"
			command += " -ojson"

			# Calculate pp
			if not self.tillerino:
				# self.pp, self.stars = self._runOppaiProcess(command)
				temp_pp, self.stars = self._runOppaiProcess(command)
				if (self.gameMode == gameModes.TAIKO and self.beatmap.starsStd > 0 and temp_pp > 800) or \
					self.stars > 50:
					# Invalidate pp for bugged taiko converteds and bugged inf pp std maps
					self.pp = 0
				else:
					self.pp = temp_pp
			else:
				pp_list = []
				for acc in [100, 99, 98, 95]:
					temp_command = command
					temp_command += " {acc:.2f}%".format(acc=acc)
					pp, self.stars = self._runOppaiProcess(temp_command)

					# If this is a broken converted, set all pp to 0 and break the loop
					if self.gameMode == gameModes.TAIKO and self.beatmap.starsStd > 0 and pp > 800:
						pp_list = [0, 0, 0, 0]
						break

					pp_list.append(pp)
				self.pp = pp_list

			log.debug("oppai ~> Calculated PP: {}, stars: {}".format(self.pp, self.stars))
		except OppaiError:
			log.error("oppai ~> oppai-ng error!")
			self.pp = 0
		except exceptions.osuApiFailException:
			log.error("oppai ~> osu!api error!")
			self.pp = 0
		except exceptions.unsupportedGameModeException:
			log.error("oppai ~> Unsupported gamemode")
			self.pp = 0
		except Exception as e:
			log.error("oppai ~> Unhandled exception: {}".format(str(e)))
			self.pp = 0
			raise
		finally:
			log.debug("oppai ~> Shutting down, pp = {}".format(self.pp))
Exemplo n.º 4
0
 def mapPath(self):
     return mapsHelper.cachedMapPath(self.beatmap.beatmapID)
Exemplo n.º 5
0
    def calculatePP(self):
        """
		Calculate total pp value with oppai and return it

		return -- total pp
		"""
        # Set variables
        self.pp = None
        ez = None
        try:
            # Build .osu map file path
            mapFile = mapsHelper.cachedMapPath(self.beatmap.beatmapID)
            mapsHelper.cacheMap(mapFile, self.beatmap)

            # Use only mods supported by oppai
            modsFixed = self.mods & 5983

            # Check gamemode
            if self.gameMode not in (gameModes.STD, gameModes.TAIKO):
                raise exceptions.unsupportedGameModeException()

            ez = oppai.ezpp_new()

            if self.misses > 0:
                oppai.ezpp_set_nmiss(ez, self.misses)
            if self.combo >= 0:
                oppai.ezpp_set_combo(ez, self.combo)
            if self.gameMode == gameModes.TAIKO:
                oppai.ezpp_set_mode_override(ez, gameModes.TAIKO)
            if not self.tillerino:
                if self.acc > 0:
                    oppai.ezpp_set_accuracy_percent(ez, self.acc)
            if self.mods > mods.NOMOD:
                oppai.ezpp_set_mods(ez, modsFixed)
            relax = (self.mods & mods.RELAX) > 0
            autopilot = (self.mods & mods.RELAX2) > 0
            if relax or autopilot:
                oppai.ezpp_set_relax_version(ez, 1)
                if relax:
                    oppai.ezpp_set_relax(ez, 1)
                elif autopilot:
                    oppai.ezpp_set_autopilot(ez, 1)

            if not self.tillerino:
                oppai.ezpp_dup(ez, mapFile)
                temp_pp = oppai.ezpp_pp(ez)
                self.stars = oppai.ezpp_stars(ez)
                if (self.gameMode == gameModes.TAIKO and self.beatmap.starsStd > 0 and temp_pp > 800) or \
                 self.stars > 50:
                    # Invalidate pp for bugged taiko converteds and bugged inf pp std maps
                    self.pp = 0
                else:
                    self.pp = temp_pp
            else:
                with open(mapFile, "r") as f:
                    data = f.read()
                oppai.ezpp_data_dup(ez, data, len(data.encode()))
                pp_list = []
                self.stars = oppai.ezpp_stars(ez)
                oppai.ezpp_set_autocalc(ez, 1)
                for acc in (100, 99, 98, 95):
                    oppai.ezpp_set_accuracy_percent(ez, acc)
                    pp = oppai.ezpp_pp(ez)
                    # If this is a broken converted, set all pp to 0 and break the loop
                    if self.gameMode == gameModes.TAIKO and self.beatmap.starsStd > 0 and pp > 800:
                        pp_list = [0, 0, 0, 0]
                        break
                    pp_list.append(pp)
                self.pp = pp_list
            log.debug("oppai ~> Calculated PP: {}, stars: {}".format(
                self.pp, self.stars))
        except exceptions.osuApiFailException:
            log.error("oppai ~> osu!api error!")
            self.pp = 0
        except exceptions.unsupportedGameModeException:
            log.error("oppai ~> Unsupported gamemode")
            self.pp = 0
        except Exception as e:
            log.error("oppai ~> Unhandled exception: {}".format(str(e)))
            self.pp = 0
            raise
        finally:
            if ez is not None:
                oppai.ezpp_free(ez)
            log.debug("oppai ~> Shutting down, pp = {}".format(self.pp))
Exemplo n.º 6
0
def update_beatmaps(args):
    beatmaps = glob.db.fetchAll(
        'select beatmapset_id as sid, beatmap_id as id, beatmap_md5 as md5 from beatmaps'
    )
    for bm in beatmaps:
        b = beatmap.beatmap()
        b.beatmapID, b.fileMD5 = bm['id'], bm['md5']
        if bm['id'] > 100000000:
            continue
        fn = mapsHelper.cachedMapPath(bm['id'])
        if mapsHelper.os.path.isfile(fn) and mapsHelper.generalUtils.fileMd5(
                fn) == bm['md5']:
            continue
        try:
            if args.download_update:
                if mapsHelper.os.path.isfile(fn):
                    if mapsHelper.generalUtils.fileMd5(fn) != bm['md5']:
                        log.info("BID {}: Hash difference ({}) expected ({})".
                                 format(
                                     bm['id'],
                                     mapsHelper.generalUtils.fileMd5(fn)[:8],
                                     bm['md5'][:8]))
                else:
                    log.info("BID {}: Not exists, downloading...".format(
                        bm['id']))
                mapsHelper.cacheMap(fn, b)
            if mapsHelper.os.path.isfile(
                    fn) and mapsHelper.generalUtils.fileMd5(fn) != bm['md5']:
                bm['update.md5'] = mapsHelper.generalUtils.fileMd5(fn)
            else:
                bm['delete.file'] = True
        except Exception as e:
            log.warning('BID {}: ({}) {}'.format(bm['id'],
                                                 type(e).__name__, e))
            pass
        if args.download_update:
            time.sleep(2.5)
    stmts = []
    bmgroup = []

    def destructor(query):
        nonlocal bmgroup
        query += " end where beatmap_id in ({})".format(','.join(
            str(bm) for bm in bmgroup))
        del bmgroup[:]
        return query

    stmt = QueryBuilder("update beatmaps set beatmap_md5=case ", '', " ")
    stmt.finisher = destructor
    for bm in beatmaps:
        if 'delete.file' in bm:
            continue
        if 'update.md5' not in bm:
            continue
        bmgroup.append(bm['id'])
        stmtline = "when beatmap_id={} then '{}'".format(
            bm['id'], bm['update.md5'])
        newQuery = stmt.appendAndBuild(stmtline)
        if newQuery:
            stmts.append(newQuery)
    if stmt.query:
        stmts.append(stmt.build())
    for s in stmts:
        # print(s)
        glob.db.execute(s)
Exemplo n.º 7
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