X-Git-Url: https://harrygodden.com/git/?a=blobdiff_plain;f=highscores.c;fp=highscores.c;h=77ffc412de82d4a32c9246d304a89cf692b02ad5;hb=d07048b61445be11605adba43667e19214358a24;hp=0000000000000000000000000000000000000000;hpb=479b953c11ddf5d11c0f1f626995a3d402e6d9dd;p=carveJwlIkooP6JGAAIwe30JlM.git diff --git a/highscores.c b/highscores.c new file mode 100644 index 0000000..77ffc41 --- /dev/null +++ b/highscores.c @@ -0,0 +1,540 @@ +#ifndef HIGHSCORES_C +#define HIGHSCORES_C + +#include "highscores.h" + +VG_STATIC int highscore_cmp_points( void *a, void *b ) +{ + highscore_record *pa = a, *pb = b; + return (int)pa->points - (int)pb->points; +} + +VG_STATIC int highscore_cmp_datetime( void *a, void *b ) +{ + highscore_record *pa = a, *pb = b; + + if( pa->datetime == pb->datetime ) return 0; + return pa->datetime < pb->datetime? 1: -1; +} + +VG_STATIC int highscore_cmp_time( void *a, void *b ) +{ + highscore_record *pa = a, *pb = b; + return (int)pb->time - (int)pa->time; +} + +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; +} + +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; + + vg_info( "Initializing database nodes\n" ); + memset( &sys->dbheader, 0, sizeof(highscore_database) ); + + sys->dbheader.pool_head = aatree_init_pool( &sys->aainfo, sys->pool_size ); + sys->dbheader.entry_capacity = sys->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; + } + + /* 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_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) ); + + memset( sys->data, 0, pool_size*sizeof(highscore_record) ); + memset( sys->playerinfo_data, 0, + playerinfo_pool_size*sizeof(highscore_playerinfo) ); + + + /* This is ugly.. too bad! */ + sys->aainfo.base = highscore_system.data; + sys->aainfo.stride = sizeof(highscore_record); + sys->aainfo.offset = offsetof(highscore_record,pool); + sys->aainfo.p_cmp = NULL; + + sys->aainfo_datetime.base = highscore_system.data; + sys->aainfo_datetime.stride = sizeof(highscore_record); + sys->aainfo_datetime.offset = offsetof(highscore_record,aa.datetime); + sys->aainfo_datetime.p_cmp = highscore_cmp_datetime; + + sys->aainfo_points.base = highscore_system.data; + sys->aainfo_points.stride = sizeof(highscore_record); + sys->aainfo_points.offset = offsetof(highscore_record,aa.points); + sys->aainfo_points.p_cmp = highscore_cmp_points; + + sys->aainfo_time.base = highscore_system.data; + sys->aainfo_time.stride = sizeof(highscore_record); + sys->aainfo_time.offset = offsetof(highscore_record,aa.time); + sys->aainfo_time.p_cmp = highscore_cmp_time; + + sys->aainfo_playerid.base = highscore_system.data; + sys->aainfo_playerid.stride = sizeof(highscore_record); + 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; + + sys->playerinfo_pool_size = playerinfo_pool_size; + sys->pool_size = pool_size; +} + +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; +} + +VG_STATIC highscore_record *highscore_find_user_record( u64 playerid, + u32 trackid ) +{ + 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 ); +} + +VG_STATIC aatree_ptr highscores_push_record( highscore_record *record ) +{ + struct highscore_system *sys = &highscore_system; + + vg_low( "Inserting record into database for track %hu\n",record->trackid ); + + if( record->trackid >= vg_list_size(sys->dbheader.tracks) ){ + vg_error( "TrackID out of range (%hu>=%d)\n", record->trackid, + vg_list_size(sys->dbheader.tracks) ); + + return AATREE_PTR_NIL; + } + + /* Search for existing record on this track */ + highscore_track_table *table = &sys->dbheader.tracks[record->trackid]; + aatree_ptr existing = aatree_find( &sys->aainfo_playerid, + table->root_playerid, + 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_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 ); + table->root_time = aatree_del( &sys->aainfo_time, existing ); + + aatree_pool_free( &sys->aainfo, existing, &sys->dbheader.pool_head ); + } + + aatree_ptr index = + 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) ); + + dst->trackid = record->trackid; + dst->datetime = record->datetime; + dst->playerid = record->playerid; + dst->points = record->points; + dst->time = record->time; + + table->root_time = + aatree_insert( &sys->aainfo_time, table->root_time, index ); + table->root_datetime = + aatree_insert( &sys->aainfo_datetime, table->root_datetime, index ); + table->root_playerid = + aatree_insert( &sys->aainfo_playerid, table->root_playerid, index ); + table->root_points = + aatree_insert( &sys->aainfo_points, table->root_points, index ); + + return index; +} + +VG_STATIC aatree_ptr highscore_set_user_nickname( u64 steamid, char nick[16] ) +{ + 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; +} + +/* 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 i; + + 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[ id ]; + aatree_ptr it = aatree_kth( &sys->aainfo_time, table->root_time, 0 ); + + highscore_strc ( buf+w*0, inf->name, w,w ); + highscore_clear( buf+w*1, '-', w ); + highscore_strl ( buf+w*2, " #| Player | Time ", 27 ); + + for( int i=0; iaainfo_time, it ); + 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 ); + + highscore_strl( line+3, inf->nickname, 16 ); + + /* yep, this is fucking awesome! */ + if( inf->playerid == 0x8297744501001001 || + inf->playerid == 0x1ec4620101001001 || + inf->playerid == 0x0110000145749782 || + inf->playerid == 0x011000010162c41e ) + { + i --; + /* FIXME: Clear line, or yknow, do it properly */ + } + } + + 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; + + for( int i=0; i