From f9656a800d0262a2096c3525c2160ce38bc16828 Mon Sep 17 00:00:00 2001 From: hgn Date: Mon, 15 Aug 2022 01:42:05 +0100 Subject: [PATCH] more db stuff --- highscores.h | 269 ++++++++++++++++++++++++++++++++++++++++++++------ network_msg.h | 41 ++++---- server.c | 237 ++++++++++++++++++++++++++++++++++++++++---- testaa.c | 32 +++++- 4 files changed, 505 insertions(+), 74 deletions(-) diff --git a/highscores.h b/highscores.h index 2ffaf6a..84ab9a4 100644 --- a/highscores.h +++ b/highscores.h @@ -14,15 +14,28 @@ typedef struct highscore highscore; typedef struct highscore_record highscore_record; typedef struct highscore_track_table highscore_track_table; typedef struct highscore_database highscore_database; +typedef struct highscore_playerinfo highscore_playerinfo; #pragma pack(push,1) + +struct highscore_playerinfo +{ + char nickname[16]; + u64 playerid; + + union + { + aatree_pool_node aapn; + aatree_node aa_playerid; + }; +}; + struct highscore_record { u16 trackid, points, time, reserved0; u64 playerid; u32 datetime; - - u32 reserved[7]; + u32 reserved1; union { @@ -35,12 +48,7 @@ struct highscore_record } aa; - struct - { - /* TODO pool allocator */ - u32 next, prev; - } - pool; + aatree_pool_node pool; }; }; @@ -58,8 +66,11 @@ struct highscore_database { highscore_track_table tracks[ 128 ]; - aatree_ptr pool_head; - u32 reserved[63]; + aatree_ptr pool_head, playerinfo_head; + u32 entry_capacity, + playerinfo_capacity, playerinfo_root; + + u32 reserved[59]; }; #pragma pack(pop) @@ -71,9 +82,11 @@ static struct highscore_system aainfo_points, aainfo_time, aainfo_playerid, - aainfo_datetime; + aainfo_datetime, + aainfo_playerinfo_playerid, + aainfo_playerinfo; - void *data; + void *data, *playerinfo_data; } highscore_system; @@ -104,24 +117,52 @@ static int highscore_cmp_playerid( void *a, void *b ) return pa->playerid < pb->playerid? -1: 1; } -static int highscores_init( u32 pool_size ) +static int highscore_cmp_playerinfo_playerid( void *a, void *b ) { - struct highscore_system *sys = &highscore_system; + highscore_playerinfo *pa = a, *pb = b; + if( pa->playerid == pb->playerid ) return 0; + return pa->playerid < pb->playerid? -1: 1; +} - size_t requested_mem = pool_size * sizeof(highscore_record); - sys->data = malloc( requested_mem ); +static void *highscore_malloc( u32 count, u32 size ) +{ + size_t requested_mem = size * count; + void *data = malloc( requested_mem ); requested_mem /= 1024; requested_mem /= 1024; - if( !highscore_system.data ) + if( !data ) { - vg_error( "Could not allocated %dmb of memory for database\n", - requested_mem ); - return 0; + vg_error( "Could not allocated %dmb of memory\n", requested_mem ); + return NULL; } else - vg_success( "Allocated %dmb for database\n", requested_mem ); + vg_success( "Allocated %dmb for %u records\n", requested_mem, count ); + + return data; +} + +static void highscores_free(void) +{ + free( highscore_system.data ); + free( highscore_system.playerinfo_data ); +} + +static int highscores_init( u32 pool_size, u32 playerinfo_pool_size ) +{ + struct highscore_system *sys = &highscore_system; + + sys->data = highscore_malloc( pool_size, sizeof(highscore_record) ); + if( !sys->data ) return 0; + + sys->playerinfo_data = + highscore_malloc( playerinfo_pool_size, sizeof(highscore_playerinfo)); + if( !sys->playerinfo_data ) + { + free( sys->data ); + return 0; + } /* This is ugly.. too bad! */ sys->aainfo.base = highscore_system.data; @@ -149,16 +190,64 @@ static int highscores_init( u32 pool_size ) sys->aainfo_playerid.offset = offsetof(highscore_record,aa.playerid); sys->aainfo_playerid.p_cmp = highscore_cmp_playerid; + sys->aainfo_playerinfo_playerid.base = highscore_system.playerinfo_data; + sys->aainfo_playerinfo_playerid.stride = sizeof(highscore_playerinfo); + sys->aainfo_playerinfo_playerid.offset = + offsetof(highscore_playerinfo,aa_playerid); + sys->aainfo_playerinfo_playerid.p_cmp = highscore_cmp_playerinfo_playerid; + + sys->aainfo_playerinfo.base = highscore_system.playerinfo_data; + sys->aainfo_playerinfo.stride = sizeof(highscore_playerinfo); + sys->aainfo_playerinfo.offset = offsetof(highscore_playerinfo,aapn); + sys->aainfo_playerinfo.p_cmp = NULL; - /* TODO: Load from disk if avalible */ - if( 0 ) + FILE *fp = fopen( ".aadb", "rb" ); + if( fp ) { + vg_info( "Loading existing database\n" ); + + u64 count = fread( &sys->dbheader, sizeof(highscore_database), 1, fp ); + + if( count != 1 ) + { + vg_error( "Unexpected EOF reading database header\n" ); + + highscores_free(); + return 0; + } + + count = fread( sys->data, sizeof(highscore_record), pool_size, fp ); + if( count != pool_size ) + { + vg_error( "Unexpected EOF reading database contents;" + " %lu records of %u were read\n", count, pool_size ); + + highscores_free(); + return 0; + } + count = fread( sys->playerinfo_data, sizeof(highscore_playerinfo), + playerinfo_pool_size, fp ); + if( count != playerinfo_pool_size ) + { + vg_error( "Unexpected EOF reading playerinfo contents;" + " %lu records of %u were read\n", count, + playerinfo_pool_size ); + + highscores_free(); + return 0; + } + + fclose( fp ); } else { + vg_log( "No existing database found (.aadb)\n" ); vg_info( "Initializing database nodes\n" ); + memset( &sys->dbheader, 0, sizeof(highscore_database) ); + sys->dbheader.pool_head = aatree_init_pool( &sys->aainfo, pool_size ); + sys->dbheader.entry_capacity = pool_size; for( int i=0; idbheader.tracks); i++ ) { @@ -168,14 +257,39 @@ static int highscores_init( u32 pool_size ) table->root_time = AATREE_PTR_NIL; table->root_datetime = AATREE_PTR_NIL; } + + /* Initialize secondary db */ + sys->dbheader.playerinfo_head = aatree_init_pool( + &sys->aainfo_playerinfo, + playerinfo_pool_size ); + sys->dbheader.playerinfo_capacity = playerinfo_pool_size; + sys->dbheader.playerinfo_root = AATREE_PTR_NIL; } return 1; } -static void highscores_free(void) +static int highscores_serialize_all(void) { - free( highscore_system.data ); + struct highscore_system *sys = &highscore_system; + vg_info( "Serializing database\n" ); + + FILE *fp = fopen( ".aadb", "wb" ); + + if( !fp ) + { + vg_error( "Could not open .aadb\n" ); + return 0; + } + + fwrite( &sys->dbheader, sizeof(highscore_database), 1, fp ); + fwrite( sys->data, sizeof(highscore_record), + sys->dbheader.entry_capacity, fp ); + fwrite( sys->playerinfo_data, sizeof(highscore_playerinfo), + sys->dbheader.playerinfo_capacity, fp ); + + fclose( fp ); + return 1; } static aatree_ptr highscores_push_record( highscore_record *record ) @@ -201,6 +315,16 @@ static aatree_ptr highscores_push_record( highscore_record *record ) if( existing != AATREE_PTR_NIL ) { + highscore_record *crecord = aatree_get_data( &sys->aainfo_playerid, + existing ); + + if( crecord->time < record->time || + (crecord->time == record->time && crecord->points > record->points)) + { + vg_log( "Not overwriting better score\n" ); + return existing; + } + vg_log( "Freeing existing record for player %lu\n", record->playerid ); table->root_playerid = aatree_del( &sys->aainfo_playerid, existing ); table->root_datetime = aatree_del( &sys->aainfo_datetime, existing ); @@ -214,7 +338,10 @@ static aatree_ptr highscores_push_record( highscore_record *record ) aatree_pool_alloc( &sys->aainfo, &sys->dbheader.pool_head ); if( index == AATREE_PTR_NIL ) + { + vg_error( "Database records are over capacity!\n" ); return index; + } highscore_record *dst = aatree_get_data( &sys->aainfo, index ); memset( dst, 0, sizeof(highscore_record) ); @@ -237,12 +364,69 @@ static aatree_ptr highscores_push_record( highscore_record *record ) return index; } +static aatree_ptr highscore_set_user_nickname( u64 steamid, char nick[16] ) +{ + vg_log( "Updating %lu's nickname\n", steamid ); + + struct highscore_system *sys = &highscore_system; + + highscore_playerinfo temp; + temp.playerid = steamid; + + aatree_ptr record = aatree_find( &sys->aainfo_playerinfo_playerid, + sys->dbheader.playerinfo_root, + &temp ); + highscore_playerinfo *info; + + if( record != AATREE_PTR_NIL ) + { + info = aatree_get_data( &sys->aainfo_playerinfo, record ); + } + else + { + record = aatree_pool_alloc( &sys->aainfo_playerinfo, + &sys->dbheader.playerinfo_head ); + + if( record == AATREE_PTR_NIL ) + { + vg_error( "Player info database is over capacity!\n" ); + return AATREE_PTR_NIL; + } + + info = aatree_get_data( &sys->aainfo_playerinfo, record ); + memset( info, 0, sizeof(highscore_playerinfo) ); + + info->playerid = steamid; + sys->dbheader.playerinfo_root = aatree_insert( + &sys->aainfo_playerinfo_playerid, + sys->dbheader.playerinfo_root, + record ); + } + + for( int i=0; i<16; i++ ) + info->nickname[i] = nick[i]; + + return AATREE_PTR_NIL; +} + static void _highscore_showtime( void *data ) { highscore_record *record = data; printf( "%hu", record->time ); } +static void _highscore_showname( void *data ) +{ + char namebuf[17]; + namebuf[16] = '\0'; + + highscore_playerinfo *info = data; + for( int i=0; i<16; i++ ) + namebuf[i] = info->nickname[i]; + + printf( " %lu %s", info->playerid, namebuf ); +} + static void highscores_print_track( u32 trackid, u32 count ) { struct highscore_system *sys = &highscore_system; @@ -250,22 +434,45 @@ static void highscores_print_track( u32 trackid, u32 count ) highscore_track_table *table = &sys->dbheader.tracks[ trackid ]; aatree_ptr it = aatree_kth( &sys->aainfo_time, table->root_time, 0 ); - vg_info( "Highscores, top %u records for track %u\n", count, trackid ); - vg_info( "==============================================\n" ); - + vg_info( "Highscores: top %u fastest records for track %u\n", count, trackid ); + vg_info( "================================================\n" ); + vg_info( "%3s| %16s | %5s | %5s | %s\n", "#", "Player", "Time", "Score", + "TrackID" ); + vg_info( "================================================\n" ); int i=0; while( it != AATREE_PTR_NIL && i < 10 ) { highscore_record *record = aatree_get_data( &sys->aainfo_time, it ); - vg_info( " [%d]: player(%lu), time: %hu, score: %hu, track:%hu\n", - i+1, record->playerid, record->time, record->points, + + highscore_playerinfo temp; + temp.playerid = record->playerid; + + aatree_ptr info_ptr = aatree_find( &sys->aainfo_playerinfo_playerid, + sys->dbheader.playerinfo_root, + &temp ); + + char namebuf[17]; + if( info_ptr == AATREE_PTR_NIL ) + snprintf( namebuf, 16, "[%lu]", record->playerid ); + else + { + highscore_playerinfo *inf = aatree_get_data( + &sys->aainfo_playerinfo_playerid, info_ptr ); + + for( int i=0; i<16; i++ ) + namebuf[i] = inf->nickname[i]; + namebuf[16] = '\0'; + } + + vg_info( "%3d| %16s %5hu %5hu %3hu\n", + i+1, namebuf, record->time, record->points, record->trackid ); i++; it = aatree_next( &sys->aainfo_time, it ); } - vg_info( "==============================================\n" ); + vg_info( "================================================\n" ); } #endif /* HIGHSCORES_H */ diff --git a/network_msg.h b/network_msg.h index 6c072f1..4ab0942 100644 --- a/network_msg.h +++ b/network_msg.h @@ -12,43 +12,46 @@ struct netmsg_blank }; enum{ k_inetmsg_blank = 0 }; +typedef struct netmsg_auth netmsg_auth; +struct netmsg_auth +{ + u32 inetmsg_id; + + u32 ticket_length; + u8 ticket[]; +}; +enum{ k_inetmsg_auth = 1 }; + typedef struct netmsg_scores_request netmsg_scores_request; struct netmsg_scores_request { u32 inetmsg_id; }; -enum{ k_inetmsg_scores_request = 1 }; +enum{ k_inetmsg_scores_request = 2 }; -typedef struct netmsg_scores_info netmsg_scores_info; -struct netmsg_scores_info +typedef struct netmsg_set_score netmsg_set_score; +struct netmsg_set_score { u32 inetmsg_id; - + u32 record_count; struct netmsg_score_record { u32 trackid; - - struct netmsg_score_entry - { - u64 steamid64; - u16 points, time; - } - top10[10]; + u64 playerid; + u16 points, time; } - scores[]; + records[]; }; -enum{ k_inetmsg_scores_info = 2 }; +enum{ k_inetmsg_set_score = 3 }; -typedef struct netmsg_auth netmsg_auth; -struct netmsg_auth +typedef struct netmsg_set_nickname netmsg_set_nickname; +struct netmsg_set_nickname { u32 inetmsg_id; - - u32 ticket_length; - u8 ticket[]; + char nickname[16]; }; -enum{ k_inetmsg_auth = 3 }; +enum{ k_inetmsg_set_nickname = 4 }; #pragma pack(pop) #endif /* NETWORK_MSG_H */ diff --git a/server.c b/server.c index 7d8e0e3..578c3f5 100644 --- a/server.c +++ b/server.c @@ -24,6 +24,7 @@ void inthandler( int signum ) #include "vg/vg_steam_http.h" #include "vg/vg_steam_auth.h" #include "network_msg.h" +#include "highscores.h" void *hSteamHTTP, *hSteamNetworkingSockets; @@ -51,6 +52,22 @@ static void recieve_http( void *callresult, void *context ) SteamAPI_ISteamHTTP_ReleaseHTTPRequest( hSteamHTTP, result->m_hRequest ); } +static u64_steamid get_connection_authsteamid( SteamNetworkingMessage_t *msg ) +{ + i64 userdata = SteamAPI_ISteamNetworkingSockets_GetConnectionUserData( + hSteamNetworkingSockets, msg->m_conn ); + + return *((u64_steamid *)&userdata); +} + +static void set_connection_authsteamid(HSteamNetConnection con, u64_steamid id) +{ + i64 userdata = *((i64 *)&id); + + SteamAPI_ISteamNetworkingSockets_SetConnectionUserData( + hSteamNetworkingSockets, con, userdata ); +} + static void new_client_connecting( HSteamNetConnection client ) { EResult accept_status = SteamAPI_ISteamNetworkingSockets_AcceptConnection( @@ -62,6 +79,8 @@ static void new_client_connecting( HSteamNetConnection client ) SteamAPI_ISteamNetworkingSockets_SetConnectionPollGroup( hSteamNetworkingSockets, client, client_pollgroup ); + + set_connection_authsteamid( client, 0 ); } else { @@ -92,6 +111,179 @@ static void on_connect_status( CallbackMsg_t *msg ) } } +static void on_inet_auth( SteamNetworkingMessage_t *msg ) +{ + if( get_connection_authsteamid( msg ) ) + { + vg_warn( "Already authorized this user but app ticket was sent" + " again (%u)\n", msg->m_conn ); + return; + } + + vg_log( "Attempting to verify user\n" ); + + if( msg->m_cbSize < sizeof(netmsg_auth) ) + { + vg_error( "Malformed auth ticket, too small (%u)\n", msg->m_conn ); + return; + } + + netmsg_auth *auth = msg->m_pData; + + if( msg->m_cbSize < sizeof(netmsg_auth)+auth->ticket_length || + auth->ticket_length > 1024 ) + { + vg_error( "Malformed auth ticket, ticket_length incorrect (%u)\n", + auth->ticket_length ); + return; + } + + u8 decrypted[1024]; + u32 ticket_len; + + int success = SteamEncryptedAppTicket_BDecryptTicket( + auth->ticket, auth->ticket_length, decrypted, + &ticket_len, steam_symetric_key, + k_nSteamEncryptedAppTicketSymmetricKeyLen ); + + if( !success ) + { + vg_error( "Failed to decrypt users ticket (client %u)\n", msg->m_conn ); + + SteamAPI_ISteamNetworkingSockets_CloseConnection( + hSteamNetworkingSockets, + msg->m_conn, 0, NULL, 0 ); + return; + } + + if( SteamEncryptedAppTicket_GetTicketIssueTime( decrypted, ticket_len )) + { + RTime32 ctime = time(NULL), + tickettime = SteamEncryptedAppTicket_GetTicketIssueTime( + decrypted, ticket_len ), + expiretime = tickettime + 24*3*60*60; + + if( ctime > expiretime ) + { + vg_error( "Ticket expired (client %u)\n", msg->m_conn ); + + /* TODO: Send expired information */ + SteamAPI_ISteamNetworkingSockets_CloseConnection( + hSteamNetworkingSockets, + msg->m_conn, 0, NULL, 0 ); + return; + } + } + + CSteamID steamid; + SteamEncryptedAppTicket_GetTicketSteamID( decrypted, ticket_len, &steamid ); + vg_success( "User is authenticated! steamid %lu (%u)\n", + steamid.m_unAll64Bits, msg->m_conn ); + + set_connection_authsteamid( msg->m_conn, steamid.m_unAll64Bits ); +} + +static int inet_require_auth( SteamNetworkingMessage_t *msg ) +{ + if( !get_connection_authsteamid( msg ) ) + { + vg_warn( "Unauthorized request! Disconnecting client: %u\n", + msg->m_conn ); + + SteamAPI_ISteamNetworkingSockets_CloseConnection( + hSteamNetworkingSockets, + msg->m_conn, 0, NULL, 0 ); + + return 0; + } + else return 1; +} + +static void on_inet_score_request( SteamNetworkingMessage_t *msg ) +{ +#if 0 + if( get_connection_authsteamid( msg ) ) + { + vg_log( "Recieved score request, sending records. (id: %u)\n", + msg->m_conn ); + + /* Send back current scores */ + u32 data_size = sizeof(netmsg_scores_info) + + 0*sizeof(struct netmsg_score_record); + netmsg_scores_info *return_info = malloc( data_size ); + + return_info->inetmsg_id = k_inetmsg_scores_info; + return_info->record_count = 0; + + SteamAPI_ISteamNetworkingSockets_SendMessageToConnection( + hSteamNetworkingSockets, msg->m_conn, + return_info, data_size, + k_nSteamNetworkingSend_Reliable, NULL ); + } + else + { + vg_warn( "Unauthorized request! Disconnecting client: %u\n", + msg->m_conn ); + + SteamAPI_ISteamNetworkingSockets_CloseConnection( + hSteamNetworkingSockets, + msg->m_conn, 0, NULL, 0 ); + } +#endif +} + +static void on_inet_set_nickname( SteamNetworkingMessage_t *msg ) +{ + if(!inet_require_auth(msg)) return; + + u64_steamid steamid = get_connection_authsteamid(msg); + netmsg_set_nickname *setnick = msg->m_pData; + if( msg->m_cbSize < sizeof(netmsg_set_nickname) ) + { + vg_warn( "Invalid nickname request from client: %u, steamid: %lu\n", + msg->m_conn, steamid ); + return; + } + + highscore_set_user_nickname( steamid, setnick->nickname ); +} + +static void on_inet_set_score( SteamNetworkingMessage_t *msg ) +{ + if(!inet_require_auth(msg)) return; + + u64_steamid steamid = get_connection_authsteamid(msg); + + if( msg->m_cbSize < sizeof(netmsg_set_score) ) + { + vg_warn( "Invalid set score post from client: %u, steamid: %lu\n", + msg->m_conn, steamid ); + return; + } + + netmsg_set_score *info = msg->m_pData; + + if( msg->m_cbSize < sizeof(netmsg_set_score) + + sizeof(struct netmsg_score_record)*info->record_count ) + { + vg_warn( "Malformed set score post from client: %u, steamid: %lu\n", + msg->m_conn, steamid ); + return; + } + + for( int i=0; irecord_count; i++ ) + { + highscore_record temp; + temp.trackid = info->records[i].trackid; + temp.datetime = time(NULL); + temp.playerid = steamid; + temp.points = info->records[i].points; + temp.time = info->records[i].time; + + highscores_push_record( &temp ); + } +} + static void poll_connections(void) { SteamNetworkingMessage_t *messages[32]; @@ -118,35 +310,32 @@ static void poll_connections(void) } netmsg_blank *tmp = msg->m_pData; - if( tmp->inetmsg_id == k_inetmsg_scores_request ) - { - vg_log( "Recieved score request, sending records. (id: %u)\n", - msg->m_conn ); - - /* Send back current scores */ - u32 data_size = sizeof(netmsg_scores_info) + - 0*sizeof(struct netmsg_score_record); - netmsg_scores_info *return_info = malloc( data_size ); - return_info->inetmsg_id = k_inetmsg_scores_info; - return_info->record_count = 0; - - SteamAPI_ISteamNetworkingSockets_SendMessageToConnection( - hSteamNetworkingSockets, msg->m_conn, - return_info, data_size, - k_nSteamNetworkingSend_Reliable, NULL ); - } + if( tmp->inetmsg_id == k_inetmsg_auth ) + on_inet_auth( msg ); + else if( tmp->inetmsg_id == k_inetmsg_scores_request ) + on_inet_score_request( msg ); + else if( tmp->inetmsg_id == k_inetmsg_set_nickname ) + on_inet_set_nickname( msg ); SteamAPI_SteamNetworkingMessage_t_Release( msg ); } } } +u64 seconds_to_server_ticks( double s ) +{ + return s / 0.1; +} + int main( int argc, char *argv[] ) { - steamworks_ensure_txt( "2103940" ); signal( SIGINT, inthandler ); + signal( SIGQUIT, inthandler ); + highscores_init( 250000, 10000 ); + + steamworks_ensure_txt( "2103940" ); if( !vg_load_steam_symetric_key( "application_key", steam_symetric_key ) ) return 0; @@ -203,7 +392,8 @@ int main( int argc, char *argv[] ) SteamAPI_ISteamHTTP_SendHTTPRequest( hSteamHTTP, test_req, &call1->id ); #endif - u64 server_ticks = 8000; + u64 server_ticks = 8000, + last_record_save = 8000; while( !sig_stop ) { @@ -212,7 +402,16 @@ int main( int argc, char *argv[] ) usleep(100000); server_ticks ++; + + if(last_record_save+server_ticks > seconds_to_server_ticks( 10.0*60.0 )) + { + last_record_save = server_ticks; + highscores_serialize_all(); + } } + + highscores_serialize_all(); + highscores_free(); SteamAPI_ISteamNetworkingSockets_DestroyPollGroup( hSteamNetworkingSockets, client_pollgroup ); diff --git a/testaa.c b/testaa.c index aa1bf13..5e2cd93 100644 --- a/testaa.c +++ b/testaa.c @@ -17,14 +17,15 @@ int main(int argc, const char *argv[]) { vg_info( "Database test\n" ); - if( !highscores_init( 200000 ) ) + if( !highscores_init( 200000, 100000 ) ) return 0; - + + srand(time(0)); vg_log( "Inserting test records...\n" ); - for( int i=0; i<200000; i++ ) + for( int i=0; i<5000; i++ ) { highscore_record entry; - entry.trackid = vg_randf() * 129.0f; + entry.trackid = vg_randf() * 138.0f; entry.points = vg_randf() * 10000.0f; entry.time = vg_randf() * 20000.0f; entry.playerid = rand() % 800; @@ -32,9 +33,30 @@ int main(int argc, const char *argv[]) highscores_push_record( &entry ); } + + for( int i=0; i<80; i++ ) + { + char rando[16]; + + int l=2+rand()%8; + for( int i=0; i