4 #include "vg/vg_store.h"
5 #include "vg/vg_stdint.h"
8 * Designed to be used across client and server,
9 * for the client its only storing the local users records, for server its
13 typedef struct highscore highscore
;
14 typedef struct highscore_record highscore_record
;
15 typedef struct highscore_track_table highscore_track_table
;
16 typedef struct highscore_database highscore_database
;
17 typedef struct highscore_playerinfo highscore_playerinfo
;
21 struct highscore_playerinfo
28 aatree_pool_node aapn
;
29 aatree_node aa_playerid
;
33 struct highscore_record
35 u16 trackid
, points
, time
, reserved0
;
51 aatree_pool_node pool
;
55 struct highscore_track_table
57 aatree_ptr root_points
,
65 struct highscore_database
67 highscore_track_table tracks
[ 128 ];
69 aatree_ptr pool_head
, playerinfo_head
;
71 playerinfo_capacity
, playerinfo_root
;
78 static struct highscore_system
80 highscore_database dbheader
;
86 aainfo_playerinfo_playerid
,
89 void *data
, *playerinfo_data
;
93 static int highscore_cmp_points( void *a
, void *b
)
95 highscore_record
*pa
= a
, *pb
= b
;
96 return (int)pa
->points
- (int)pb
->points
;
99 static int highscore_cmp_datetime( void *a
, void *b
)
101 highscore_record
*pa
= a
, *pb
= b
;
103 if( pa
->datetime
== pb
->datetime
) return 0;
104 return pa
->datetime
< pb
->datetime
? 1: -1;
107 static int highscore_cmp_time( void *a
, void *b
)
109 highscore_record
*pa
= a
, *pb
= b
;
110 return (int)pb
->time
- (int)pa
->time
;
113 static int highscore_cmp_playerid( void *a
, void *b
)
115 highscore_record
*pa
= a
, *pb
= b
;
116 if( pa
->playerid
== pb
->playerid
) return 0;
117 return pa
->playerid
< pb
->playerid
? -1: 1;
120 static int highscore_cmp_playerinfo_playerid( void *a
, void *b
)
122 highscore_playerinfo
*pa
= a
, *pb
= b
;
123 if( pa
->playerid
== pb
->playerid
) return 0;
124 return pa
->playerid
< pb
->playerid
? -1: 1;
127 static void *highscore_malloc( u32 count
, u32 size
)
129 size_t requested_mem
= size
* count
;
130 void *data
= malloc( requested_mem
);
132 requested_mem
/= 1024;
133 requested_mem
/= 1024;
137 vg_error( "Could not allocated %dmb of memory\n", requested_mem
);
141 vg_success( "Allocated %dmb for %u records\n", requested_mem
, count
);
146 static void highscores_free(void)
148 free( highscore_system
.data
);
149 free( highscore_system
.playerinfo_data
);
152 static int highscores_init( u32 pool_size
, u32 playerinfo_pool_size
)
154 struct highscore_system
*sys
= &highscore_system
;
156 sys
->data
= highscore_malloc( pool_size
, sizeof(highscore_record
) );
157 if( !sys
->data
) return 0;
159 sys
->playerinfo_data
=
160 highscore_malloc( playerinfo_pool_size
, sizeof(highscore_playerinfo
));
161 if( !sys
->playerinfo_data
)
167 /* This is ugly.. too bad! */
168 sys
->aainfo
.base
= highscore_system
.data
;
169 sys
->aainfo
.stride
= sizeof(highscore_record
);
170 sys
->aainfo
.offset
= offsetof(highscore_record
,pool
);
171 sys
->aainfo
.p_cmp
= NULL
;
173 sys
->aainfo_datetime
.base
= highscore_system
.data
;
174 sys
->aainfo_datetime
.stride
= sizeof(highscore_record
);
175 sys
->aainfo_datetime
.offset
= offsetof(highscore_record
,aa
.datetime
);
176 sys
->aainfo_datetime
.p_cmp
= highscore_cmp_datetime
;
178 sys
->aainfo_points
.base
= highscore_system
.data
;
179 sys
->aainfo_points
.stride
= sizeof(highscore_record
);
180 sys
->aainfo_points
.offset
= offsetof(highscore_record
,aa
.points
);
181 sys
->aainfo_points
.p_cmp
= highscore_cmp_points
;
183 sys
->aainfo_time
.base
= highscore_system
.data
;
184 sys
->aainfo_time
.stride
= sizeof(highscore_record
);
185 sys
->aainfo_time
.offset
= offsetof(highscore_record
,aa
.time
);
186 sys
->aainfo_time
.p_cmp
= highscore_cmp_time
;
188 sys
->aainfo_playerid
.base
= highscore_system
.data
;
189 sys
->aainfo_playerid
.stride
= sizeof(highscore_record
);
190 sys
->aainfo_playerid
.offset
= offsetof(highscore_record
,aa
.playerid
);
191 sys
->aainfo_playerid
.p_cmp
= highscore_cmp_playerid
;
193 sys
->aainfo_playerinfo_playerid
.base
= highscore_system
.playerinfo_data
;
194 sys
->aainfo_playerinfo_playerid
.stride
= sizeof(highscore_playerinfo
);
195 sys
->aainfo_playerinfo_playerid
.offset
=
196 offsetof(highscore_playerinfo
,aa_playerid
);
197 sys
->aainfo_playerinfo_playerid
.p_cmp
= highscore_cmp_playerinfo_playerid
;
199 sys
->aainfo_playerinfo
.base
= highscore_system
.playerinfo_data
;
200 sys
->aainfo_playerinfo
.stride
= sizeof(highscore_playerinfo
);
201 sys
->aainfo_playerinfo
.offset
= offsetof(highscore_playerinfo
,aapn
);
202 sys
->aainfo_playerinfo
.p_cmp
= NULL
;
204 FILE *fp
= fopen( ".aadb", "rb" );
207 vg_info( "Loading existing database\n" );
209 u64 count
= fread( &sys
->dbheader
, sizeof(highscore_database
), 1, fp
);
213 vg_error( "Unexpected EOF reading database header\n" );
219 count
= fread( sys
->data
, sizeof(highscore_record
), pool_size
, fp
);
220 if( count
!= pool_size
)
222 vg_error( "Unexpected EOF reading database contents;"
223 " %lu records of %u were read\n", count
, pool_size
);
229 count
= fread( sys
->playerinfo_data
, sizeof(highscore_playerinfo
),
230 playerinfo_pool_size
, fp
);
231 if( count
!= playerinfo_pool_size
)
233 vg_error( "Unexpected EOF reading playerinfo contents;"
234 " %lu records of %u were read\n", count
,
235 playerinfo_pool_size
);
245 vg_log( "No existing database found (.aadb)\n" );
246 vg_info( "Initializing database nodes\n" );
247 memset( &sys
->dbheader
, 0, sizeof(highscore_database
) );
249 sys
->dbheader
.pool_head
= aatree_init_pool( &sys
->aainfo
, pool_size
);
250 sys
->dbheader
.entry_capacity
= pool_size
;
252 for( int i
=0; i
<vg_list_size(sys
->dbheader
.tracks
); i
++ )
254 highscore_track_table
*table
= &sys
->dbheader
.tracks
[i
];
255 table
->root_points
= AATREE_PTR_NIL
;
256 table
->root_playerid
= AATREE_PTR_NIL
;
257 table
->root_time
= AATREE_PTR_NIL
;
258 table
->root_datetime
= AATREE_PTR_NIL
;
261 /* Initialize secondary db */
262 sys
->dbheader
.playerinfo_head
= aatree_init_pool(
263 &sys
->aainfo_playerinfo
,
264 playerinfo_pool_size
);
265 sys
->dbheader
.playerinfo_capacity
= playerinfo_pool_size
;
266 sys
->dbheader
.playerinfo_root
= AATREE_PTR_NIL
;
272 static int highscores_serialize_all(void)
274 struct highscore_system
*sys
= &highscore_system
;
275 vg_info( "Serializing database\n" );
277 FILE *fp
= fopen( ".aadb", "wb" );
281 vg_error( "Could not open .aadb\n" );
285 fwrite( &sys
->dbheader
, sizeof(highscore_database
), 1, fp
);
286 fwrite( sys
->data
, sizeof(highscore_record
),
287 sys
->dbheader
.entry_capacity
, fp
);
288 fwrite( sys
->playerinfo_data
, sizeof(highscore_playerinfo
),
289 sys
->dbheader
.playerinfo_capacity
, fp
);
295 static aatree_ptr
highscores_push_record( highscore_record
*record
)
297 struct highscore_system
*sys
= &highscore_system
;
299 /* TODO: Verify steam ID */
300 vg_log( "Inserting record into database for track %hu\n",record
->trackid
);
302 if( record
->trackid
>= vg_list_size(sys
->dbheader
.tracks
) )
304 vg_error( "TrackID out of range (%hu>=%d)\n", record
->trackid
,
305 vg_list_size(sys
->dbheader
.tracks
) );
307 return AATREE_PTR_NIL
;
310 /* Search for existing record on this track */
311 highscore_track_table
*table
= &sys
->dbheader
.tracks
[record
->trackid
];
312 aatree_ptr existing
= aatree_find( &sys
->aainfo_playerid
,
313 table
->root_playerid
,
316 if( existing
!= AATREE_PTR_NIL
)
318 highscore_record
*crecord
= aatree_get_data( &sys
->aainfo_playerid
,
321 if( crecord
->time
< record
->time
||
322 (crecord
->time
== record
->time
&& crecord
->points
> record
->points
))
324 vg_log( "Not overwriting better score\n" );
328 vg_log( "Freeing existing record for player %lu\n", record
->playerid
);
329 table
->root_playerid
= aatree_del( &sys
->aainfo_playerid
, existing
);
330 table
->root_datetime
= aatree_del( &sys
->aainfo_datetime
, existing
);
331 table
->root_points
= aatree_del( &sys
->aainfo_points
, existing
);
332 table
->root_time
= aatree_del( &sys
->aainfo_time
, existing
);
334 aatree_pool_free( &sys
->aainfo
, existing
, &sys
->dbheader
.pool_head
);
338 aatree_pool_alloc( &sys
->aainfo
, &sys
->dbheader
.pool_head
);
340 if( index
== AATREE_PTR_NIL
)
342 vg_error( "Database records are over capacity!\n" );
346 highscore_record
*dst
= aatree_get_data( &sys
->aainfo
, index
);
347 memset( dst
, 0, sizeof(highscore_record
) );
349 dst
->trackid
= record
->trackid
;
350 dst
->datetime
= record
->datetime
;
351 dst
->playerid
= record
->playerid
;
352 dst
->points
= record
->points
;
353 dst
->time
= record
->time
;
356 aatree_insert( &sys
->aainfo_time
, table
->root_time
, index
);
357 table
->root_datetime
=
358 aatree_insert( &sys
->aainfo_datetime
, table
->root_datetime
, index
);
359 table
->root_playerid
=
360 aatree_insert( &sys
->aainfo_playerid
, table
->root_playerid
, index
);
362 aatree_insert( &sys
->aainfo_points
, table
->root_points
, index
);
367 static aatree_ptr
highscore_set_user_nickname( u64 steamid
, char nick
[16] )
369 vg_log( "Updating %lu's nickname\n", steamid
);
371 struct highscore_system
*sys
= &highscore_system
;
373 highscore_playerinfo temp
;
374 temp
.playerid
= steamid
;
376 aatree_ptr record
= aatree_find( &sys
->aainfo_playerinfo_playerid
,
377 sys
->dbheader
.playerinfo_root
,
379 highscore_playerinfo
*info
;
381 if( record
!= AATREE_PTR_NIL
)
383 info
= aatree_get_data( &sys
->aainfo_playerinfo
, record
);
387 record
= aatree_pool_alloc( &sys
->aainfo_playerinfo
,
388 &sys
->dbheader
.playerinfo_head
);
390 if( record
== AATREE_PTR_NIL
)
392 vg_error( "Player info database is over capacity!\n" );
393 return AATREE_PTR_NIL
;
396 info
= aatree_get_data( &sys
->aainfo_playerinfo
, record
);
397 memset( info
, 0, sizeof(highscore_playerinfo
) );
399 info
->playerid
= steamid
;
400 sys
->dbheader
.playerinfo_root
= aatree_insert(
401 &sys
->aainfo_playerinfo_playerid
,
402 sys
->dbheader
.playerinfo_root
,
406 for( int i
=0; i
<16; i
++ )
407 info
->nickname
[i
] = nick
[i
];
409 return AATREE_PTR_NIL
;
412 static void _highscore_showtime( void *data
)
414 highscore_record
*record
= data
;
415 printf( "%hu", record
->time
);
418 static void _highscore_showname( void *data
)
423 highscore_playerinfo
*info
= data
;
424 for( int i
=0; i
<16; i
++ )
425 namebuf
[i
] = info
->nickname
[i
];
427 printf( " %lu %s", info
->playerid
, namebuf
);
430 static void highscores_print_track( u32 trackid
, u32 count
)
432 struct highscore_system
*sys
= &highscore_system
;
434 highscore_track_table
*table
= &sys
->dbheader
.tracks
[ trackid
];
435 aatree_ptr it
= aatree_kth( &sys
->aainfo_time
, table
->root_time
, 0 );
437 vg_info( "Highscores: top %u fastest records for track %u\n", count
, trackid
);
438 vg_info( "================================================\n" );
439 vg_info( "%3s| %16s | %5s | %5s | %s\n", "#", "Player", "Time", "Score",
441 vg_info( "================================================\n" );
443 while( it
!= AATREE_PTR_NIL
&& i
< 10 )
445 highscore_record
*record
= aatree_get_data( &sys
->aainfo_time
, it
);
447 highscore_playerinfo temp
;
448 temp
.playerid
= record
->playerid
;
450 aatree_ptr info_ptr
= aatree_find( &sys
->aainfo_playerinfo_playerid
,
451 sys
->dbheader
.playerinfo_root
,
455 if( info_ptr
== AATREE_PTR_NIL
)
456 snprintf( namebuf
, 16, "[%lu]", record
->playerid
);
459 highscore_playerinfo
*inf
= aatree_get_data(
460 &sys
->aainfo_playerinfo_playerid
, info_ptr
);
462 for( int i
=0; i
<16; i
++ )
463 namebuf
[i
] = inf
->nickname
[i
];
467 vg_info( "%3d| %16s %5hu %5hu %3hu\n",
468 i
+1, namebuf
, record
->time
, record
->points
,
472 it
= aatree_next( &sys
->aainfo_time
, it
);
475 vg_info( "================================================\n" );
478 #endif /* HIGHSCORES_H */