clear runs when respawning
[carveJwlIkooP6JGAAIwe30JlM.git] / highscores.c
1 #ifndef HIGHSCORES_C
2 #define HIGHSCORES_C
3
4 #include "highscores.h"
5
6 VG_STATIC int highscore_cmp_points( void *a, void *b )
7 {
8 highscore_record *pa = a, *pb = b;
9 return (int)pa->points - (int)pb->points;
10 }
11
12 VG_STATIC int highscore_cmp_datetime( void *a, void *b )
13 {
14 highscore_record *pa = a, *pb = b;
15
16 if( pa->datetime == pb->datetime ) return 0;
17 return pa->datetime < pb->datetime? 1: -1;
18 }
19
20 VG_STATIC int highscore_cmp_time( void *a, void *b )
21 {
22 highscore_record *pa = a, *pb = b;
23 return (int)pb->time - (int)pa->time;
24 }
25
26 VG_STATIC int highscore_cmp_playerid( void *a, void *b )
27 {
28 highscore_record *pa = a, *pb = b;
29 if( pa->playerid == pb->playerid ) return 0;
30 return pa->playerid < pb->playerid? -1: 1;
31 }
32
33 VG_STATIC int highscore_cmp_playerinfo_playerid( void *a, void *b )
34 {
35 highscore_playerinfo *pa = a, *pb = b;
36 if( pa->playerid == pb->playerid ) return 0;
37 return pa->playerid < pb->playerid? -1: 1;
38 }
39
40 VG_STATIC void highscores_create_db(void)
41 {
42 struct highscore_system *sys = &highscore_system;
43
44 vg_info( "Initializing database nodes\n" );
45 memset( &sys->dbheader, 0, sizeof(highscore_database) );
46
47 sys->dbheader.pool_head = aatree_init_pool( &sys->aainfo, sys->pool_size );
48 sys->dbheader.entry_capacity = sys->pool_size;
49
50 for( int i=0; i<vg_list_size(sys->dbheader.tracks); i++ )
51 {
52 highscore_track_table *table = &sys->dbheader.tracks[i];
53 table->root_points = AATREE_PTR_NIL;
54 table->root_playerid = AATREE_PTR_NIL;
55 table->root_time = AATREE_PTR_NIL;
56 table->root_datetime = AATREE_PTR_NIL;
57 }
58
59 /* Initialize secondary db */
60 sys->dbheader.playerinfo_head = aatree_init_pool(
61 &sys->aainfo_playerinfo,
62 sys->playerinfo_pool_size );
63 sys->dbheader.playerinfo_capacity = sys->playerinfo_pool_size;
64 sys->dbheader.playerinfo_root = AATREE_PTR_NIL;
65 }
66
67 VG_STATIC int highscores_read(void)
68 {
69 struct highscore_system *sys = &highscore_system;
70
71 FILE *fp = fopen( ".aadb", "rb" );
72 if( fp ){
73 vg_info( "Loading existing database\n" );
74
75 u64 count = fread( &sys->dbheader, sizeof(highscore_database), 1, fp );
76
77 if( count != 1 )
78 {
79 vg_error( "Unexpected EOF reading database header\n" );
80 return 0;
81 }
82
83 count = fread( sys->data, sizeof(highscore_record),
84 sys->pool_size, fp );
85
86 if( count != sys->pool_size )
87 {
88 vg_error( "Unexpected EOF reading database contents;"
89 " %lu records of %u were read\n", count, sys->pool_size );
90 return 0;
91 }
92
93 count = fread( sys->playerinfo_data, sizeof(highscore_playerinfo),
94 sys->playerinfo_pool_size, fp );
95
96 if( count != sys->playerinfo_pool_size )
97 {
98 vg_error( "Unexpected EOF reading playerinfo contents;"
99 " %lu records of %u were read\n", count,
100 sys->playerinfo_pool_size );
101 return 0;
102 }
103
104 fclose( fp );
105 return 1;
106 }
107 else
108 {
109 vg_low( "No existing database found (.aadb)\n" );
110 return 0;
111 }
112 }
113
114 VG_STATIC void highscores_init( u32 pool_size, u32 playerinfo_pool_size )
115 {
116 struct highscore_system *sys = &highscore_system;
117
118 sys->data = vg_linear_alloc( vg_mem.rtmemory,
119 pool_size*sizeof(highscore_record) );
120
121 sys->playerinfo_data =
122 vg_linear_alloc( vg_mem.rtmemory,
123 playerinfo_pool_size * sizeof(highscore_playerinfo) );
124
125 memset( sys->data, 0, pool_size*sizeof(highscore_record) );
126 memset( sys->playerinfo_data, 0,
127 playerinfo_pool_size*sizeof(highscore_playerinfo) );
128
129
130 /* This is ugly.. too bad! */
131 sys->aainfo.base = highscore_system.data;
132 sys->aainfo.stride = sizeof(highscore_record);
133 sys->aainfo.offset = offsetof(highscore_record,pool);
134 sys->aainfo.p_cmp = NULL;
135
136 sys->aainfo_datetime.base = highscore_system.data;
137 sys->aainfo_datetime.stride = sizeof(highscore_record);
138 sys->aainfo_datetime.offset = offsetof(highscore_record,aa.datetime);
139 sys->aainfo_datetime.p_cmp = highscore_cmp_datetime;
140
141 sys->aainfo_points.base = highscore_system.data;
142 sys->aainfo_points.stride = sizeof(highscore_record);
143 sys->aainfo_points.offset = offsetof(highscore_record,aa.points);
144 sys->aainfo_points.p_cmp = highscore_cmp_points;
145
146 sys->aainfo_time.base = highscore_system.data;
147 sys->aainfo_time.stride = sizeof(highscore_record);
148 sys->aainfo_time.offset = offsetof(highscore_record,aa.time);
149 sys->aainfo_time.p_cmp = highscore_cmp_time;
150
151 sys->aainfo_playerid.base = highscore_system.data;
152 sys->aainfo_playerid.stride = sizeof(highscore_record);
153 sys->aainfo_playerid.offset = offsetof(highscore_record,aa.playerid);
154 sys->aainfo_playerid.p_cmp = highscore_cmp_playerid;
155
156 sys->aainfo_playerinfo_playerid.base = highscore_system.playerinfo_data;
157 sys->aainfo_playerinfo_playerid.stride = sizeof(highscore_playerinfo);
158 sys->aainfo_playerinfo_playerid.offset =
159 offsetof(highscore_playerinfo,aa_playerid);
160 sys->aainfo_playerinfo_playerid.p_cmp = highscore_cmp_playerinfo_playerid;
161
162 sys->aainfo_playerinfo.base = highscore_system.playerinfo_data;
163 sys->aainfo_playerinfo.stride = sizeof(highscore_playerinfo);
164 sys->aainfo_playerinfo.offset = offsetof(highscore_playerinfo,aapn);
165 sys->aainfo_playerinfo.p_cmp = NULL;
166
167 sys->playerinfo_pool_size = playerinfo_pool_size;
168 sys->pool_size = pool_size;
169 }
170
171 VG_STATIC int highscores_serialize_all(void)
172 {
173 struct highscore_system *sys = &highscore_system;
174 vg_info( "Serializing database\n" );
175
176 FILE *fp = fopen( ".aadb", "wb" );
177
178 if( !fp )
179 {
180 vg_error( "Could not open .aadb\n" );
181 return 0;
182 }
183
184 fwrite( &sys->dbheader, sizeof(highscore_database), 1, fp );
185 fwrite( sys->data, sizeof(highscore_record),
186 sys->dbheader.entry_capacity, fp );
187 fwrite( sys->playerinfo_data, sizeof(highscore_playerinfo),
188 sys->dbheader.playerinfo_capacity, fp );
189
190 fclose( fp );
191 return 1;
192 }
193
194 VG_STATIC highscore_record *highscore_find_user_record( u64 playerid,
195 u32 trackid )
196 {
197 struct highscore_system *sys = &highscore_system;
198
199 highscore_track_table *table = &sys->dbheader.tracks[trackid];
200 highscore_record temp;
201 temp.playerid = playerid;
202
203 aatree_ptr find =
204 aatree_find( &sys->aainfo_playerid, table->root_playerid, &temp );
205
206 if( find == AATREE_PTR_NIL )
207 return NULL;
208
209 return aatree_get_data( &sys->aainfo_playerid, find );
210 }
211
212 VG_STATIC aatree_ptr highscores_push_record( highscore_record *record )
213 {
214 struct highscore_system *sys = &highscore_system;
215
216 vg_low( "Inserting record into database for track %hu\n",record->trackid );
217
218 if( record->trackid >= vg_list_size(sys->dbheader.tracks) ){
219 vg_error( "TrackID out of range (%hu>=%d)\n", record->trackid,
220 vg_list_size(sys->dbheader.tracks) );
221
222 return AATREE_PTR_NIL;
223 }
224
225 /* Search for existing record on this track */
226 highscore_track_table *table = &sys->dbheader.tracks[record->trackid];
227 aatree_ptr existing = aatree_find( &sys->aainfo_playerid,
228 table->root_playerid,
229 record );
230
231 if( existing != AATREE_PTR_NIL ){
232 highscore_record *crecord = aatree_get_data( &sys->aainfo_playerid,
233 existing );
234
235 if( crecord->time < record->time ||
236 (crecord->time == record->time && crecord->points > record->points))
237 {
238 vg_low( "Not overwriting better score\n" );
239 return existing;
240 }
241
242 vg_low( "Freeing existing record for player %lu\n", record->playerid );
243 table->root_playerid = aatree_del( &sys->aainfo_playerid, existing );
244 table->root_datetime = aatree_del( &sys->aainfo_datetime, existing );
245 table->root_points = aatree_del( &sys->aainfo_points, existing );
246 table->root_time = aatree_del( &sys->aainfo_time, existing );
247
248 aatree_pool_free( &sys->aainfo, existing, &sys->dbheader.pool_head );
249 }
250
251 aatree_ptr index =
252 aatree_pool_alloc( &sys->aainfo, &sys->dbheader.pool_head );
253
254 if( index == AATREE_PTR_NIL )
255 {
256 vg_error( "Database records are over capacity!\n" );
257 return index;
258 }
259
260 highscore_record *dst = aatree_get_data( &sys->aainfo, index );
261 memset( dst, 0, sizeof(highscore_record) );
262
263 dst->trackid = record->trackid;
264 dst->datetime = record->datetime;
265 dst->playerid = record->playerid;
266 dst->points = record->points;
267 dst->time = record->time;
268
269 table->root_time =
270 aatree_insert( &sys->aainfo_time, table->root_time, index );
271 table->root_datetime =
272 aatree_insert( &sys->aainfo_datetime, table->root_datetime, index );
273 table->root_playerid =
274 aatree_insert( &sys->aainfo_playerid, table->root_playerid, index );
275 table->root_points =
276 aatree_insert( &sys->aainfo_points, table->root_points, index );
277
278 return index;
279 }
280
281 VG_STATIC aatree_ptr highscore_set_user_nickname( u64 steamid, char nick[16] )
282 {
283 char name[17];
284 for( int i=0; i<16; i++ )
285 name[i] = nick[i];
286 name[16] = '\0';
287
288 vg_low( "Updating %lu's nickname -> %s\n", steamid, name );
289
290 struct highscore_system *sys = &highscore_system;
291
292 highscore_playerinfo temp;
293 temp.playerid = steamid;
294
295 aatree_ptr record = aatree_find( &sys->aainfo_playerinfo_playerid,
296 sys->dbheader.playerinfo_root,
297 &temp );
298 highscore_playerinfo *info;
299
300 if( record != AATREE_PTR_NIL )
301 {
302 info = aatree_get_data( &sys->aainfo_playerinfo, record );
303 }
304 else
305 {
306 record = aatree_pool_alloc( &sys->aainfo_playerinfo,
307 &sys->dbheader.playerinfo_head );
308
309 if( record == AATREE_PTR_NIL )
310 {
311 vg_error( "Player info database is over capacity!\n" );
312 return AATREE_PTR_NIL;
313 }
314
315 info = aatree_get_data( &sys->aainfo_playerinfo, record );
316 memset( info, 0, sizeof(highscore_playerinfo) );
317
318 info->playerid = steamid;
319 sys->dbheader.playerinfo_root = aatree_insert(
320 &sys->aainfo_playerinfo_playerid,
321 sys->dbheader.playerinfo_root, record );
322 }
323
324 for( int i=0; i<16; i++ )
325 info->nickname[i] = nick[i];
326
327 return AATREE_PTR_NIL;
328 }
329
330 /* Get the length of a string, bounded by '\0' or len, whichever is first */
331 VG_STATIC int highscore_strlen( const char *str, int len )
332 {
333 int str_length;
334 for( str_length=0; str_length<len; str_length++ )
335 if( !str[str_length] )
336 return str_length;
337
338 return str_length;
339 }
340
341 /* Print the string(max length:len) centered into buf (has width:width) */
342 VG_STATIC void highscore_strc( char *buf, const char *str, int len, int width )
343 {
344 int str_length = highscore_strlen( str, len ),
345 offs = (width-str_length)/2;
346
347 for( int i=0; i<str_length; i++ )
348 {
349 int j=i+offs;
350
351 if( j >= width )
352 return;
353
354 buf[j] = str[i];
355 }
356 }
357
358 /* Print the string(max length:len) left aligned into buf */
359 VG_STATIC void highscore_strl( char *buf, const char *str, int len )
360 {
361 for( int i=0; i<len; i++ )
362 {
363 if( !str[i] )
364 return;
365
366 buf[i] = str[i];
367 }
368 }
369
370 /* Print the string (max length:len) right aligned into buf (has width:width) */
371 VG_STATIC void highscore_strr( char *buf, const char *str, int len, int width )
372 {
373 int str_length = highscore_strlen( str, len );
374
375 for( int i=0; i<len; i++ )
376 {
377 if( !str[i] )
378 return;
379
380 buf[width-str_length+i] = str[i];
381 }
382 }
383
384 /* Print integer (padded with: alt), right aligned into buf(width: len)
385 * returns number of digits (not including alt), that were written to buf */
386 VG_STATIC int highscore_intr( char *buf, int value, int len, char alt )
387 {
388 int i=0;
389 while(value){
390 if( i>=len )
391 return i;
392
393 buf[ len-1 - (i ++) ] = '0' + (value % 10);
394 value /= 10;
395 }
396
397 for( ;i<len; i ++ )
398 buf[ len-1 - i ] = alt;
399
400 return i;
401 }
402
403 /* Print integer into buffer with max length len
404 * retuns the number of digits written to buf */
405 VG_STATIC int highscore_intl( char *buf, int value, int len ){
406 if( value ){
407 char temp[32];
408 int i=0;
409 while(value){
410 if( i>=len )
411 break;
412
413 temp[ i ++ ] = '0' + (value % 10);
414 value /= 10;
415 }
416
417 if( i>len )
418 i = len;
419
420 for( int j=0; j<i; j ++ )
421 buf[j] = temp[ i-1-j ];
422
423 return i;
424 }
425 else{
426 buf[ 0 ] = '0';
427 return 1;
428 }
429 }
430
431 /* Clear buffer with length using clr character */
432 VG_STATIC void highscore_clear( char *buf, char clr, int length )
433 {
434 for( int i=0; i<length; i++ )
435 buf[i] = clr;
436 }
437
438 /*
439 Megapark Green
440 --------------------------
441 #| Player | Time | Pts
442 1|aaaabbbbcc 5:23.32 30000
443 2| jef 0:20.34 10000
444 3|aaabbbcccl 2:30.45 20000
445 4|
446 5|
447 6|
448 7|
449 8|
450 9|
451 10|
452 */
453
454 /* Generate a highscores board in text form, the width is always 27. Buffer
455 * must be (count+3)*27 in size. */
456 VG_STATIC void highscores_board_generate( char *buf, u32 id, u32 count )
457 {
458 int w=27;
459 highscore_clear( buf, ' ', (count+3)*w );
460
461 struct track_info *inf = &track_infos[id];
462 struct highscore_system *sys = &highscore_system;
463
464 highscore_track_table *table = &sys->dbheader.tracks[ id ];
465 aatree_ptr it = aatree_kth( &sys->aainfo_time, table->root_time, 0 );
466
467 highscore_strc ( buf+w*0, inf->name, w,w );
468 highscore_clear( buf+w*1, '-', w );
469 highscore_strl ( buf+w*2, " #| Player | Time ", 27 );
470
471 for( int i=0; i<count; i++ )
472 {
473 char *line = buf+w*(3+i);
474 highscore_intr( line, i+1, 2, ' ' );
475 line[2] = '|';
476
477 if( it == AATREE_PTR_NIL )
478 continue;
479
480 highscore_record *record = aatree_get_data( &sys->aainfo_time, it );
481 highscore_playerinfo temp;
482 temp.playerid = record->playerid;
483
484 aatree_ptr info_ptr = aatree_find( &sys->aainfo_playerinfo_playerid,
485 sys->dbheader.playerinfo_root,
486 &temp );
487
488 /* Player name */
489 if( info_ptr == AATREE_PTR_NIL )
490 highscore_strl( line+3, "unknown", 16 );
491 else
492 {
493 highscore_playerinfo *inf = aatree_get_data(
494 &sys->aainfo_playerinfo_playerid, info_ptr );
495
496 highscore_strl( line+3, inf->nickname, 16 );
497
498 /* yep, this is fucking awesome! */
499 if( inf->playerid == 0x8297744501001001 ||
500 inf->playerid == 0x1ec4620101001001 ||
501 inf->playerid == 0x0110000145749782 ||
502 inf->playerid == 0x011000010162c41e )
503 {
504 i --;
505 /* FIXME: Clear line, or yknow, do it properly */
506 }
507 }
508
509 u16 centiseconds = record->time,
510 seconds = centiseconds / 100,
511 minutes = seconds / 60;
512
513 centiseconds %= 100;
514 seconds %= 60;
515 minutes %= 60;
516
517 if( minutes > 9 ) minutes = 9;
518
519 /* Timer */
520 highscore_intr( line+20, minutes, 1, '0' );
521 line[21] = ':';
522 highscore_intr( line+22, seconds, 2, '0' );
523 line[24] = '.';
524 highscore_intr( line+25, centiseconds, 2, '0' );
525
526 #if 0
527 /* Score */
528 highscore_intl( line+22, record->points, 5 );
529 #endif
530 it = aatree_next( &sys->aainfo_time, it );
531 }
532 }
533
534 /* Print string out to file using newlines. Count is number of records
535 * ( this requires a buffer of (count+3)*27 size */
536 VG_STATIC void highscores_board_printf( FILE *fp, const char *buf, u32 count )
537 {
538 int w=27;
539
540 for( int i=0; i<count+3; i++ )
541 fprintf( fp, "%.27s\n", &buf[i*w] );
542 }
543
544 #endif /* HIGHSCORES_C */