This commit is contained in:
depreciate 2019-02-11 21:57:05 +10:30
parent 45106ac18a
commit 410499ce79
5 changed files with 340 additions and 179 deletions

View 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())

View File

@ -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
View 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
}

View File

@ -1,16 +1,15 @@
from objects import rxscore
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_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 = score.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 = ""
@ -119,19 +119,16 @@ class scoreboard:
friends = "AND (scores_relax.userid IN (SELECT user2 FROM users_relationships WHERE user1 = %(userid)s) OR scores_relax.userid = %(userid)s)"
else:
friends = ""
if self.beatmap.rankedStatus == rankedStatuses.LOVED:
order = "ORDER BY score DESC"
else:
order = "ORDER BY pp DESC"
order = "ORDER BY pp DESC"
if isPremium: # Premium members can see up to 100 scores on leaderboards
limit = "LIMIT 100"
else:
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)
@ -141,11 +138,11 @@ class scoreboard:
if topScores is not None:
for topScore in topScores:
# Create score object
s = rxscore.score(topScore["id"], setData=False)
s = score.score(topScore["id"], setData=False)
# 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 +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
@ -172,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
@ -203,12 +199,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 +218,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 +226,6 @@ class scoreboard:
def getScoresData(self):
"""
Return scores data for getscores
return -- score data in getscores format
"""
data = ""
@ -243,11 +236,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

View File

@ -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 = ""
@ -113,7 +119,7 @@ class scoreboard:
friends = "AND (scores.userid IN (SELECT user2 FROM users_relationships WHERE user1 = %(userid)s) OR scores.userid = %(userid)s)"
else:
friends = ""
order = "ORDER BY score DESC"
if isPremium: # Premium members can see up to 100 scores on leaderboards
@ -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