2018-12-09 05:15:56 +00:00
from objects import score
from common.ripple import userUtils
from constants import rankedStatuses
from common.constants import mods as modsEnum
from objects import glob
2019-02-11 11:27:05 +00:00
from common.constants import privileges
2018-12-09 05:15:56 +00:00
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 ( )
2019-02-11 11:27:05 +00:00
@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 " ]
2018-12-09 05:15:56 +00:00
def setScores ( self ) :
"""
Set scores list
"""
# 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
2019-02-11 11:27:05 +00:00
personalBestScoreID = self . getPersonalBestID ( )
isPremium = userUtils . getPrivileges ( self . userID ) & privileges . USER_PREMIUM
2018-12-09 05:15:56 +00:00
# Output our personal best if found
2019-02-11 11:27:05 +00:00
if personalBestScoreID is not None :
s = score . score ( personalBestScoreID )
2018-12-09 05:15:56 +00:00
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 :
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)
2019-02-11 13:34:59 +00:00
if self . mods > - 1 :
2018-12-09 05:15:56 +00:00
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 = " "
2019-02-11 11:27:05 +00:00
2018-12-09 05:15:56 +00:00
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
2019-02-11 11:27:05 +00:00
query = self . buildQuery ( locals ( ) )
2018-12-09 05:15:56 +00:00
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 )
2019-02-11 11:27:05 +00:00
s . rank = c
2018-12-09 05:15:56 +00:00
# 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
2019-02-11 11:27:05 +00:00
query = self . buildQuery ( locals ( ) )
2018-12-09 05:15:56 +00:00
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
2019-02-11 11:27:05 +00:00
if personalBestScoreID is not None and self . personalBestRank < 1 :
2018-12-09 05:15:56 +00:00
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
2019-02-11 11:27:05 +00:00
if personalBestScoreID is not None and self . personalBestRank < 1 :
self . setPersonalBestRank ( )
2018-12-09 05:15:56 +00:00
# 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 )
2019-02-11 11:27:05 +00:00
def setPersonalBestRank ( self ) :
2018-12-09 05:15:56 +00:00
"""
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 """
2019-02-11 13:34:59 +00:00
2018-12-09 05:15:56 +00:00
# Country
if self . country :
query + = " AND users_stats.country = (SELECT country FROM users_stats WHERE id = %(userid)s LIMIT 1) "
2019-02-11 13:34:59 +00:00
2018-12-09 05:15:56 +00:00
# Mods
if self . mods > - 1 :
query + = " AND scores.mods = %(mods)s "
2019-02-11 13:34:59 +00:00
2018-12-09 05:15:56 +00:00
# Friends
if self . friends :
query + = " AND (scores.userid IN (SELECT user2 FROM users_relationships WHERE user1 = %(userid)s ) OR scores.userid = %(userid)s ) "
2019-02-11 13:34:59 +00:00
2018-12-09 05:15:56 +00:00
# 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
2019-02-11 11:27:05 +00:00
self . setPersonalBestRank ( ) # sets self.personalBestRank with the huge query
self . scores [ 0 ] . rank = self . personalBestRank
2018-12-09 05:15:56 +00:00
data + = self . scores [ 0 ] . getData ( )
# Output top 50 scores
for i in self . scores [ 1 : ] :
2019-02-11 13:34:59 +00:00
data + = i . getData ( pp = self . mods > - 1 )
2018-12-09 05:15:56 +00:00
return data