commit
fc5a025bf0
8
handlers/generalHelper.py
Normal file
8
handlers/generalHelper.py
Normal file
|
@ -0,0 +1,8 @@
|
|||
def zingonify(d):
|
||||
"""
|
||||
Zingonifies a string
|
||||
:param d: input dict
|
||||
:return: zingonified dict as str
|
||||
"""
|
||||
|
||||
return "|".join(f"{k}:{v}" for k, v in d.items())
|
|
@ -30,6 +30,8 @@ from objects import score
|
|||
from objects import scoreboard
|
||||
from objects import relaxboard
|
||||
from objects import rxscore
|
||||
from helpers.generalHelper import zingonify
|
||||
from objects.charts import BeatmapChart, OverallChart
|
||||
from common import generalUtils
|
||||
|
||||
MODULE_NAME = "submit_modular"
|
||||
|
@ -215,6 +217,19 @@ class handler(requestsManager.asyncRequestHandler):
|
|||
log.warning("**{}** ({}) has been restricted due to notepad hack".format(username, userID), "cm")
|
||||
return
|
||||
|
||||
# Right before submitting the score, get the personal best score object (we need it for charts)
|
||||
if s.passed and s.oldPersonalBest > 0:
|
||||
oldPersonalBestRank = glob.personalBestCache.get(userID, s.fileMd5)
|
||||
if oldPersonalBestRank == 0:
|
||||
oldScoreboard = scoreboard.scoreboard(username, s.gameMode, beatmapInfo, False)
|
||||
oldScoreboard.setPersonalBestRank()
|
||||
oldPersonalBestRank = max(oldScoreboard.personalBestRank, 0)
|
||||
oldPersonalBest = score.score(s.oldPersonalBest, oldPersonalBestRank)
|
||||
else:
|
||||
oldPersonalBestRank = 0
|
||||
oldPersonalBest = None
|
||||
|
||||
|
||||
# Save score in db
|
||||
s.saveScoreInDB()
|
||||
|
||||
|
@ -331,17 +346,6 @@ class handler(requestsManager.asyncRequestHandler):
|
|||
oldUserData = glob.userStatsCache.get(userID, s.gameMode)
|
||||
oldRank = userUtils.getGameRank(userID, s.gameMode)
|
||||
|
||||
# Try to get oldPersonalBestRank from cache
|
||||
oldPersonalBestRank = glob.personalBestCache.get(userID, s.fileMd5)
|
||||
if oldPersonalBestRank == 0:
|
||||
# oldPersonalBestRank not found in cache, get it from db
|
||||
if isRelaxing:
|
||||
oldScoreboard = relaxboard.scoreboard(username, s.gameMode, beatmapInfo, False)
|
||||
else:
|
||||
oldScoreboard = scoreboard.scoreboard(username, s.gameMode, beatmapInfo, False)
|
||||
|
||||
oldScoreboard.setPersonalBest()
|
||||
oldPersonalBestRank = oldScoreboard.personalBestRank if oldScoreboard.personalBestRank > 0 else 0
|
||||
|
||||
# Always update users stats (total/ranked score, playcount, level, acc and pp)
|
||||
# even if not passed
|
||||
|
@ -401,8 +405,11 @@ class handler(requestsManager.asyncRequestHandler):
|
|||
# Trigger bancho stats cache update
|
||||
glob.redis.publish("peppy:update_rxcached_stats", userID)
|
||||
|
||||
# Get personal best after submitting the score
|
||||
newScoreboard = relaxboard.scoreboard(username, s.gameMode, beatmapInfo, True)
|
||||
newScoreboard = relaxboard.scoreboard(username, s.gameMode, beatmapInfo, False)
|
||||
newScoreboard.setPersonalBestRank()
|
||||
personalBestID = newScoreboard.getPersonalBestID()
|
||||
assert personalBestID is not None
|
||||
currentPersonalBest = rxscore.score(personalBestID, newScoreboard.personalBestRank)
|
||||
|
||||
# Get rank info (current rank, pp/score to next rank, user who is 1 rank above us)
|
||||
rankInfo = leaderboardHelper.rxgetRankInfo(userID, s.gameMode)
|
||||
|
@ -411,53 +418,70 @@ class handler(requestsManager.asyncRequestHandler):
|
|||
# Trigger bancho stats cache update
|
||||
glob.redis.publish("peppy:update_cached_stats", userID)
|
||||
|
||||
# Get personal best after submitting the score
|
||||
newScoreboard = scoreboard.scoreboard(username, s.gameMode, beatmapInfo, True)
|
||||
newScoreboard = scoreboard.scoreboard(username, s.gameMode, beatmapInfo, False)
|
||||
newScoreboard.setPersonalBestRank()
|
||||
personalBestID = newScoreboard.getPersonalBestID()
|
||||
assert personalBestID is not None
|
||||
currentPersonalBest = score.score(personalBestID, newScoreboard.personalBestRank)
|
||||
|
||||
# Get rank info (current rank, pp/score to next rank, user who is 1 rank above us)
|
||||
rankInfo = leaderboardHelper.getRankInfo(userID, s.gameMode)
|
||||
|
||||
# Output dictionary
|
||||
output = collections.OrderedDict()
|
||||
output["beatmapId"] = beatmapInfo.beatmapID
|
||||
output["beatmapSetId"] = beatmapInfo.beatmapSetID
|
||||
output["beatmapPlaycount"] = beatmapInfo.playcount
|
||||
output["beatmapPasscount"] = beatmapInfo.passcount
|
||||
#output["approvedDate"] = "2015-07-09 23:20:14\n"
|
||||
output["approvedDate"] = "\n"
|
||||
output["chartId"] = "overall"
|
||||
output["chartName"] = "Overall Ranking"
|
||||
output["chartEndDate"] = ""
|
||||
output["beatmapRankingBefore"] = oldPersonalBestRank
|
||||
output["beatmapRankingAfter"] = newScoreboard.personalBestRank
|
||||
output["rankedScoreBefore"] = oldUserData["rankedScore"]
|
||||
output["rankedScoreAfter"] = newUserData["rankedScore"]
|
||||
output["totalScoreBefore"] = oldUserData["totalScore"]
|
||||
output["totalScoreAfter"] = newUserData["totalScore"]
|
||||
output["playCountBefore"] = newUserData["playcount"]
|
||||
output["accuracyBefore"] = float(oldUserData["accuracy"])/100
|
||||
output["accuracyAfter"] = float(newUserData["accuracy"])/100
|
||||
output["rankBefore"] = oldRank
|
||||
output["rankAfter"] = rankInfo["currentRank"]
|
||||
output["toNextRank"] = rankInfo["difference"]
|
||||
output["toNextRankUser"] = rankInfo["nextUsername"]
|
||||
output["achievements"] = ""
|
||||
output["achievements-new"] = secret.achievements.utils.achievements_response(new_achievements)
|
||||
output["onlineScoreId"] = s.scoreID
|
||||
if newCharts:
|
||||
log.debug("Using new charts")
|
||||
dicts = [
|
||||
collections.OrderedDict([
|
||||
("beatmapId", beatmapInfo.beatmapID),
|
||||
("beatmapSetId", beatmapInfo.beatmapSetID),
|
||||
("beatmapPlaycount", beatmapInfo.playcount + 1),
|
||||
("beatmapPasscount", beatmapInfo.passcount + (s.completed == 3)),
|
||||
("approvedDate", beatmapInfo.rankingDate)
|
||||
]),
|
||||
BeatmapChart(
|
||||
oldPersonalBest if s.completed == 3 else currentPersonalBest,
|
||||
currentPersonalBest if s.completed == 3 else s,
|
||||
beatmapInfo.beatmapID,
|
||||
),
|
||||
OverallChart(
|
||||
userID, oldUserData, newUserData, beatmapInfo, s, new_achievements, oldRank, rankInfo["currentRank"]
|
||||
)
|
||||
]
|
||||
else:
|
||||
log.debug("Using old charts")
|
||||
dicts = [
|
||||
collections.OrderedDict([
|
||||
("beatmapId", beatmapInfo.beatmapID),
|
||||
("beatmapSetId", beatmapInfo.beatmapSetID),
|
||||
("beatmapPlaycount", beatmapInfo.playcount),
|
||||
("beatmapPasscount", beatmapInfo.passcount),
|
||||
("approvedDate", beatmapInfo.rankingDate)
|
||||
]),
|
||||
collections.OrderedDict([
|
||||
("chartId", "overall"),
|
||||
("chartName", "Overall Ranking"),
|
||||
("chartEndDate", ""),
|
||||
("beatmapRankingBefore", oldPersonalBestRank),
|
||||
("beatmapRankingAfter", newScoreboard.personalBestRank),
|
||||
("rankedScoreBefore", oldUserData["rankedScore"]),
|
||||
("rankedScoreAfter", newUserData["rankedScore"]),
|
||||
("totalScoreBefore", oldUserData["totalScore"]),
|
||||
("totalScoreAfter", newUserData["totalScore"]),
|
||||
("playCountBefore", newUserData["playcount"]),
|
||||
("accuracyBefore", float(oldUserData["accuracy"])/100),
|
||||
("accuracyAfter", float(newUserData["accuracy"])/100),
|
||||
("rankBefore", oldRank),
|
||||
("rankAfter", rankInfo["currentRank"]),
|
||||
("toNextRank", rankInfo["difference"]),
|
||||
("toNextRankUser", rankInfo["nextUsername"]),
|
||||
("achievements", ""),
|
||||
("achievements-new", secret.achievements.utils.achievements_response(new_achievements)),
|
||||
("onlineScoreId", s.scoreID)
|
||||
])
|
||||
]
|
||||
output = "\n".join(zingonify(x) for x in dicts)
|
||||
|
||||
# Build final string
|
||||
msg = ""
|
||||
for line, val in output.items():
|
||||
msg += "{}:{}".format(line, val)
|
||||
if val != "\n":
|
||||
if (len(output) - 1) != list(output.keys()).index(line):
|
||||
msg += "|"
|
||||
else:
|
||||
msg += "\n"
|
||||
|
||||
# Some debug messages
|
||||
log.debug("Generated output for online ranking screen!")
|
||||
log.debug(msg)
|
||||
log.debug(output)
|
||||
|
||||
# Send message to #announce if we're rank #1
|
||||
if newScoreboard.personalBestRank == 1 and s.completed == 3 and not restricted:
|
||||
|
|
131
objects/charts.py
Normal file
131
objects/charts.py
Normal file
|
@ -0,0 +1,131 @@
|
|||
from secret.achievements.utils import achievements_response
|
||||
|
||||
|
||||
class Chart:
|
||||
"""
|
||||
Chart base class
|
||||
"""
|
||||
|
||||
|
||||
def __init__(self, id_, url, name):
|
||||
"""
|
||||
Initializes a new chart.
|
||||
|
||||
:param id_: chart id. Currently known values are 'beatmap' and 'overall'
|
||||
:param url: URL to open when clicking on the chart title.
|
||||
:param name: chart name displayed in the game client
|
||||
"""
|
||||
self.id_ = id_
|
||||
self.url = url
|
||||
self.name = name
|
||||
|
||||
def items(self):
|
||||
"""
|
||||
`items()` method that allows this class to be used as a iterable dict
|
||||
|
||||
:return:
|
||||
"""
|
||||
return self.output_attrs.items()
|
||||
|
||||
@property
|
||||
def output_attrs(self):
|
||||
"""
|
||||
An unzingonified dict containing the stuff that will be sent to the game client
|
||||
|
||||
:return: dict
|
||||
"""
|
||||
return {
|
||||
"chartId": self.id_,
|
||||
"chartUrl": self.url,
|
||||
"chartName": self.name
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def before_after_dict(name, values, none_value="0"):
|
||||
"""
|
||||
Turns a tuple with two elements in a dict with two elements.
|
||||
|
||||
:param name: prefix of the keys
|
||||
:param values: (value_before, value_after). value_before and value_after can be None.
|
||||
:param none_value: value to use instead of None (None, when zingonified, is not recognized by the game client)
|
||||
:return: { XXXBefore -> first element, XXXAfter -> second element }, where XXX is `name`
|
||||
"""
|
||||
return {
|
||||
f"{name}{'Before' if i == 0 else 'After'}": x if x is not None else none_value for i, x in enumerate(values)
|
||||
}
|
||||
|
||||
|
||||
class BeatmapChart(Chart):
|
||||
"""
|
||||
Beatmap ranking chart
|
||||
"""
|
||||
def __init__(self, old_score, new_score, beatmap_id):
|
||||
"""
|
||||
Initializes a new BeatmapChart object.
|
||||
|
||||
:param old_score: score object of the old score
|
||||
:param new_score: score object of the currently submitted score
|
||||
:param beatmap_id: beatmap id, for the clickable link
|
||||
"""
|
||||
super(BeatmapChart, self).__init__("beatmap", f"https://akatsuki.pw/b/{beatmap_id}", "Beatmap Ranking")
|
||||
self.rank = (old_score.rank if old_score is not None else None, new_score.rank)
|
||||
self.max_combo = (old_score.maxCombo if old_score is not None else None, new_score.maxCombo)
|
||||
self.accuracy = (old_score.accuracy * 100 if old_score is not None else None, new_score.accuracy * 100)
|
||||
self.ranked_score = (old_score.score if old_score is not None else None, new_score.score)
|
||||
self.pp = (old_score.pp if old_score is not None else None, new_score.pp)
|
||||
self.score_id = new_score.scoreID
|
||||
|
||||
@property
|
||||
def output_attrs(self):
|
||||
return {
|
||||
**super(BeatmapChart, self).output_attrs,
|
||||
**self.before_after_dict("rank", self.rank, none_value=""),
|
||||
**self.before_after_dict("maxCombo", self.max_combo),
|
||||
**self.before_after_dict("accuracy", self.accuracy),
|
||||
**self.before_after_dict("rankedScore", self.ranked_score),
|
||||
**self.before_after_dict("totalScore", self.ranked_score),
|
||||
**self.before_after_dict("pp", self.pp),
|
||||
"onlineScoreId": self.score_id
|
||||
}
|
||||
|
||||
|
||||
class OverallChart(Chart):
|
||||
"""
|
||||
Overall ranking chart achievements
|
||||
"""
|
||||
def __init__(self, user_id, old_user_stats, new_user_stats, bi, score, new_achievements, old_rank, new_rank):
|
||||
"""
|
||||
Initializes a new OverallChart object.
|
||||
This constructor sucks because LETS itself sucks.
|
||||
|
||||
:param user_id: id of the user
|
||||
:param old_user_stats: user stats dict before submitting the score
|
||||
:param new_user_stats: user stats dict after submitting the score
|
||||
:param score: score object of the scores that has just been submitted
|
||||
:param new_achievements: achievements unlocked list
|
||||
:param old_rank: global rank before submitting the scpre
|
||||
:param new_rank: global rank after submitting the score
|
||||
"""
|
||||
super(OverallChart, self).__init__("overall", f"https://akatsuki.pw/u/{user_id}", "Overall Ranking")
|
||||
self.rank = (old_rank, new_rank)
|
||||
self.ranked_score = (old_user_stats["rankedScore"], new_user_stats["rankedScore"])
|
||||
self.total_score = (old_user_stats["totalScore"], new_user_stats["totalScore"])
|
||||
self.max_combo = (bi.maxCombo, bi.maxCombo)
|
||||
self.accuracy = (old_user_stats["accuracy"], new_user_stats["accuracy"])
|
||||
self.pp = (old_user_stats["pp"], new_user_stats["pp"])
|
||||
self.new_achievements = new_achievements
|
||||
self.score_id = score.scoreID
|
||||
|
||||
@property
|
||||
def output_attrs(self):
|
||||
return {
|
||||
**super(OverallChart, self).output_attrs,
|
||||
**self.before_after_dict("rank", self.rank),
|
||||
**self.before_after_dict("rankedScore", self.ranked_score),
|
||||
**self.before_after_dict("totalScore", self.total_score),
|
||||
**self.before_after_dict("maxCombo", self.max_combo),
|
||||
**self.before_after_dict("accuracy", self.accuracy),
|
||||
**self.before_after_dict("pp", self.pp),
|
||||
"achievements-new": achievements_response(self.new_achievements),
|
||||
"onlineScoreId": self.score_id
|
||||
}
|
|
@ -2,15 +2,14 @@ from objects import rxscore
|
|||
from common.ripple import userUtils
|
||||
from constants import rankedStatuses
|
||||
from common.constants import mods as modsEnum
|
||||
from common.constants import privileges
|
||||
from objects import glob
|
||||
from common.constants import privileges
|
||||
|
||||
|
||||
class scoreboard:
|
||||
def __init__(self, username, gameMode, beatmap, setScores = True, country = False, friends = False, mods = -1):
|
||||
"""
|
||||
Initialize a leaderboard object
|
||||
|
||||
username -- username of who's requesting the scoreboard. None if not known
|
||||
gameMode -- requested gameMode
|
||||
beatmap -- beatmap objecy relative to this leaderboard
|
||||
|
@ -29,15 +28,48 @@ class scoreboard:
|
|||
if setScores:
|
||||
self.setScores()
|
||||
|
||||
@staticmethod
|
||||
def buildQuery(params):
|
||||
return "{select} {joins} {country} {mods} {friends} {order} {limit}".format(**params)
|
||||
|
||||
def getPersonalBestID(self):
|
||||
if self.userID == 0:
|
||||
return None
|
||||
|
||||
# Query parts
|
||||
cdef str select = ""
|
||||
cdef str joins = ""
|
||||
cdef str country = ""
|
||||
cdef str mods = ""
|
||||
cdef str friends = ""
|
||||
cdef str order = ""
|
||||
cdef str limit = ""
|
||||
select = "SELECT id FROM scores_relax WHERE userid = %(userid)s AND beatmap_md5 = %(md5)s AND play_mode = %(mode)s AND completed = 3"
|
||||
|
||||
# Mods
|
||||
if self.mods > -1:
|
||||
mods = "AND mods = %(mods)s"
|
||||
|
||||
# Friends ranking
|
||||
if self.friends:
|
||||
friends = "AND (scores_relax.userid IN (SELECT user2 FROM users_relationships WHERE user1 = %(userid)s) OR scores_relax.userid = %(userid)s)"
|
||||
|
||||
# Sort and limit at the end
|
||||
order = "ORDER BY pp DESC"
|
||||
limit = "LIMIT 1"
|
||||
|
||||
# Build query, get params and run query
|
||||
query = self.buildQuery(locals())
|
||||
params = {"userid": self.userID, "md5": self.beatmap.fileMD5, "mode": self.gameMode, "mods": self.mods}
|
||||
id_ = glob.db.fetch(query, params)
|
||||
if id_ is None:
|
||||
return None
|
||||
return id_["id"]
|
||||
|
||||
def setScores(self):
|
||||
"""
|
||||
Set scores list
|
||||
"""
|
||||
|
||||
isPremium = userUtils.getPrivileges(self.userID) & privileges.USER_PREMIUM
|
||||
|
||||
def buildQuery(params):
|
||||
return "{select} {joins} {country} {mods} {friends} {order} {limit}".format(**params)
|
||||
# Reset score list
|
||||
self.scores = []
|
||||
self.scores.append(-1)
|
||||
|
@ -56,38 +88,11 @@ class scoreboard:
|
|||
cdef str limit = ""
|
||||
|
||||
# Find personal best score
|
||||
if self.userID != 0:
|
||||
# Query parts
|
||||
select = "SELECT id FROM scores_relax WHERE userid = %(userid)s AND beatmap_md5 = %(md5)s AND play_mode = %(mode)s AND completed = 3"
|
||||
|
||||
# Mods
|
||||
if self.mods > -1:
|
||||
mods = "AND mods = %(mods)s"
|
||||
|
||||
# Friends ranking
|
||||
if self.friends:
|
||||
friends = "AND (scores_relax.userid IN (SELECT user2 FROM users_relationships WHERE user1 = %(userid)s) OR scores_relax.userid = %(userid)s)"
|
||||
else:
|
||||
friends = ""
|
||||
|
||||
# Sort and limit at the end
|
||||
if self.beatmap.rankedStatus == rankedStatuses.LOVED:
|
||||
order = "ORDER BY score DESC"
|
||||
else:
|
||||
order = "ORDER BY pp DESC"
|
||||
|
||||
limit = "LIMIT 1"
|
||||
|
||||
# Build query, get params and run query
|
||||
query = buildQuery(locals())
|
||||
params = {"userid": self.userID, "md5": self.beatmap.fileMD5, "mode": self.gameMode, "mods": self.mods}
|
||||
personalBestScore = glob.db.fetch(query, params)
|
||||
else:
|
||||
personalBestScore = None
|
||||
|
||||
personalBestScoreID = self.getPersonalBestID()
|
||||
isPremium = userUtils.getPrivileges(self.userID) & privileges.USER_PREMIUM
|
||||
# Output our personal best if found
|
||||
if personalBestScore is not None:
|
||||
s = rxscore.score(personalBestScore["id"])
|
||||
if personalBestScoreID is not None:
|
||||
s = rxscore.score(personalBestScoreID)
|
||||
self.scores[0] = s
|
||||
else:
|
||||
# No personal best
|
||||
|
@ -99,17 +104,12 @@ class scoreboard:
|
|||
|
||||
# Country ranking
|
||||
if self.country:
|
||||
""" Honestly this is more of a preference thing than something that should be premium only?
|
||||
if isPremium:
|
||||
country = "AND user_clans.clan = (SELECT clan FROM user_clans WHERE user = %(userid)s LIMIT 1)"
|
||||
else:
|
||||
"""
|
||||
country = "AND users_stats.country = (SELECT country FROM users_stats WHERE id = %(userid)s LIMIT 1)"
|
||||
else:
|
||||
country = ""
|
||||
|
||||
# Mods ranking (ignore auto, since we use it for pp sorting)
|
||||
if self.mods > -1:
|
||||
if self.mods > -1 and self.mods & modsEnum.AUTOPLAY == 0:
|
||||
mods = "AND scores_relax.mods = %(mods)s"
|
||||
else:
|
||||
mods = ""
|
||||
|
@ -131,7 +131,7 @@ class scoreboard:
|
|||
limit = "LIMIT 50"
|
||||
|
||||
# Build query, get params and run query
|
||||
query = buildQuery(locals())
|
||||
query = self.buildQuery(locals())
|
||||
params = {"beatmap_md5": self.beatmap.fileMD5, "play_mode": self.gameMode, "userid": self.userID, "mods": self.mods}
|
||||
topScores = glob.db.fetchAll(query, params)
|
||||
|
||||
|
@ -145,7 +145,7 @@ class scoreboard:
|
|||
|
||||
# Set data and rank from topScores's row
|
||||
s.setDataFromDict(topScore)
|
||||
s.setRank(c)
|
||||
s.rank = c
|
||||
|
||||
# Check if this top 50 score is our personal best
|
||||
if s.playerName == self.username:
|
||||
|
@ -160,9 +160,8 @@ class scoreboard:
|
|||
# Count all scores on this map
|
||||
select = "SELECT COUNT(*) AS count"
|
||||
limit = "LIMIT 1"
|
||||
|
||||
# Build query, get params and run query
|
||||
query = buildQuery(locals())
|
||||
query = self.buildQuery(locals())
|
||||
count = glob.db.fetch(query, params)
|
||||
if count == None:
|
||||
self.totalScores = 0
|
||||
|
@ -172,19 +171,19 @@ class scoreboard:
|
|||
self.totalScores = c-1'''
|
||||
|
||||
# If personal best score was not in top 50, try to get it from cache
|
||||
if personalBestScore is not None and self.personalBestRank < 1:
|
||||
if personalBestScoreID is not None and self.personalBestRank < 1:
|
||||
self.personalBestRank = glob.personalBestCache.get(self.userID, self.beatmap.fileMD5, self.country, self.friends, self.mods)
|
||||
|
||||
# It's not even in cache, get it from db
|
||||
if personalBestScore is not None and self.personalBestRank < 1:
|
||||
self.setPersonalBest()
|
||||
if personalBestScoreID is not None and self.personalBestRank < 1:
|
||||
self.setPersonalBestRank()
|
||||
|
||||
# Cache our personal best rank so we can eventually use it later as
|
||||
# before personal best rank" in submit modular when building ranking panel
|
||||
if self.personalBestRank >= 1:
|
||||
glob.personalBestCache.set(self.userID, self.personalBestRank, self.beatmap.fileMD5)
|
||||
|
||||
def setPersonalBest(self):
|
||||
def setPersonalBestRank(self):
|
||||
"""
|
||||
Set personal best rank ONLY
|
||||
Ikr, that query is HUGE but xd
|
||||
|
@ -203,12 +202,15 @@ class scoreboard:
|
|||
if hasScore is None:
|
||||
return
|
||||
|
||||
|
||||
# We have a score, run the huge query
|
||||
# Base query
|
||||
query = """SELECT COUNT(*) AS rank FROM scores_relax STRAIGHT_JOIN users ON scores_relax.userid = users.id STRAIGHT_JOIN users_stats ON users.id = users_stats.id WHERE scores_relax.{PPorScore} >= (
|
||||
SELECT {PPorScore} FROM scores_relax WHERE beatmap_md5 = %(md5)s AND play_mode = %(mode)s AND completed = 3 AND userid = %(userid)s LIMIT 1
|
||||
) AND scores_relax.beatmap_md5 = %(md5)s AND scores_relax.play_mode = %(mode)s AND scores_relax.completed = 3 AND users.privileges & 1 > 0""".format(PPorScore="score" if self.beatmap.rankedStatus == rankedStatuses.LOVED else "pp")
|
||||
|
||||
overwrite = "pp"
|
||||
# We have a score, run the huge query
|
||||
# Base query
|
||||
query = """SELECT COUNT(*) AS rank FROM scores_relax STRAIGHT_JOIN users ON scores_relax.userid = users.id STRAIGHT_JOIN users_stats ON users.id = users_stats.id WHERE scores_relax.{0} >= (
|
||||
SELECT {0} FROM scores_relax WHERE beatmap_md5 = %(md5)s AND play_mode = %(mode)s AND completed = 3 AND userid = %(userid)s LIMIT 1
|
||||
) AND scores_relax.beatmap_md5 = %(md5)s AND scores_relax.play_mode = %(mode)s AND scores_relax.completed = 3 AND users.privileges & 1 > 0""".format(overwrite)
|
||||
# Country
|
||||
if self.country:
|
||||
query += " AND users_stats.country = (SELECT country FROM users_stats WHERE id = %(userid)s LIMIT 1)"
|
||||
|
@ -219,12 +221,7 @@ class scoreboard:
|
|||
if self.friends:
|
||||
query += " AND (scores_relax.userid IN (SELECT user2 FROM users_relationships WHERE user1 = %(userid)s) OR scores_relax.userid = %(userid)s)"
|
||||
# Sort and limit at the end
|
||||
|
||||
if self.beatmap.rankedStatus == rankedStatuses.LOVED:
|
||||
query += " ORDER BY score DESC LIMIT 1"
|
||||
else:
|
||||
query += " ORDER BY pp DESC LIMIT 1"
|
||||
|
||||
query += " ORDER BY pp DESC LIMIT 1".format(overwrite)
|
||||
result = glob.db.fetch(query, {"md5": self.beatmap.fileMD5, "userid": self.userID, "mode": self.gameMode, "mods": self.mods})
|
||||
if result is not None:
|
||||
self.personalBestRank = result["rank"]
|
||||
|
@ -232,7 +229,6 @@ class scoreboard:
|
|||
def getScoresData(self):
|
||||
"""
|
||||
Return scores data for getscores
|
||||
|
||||
return -- score data in getscores format
|
||||
"""
|
||||
data = ""
|
||||
|
@ -243,11 +239,12 @@ class scoreboard:
|
|||
data += "\n"
|
||||
else:
|
||||
# Set personal best score rank
|
||||
self.setPersonalBest() # sets self.personalBestRank with the huge query
|
||||
self.scores[0].setRank(self.personalBestRank)
|
||||
data += self.scores[0].getData()
|
||||
self.setPersonalBestRank() # sets self.personalBestRank with the huge query
|
||||
self.scores[0].rank = self.personalBestRank
|
||||
data += self.scores[0].getData(pp=True)
|
||||
|
||||
# Output top 50 scores
|
||||
for i in self.scores[1:]:
|
||||
data += i.getData(pp=self.beatmap.rankedStatus != rankedStatuses.LOVED)
|
||||
data += i.getData(pp=True)
|
||||
|
||||
return data
|
||||
|
|
|
@ -2,15 +2,14 @@ from objects import score
|
|||
from common.ripple import userUtils
|
||||
from constants import rankedStatuses
|
||||
from common.constants import mods as modsEnum
|
||||
from common.constants import privileges
|
||||
from objects import glob
|
||||
from common.constants import privileges
|
||||
|
||||
|
||||
class scoreboard:
|
||||
def __init__(self, username, gameMode, beatmap, setScores = True, country = False, friends = False, mods = -1):
|
||||
"""
|
||||
Initialize a leaderboard object
|
||||
|
||||
username -- username of who's requesting the scoreboard. None if not known
|
||||
gameMode -- requested gameMode
|
||||
beatmap -- beatmap objecy relative to this leaderboard
|
||||
|
@ -29,15 +28,48 @@ class scoreboard:
|
|||
if setScores:
|
||||
self.setScores()
|
||||
|
||||
@staticmethod
|
||||
def buildQuery(params):
|
||||
return "{select} {joins} {country} {mods} {friends} {order} {limit}".format(**params)
|
||||
|
||||
def getPersonalBestID(self):
|
||||
if self.userID == 0:
|
||||
return None
|
||||
|
||||
# Query parts
|
||||
cdef str select = ""
|
||||
cdef str joins = ""
|
||||
cdef str country = ""
|
||||
cdef str mods = ""
|
||||
cdef str friends = ""
|
||||
cdef str order = ""
|
||||
cdef str limit = ""
|
||||
select = "SELECT id FROM scores WHERE userid = %(userid)s AND beatmap_md5 = %(md5)s AND play_mode = %(mode)s AND completed = 3"
|
||||
|
||||
# Mods
|
||||
if self.mods > -1:
|
||||
mods = "AND mods = %(mods)s"
|
||||
|
||||
# Friends ranking
|
||||
if self.friends:
|
||||
friends = "AND (scores.userid IN (SELECT user2 FROM users_relationships WHERE user1 = %(userid)s) OR scores.userid = %(userid)s)"
|
||||
|
||||
# Sort and limit at the end
|
||||
order = "ORDER BY score DESC"
|
||||
limit = "LIMIT 1"
|
||||
|
||||
# Build query, get params and run query
|
||||
query = self.buildQuery(locals())
|
||||
params = {"userid": self.userID, "md5": self.beatmap.fileMD5, "mode": self.gameMode, "mods": self.mods}
|
||||
id_ = glob.db.fetch(query, params)
|
||||
if id_ is None:
|
||||
return None
|
||||
return id_["id"]
|
||||
|
||||
def setScores(self):
|
||||
"""
|
||||
Set scores list
|
||||
"""
|
||||
|
||||
isPremium = userUtils.getPrivileges(self.userID) & privileges.USER_PREMIUM
|
||||
|
||||
def buildQuery(params):
|
||||
return "{select} {joins} {country} {mods} {friends} {order} {limit}".format(**params)
|
||||
# Reset score list
|
||||
self.scores = []
|
||||
self.scores.append(-1)
|
||||
|
@ -56,32 +88,11 @@ class scoreboard:
|
|||
cdef str limit = ""
|
||||
|
||||
# Find personal best score
|
||||
if self.userID != 0:
|
||||
# Query parts
|
||||
select = "SELECT id FROM scores WHERE userid = %(userid)s AND beatmap_md5 = %(md5)s AND play_mode = %(mode)s AND completed = 3"
|
||||
|
||||
# Mods
|
||||
if self.mods > -1:
|
||||
mods = "AND mods = %(mods)s"
|
||||
|
||||
# Friends ranking
|
||||
if self.friends:
|
||||
friends = "AND (scores.userid IN (SELECT user2 FROM users_relationships WHERE user1 = %(userid)s) OR scores.userid = %(userid)s)"
|
||||
|
||||
# Sort and limit at the end
|
||||
order = "ORDER BY score DESC"
|
||||
limit = "LIMIT 1"
|
||||
|
||||
# Build query, get params and run query
|
||||
query = buildQuery(locals())
|
||||
params = {"userid": self.userID, "md5": self.beatmap.fileMD5, "mode": self.gameMode, "mods": self.mods}
|
||||
personalBestScore = glob.db.fetch(query, params)
|
||||
else:
|
||||
personalBestScore = None
|
||||
|
||||
personalBestScoreID = self.getPersonalBestID()
|
||||
isPremium = userUtils.getPrivileges(self.userID) & privileges.USER_PREMIUM
|
||||
# Output our personal best if found
|
||||
if personalBestScore is not None:
|
||||
s = score.score(personalBestScore["id"])
|
||||
if personalBestScoreID is not None:
|
||||
s = score.score(personalBestScoreID)
|
||||
self.scores[0] = s
|
||||
else:
|
||||
# No personal best
|
||||
|
@ -93,17 +104,12 @@ class scoreboard:
|
|||
|
||||
# Country ranking
|
||||
if self.country:
|
||||
""" Honestly this is more of a preference thing than something that should be premium only?
|
||||
if isPremium:
|
||||
country = "AND user_clans.clan = (SELECT clan FROM user_clans WHERE user = %(userid)s LIMIT 1)"
|
||||
else:
|
||||
"""
|
||||
country = "AND users_stats.country = (SELECT country FROM users_stats WHERE id = %(userid)s LIMIT 1)"
|
||||
else:
|
||||
country = ""
|
||||
|
||||
# Mods ranking (ignore auto, since we use it for pp sorting)
|
||||
if self.mods > -1:
|
||||
if self.mods > -1 and self.mods & modsEnum.AUTOPLAY == 0:
|
||||
mods = "AND scores.mods = %(mods)s"
|
||||
else:
|
||||
mods = ""
|
||||
|
@ -122,7 +128,7 @@ class scoreboard:
|
|||
limit = "LIMIT 50"
|
||||
|
||||
# Build query, get params and run query
|
||||
query = buildQuery(locals())
|
||||
query = self.buildQuery(locals())
|
||||
params = {"beatmap_md5": self.beatmap.fileMD5, "play_mode": self.gameMode, "userid": self.userID, "mods": self.mods}
|
||||
topScores = glob.db.fetchAll(query, params)
|
||||
|
||||
|
@ -136,7 +142,7 @@ class scoreboard:
|
|||
|
||||
# Set data and rank from topScores's row
|
||||
s.setDataFromDict(topScore)
|
||||
s.setRank(c)
|
||||
s.rank = c
|
||||
|
||||
# Check if this top 50 score is our personal best
|
||||
if s.playerName == self.username:
|
||||
|
@ -151,9 +157,8 @@ class scoreboard:
|
|||
# Count all scores on this map
|
||||
select = "SELECT COUNT(*) AS count"
|
||||
limit = "LIMIT 1"
|
||||
|
||||
# Build query, get params and run query
|
||||
query = buildQuery(locals())
|
||||
query = self.buildQuery(locals())
|
||||
count = glob.db.fetch(query, params)
|
||||
if count == None:
|
||||
self.totalScores = 0
|
||||
|
@ -163,19 +168,19 @@ class scoreboard:
|
|||
self.totalScores = c-1'''
|
||||
|
||||
# If personal best score was not in top 50, try to get it from cache
|
||||
if personalBestScore is not None and self.personalBestRank < 1:
|
||||
if personalBestScoreID is not None and self.personalBestRank < 1:
|
||||
self.personalBestRank = glob.personalBestCache.get(self.userID, self.beatmap.fileMD5, self.country, self.friends, self.mods)
|
||||
|
||||
# It's not even in cache, get it from db
|
||||
if personalBestScore is not None and self.personalBestRank < 1:
|
||||
self.setPersonalBest()
|
||||
if personalBestScoreID is not None and self.personalBestRank < 1:
|
||||
self.setPersonalBestRank()
|
||||
|
||||
# Cache our personal best rank so we can eventually use it later as
|
||||
# before personal best rank" in submit modular when building ranking panel
|
||||
if self.personalBestRank >= 1:
|
||||
glob.personalBestCache.set(self.userID, self.personalBestRank, self.beatmap.fileMD5)
|
||||
|
||||
def setPersonalBest(self):
|
||||
def setPersonalBestRank(self):
|
||||
"""
|
||||
Set personal best rank ONLY
|
||||
Ikr, that query is HUGE but xd
|
||||
|
@ -217,7 +222,6 @@ class scoreboard:
|
|||
def getScoresData(self):
|
||||
"""
|
||||
Return scores data for getscores
|
||||
|
||||
return -- score data in getscores format
|
||||
"""
|
||||
data = ""
|
||||
|
@ -228,8 +232,8 @@ class scoreboard:
|
|||
data += "\n"
|
||||
else:
|
||||
# Set personal best score rank
|
||||
self.setPersonalBest() # sets self.personalBestRank with the huge query
|
||||
self.scores[0].setRank(self.personalBestRank)
|
||||
self.setPersonalBestRank() # sets self.personalBestRank with the huge query
|
||||
self.scores[0].rank = self.personalBestRank
|
||||
data += self.scores[0].getData()
|
||||
|
||||
# Output top 50 scores
|
||||
|
|
Reference in New Issue
Block a user