2022-11-19 01:49:36 +00:00
# include "player_database_service.hpp"
2023-03-01 21:27:15 +00:00
2023-05-01 23:23:07 +02:00
# include "backend/bool_command.hpp"
2022-11-19 01:49:36 +00:00
# include "file_manager.hpp"
2023-07-14 09:02:47 +00:00
# include "gta/enums.hpp"
2023-02-21 11:52:05 +01:00
# include "pointers.hpp"
2023-03-01 21:27:15 +00:00
# include "util/session.hpp"
2022-11-19 01:49:36 +00:00
namespace big
{
2023-07-14 09:02:47 +00:00
bool_command g_player_db_auto_update_online_states ( " player_db_auto_update_states " , " Auto Update Tracked Player States " , " Toggling this feature will automatically update the tracked players' online states every minute " ,
2023-05-01 23:23:07 +02:00
g . player_db . update_player_online_states ) ;
2023-07-14 09:02:47 +00:00
void player_database_service : : handle_session_type_change ( persistent_player & player , GSType new_session_type )
{
if ( ! player . notify_online )
return ;
if ( g . player_db . notify_when_joinable & & ! is_joinable_session ( player . session_type ) & & is_joinable_session ( new_session_type ) )
{
g_notification_service - > push_success ( " Player DB " , std : : format ( " {} is now in a joinable session " , player . name ) ) ;
}
else if ( g . player_db . notify_when_online & & ( player . session_type = = GSType : : Invalid | | player . session_type = = GSType : : Unknown ) & & new_session_type ! = GSType : : Invalid )
{
g_notification_service - > push_success ( " Player DB " , std : : format ( " {} is now online " , player . name ) ) ;
}
else if ( g . player_db . notify_when_unjoinable & & is_joinable_session ( player . session_type ) & & ! is_joinable_session ( new_session_type ) & & new_session_type ! = GSType : : Invalid )
{
g_notification_service - > push ( " Player DB " , std : : format ( " {} is no longer in a joinable session " , player . name ) ) ;
}
else if ( g . player_db . notify_when_offline & & player . session_type ! = GSType : : Invalid & & player . session_type ! = GSType : : Unknown & & new_session_type = = GSType : : Invalid )
{
g_notification_service - > push ( " Player DB " , std : : format ( " {} is no longer online " , player . name ) ) ;
}
if ( g . player_db . notify_on_session_type_change & & ( int ) new_session_type > = ( int ) GSType : : InviteOnly & & ( int ) new_session_type < ( int ) GSType : : Max )
{
g_notification_service - > push ( " Player DB " , std : : format ( " {} is now in a{} {} session " , player . name , new_session_type = = GSType : : InviteOnly ? " n " : " " , get_session_type_str ( new_session_type ) ) ) ;
}
}
2022-11-19 01:49:36 +00:00
player_database_service : : player_database_service ( ) :
2023-07-08 17:54:59 +02:00
m_file_path ( g_file_manager . get_project_file ( " ./players.json " ) . get_path ( ) )
2022-11-19 01:49:36 +00:00
{
load ( ) ;
g_player_database_service = this ;
}
player_database_service : : ~ player_database_service ( )
{
g_player_database_service = nullptr ;
}
void player_database_service : : save ( )
{
nlohmann : : json json ;
2023-03-01 21:27:15 +00:00
2022-11-19 01:49:36 +00:00
for ( auto & [ rid , player ] : m_players )
{
json [ std : : to_string ( rid ) ] = player ;
}
std : : ofstream file_stream ( m_file_path ) ;
file_stream < < json ;
}
void player_database_service : : load ( )
{
m_selected = nullptr ;
if ( std : : filesystem : : exists ( m_file_path ) )
{
2023-07-01 22:25:40 +00:00
try
{
std : : ifstream file_stream ( m_file_path ) ;
2022-11-19 01:49:36 +00:00
2023-07-01 22:25:40 +00:00
nlohmann : : json json ;
file_stream > > json ;
file_stream . close ( ) ;
2022-11-19 01:49:36 +00:00
2023-07-01 22:25:40 +00:00
for ( auto & [ key , value ] : json . items ( ) )
{
auto player = value . get < std : : shared_ptr < persistent_player > > ( ) ;
m_players [ std : : stoll ( key ) ] = player ;
2023-05-01 23:23:07 +02:00
2023-07-01 22:25:40 +00:00
std : : string lower = player - > name ;
std : : transform ( lower . begin ( ) , lower . end ( ) , lower . begin ( ) , : : tolower ) ;
m_sorted_players [ lower ] = player ;
}
}
catch ( std : : exception & e )
{
LOG ( WARNING ) < < " Failed to load player database file. " < < e . what ( ) ;
2022-11-19 01:49:36 +00:00
}
}
}
2023-05-01 23:23:07 +02:00
std : : unordered_map < std : : uint64_t , std : : shared_ptr < persistent_player > > & player_database_service : : get_players ( )
2022-11-19 01:49:36 +00:00
{
return m_players ;
}
2023-05-01 23:23:07 +02:00
std : : map < std : : string , std : : shared_ptr < persistent_player > > & player_database_service : : get_sorted_players ( )
{
return m_sorted_players ;
}
std : : shared_ptr < persistent_player > player_database_service : : add_player ( std : : int64_t rid , const std : : string_view name )
{
std : : string lower = name . data ( ) ;
std : : transform ( lower . begin ( ) , lower . end ( ) , lower . begin ( ) , : : tolower ) ;
if ( m_players . contains ( rid ) )
{
m_sorted_players . erase ( lower ) ;
}
auto player = std : : make_shared < persistent_player > ( name . data ( ) , rid ) ;
m_players [ rid ] = player ;
m_sorted_players [ lower ] = player ;
return player ;
}
std : : shared_ptr < persistent_player > player_database_service : : get_player_by_rockstar_id ( std : : uint64_t rockstar_id )
2022-11-19 01:49:36 +00:00
{
if ( m_players . contains ( rockstar_id ) )
2023-05-01 23:23:07 +02:00
return m_players [ rockstar_id ] ;
2022-11-19 01:49:36 +00:00
return nullptr ;
}
2023-05-01 23:23:07 +02:00
std : : shared_ptr < persistent_player > player_database_service : : get_or_create_player ( player_ptr player )
2022-11-19 01:49:36 +00:00
{
2023-02-04 16:35:18 +00:00
if ( m_players . contains ( player - > get_net_data ( ) - > m_gamer_handle . m_rockstar_id ) )
2023-05-01 23:23:07 +02:00
return m_players [ player - > get_net_data ( ) - > m_gamer_handle . m_rockstar_id ] ;
2022-11-19 01:49:36 +00:00
else
{
2023-05-01 23:23:07 +02:00
auto player_ptr = add_player ( player - > get_net_data ( ) - > m_gamer_handle . m_rockstar_id , player - > get_name ( ) ) ;
2022-11-19 01:49:36 +00:00
save ( ) ;
2023-05-01 23:23:07 +02:00
return player_ptr ;
2022-11-19 01:49:36 +00:00
}
}
void player_database_service : : update_rockstar_id ( std : : uint64_t old , std : : uint64_t _new )
{
2023-03-01 21:27:15 +00:00
auto player = m_players . extract ( old ) ;
2022-11-19 01:49:36 +00:00
player . key ( ) = _new ;
m_players . insert ( std : : move ( player ) ) ;
}
void player_database_service : : remove_rockstar_id ( std : : uint64_t rockstar_id )
{
if ( m_selected & & m_selected - > rockstar_id = = rockstar_id )
m_selected = nullptr ;
2023-05-01 23:23:07 +02:00
if ( auto it = m_players . find ( rockstar_id ) ; it ! = m_players . end ( ) )
{
std : : string lower = it - > second - > name ;
std : : transform ( lower . begin ( ) , lower . end ( ) , lower . begin ( ) , : : tolower ) ;
m_sorted_players . erase ( lower ) ;
m_players . erase ( it ) ;
}
2022-11-19 01:49:36 +00:00
}
2023-05-01 23:23:07 +02:00
void player_database_service : : set_selected ( std : : shared_ptr < persistent_player > selected )
2022-11-19 01:49:36 +00:00
{
m_selected = selected ;
}
2023-05-01 23:23:07 +02:00
std : : shared_ptr < persistent_player > player_database_service : : get_selected ( )
2022-11-19 01:49:36 +00:00
{
return m_selected ;
}
2023-02-21 11:52:05 +01:00
2023-05-01 23:23:07 +02:00
void player_database_service : : start_update_loop ( )
{
if ( ! g . player_db . update_player_online_states )
return ;
g_thread_pool - > push ( [ this ] {
2023-07-14 09:02:47 +00:00
static auto last_update = std : : chrono : : high_resolution_clock : : now ( ) - 45 s ;
2023-05-01 23:23:07 +02:00
while ( g_running & & g . player_db . update_player_online_states )
{
const auto cur = std : : chrono : : high_resolution_clock : : now ( ) ;
2023-07-14 09:02:47 +00:00
if ( cur - last_update > 45 s )
2023-05-01 23:23:07 +02:00
{
g_fiber_pool - > queue_job ( [ this ] {
2023-07-14 09:02:47 +00:00
update_player_states ( true ) ;
2023-05-01 23:23:07 +02:00
} ) ;
last_update = cur ;
}
std : : this_thread : : sleep_for ( 1 s ) ;
}
} ) ;
2023-02-21 11:52:05 +01:00
}
2023-07-14 09:02:47 +00:00
void player_database_service : : update_player_states ( bool tracked_only )
2023-02-21 11:52:05 +01:00
{
2023-07-14 09:02:47 +00:00
const auto player_count = m_players . size ( ) ;
constexpr auto bucket_size = 100 ;
2023-02-21 11:52:05 +01:00
2023-07-14 09:02:47 +00:00
std : : vector < std : : vector < rage : : rlScHandle > > gamer_handle_buckets ;
gamer_handle_buckets . resize ( std : : ceil ( player_count / ( float ) bucket_size ) ) ;
2023-05-01 23:23:07 +02:00
2023-07-14 09:02:47 +00:00
size_t i = 0 ;
for ( auto & player : m_players )
2023-05-01 23:23:07 +02:00
{
2023-07-14 09:02:47 +00:00
if ( ! tracked_only | | player . second - > notify_online )
{
gamer_handle_buckets [ i / bucket_size ] . push_back ( player . second - > rockstar_id ) ;
i + + ;
}
2023-05-01 23:23:07 +02:00
}
2023-07-14 09:02:47 +00:00
if ( i = = 0 )
return ;
2023-05-01 23:23:07 +02:00
for ( auto & bucket : gamer_handle_buckets )
2023-02-21 11:52:05 +01:00
{
2023-07-14 09:02:47 +00:00
rage : : rlTaskStatus status { } ;
rage : : rlQueryPresenceAttributesContext contexts [ bucket_size ] [ 2 ] { } ;
rage : : rlQueryPresenceAttributesContext * contexts_per_player [ bucket_size ] { } ;
2023-02-21 11:52:05 +01:00
2023-07-14 09:02:47 +00:00
for ( int i = 0 ; i < bucket_size ; i + + )
{
contexts [ i ] [ 0 ] . m_presence_attibute_type = 3 ;
strcpy ( contexts [ i ] [ 0 ] . m_presence_attribute_key , " gstype " ) ;
strcpy ( contexts [ i ] [ 0 ] . m_presence_attribute_value , " -1 " ) ;
contexts [ i ] [ 1 ] . m_presence_attibute_type = 3 ;
strcpy ( contexts [ i ] [ 1 ] . m_presence_attribute_key , " gsinfo " ) ;
contexts_per_player [ i ] = contexts [ i ] ;
}
if ( g_pointers - > m_sc . m_start_get_presence_attributes ( 0 , bucket . data ( ) , bucket . size ( ) , contexts_per_player , 2 , & status ) )
2023-02-21 11:52:05 +01:00
{
2023-05-01 23:23:07 +02:00
while ( status . status = = 1 )
{
2023-02-21 11:52:05 +01:00
script : : get_current ( ) - > yield ( ) ;
2023-05-01 23:23:07 +02:00
}
2023-02-21 11:52:05 +01:00
2023-07-14 09:02:47 +00:00
if ( status . status = = 3 )
2023-02-21 11:52:05 +01:00
{
2023-07-14 09:02:47 +00:00
for ( size_t i = 0 ; i < bucket . size ( ) ; + + i )
2023-05-01 23:23:07 +02:00
{
2023-07-14 09:02:47 +00:00
if ( const auto & it = m_players . find ( bucket [ i ] . m_rockstar_id ) ; it ! = m_players . end ( ) )
2023-06-06 13:37:45 +02:00
{
2023-07-14 09:02:47 +00:00
rage : : rlSessionInfo info { } ;
info . m_session_token = - 1 ;
GSType gstype = ( GSType ) atoi ( contexts [ i ] [ 0 ] . m_presence_attribute_value ) ;
if ( ! g_pointers - > m_gta . m_decode_session_info ( & info , contexts [ i ] [ 1 ] . m_presence_attribute_value , nullptr ) )
gstype = GSType : : Invalid ;
if ( it - > second - > session_type ! = gstype )
{
handle_session_type_change ( * it - > second , gstype ) ;
}
else if ( it - > second - > notify_online & & it - > second - > session_id ! = info . m_session_token )
2023-06-06 13:37:45 +02:00
{
2023-07-14 09:02:47 +00:00
g_notification_service - > push ( " Player DB " ,
std : : format ( " {} has joined a new session " , it - > second - > name ) ) ;
2023-06-06 13:37:45 +02:00
}
2023-07-14 09:02:47 +00:00
it - > second - > session_type = gstype ;
it - > second - > session_id = info . m_session_token ;
2023-06-06 13:37:45 +02:00
}
2023-05-01 23:23:07 +02:00
}
2023-02-21 11:52:05 +01:00
}
2023-07-14 09:02:47 +00:00
else
{
LOG ( WARNING ) < < " Presence attribute endpoint failed " ;
}
2023-02-21 11:52:05 +01:00
}
}
}
2023-07-14 09:02:47 +00:00
bool player_database_service : : is_joinable_session ( GSType type )
{
return type = = GSType : : Public | | type = = GSType : : OpenCrew ;
}
const char * player_database_service : : get_session_type_str ( GSType type )
{
switch ( type )
{
case GSType : : Invalid : return " Offline " ;
case GSType : : InviteOnly : return " Invite Only " ;
case GSType : : FriendsOnly : return " Friends Only " ;
case GSType : : ClosedCrew : return " Closed Crew " ;
case GSType : : OpenCrew : return " Crew " ;
case GSType : : Job : return " In Mission " ;
case GSType : : Public : return " Public " ;
case GSType : : Modder : return " Unknown (Concealed By Modder) " ;
}
return " Unknown " ;
}
2022-11-19 01:49:36 +00:00
}