X-Git-Url: https://harrygodden.com/git/?a=blobdiff_plain;f=highscores.h;h=f2c5eabc855b0654a1d3c3d5b6eca3dcdedf8b56;hb=47941822dae18a018c985847b052e70214a3ccc6;hp=2ffaf6acdd19f90bea341182801835b31c181900;hpb=5ee174baa9b2c30e01dc0ca0dfa38f916f805636;p=carveJwlIkooP6JGAAIwe30JlM.git diff --git a/highscores.h b/highscores.h index 2ffaf6a..f2c5eab 100644 --- a/highscores.h +++ b/highscores.h @@ -1,8 +1,13 @@ +/* + * Copyright (C) 2021-2022 Mt.ZERO Software, Harry Godden - All Rights Reserved + */ + #ifndef HIGHSCORES_H #define HIGHSCORES_H #include "vg/vg_store.h" #include "vg/vg_stdint.h" +#include "world_info.h" /* * Designed to be used across client and server, @@ -14,15 +19,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 +53,7 @@ struct highscore_record } aa; - struct - { - /* TODO pool allocator */ - u32 next, prev; - } - pool; + aatree_pool_node pool; }; }; @@ -58,32 +71,40 @@ 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) -static struct highscore_system +VG_STATIC struct highscore_system { highscore_database dbheader; aatree aainfo, aainfo_points, aainfo_time, aainfo_playerid, - aainfo_datetime; + aainfo_datetime, + aainfo_playerinfo_playerid, + aainfo_playerinfo; + + void *data, + *playerinfo_data; - void *data; + u32 pool_size, playerinfo_pool_size; } highscore_system; -static int highscore_cmp_points( void *a, void *b ) +VG_STATIC int highscore_cmp_points( void *a, void *b ) { highscore_record *pa = a, *pb = b; return (int)pa->points - (int)pb->points; } -static int highscore_cmp_datetime( void *a, void *b ) +VG_STATIC int highscore_cmp_datetime( void *a, void *b ) { highscore_record *pa = a, *pb = b; @@ -91,37 +112,112 @@ static int highscore_cmp_datetime( void *a, void *b ) return pa->datetime < pb->datetime? 1: -1; } -static int highscore_cmp_time( void *a, void *b ) +VG_STATIC int highscore_cmp_time( void *a, void *b ) { highscore_record *pa = a, *pb = b; return (int)pb->time - (int)pa->time; } -static int highscore_cmp_playerid( void *a, void *b ) +VG_STATIC int highscore_cmp_playerid( void *a, void *b ) { highscore_record *pa = a, *pb = b; if( pa->playerid == pb->playerid ) return 0; return pa->playerid < pb->playerid? -1: 1; } -static int highscores_init( u32 pool_size ) +VG_STATIC int highscore_cmp_playerinfo_playerid( void *a, void *b ) +{ + highscore_playerinfo *pa = a, *pb = b; + if( pa->playerid == pb->playerid ) return 0; + return pa->playerid < pb->playerid? -1: 1; +} + +VG_STATIC void highscores_create_db(void) { struct highscore_system *sys = &highscore_system; - size_t requested_mem = pool_size * sizeof(highscore_record); - sys->data = malloc( requested_mem ); + vg_info( "Initializing database nodes\n" ); + memset( &sys->dbheader, 0, sizeof(highscore_database) ); - requested_mem /= 1024; - requested_mem /= 1024; + sys->dbheader.pool_head = aatree_init_pool( &sys->aainfo, sys->pool_size ); + sys->dbheader.entry_capacity = sys->pool_size; - if( !highscore_system.data ) + for( int i=0; idbheader.tracks); i++ ) { - vg_error( "Could not allocated %dmb of memory for database\n", - requested_mem ); - return 0; + highscore_track_table *table = &sys->dbheader.tracks[i]; + table->root_points = AATREE_PTR_NIL; + table->root_playerid = AATREE_PTR_NIL; + 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, + sys->playerinfo_pool_size ); + sys->dbheader.playerinfo_capacity = sys->playerinfo_pool_size; + sys->dbheader.playerinfo_root = AATREE_PTR_NIL; +} + +VG_STATIC int highscores_read(void) +{ + struct highscore_system *sys = &highscore_system; + + 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" ); + return 0; + } + + count = fread( sys->data, sizeof(highscore_record), + sys->pool_size, fp ); + + if( count != sys->pool_size ) + { + vg_error( "Unexpected EOF reading database contents;" + " %lu records of %u were read\n", count, sys->pool_size ); + return 0; + } + + count = fread( sys->playerinfo_data, sizeof(highscore_playerinfo), + sys->playerinfo_pool_size, fp ); + + if( count != sys->playerinfo_pool_size ) + { + vg_error( "Unexpected EOF reading playerinfo contents;" + " %lu records of %u were read\n", count, + sys->playerinfo_pool_size ); + return 0; + } + + fclose( fp ); + return 1; } else - vg_success( "Allocated %dmb for database\n", requested_mem ); + { + vg_low( "No existing database found (.aadb)\n" ); + return 0; + } +} + +VG_STATIC void highscores_init( u32 pool_size, u32 playerinfo_pool_size ) +{ + struct highscore_system *sys = &highscore_system; + + sys->data = vg_linear_alloc( vg_mem.rtmemory, + pool_size*sizeof(highscore_record) ); + + sys->playerinfo_data = + vg_linear_alloc( vg_mem.rtmemory, + playerinfo_pool_size * sizeof(highscore_playerinfo) ); + /* This is ugly.. too bad! */ sys->aainfo.base = highscore_system.data; @@ -149,41 +245,66 @@ 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; - /* TODO: Load from disk if avalible */ - if( 0 ) - { + 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; - } - else - { - vg_info( "Initializing database nodes\n" ); - sys->dbheader.pool_head = aatree_init_pool( &sys->aainfo, pool_size ); + sys->playerinfo_pool_size = playerinfo_pool_size; + sys->pool_size = pool_size; +} - for( int i=0; idbheader.tracks); i++ ) - { - highscore_track_table *table = &sys->dbheader.tracks[i]; - table->root_points = AATREE_PTR_NIL; - table->root_playerid = AATREE_PTR_NIL; - table->root_time = AATREE_PTR_NIL; - table->root_datetime = AATREE_PTR_NIL; - } +VG_STATIC int highscores_serialize_all(void) +{ + 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 void highscores_free(void) +VG_STATIC highscore_record *highscore_find_user_record( u64 playerid, u32 trackid ) { - free( highscore_system.data ); + struct highscore_system *sys = &highscore_system; + + highscore_track_table *table = &sys->dbheader.tracks[trackid]; + highscore_record temp; + temp.playerid = playerid; + + aatree_ptr find = + aatree_find( &sys->aainfo_playerid, table->root_playerid, &temp ); + + if( find == AATREE_PTR_NIL ) + return NULL; + + return aatree_get_data( &sys->aainfo_playerid, find ); } -static aatree_ptr highscores_push_record( highscore_record *record ) +VG_STATIC aatree_ptr highscores_push_record( highscore_record *record ) { struct highscore_system *sys = &highscore_system; - /* TODO: Verify steam ID */ - vg_log( "Inserting record into database for track %hu\n",record->trackid ); + vg_low( "Inserting record into database for track %hu\n",record->trackid ); if( record->trackid >= vg_list_size(sys->dbheader.tracks) ) { @@ -201,7 +322,17 @@ static aatree_ptr highscores_push_record( highscore_record *record ) if( existing != AATREE_PTR_NIL ) { - vg_log( "Freeing existing record for player %lu\n", record->playerid ); + highscore_record *crecord = aatree_get_data( &sys->aainfo_playerid, + existing ); + + if( crecord->time < record->time || + (crecord->time == record->time && crecord->points > record->points)) + { + vg_low( "Not overwriting better score\n" ); + return existing; + } + + vg_low( "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 ); table->root_points = aatree_del( &sys->aainfo_points, existing ); @@ -214,7 +345,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,35 +371,251 @@ static aatree_ptr highscores_push_record( highscore_record *record ) return index; } -static void _highscore_showtime( void *data ) +VG_STATIC aatree_ptr highscore_set_user_nickname( u64 steamid, char nick[16] ) { - highscore_record *record = data; - printf( "%hu", record->time ); + char name[17]; + for( int i=0; i<16; i++ ) + name[i] = nick[i]; + name[16] = '\0'; + + vg_low( "Updating %lu's nickname -> %s\n", steamid, name ); + + 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 highscores_print_track( u32 trackid, u32 count ) +/* Get the length of a string, bounded by '\0' or len, whichever is first */ +VG_STATIC int highscore_strlen( const char *str, int len ) { + int str_length; + for( str_length=0; str_length= width ) + return; + + buf[j] = str[i]; + } +} + +/* Print the string(max length:len) left aligned into buf */ +VG_STATIC void highscore_strl( char *buf, const char *str, int len ) +{ + for( int i=0; i=len ) + return; + + buf[ len-1 - (i ++) ] = '0' + (value % 10); + value /= 10; + } + + for( ;i=len ) + break; + + temp[ i ++ ] = '0' + (value % 10); + value /= 10; + } + + if( i>len ) + i = len; + + for( int j=0; jdbheader.tracks[ trackid ]; + highscore_track_table *table = &sys->dbheader.tracks[ id ]; 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" ); + highscore_strc ( buf+w*0, inf->name, w,w ); + highscore_clear( buf+w*1, '-', w ); + highscore_strl ( buf+w*2, " #| Player | Time ", 27 ); - int i=0; - while( it != AATREE_PTR_NIL && i < 10 ) + for( int i=0; iaainfo_time, it ); - vg_info( " [%d]: player(%lu), time: %hu, score: %hu, track:%hu\n", - i+1, record->playerid, record->time, record->points, - record->trackid ); + highscore_playerinfo temp; + temp.playerid = record->playerid; + + aatree_ptr info_ptr = aatree_find( &sys->aainfo_playerinfo_playerid, + sys->dbheader.playerinfo_root, + &temp ); + + /* Player name */ + if( info_ptr == AATREE_PTR_NIL ) + highscore_strl( line+3, "unknown", 16 ); + else + { + highscore_playerinfo *inf = aatree_get_data( + &sys->aainfo_playerinfo_playerid, info_ptr ); - i++; + highscore_strl( line+3, inf->nickname, 16 ); + } + + u16 centiseconds = record->time, + seconds = centiseconds / 100, + minutes = seconds / 60; + + centiseconds %= 100; + seconds %= 60; + minutes %= 60; + + if( minutes > 9 ) minutes = 9; + + /* Timer */ + highscore_intr( line+20, minutes, 1, '0' ); + line[21] = ':'; + highscore_intr( line+22, seconds, 2, '0' ); + line[24] = '.'; + highscore_intr( line+25, centiseconds, 2, '0' ); + +#if 0 + /* Score */ + highscore_intl( line+22, record->points, 5 ); +#endif it = aatree_next( &sys->aainfo_time, it ); } +} + +/* Print string out to file using newlines. Count is number of records + * ( this requires a buffer of (count+3)*27 size */ +VG_STATIC void highscores_board_printf( FILE *fp, const char *buf, u32 count ) +{ + int w=27; - vg_info( "==============================================\n" ); + for( int i=0; i