2021-12-05 17:54:36 +00:00
# include "LeaderboardManager.h"
# include <utility>
# include "Database.h"
# include "EntityManager.h"
# include "Character.h"
2021-12-22 03:15:29 +00:00
# include "Game.h"
2021-12-05 17:54:36 +00:00
# include "GameMessages.h"
# include "dLogger.h"
2021-12-22 03:15:29 +00:00
# include "dConfig.h"
2022-12-18 15:46:04 +00:00
# include "CDClientManager.h"
2023-03-17 14:36:21 +00:00
# include "GeneralUtils.h"
# include "Entity.h"
2023-04-13 07:45:03 +00:00
# include <sstream>
2023-03-17 14:36:21 +00:00
# include "CDActivitiesTable.h"
2023-04-13 07:45:03 +00:00
# include "Metrics.hpp"
2023-04-13 04:57:58 +00:00
Leaderboard : : Leaderboard ( const GameID gameID , const Leaderboard : : InfoType infoType , const bool weekly , const Leaderboard : : Type leaderboardType ) {
2021-12-05 17:54:36 +00:00
this - > relatedPlayer = relatedPlayer ;
this - > gameID = gameID ;
this - > weekly = weekly ;
this - > infoType = infoType ;
2023-04-13 04:57:58 +00:00
this - > entries = entries ;
2021-12-05 17:54:36 +00:00
this - > leaderboardType = leaderboardType ;
}
2023-04-13 04:57:58 +00:00
void Leaderboard : : Serialize ( RakNet : : BitStream * bitStream ) const {
2023-04-13 07:45:03 +00:00
std : : ostringstream leaderboard ;
2022-07-28 13:39:57 +00:00
2023-04-13 07:45:03 +00:00
leaderboard < < " ADO.Result=7:1 \n " ; // Unused in 1.10.64, but is in captures
leaderboard < < " Result.Count=1:1 \n " ; // number of results, always 1?
leaderboard < < " Result[0].Index=0:RowNumber \n " ; // "Primary key"
leaderboard < < " Result[0].RowCount=1: " < < entries . size ( ) < < ' \n ' ; // number of rows
2022-07-28 13:39:57 +00:00
2021-12-05 17:54:36 +00:00
auto index = 0 ;
for ( const auto & entry : entries ) {
2023-04-13 07:45:03 +00:00
leaderboard < < " Result[0].Row[ " < < index < < " ].LastPlayed=8: " < < entry . lastPlayed < < ' \n ' ;
leaderboard < < " Result[0].Row[ " < < index < < " ].CharacterID=8: " < < entry . playerID < < ' \n ' ;
leaderboard < < " Result[0].Row[ " < < index < < " ].NumPlayed=1:1 \n " ; // number of times the activity was played
leaderboard < < " Result[0].Row[ " < < index < < " ].RowNumber=8: " < < entry . placement < < ' \n ' ;
leaderboard < < " Result[0].Row[ " < < index < < " ].Time=1: " < < entry . time < < ' \n ' ;
2022-07-28 13:39:57 +00:00
2021-12-05 17:54:36 +00:00
// Only these minigames have a points system
if ( leaderboardType = = Survival | | leaderboardType = = ShootingGallery ) {
2023-04-13 07:45:03 +00:00
leaderboard < < " Result[0].Row[ " < < index < < " ].Points=1: " < < entry . score < < ' \n ' ;
2021-12-05 17:54:36 +00:00
} else if ( leaderboardType = = SurvivalNS ) {
2023-04-13 07:45:03 +00:00
leaderboard < < " Result[0].Row[ " < < index < < " ].Wave=1: " < < entry . score < < ' \n ' ;
2021-12-05 17:54:36 +00:00
}
2022-07-28 13:39:57 +00:00
2023-04-13 07:45:03 +00:00
leaderboard < < " Result[0].Row[ " < < index < < " ].name=0: " < < entry . playerName < < ' \n ' ;
2021-12-05 17:54:36 +00:00
index + + ;
}
2022-07-28 13:39:57 +00:00
2023-04-13 04:57:58 +00:00
// Serialize the thing to a BitStream
2023-04-13 07:45:03 +00:00
bitStream - > Write ( leaderboard . str ( ) . c_str ( ) , leaderboard . tellp ( ) ) ;
2021-12-05 17:54:36 +00:00
}
2023-04-13 04:57:58 +00:00
void Leaderboard : : SetupLeaderboard ( ) {
// Setup query based on activity.
// Where clause will vary based on what query we are doing
2021-12-05 17:54:36 +00:00
}
void Leaderboard : : Send ( LWOOBJID targetID ) const {
auto * player = EntityManager : : Instance ( ) - > GetEntity ( relatedPlayer ) ;
if ( player ! = nullptr ) {
GameMessages : : SendActivitySummaryLeaderboardData ( targetID , this , player - > GetSystemAddress ( ) ) ;
}
}
void LeaderboardManager : : SaveScore ( LWOOBJID playerID , uint32_t gameID , uint32_t score , uint32_t time ) {
const auto * player = EntityManager : : Instance ( ) - > GetEntity ( playerID ) ;
if ( player = = nullptr )
return ;
2022-07-28 13:39:57 +00:00
2021-12-05 17:54:36 +00:00
auto * character = player - > GetCharacter ( ) ;
2023-04-13 04:57:58 +00:00
if ( ! character )
2021-12-05 17:54:36 +00:00
return ;
2022-07-28 13:39:57 +00:00
2023-04-13 04:57:58 +00:00
std : : unique_ptr < sql : : PreparedStatement > select ( Database : : CreatePreppedStmt ( " SELECT time, score FROM leaderboard WHERE character_id = ? AND game_id = ?; " ) ) ;
2022-07-28 13:39:57 +00:00
2021-12-05 17:54:36 +00:00
select - > setUInt64 ( 1 , character - > GetID ( ) ) ;
select - > setInt ( 2 , gameID ) ;
2022-07-28 13:39:57 +00:00
2021-12-05 17:54:36 +00:00
auto any = false ;
auto * result = select - > executeQuery ( ) ;
auto leaderboardType = GetLeaderboardType ( gameID ) ;
2022-07-28 13:39:57 +00:00
2021-12-05 17:54:36 +00:00
// Check if the new score is a high score
while ( result - > next ( ) ) {
any = true ;
2022-07-28 13:39:57 +00:00
2021-12-05 17:54:36 +00:00
const auto storedTime = result - > getInt ( 1 ) ;
const auto storedScore = result - > getInt ( 2 ) ;
auto highscore = true ;
2021-12-22 03:15:29 +00:00
bool classicSurvivalScoring = Game : : config - > GetValue ( " classic_survival_scoring " ) = = " 1 " ;
2022-07-28 13:39:57 +00:00
2021-12-05 17:54:36 +00:00
switch ( leaderboardType ) {
2023-04-13 04:57:58 +00:00
case Leaderboard : : Type : : ShootingGallery :
2021-12-05 17:54:36 +00:00
if ( score < = storedScore )
highscore = false ;
break ;
2023-04-13 04:57:58 +00:00
case Leaderboard : : Type : : Racing :
2021-12-22 03:15:29 +00:00
if ( time > = storedTime )
highscore = false ;
break ;
2023-04-13 04:57:58 +00:00
case Leaderboard : : Type : : MonumentRace :
2021-12-22 03:15:29 +00:00
if ( time > = storedTime )
2021-12-05 17:54:36 +00:00
highscore = false ;
break ;
2023-04-13 04:57:58 +00:00
case Leaderboard : : Type : : FootRace :
2021-12-05 17:54:36 +00:00
if ( time < = storedTime )
highscore = false ;
break ;
2023-04-13 04:57:58 +00:00
case Leaderboard : : Type : : Survival :
2021-12-22 03:15:29 +00:00
if ( classicSurvivalScoring ) {
if ( time < = storedTime ) { // Based on time (LU live)
highscore = false ;
}
} else {
if ( score < = storedScore ) // Based on score (DLU)
highscore = false ;
}
break ;
2023-04-13 04:57:58 +00:00
case Leaderboard : : Type : : SurvivalNS :
2022-05-25 02:03:40 +00:00
if ( ! ( score > storedScore | | ( time < storedTime & & score > = storedScore ) ) )
2021-12-05 17:54:36 +00:00
highscore = false ;
break ;
default :
highscore = false ;
}
2022-07-28 13:39:57 +00:00
2021-12-05 17:54:36 +00:00
if ( ! highscore ) {
delete result ;
return ;
}
}
2022-07-28 13:39:57 +00:00
2021-12-05 17:54:36 +00:00
delete result ;
2022-07-28 13:39:57 +00:00
2021-12-05 17:54:36 +00:00
if ( any ) {
2021-12-22 03:15:29 +00:00
auto * statement = Database : : CreatePreppedStmt ( " UPDATE leaderboard SET time = ?, score = ?, last_played=SYSDATE() WHERE character_id = ? AND game_id = ?; " ) ;
2021-12-05 17:54:36 +00:00
statement - > setInt ( 1 , time ) ;
statement - > setInt ( 2 , score ) ;
statement - > setUInt64 ( 3 , character - > GetID ( ) ) ;
statement - > setInt ( 4 , gameID ) ;
statement - > execute ( ) ;
2022-07-28 13:39:57 +00:00
2021-12-05 17:54:36 +00:00
delete statement ;
} else {
2021-12-22 03:30:37 +00:00
// Note: last_played will be set to SYSDATE() by default when inserting into leaderboard
2021-12-05 17:54:36 +00:00
auto * statement = Database : : CreatePreppedStmt ( " INSERT INTO leaderboard (character_id, game_id, time, score) VALUES (?, ?, ?, ?); " ) ;
statement - > setUInt64 ( 1 , character - > GetID ( ) ) ;
statement - > setInt ( 2 , gameID ) ;
statement - > setInt ( 3 , time ) ;
statement - > setInt ( 4 , score ) ;
statement - > execute ( ) ;
2022-07-28 13:39:57 +00:00
2021-12-05 17:54:36 +00:00
delete statement ;
}
}
2023-04-13 04:57:58 +00:00
void LeaderboardManager : : SendLeaderboard ( uint32_t gameID , Leaderboard : : InfoType infoType , bool weekly , LWOOBJID targetID ,
2021-12-05 17:54:36 +00:00
LWOOBJID playerID ) {
2023-04-13 04:57:58 +00:00
// Create the leaderboard here and then send it right after. On the stack.
Leaderboard leaderboard ( gameID , infoType , weekly , GetLeaderboardType ( gameID ) ) ;
leaderboard . SetupLeaderboard ( ) ;
leaderboard . Send ( targetID ) ;
2021-12-05 17:54:36 +00:00
}
2023-04-13 04:57:58 +00:00
// Done
Leaderboard : : Type LeaderboardManager : : GetLeaderboardType ( const GameID gameID ) {
auto lookup = leaderboardCache . find ( gameID ) ;
if ( lookup ! = leaderboardCache . end ( ) ) return lookup - > second ;
2023-03-17 14:36:21 +00:00
auto * activitiesTable = CDClientManager : : Instance ( ) . GetTable < CDActivitiesTable > ( ) ;
2021-12-05 17:54:36 +00:00
std : : vector < CDActivities > activities = activitiesTable - > Query ( [ = ] ( const CDActivities & entry ) {
return ( entry . ActivityID = = gameID ) ;
} ) ;
2023-04-13 04:57:58 +00:00
auto type = activities . empty ( ) ? static_cast < Leaderboard : : Type > ( activities . at ( 0 ) . leaderboardType ) : Leaderboard : : Type : : None ;
leaderboardCache . insert_or_assign ( gameID , type ) ;
return type ;
2021-12-05 17:54:36 +00:00
}