2023-09-10 12:59:22 +01:00
import LatLng from "./LatLng" ;
2022-11-16 15:25:46 +00:00
import { RankingModes } from "../enums/RankingModes" ;
2023-09-10 12:59:22 +01:00
import Match from "./Match" ;
import DataStream from "./DataStream" ;
import StatusUpdate from "../packets/StatusUpdate" ;
import Shared from "../objects/Shared" ;
import Slot from "./Slot" ;
2022-11-16 15:25:46 +00:00
const rankingModes = [
"pp_raw" ,
"ranked_score" ,
"avg_accuracy"
] ;
2023-09-10 12:59:22 +01:00
export default class User {
2022-11-16 15:25:46 +00:00
private static readonly EMPTY_BUFFER = Buffer . alloc ( 0 ) ;
2023-08-20 13:03:01 +01:00
public shared :Shared ;
2022-11-19 01:06:03 +00:00
2022-11-16 15:25:46 +00:00
public id :number ;
public username :string ;
public uuid :string ;
public readonly connectTime :number = Date . now ( ) ;
public timeoutTime :number = Date . now ( ) + 30000 ;
public queue :Buffer = User . EMPTY_BUFFER ;
// Binato data
public rankingMode :RankingModes = RankingModes . PP ;
2022-11-27 17:37:28 +00:00
public spectatorStream? :DataStream ;
public spectatingUser? :User ;
2022-11-16 15:25:46 +00:00
// osu! data
public playMode :number = 0 ;
public countryID :number = 0 ;
public location :LatLng = new LatLng ( 0 , 0 ) ;
public joinedChannels :Array < string > = new Array < string > ( ) ;
// Presence data
public actionID :number = 0 ;
public actionText :string = "" ;
public actionMods :number = 0 ;
public beatmapChecksum :string = "" ;
public beatmapID :number = 0 ;
public currentMods :number = 0 ;
// Cached db data
public rankedScore :number = 0 ;
public accuracy :number = 0 ;
public playCount :number = 0 ;
public totalScore :number = 0 ;
public rank :number = 0 ;
public pp :number = 0 ;
// Multiplayer data
2022-11-23 00:48:28 +00:00
public match? :Match ;
public matchSlot? :Slot ;
public get inMatch() {
return this . match instanceof Match ;
}
2022-11-16 15:25:46 +00:00
// Tournament client flag
public isTourneyUser :boolean = false ;
2022-11-23 00:48:28 +00:00
static Equals ( user0 :User , user1 :User ) {
return user0 . uuid === user1 . uuid ;
}
2023-08-20 13:03:01 +01:00
public constructor ( id :number , username :string , uuid :string , shared :Shared ) {
2022-11-16 15:25:46 +00:00
this . id = id ;
this . username = username ;
this . uuid = uuid ;
2023-08-20 13:03:01 +01:00
this . shared = shared ;
2022-11-16 15:25:46 +00:00
}
// Concats new actions to the user's queue
public addActionToQueue ( newData :Buffer ) {
this . queue = Buffer . concat ( [ this . queue , newData ] , this . queue . length + newData . length ) ;
}
clearQueue() {
this . queue = User . EMPTY_BUFFER ;
}
// Updates the user's current action
2023-08-20 13:03:01 +01:00
updatePresence ( action :any ) {
2022-11-16 15:25:46 +00:00
this . actionID = action . status ;
this . actionText = action . statusText ;
this . beatmapChecksum = action . beatmapChecksum ;
this . currentMods = action . currentMods ;
this . actionMods = action . currentMods ;
if ( action . playMode != this . playMode ) {
this . updateUserInfo ( true ) ;
this . playMode = action . playMode ;
}
this . beatmapID = action . beatmapId ;
}
// Gets the user's score information from the database and caches it
2023-08-20 13:03:01 +01:00
async updateUserInfo ( forceUpdate :boolean = false ) {
const userScoreDB = await this . shared . database . query ( "SELECT * FROM users_modes_info WHERE user_id = ? AND mode_id = ? LIMIT 1" , [ this . id , this . playMode ] ) ;
2022-11-16 15:25:46 +00:00
const mappedRankingMode = rankingModes [ this . rankingMode ] ;
2023-08-20 13:03:01 +01:00
const userRankDB = await this . shared . database . query ( ` SELECT user_id, ${ mappedRankingMode } FROM users_modes_info WHERE mode_id = ? ORDER BY ${ mappedRankingMode } DESC ` , [ this . playMode ] ) ;
2022-11-16 15:25:46 +00:00
if ( userScoreDB == null || userRankDB == null ) throw "fuck" ;
// Handle "if we should update" checks for each rankingMode
let userScoreUpdate = false ;
switch ( this . rankingMode ) {
case RankingModes . PP :
if ( this . pp != userScoreDB . pp_raw )
userScoreUpdate = true ;
break ;
case RankingModes . RANKED_SCORE :
if ( this . rankedScore != userScoreDB . ranked_score )
userScoreUpdate = true ;
break ;
case RankingModes . AVG_ACCURACY :
if ( this . accuracy != userScoreDB . avg_accuracy )
userScoreUpdate = true ;
break ;
}
this . rankedScore = userScoreDB . ranked_score ;
this . totalScore = userScoreDB . total_score ;
this . accuracy = userScoreDB . avg_accuracy ;
this . playCount = userScoreDB . playcount ;
// Fetch rank
for ( let i = 0 ; i < userRankDB . length ; i ++ ) {
if ( userRankDB [ i ] [ "user_id" ] == this . id ) {
this . rank = i + 1 ;
break ;
}
}
// Set PP to none if ranking mode is not PP
if ( this . rankingMode == 0 ) this . pp = userScoreDB . pp_raw ;
else this . pp = 0 ;
if ( userScoreUpdate || forceUpdate ) {
2022-11-19 15:06:03 +00:00
StatusUpdate ( this , this . id ) ;
2022-11-16 15:25:46 +00:00
}
}
}