4 #include "vg/vg_store.h"
5 #include "vg/vg_stdint.h"
6 #include "world_info.h"
9 * Designed to be used across client and server,
10 * for the client its only storing the local users records, for server its
14 typedef struct highscore highscore
;
15 typedef struct highscore_record highscore_record
;
16 typedef struct highscore_track_table highscore_track_table
;
17 typedef struct highscore_database highscore_database
;
18 typedef struct highscore_playerinfo highscore_playerinfo
;
22 struct highscore_playerinfo
29 aatree_pool_node aapn
;
30 aatree_node aa_playerid
;
34 struct highscore_record
36 u16 trackid
, points
, time
, reserved0
;
52 aatree_pool_node pool
;
56 struct highscore_track_table
58 aatree_ptr root_points
,
66 struct highscore_database
68 highscore_track_table tracks
[ 128 ];
70 aatree_ptr pool_head
, playerinfo_head
;
72 playerinfo_capacity
, playerinfo_root
;
79 static struct highscore_system
81 highscore_database dbheader
;
87 aainfo_playerinfo_playerid
,
90 void *data
, *playerinfo_data
;
94 static int highscore_cmp_points( void *a
, void *b
)
96 highscore_record
*pa
= a
, *pb
= b
;
97 return (int)pa
->points
- (int)pb
->points
;
100 static int highscore_cmp_datetime( void *a
, void *b
)
102 highscore_record
*pa
= a
, *pb
= b
;
104 if( pa
->datetime
== pb
->datetime
) return 0;
105 return pa
->datetime
< pb
->datetime
? 1: -1;
108 static int highscore_cmp_time( void *a
, void *b
)
110 highscore_record
*pa
= a
, *pb
= b
;
111 return (int)pb
->time
- (int)pa
->time
;
114 static int highscore_cmp_playerid( void *a
, void *b
)
116 highscore_record
*pa
= a
, *pb
= b
;
117 if( pa
->playerid
== pb
->playerid
) return 0;
118 return pa
->playerid
< pb
->playerid
? -1: 1;
121 static int highscore_cmp_playerinfo_playerid( void *a
, void *b
)
123 highscore_playerinfo
*pa
= a
, *pb
= b
;
124 if( pa
->playerid
== pb
->playerid
) return 0;
125 return pa
->playerid
< pb
->playerid
? -1: 1;
128 static void *highscore_malloc( u32 count
, u32 size
)
130 size_t requested_mem
= size
* count
;
131 void *data
= malloc( requested_mem
);
133 requested_mem
/= 1024;
134 requested_mem
/= 1024;
138 vg_error( "Could not allocated %dmb of memory\n", requested_mem
);
142 vg_success( "Allocated %dmb for %u records\n", requested_mem
, count
);
147 static void highscores_free(void)
149 free( highscore_system
.data
);
150 free( highscore_system
.playerinfo_data
);
153 static int highscores_init( u32 pool_size
, u32 playerinfo_pool_size
)
155 struct highscore_system
*sys
= &highscore_system
;
157 sys
->data
= highscore_malloc( pool_size
, sizeof(highscore_record
) );
158 if( !sys
->data
) return 0;
160 sys
->playerinfo_data
=
161 highscore_malloc( playerinfo_pool_size
, sizeof(highscore_playerinfo
));
162 if( !sys
->playerinfo_data
)
168 /* This is ugly.. too bad! */
169 sys
->aainfo
.base
= highscore_system
.data
;
170 sys
->aainfo
.stride
= sizeof(highscore_record
);
171 sys
->aainfo
.offset
= offsetof(highscore_record
,pool
);
172 sys
->aainfo
.p_cmp
= NULL
;
174 sys
->aainfo_datetime
.base
= highscore_system
.data
;
175 sys
->aainfo_datetime
.stride
= sizeof(highscore_record
);
176 sys
->aainfo_datetime
.offset
= offsetof(highscore_record
,aa
.datetime
);
177 sys
->aainfo_datetime
.p_cmp
= highscore_cmp_datetime
;
179 sys
->aainfo_points
.base
= highscore_system
.data
;
180 sys
->aainfo_points
.stride
= sizeof(highscore_record
);
181 sys
->aainfo_points
.offset
= offsetof(highscore_record
,aa
.points
);
182 sys
->aainfo_points
.p_cmp
= highscore_cmp_points
;
184 sys
->aainfo_time
.base
= highscore_system
.data
;
185 sys
->aainfo_time
.stride
= sizeof(highscore_record
);
186 sys
->aainfo_time
.offset
= offsetof(highscore_record
,aa
.time
);
187 sys
->aainfo_time
.p_cmp
= highscore_cmp_time
;
189 sys
->aainfo_playerid
.base
= highscore_system
.data
;
190 sys
->aainfo_playerid
.stride
= sizeof(highscore_record
);
191 sys
->aainfo_playerid
.offset
= offsetof(highscore_record
,aa
.playerid
);
192 sys
->aainfo_playerid
.p_cmp
= highscore_cmp_playerid
;
194 sys
->aainfo_playerinfo_playerid
.base
= highscore_system
.playerinfo_data
;
195 sys
->aainfo_playerinfo_playerid
.stride
= sizeof(highscore_playerinfo
);
196 sys
->aainfo_playerinfo_playerid
.offset
=
197 offsetof(highscore_playerinfo
,aa_playerid
);
198 sys
->aainfo_playerinfo_playerid
.p_cmp
= highscore_cmp_playerinfo_playerid
;
200 sys
->aainfo_playerinfo
.base
= highscore_system
.playerinfo_data
;
201 sys
->aainfo_playerinfo
.stride
= sizeof(highscore_playerinfo
);
202 sys
->aainfo_playerinfo
.offset
= offsetof(highscore_playerinfo
,aapn
);
203 sys
->aainfo_playerinfo
.p_cmp
= NULL
;
205 FILE *fp
= fopen( ".aadb", "rb" );
208 vg_info( "Loading existing database\n" );
210 u64 count
= fread( &sys
->dbheader
, sizeof(highscore_database
), 1, fp
);
214 vg_error( "Unexpected EOF reading database header\n" );
220 count
= fread( sys
->data
, sizeof(highscore_record
), pool_size
, fp
);
221 if( count
!= pool_size
)
223 vg_error( "Unexpected EOF reading database contents;"
224 " %lu records of %u were read\n", count
, pool_size
);
230 count
= fread( sys
->playerinfo_data
, sizeof(highscore_playerinfo
),
231 playerinfo_pool_size
, fp
);
232 if( count
!= playerinfo_pool_size
)
234 vg_error( "Unexpected EOF reading playerinfo contents;"
235 " %lu records of %u were read\n", count
,
236 playerinfo_pool_size
);
246 vg_log( "No existing database found (.aadb)\n" );
247 vg_info( "Initializing database nodes\n" );
248 memset( &sys
->dbheader
, 0, sizeof(highscore_database
) );
250 sys
->dbheader
.pool_head
= aatree_init_pool( &sys
->aainfo
, pool_size
);
251 sys
->dbheader
.entry_capacity
= pool_size
;
253 for( int i
=0; i
<vg_list_size(sys
->dbheader
.tracks
); i
++ )
255 highscore_track_table
*table
= &sys
->dbheader
.tracks
[i
];
256 table
->root_points
= AATREE_PTR_NIL
;
257 table
->root_playerid
= AATREE_PTR_NIL
;
258 table
->root_time
= AATREE_PTR_NIL
;
259 table
->root_datetime
= AATREE_PTR_NIL
;
262 /* Initialize secondary db */
263 sys
->dbheader
.playerinfo_head
= aatree_init_pool(
264 &sys
->aainfo_playerinfo
,
265 playerinfo_pool_size
);
266 sys
->dbheader
.playerinfo_capacity
= playerinfo_pool_size
;
267 sys
->dbheader
.playerinfo_root
= AATREE_PTR_NIL
;
273 static int highscores_serialize_all(void)
275 struct highscore_system
*sys
= &highscore_system
;
276 vg_info( "Serializing database\n" );
278 FILE *fp
= fopen( ".aadb", "wb" );
282 vg_error( "Could not open .aadb\n" );
286 fwrite( &sys
->dbheader
, sizeof(highscore_database
), 1, fp
);
287 fwrite( sys
->data
, sizeof(highscore_record
),
288 sys
->dbheader
.entry_capacity
, fp
);
289 fwrite( sys
->playerinfo_data
, sizeof(highscore_playerinfo
),
290 sys
->dbheader
.playerinfo_capacity
, fp
);
296 static aatree_ptr
highscores_push_record( highscore_record
*record
)
298 struct highscore_system
*sys
= &highscore_system
;
300 /* TODO: Verify steam ID */
301 vg_log( "Inserting record into database for track %hu\n",record
->trackid
);
303 if( record
->trackid
>= vg_list_size(sys
->dbheader
.tracks
) )
305 vg_error( "TrackID out of range (%hu>=%d)\n", record
->trackid
,
306 vg_list_size(sys
->dbheader
.tracks
) );
308 return AATREE_PTR_NIL
;
311 /* Search for existing record on this track */
312 highscore_track_table
*table
= &sys
->dbheader
.tracks
[record
->trackid
];
313 aatree_ptr existing
= aatree_find( &sys
->aainfo_playerid
,
314 table
->root_playerid
,
317 if( existing
!= AATREE_PTR_NIL
)
319 highscore_record
*crecord
= aatree_get_data( &sys
->aainfo_playerid
,
322 if( crecord
->time
< record
->time
||
323 (crecord
->time
== record
->time
&& crecord
->points
> record
->points
))
325 vg_log( "Not overwriting better score\n" );
329 vg_log( "Freeing existing record for player %lu\n", record
->playerid
);
330 table
->root_playerid
= aatree_del( &sys
->aainfo_playerid
, existing
);
331 table
->root_datetime
= aatree_del( &sys
->aainfo_datetime
, existing
);
332 table
->root_points
= aatree_del( &sys
->aainfo_points
, existing
);
333 table
->root_time
= aatree_del( &sys
->aainfo_time
, existing
);
335 aatree_pool_free( &sys
->aainfo
, existing
, &sys
->dbheader
.pool_head
);
339 aatree_pool_alloc( &sys
->aainfo
, &sys
->dbheader
.pool_head
);
341 if( index
== AATREE_PTR_NIL
)
343 vg_error( "Database records are over capacity!\n" );
347 highscore_record
*dst
= aatree_get_data( &sys
->aainfo
, index
);
348 memset( dst
, 0, sizeof(highscore_record
) );
350 dst
->trackid
= record
->trackid
;
351 dst
->datetime
= record
->datetime
;
352 dst
->playerid
= record
->playerid
;
353 dst
->points
= record
->points
;
354 dst
->time
= record
->time
;
357 aatree_insert( &sys
->aainfo_time
, table
->root_time
, index
);
358 table
->root_datetime
=
359 aatree_insert( &sys
->aainfo_datetime
, table
->root_datetime
, index
);
360 table
->root_playerid
=
361 aatree_insert( &sys
->aainfo_playerid
, table
->root_playerid
, index
);
363 aatree_insert( &sys
->aainfo_points
, table
->root_points
, index
);
368 static aatree_ptr
highscore_set_user_nickname( u64 steamid
, char nick
[10] )
371 for( int i
=0; i
<10; i
++ )
375 vg_log( "Updating %lu's nickname -> %s\n", steamid
, name
);
377 struct highscore_system
*sys
= &highscore_system
;
379 highscore_playerinfo temp
;
380 temp
.playerid
= steamid
;
382 aatree_ptr record
= aatree_find( &sys
->aainfo_playerinfo_playerid
,
383 sys
->dbheader
.playerinfo_root
,
385 highscore_playerinfo
*info
;
387 if( record
!= AATREE_PTR_NIL
)
389 info
= aatree_get_data( &sys
->aainfo_playerinfo
, record
);
393 record
= aatree_pool_alloc( &sys
->aainfo_playerinfo
,
394 &sys
->dbheader
.playerinfo_head
);
396 if( record
== AATREE_PTR_NIL
)
398 vg_error( "Player info database is over capacity!\n" );
399 return AATREE_PTR_NIL
;
402 info
= aatree_get_data( &sys
->aainfo_playerinfo
, record
);
403 memset( info
, 0, sizeof(highscore_playerinfo
) );
405 info
->playerid
= steamid
;
406 sys
->dbheader
.playerinfo_root
= aatree_insert(
407 &sys
->aainfo_playerinfo_playerid
,
408 sys
->dbheader
.playerinfo_root
,
412 for( int i
=0; i
<10; i
++ )
413 info
->nickname
[i
] = nick
[i
];
415 return AATREE_PTR_NIL
;
418 /* Get the length of a string, bounded by '\0' or len, whichever is first */
419 static int highscore_strlen( const char *str
, int len
)
422 for( str_length
=0; str_length
<len
; str_length
++ )
423 if( !str
[str_length
] )
429 /* Print the string(max length:len) centered into buf (has width:width) */
430 static void highscore_strc( char *buf
, const char *str
, int len
, int width
)
432 int str_length
= highscore_strlen( str
, len
),
433 offs
= (width
-str_length
)/2;
435 for( int i
=0; i
<str_length
; i
++ )
446 /* Print the string(max length:len) left aligned into buf */
447 static void highscore_strl( char *buf
, const char *str
, int len
)
449 for( int i
=0; i
<len
; i
++ )
458 /* Print the string (max length:len) right aligned into buf (has width:width) */
459 static void highscore_strr( char *buf
, const char *str
, int len
, int width
)
461 int str_length
= highscore_strlen( str
, len
);
463 for( int i
=0; i
<len
; i
++ )
468 buf
[width
-str_length
+i
] = str
[i
];
472 /* Print integer (padded with: alt), right aligned into buf(width: len) */
473 static void highscore_intr( char *buf
, int value
, int len
, char alt
)
481 buf
[ len
-1 - (i
++) ] = '0' + (value
% 10);
486 buf
[ len
-1 - i
] = alt
;
489 /* Print integer into buffer with max length len */
490 static void highscore_intl( char *buf
, int value
, int len
)
500 temp
[ i
++ ] = '0' + (value
% 10);
507 for( int j
=0; j
<i
; j
++ )
509 buf
[j
] = temp
[ i
-1-j
];
513 /* Clear buffer with length using clr character */
514 static void highscore_clear( char *buf
, char clr
, int length
)
516 for( int i
=0; i
<length
; i
++ )
522 --------------------------
523 #| Player | Time | Pts
524 1|aaaabbbbcc 5:23.32 30000
526 3|aaabbbcccl 2:30.45 20000
536 /* Generate a highscores board in text form, the width is always 27. Buffer
537 * must be (count+3)*27 in size. */
538 static void highscores_board_generate( char *buf
, u32 id
, u32 count
)
541 highscore_clear( buf
, ' ', (count
+3)*w
);
543 struct track_info
*inf
= &track_infos
[id
];
544 struct highscore_system
*sys
= &highscore_system
;
546 highscore_track_table
*table
= &sys
->dbheader
.tracks
[ id
];
547 aatree_ptr it
= aatree_kth( &sys
->aainfo_time
, table
->root_time
, 0 );
549 highscore_strc ( buf
+w
*0, inf
->name
, w
,w
);
550 highscore_clear( buf
+w
*1, '-', w
);
551 highscore_strl ( buf
+w
*2, " #| Player | Time | Pts", 27 );
553 for( int i
=0; i
<count
; i
++ )
555 char *line
= buf
+w
*(3+i
);
556 highscore_intr( line
, i
+1, 2, ' ' );
559 if( it
== AATREE_PTR_NIL
)
562 highscore_record
*record
= aatree_get_data( &sys
->aainfo_time
, it
);
563 highscore_playerinfo temp
;
564 temp
.playerid
= record
->playerid
;
566 aatree_ptr info_ptr
= aatree_find( &sys
->aainfo_playerinfo_playerid
,
567 sys
->dbheader
.playerinfo_root
,
571 if( info_ptr
== AATREE_PTR_NIL
)
572 highscore_strl( line
+3, "unknown", 10 );
575 highscore_playerinfo
*inf
= aatree_get_data(
576 &sys
->aainfo_playerinfo_playerid
, info_ptr
);
578 highscore_strl( line
+3, inf
->nickname
, 10 );
581 u16 miliseconds
= record
->time
,
582 seconds
= miliseconds
/ 100,
583 minutes
= seconds
/ 60;
589 if( minutes
> 9 ) minutes
= 9;
592 highscore_intr( line
+14, minutes
, 1, '0' );
594 highscore_intr( line
+16, seconds
, 2, '0' );
596 highscore_intr( line
+19, miliseconds
, 2, '0' );
599 highscore_intl( line
+22, record
->time
, 5 );
600 it
= aatree_next( &sys
->aainfo_time
, it
);
604 /* Print string out to file using newlines. Count is number of records
605 * ( this requires a buffer of (count+3)*27 size */
606 static void highscores_board_printf( FILE *fp
, const char *buf
, u32 count
)
610 for( int i
=0; i
<count
+3; i
++ )
611 fprintf( fp
, "%.27s\n", &buf
[i
*w
] );
614 #endif /* HIGHSCORES_H */