def _runOppaiProcess(self, command): log.debug("oppai ~> running {}".format(command)) process = subprocess.run(command, shell=True, stdout=subprocess.PIPE) try: output = json.loads(process.stdout.decode("utf-8", errors="ignore")) if "code" not in output or "errstr" not in output: raise OppaiError("No code in json output") if output["code"] != 200: raise OppaiError("oppai error {}: {}".format( output["code"], output["errstr"])) if "pp" not in output or "stars" not in output: raise OppaiError("No pp/stars entry in oppai json output") pp = output["pp"] stars = output["stars"] if (self.gameMode == gameModes.STD): mod = "difficulty_{}".format( scoreUtils.readableMods(self.mods & 80)) if (mod == "difficulty_HR" or mod == "difficulty_DT"): diff = glob.db.fetch( "SELECT {} FROM beatmaps WHERE beatmap_md5 = '{}'". format(mod, self.beatmap.fileMD5))[mod] if (diff == 0): glob.db.execute( "UPDATE beatmaps SET {} = {} WHERE beatmap_md5 = '{}'" .format(mod, round(stars, 5), self.beatmap.fileMD5)) log.debug("oppai ~> full output: {}".format(output)) log.debug("oppai ~> pp: {}, stars: {}".format(pp, stars)) except (json.JSONDecodeError, IndexError, OppaiError) as e: raise OppaiError(e) return pp, stars
def recalcFirstPlaces(userID): c = glob.db.fetch("select * from user_first_places WHERE userid = %s", [userID]) fs = glob.db.fetchAll( "SELECT o.pp pp, o.mods mods, beatmap_id, difficulty_std stars, difficulty_hr starsHR, difficulty_dt starsDT, hit_length length FROM (SELECT scores.* from scores JOIN users ON scores.userid = users.id WHERE privileges > 2 AND completed = 3 AND play_mode = 0) o LEFT JOIN (SELECT scores.* from scores JOIN users ON scores.userid = users.id WHERE privileges > 2 AND completed = 3 AND play_mode = 0) b on o.beatmap_md5 = b.beatmap_md5 AND o.pp < b.pp JOIN beatmaps ON o.beatmap_md5 = beatmaps.beatmap_md5 WHERE b.pp is NULL AND o.userid = %s AND o.play_mode = 0 AND o.completed = 3 AND o.pp > 40", [userID]) if fs is None: return fvalue = 0 for f in fs: mod = scoreUtils.readableMods(int(f["mods"]) & 80) stars = f["stars"] if (mod == "HR" or mod == "DT"): if (f["stars{}".format(mod)] <= 0): log.error("stars <= 0 beatmap: {}".format(f["beatmap_id"])) else: stars = f["stars{}".format(mod)] cv = addModsBonus(stars, f["mods"], f["pp"])**4.45 / 800 fvalue += cv * lengthBonusMultiplier(f["length"]) fvalue = round(fvalue) if c is None: glob.db.execute( "INSERT INTO user_first_places(userid,value) VALUES(%s,%s)", [userID, fvalue]) else: glob.db.execute( "UPDATE user_first_places SET value = %s WHERE userID = %s", [fvalue, userID])
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))
def handle(userToken, packetData): # Get usertoken data userID = userToken.userID username = userToken.username # Make sure we are not banned #if userUtils.isBanned(userID): # userToken.enqueue(serverPackets.loginBanned()) # return # Send restricted message if needed #if userToken.restricted: # userToken.checkRestricted(True) # Change action packet packetData = clientPackets.userActionChange(packetData) # If we are not in spectate status but we're spectating someone, stop spectating ''' if userToken.spectating != 0 and userToken.actionID != actions.WATCHING and userToken.actionID != actions.IDLE and userToken.actionID != actions.AFK: userToken.stopSpectating() # If we are not in multiplayer but we are in a match, part match if userToken.matchID != -1 and userToken.actionID != actions.MULTIPLAYING and userToken.actionID != actions.MULTIPLAYER and userToken.actionID != actions.AFK: userToken.partMatch() ''' # Update cached stats if relax status changed if packetData["actionMods"] & 128 != userToken.relax: userToken.relax = packetData["actionMods"] & 128 notifs = userUtils.checkAkatsukiNotifications(userID) if packetData["actionMods"] & 128: if not notifs: userToken.enqueue( serverPackets.notification("You've switched to Relax!")) userToken.rxupdateCachedStats() else: if not notifs: userToken.enqueue( serverPackets.notification("You've switched to Regular!")) userToken.updateCachedStats() # Update cached stats if our pp changed if we've just submitted a score or we've changed gameMode if (userToken.actionID == actions.PLAYING or userToken.actionID == actions.MULTIPLAYING) or (userToken.pp != userUtils.getPP( userID, userToken.gameMode)) or (userToken.gameMode != packetData["gameMode"]): if packetData["actionMods"] & 128: userToken.rxupdateCachedStats() else: userToken.updateCachedStats() # Update cached stats if we've changed gamemode if userToken.gameMode != packetData["gameMode"]: userToken.gameMode = packetData["gameMode"] if packetData["actionMods"] & 128: userToken.rxupdateCachedStats() else: userToken.updateCachedStats() # Always update action id, text, md5 and beatmapID userToken.actionID = packetData["actionID"] userToken.actionText = packetData[ "actionText"] + " +" + scoreUtils.readableMods( int(packetData["actionMods"])) userToken.actionMd5 = packetData["actionMd5"] userToken.actionMods = packetData["actionMods"] userToken.beatmapID = packetData["beatmapID"] # Enqueue our new user panel and stats to us and our spectators recipients = [userToken] if len(userToken.spectators) > 0: for i in userToken.spectators: if i in glob.tokens.tokens: recipients.append(glob.tokens.tokens[i]) for i in recipients: if i is not None: # Force our own packet force = True if i == userToken else False i.enqueue(serverPackets.userPanel(userID, force)) i.enqueue(serverPackets.userStats(userID, force)) # Console output log.info("{} changed action: {} [{}][{}][{}].".format( username, str(userToken.actionID), userToken.actionText, userToken.actionMd5, userToken.beatmapID))
def getPP(self, tillerino=False, stars=False): """ Calculate total pp value with oppai and return it return -- total pp """ # Set variables self.pp = 0 try: # Build .osu map file path mapFile = "{path}/maps/{map}".format(path=self.OPPAI_FOLDER, map=self.map) mapsHelper.cacheMap(mapFile, self.beatmap) # Base command command = fixPath("{path}/oppai {mapFile}".format( path=self.OPPAI_FOLDER, mapFile=mapFile)) # Use only mods supported by oppai. modsFixed = self.mods & 14303 command += " scorev{ver}".format( ver=scoreUtils.scoreType(self.mods)) # Add params if needed if not self.tillerino: 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 = round(temp_pp, 2) 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(round(pp, 2)) self.pp = pp_list # Debug output if glob.debug: consoleHelper.printRippoppaiMessage( "Executing {}".format(command)) # oppai output """process = subprocess.run(command, shell=True, stdout=subprocess.PIPE) output = process.stdout.decode("utf-8") # Get standard or tillerino output sep = "\n" if UNIX else "\r\n" if output == ['']: # This happens if mode not supported or something self.pp = 0 self.stars = None return self.pp output = output.split(sep) # get rid of pesky warnings!!! try: float(output[0]) except ValueError: del output[0] if tillerino: # Get tillerino output (multiple lines) if stars: self.pp = output[:-2] self.stars = float(output[-2]) else: self.pp = output.split(sep)[:-1] # -1 because there's an empty line at the end else: # Get standard output (:l to remove (/r)/n at the end) l = -1 if UNIX else -2 if stars: self.pp = float(output[len(output)-2][:l-1]) else: self.pp = float(output[len(output)-2][:l]) """ # Debug output consoleHelper.printRippoppaiMessage("Calculated pp: {}".format( self.pp)) 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))
def getPP(self, tillerino=False, stars=False): """ Calculate total pp value with oppai and return it return -- total pp """ # Set variables self.pp = 0 try: # Build .osu map file path mapFile = "{path}/maps/{map}".format(path=self.OPPAI_FOLDER, map=self.map) try: # Check if we have to download the .osu file download = False if not os.path.isfile(mapFile): # .osu file doesn't exist. We must download it if glob.debug: consoleHelper.printColored( "[!] {} doesn't exist".format(mapFile), bcolors.YELLOW) download = True else: # File exists, check md5 if generalUtils.fileMd5(mapFile) != self.beatmap.fileMD5: # MD5 don't match, redownload .osu file if glob.debug: consoleHelper.printColored( "[!] Beatmaps md5 don't match", bcolors.YELLOW) download = True # Download .osu file if needed if download: if glob.debug: consoleHelper.printRippoppaiMessage( "Downloading {} from osu! servers...".format( self.beatmap.beatmapID)) # Get .osu file from osu servers fileContent = osuapiHelper.getOsuFileFromID( self.beatmap.beatmapID) # Make sure osu servers returned something if fileContent is None: raise exceptions.osuApiFailException(MODULE_NAME) # Delete old .osu file if it exists if os.path.isfile(mapFile): os.remove(mapFile) # Save .osu file with open(mapFile, "wb+") as f: f.write(fileContent.encode("latin-1")) else: # Map file is already in folder if glob.debug: consoleHelper.printRippoppaiMessage( "Found beatmap file {}".format(mapFile)) except exceptions.osuApiFailException: pass # Base command command = fixPath("{path}/oppai {mapFile}".format( path=self.OPPAI_FOLDER, mapFile=mapFile)) # Use only mods supported by oppai. modsFixed = self.mods & 5979 # Add params if needed 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 tillerino: command += " tillerino" if stars: command += " stars" # Debug output if glob.debug: consoleHelper.printRippoppaiMessage( "Executing {}".format(command)) # oppai output process = subprocess.run(command, shell=True, stdout=subprocess.PIPE) output = process.stdout.decode("utf-8") # Get standard or tillerino output sep = "\n" if UNIX else "\r\n" if output == ['']: # This happens if mode not supported or something self.pp = 0 self.stars = None return self.pp output = output.split(sep) # get rid of pesky warnings!!! try: float(output[0]) except ValueError: del output[0] if tillerino: # Get tillerino output (multiple lines) if stars: self.pp = output[:-2] self.stars = float(output[-2]) else: self.pp = output.split( sep)[: -1] # -1 because there's an empty line at the end else: # Get standard output (:l to remove (/r)/n at the end) l = -1 if UNIX else -2 if stars: self.pp = float(output[len(output) - 2][:l - 1]) else: self.pp = float(output[len(output) - 2][:l]) # Debug output if glob.debug: consoleHelper.printRippoppaiMessage("Calculated pp: {}".format( self.pp)) finally: return self.pp