This repository has been archived on 2022-02-23. You can view files and clone it, but cannot push or open issues or pull requests.
lets/objects/scoreboard.pyx

240 lines
8.2 KiB
Cython

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
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
setScores -- if True, will get personal/top 50 scores automatically. Optional. Default: True
"""
self.scores = [] # list containing all top 50 scores objects. First object is personal best
self.totalScores = 0
self.personalBestRank = -1 # our personal best rank, -1 if not found yet
self.username = username # username of who's requesting the scoreboard. None if not known
self.userID = userUtils.getID(self.username) # username's userID
self.gameMode = gameMode # requested gameMode
self.beatmap = beatmap # beatmap objecy relative to this leaderboard
self.country = country
self.friends = friends
self.mods = mods
if setScores:
self.setScores()
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)
# Make sure the beatmap is ranked
if self.beatmap.rankedStatus < rankedStatuses.RANKED:
return
# Query parts
cdef str select = ""
cdef str joins = ""
cdef str country = ""
cdef str mods = ""
cdef str friends = ""
cdef str order = ""
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
# Output our personal best if found
if personalBestScore is not None:
s = score.score(personalBestScore["id"])
self.scores[0] = s
else:
# No personal best
self.scores[0] = -1
# Get top 50 scores
select = "SELECT *"
joins = "FROM scores STRAIGHT_JOIN users ON scores.userid = users.id STRAIGHT_JOIN users_stats ON users.id = users_stats.id WHERE scores.beatmap_md5 = %(beatmap_md5)s AND scores.play_mode = %(play_mode)s AND scores.completed = 3 AND (users.privileges & 1 > 0 OR users.id = %(userid)s)"
# 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:
mods = "AND scores.mods = %(mods)s"
else:
mods = ""
# Friends ranking
if self.friends:
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
limit = "LIMIT 100"
else:
limit = "LIMIT 50"
# Build query, get params and run query
query = buildQuery(locals())
params = {"beatmap_md5": self.beatmap.fileMD5, "play_mode": self.gameMode, "userid": self.userID, "mods": self.mods}
topScores = glob.db.fetchAll(query, params)
# Set data for all scores
cdef int c = 1
cdef dict topScore
if topScores is not None:
for topScore in topScores:
# Create score object
s = score.score(topScore["id"], setData=False)
# Set data and rank from topScores's row
s.setDataFromDict(topScore)
s.setRank(c)
# Check if this top 50 score is our personal best
if s.playerName == self.username:
self.personalBestRank = c
# Add this score to scores list and increment rank
self.scores.append(s)
c+=1
'''# If we have more than 50 scores, run query to get scores count
if c >= 50:
# Count all scores on this map
select = "SELECT COUNT(*) AS count"
limit = "LIMIT 1"
# Build query, get params and run query
query = buildQuery(locals())
count = glob.db.fetch(query, params)
if count == None:
self.totalScores = 0
else:
self.totalScores = count["count"]
else:
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:
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()
# 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):
"""
Set personal best rank ONLY
Ikr, that query is HUGE but xd
"""
# Before running the HUGE query, make sure we have a score on that map
cdef str query = "SELECT id FROM scores WHERE beatmap_md5 = %(md5)s AND userid = %(userid)s AND play_mode = %(mode)s AND completed = 3"
# Mods
if self.mods > -1:
query += " AND scores.mods = %(mods)s"
# Friends ranking
if self.friends:
query += " AND (scores.userid IN (SELECT user2 FROM users_relationships WHERE user1 = %(userid)s) OR scores.userid = %(userid)s)"
# Sort and limit at the end
query += " LIMIT 1"
hasScore = glob.db.fetch(query, {"md5": self.beatmap.fileMD5, "userid": self.userID, "mode": self.gameMode, "mods": self.mods})
if hasScore is None:
return
# We have a score, run the huge query
# Base query
query = """SELECT COUNT(*) AS rank FROM scores STRAIGHT_JOIN users ON scores.userid = users.id STRAIGHT_JOIN users_stats ON users.id = users_stats.id WHERE scores.score >= (
SELECT score FROM scores WHERE beatmap_md5 = %(md5)s AND play_mode = %(mode)s AND completed = 3 AND userid = %(userid)s LIMIT 1
) AND scores.beatmap_md5 = %(md5)s AND scores.play_mode = %(mode)s AND scores.completed = 3 AND users.privileges & 1 > 0"""
# Country
if self.country:
query += " AND users_stats.country = (SELECT country FROM users_stats WHERE id = %(userid)s LIMIT 1)"
# Mods
if self.mods > -1:
query += " AND scores.mods = %(mods)s"
# Friends
if self.friends:
query += " AND (scores.userid IN (SELECT user2 FROM users_relationships WHERE user1 = %(userid)s) OR scores.userid = %(userid)s)"
# Sort and limit at the end
query += " ORDER BY score DESC LIMIT 1"
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"]
def getScoresData(self):
"""
Return scores data for getscores
return -- score data in getscores format
"""
data = ""
# Output personal best
if self.scores[0] == -1:
# We don't have a personal best score
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()
# Output top 50 scores
for i in self.scores[1:]:
data += i.getData(pp=self.mods > -1 and self.mods & modsEnum.AUTOPLAY > 0)
return data