2 * Copyright (C) 2021-2022 Mt.ZERO Software, Harry Godden - All Rights Reserved
8 #include "vg/vg_store.h"
9 #include "vg/vg_stdint.h"
10 #include "world_info.h"
13 * Designed to be used across client and server,
14 * for the client its only storing the local users records, for server its
18 typedef struct highscore highscore
;
19 typedef struct highscore_record highscore_record
;
20 typedef struct highscore_track_table highscore_track_table
;
21 typedef struct highscore_database highscore_database
;
22 typedef struct highscore_playerinfo highscore_playerinfo
;
26 struct highscore_playerinfo
33 aatree_pool_node aapn
;
34 aatree_node aa_playerid
;
38 struct highscore_record
40 u16 trackid
, points
, time
, reserved0
;
56 aatree_pool_node pool
;
60 struct highscore_track_table
62 aatree_ptr root_points
,
70 struct highscore_database
72 highscore_track_table tracks
[ 128 ];
74 aatree_ptr pool_head
, playerinfo_head
;
76 playerinfo_capacity
, playerinfo_root
;
83 VG_STATIC
struct highscore_system
85 highscore_database dbheader
;
91 aainfo_playerinfo_playerid
,
97 u32 pool_size
, playerinfo_pool_size
;
101 VG_STATIC
int highscore_cmp_points( void *a
, void *b
)
103 highscore_record
*pa
= a
, *pb
= b
;
104 return (int)pa
->points
- (int)pb
->points
;
107 VG_STATIC
int highscore_cmp_datetime( void *a
, void *b
)
109 highscore_record
*pa
= a
, *pb
= b
;
111 if( pa
->datetime
== pb
->datetime
) return 0;
112 return pa
->datetime
< pb
->datetime
? 1: -1;
115 VG_STATIC
int highscore_cmp_time( void *a
, void *b
)
117 highscore_record
*pa
= a
, *pb
= b
;
118 return (int)pb
->time
- (int)pa
->time
;
121 VG_STATIC
int highscore_cmp_playerid( void *a
, void *b
)
123 highscore_record
*pa
= a
, *pb
= b
;
124 if( pa
->playerid
== pb
->playerid
) return 0;
125 return pa
->playerid
< pb
->playerid
? -1: 1;
128 VG_STATIC
int highscore_cmp_playerinfo_playerid( void *a
, void *b
)
130 highscore_playerinfo
*pa
= a
, *pb
= b
;
131 if( pa
->playerid
== pb
->playerid
) return 0;
132 return pa
->playerid
< pb
->playerid
? -1: 1;
135 VG_STATIC
void highscores_create_db(void)
137 struct highscore_system
*sys
= &highscore_system
;
139 vg_info( "Initializing database nodes\n" );
140 memset( &sys
->dbheader
, 0, sizeof(highscore_database
) );
142 sys
->dbheader
.pool_head
= aatree_init_pool( &sys
->aainfo
, sys
->pool_size
);
143 sys
->dbheader
.entry_capacity
= sys
->pool_size
;
145 for( int i
=0; i
<vg_list_size(sys
->dbheader
.tracks
); i
++ )
147 highscore_track_table
*table
= &sys
->dbheader
.tracks
[i
];
148 table
->root_points
= AATREE_PTR_NIL
;
149 table
->root_playerid
= AATREE_PTR_NIL
;
150 table
->root_time
= AATREE_PTR_NIL
;
151 table
->root_datetime
= AATREE_PTR_NIL
;
154 /* Initialize secondary db */
155 sys
->dbheader
.playerinfo_head
= aatree_init_pool(
156 &sys
->aainfo_playerinfo
,
157 sys
->playerinfo_pool_size
);
158 sys
->dbheader
.playerinfo_capacity
= sys
->playerinfo_pool_size
;
159 sys
->dbheader
.playerinfo_root
= AATREE_PTR_NIL
;
162 VG_STATIC
int highscores_read(void)
164 struct highscore_system
*sys
= &highscore_system
;
166 FILE *fp
= fopen( ".aadb", "rb" );
169 vg_info( "Loading existing database\n" );
171 u64 count
= fread( &sys
->dbheader
, sizeof(highscore_database
), 1, fp
);
175 vg_error( "Unexpected EOF reading database header\n" );
179 count
= fread( sys
->data
, sizeof(highscore_record
),
180 sys
->pool_size
, fp
);
182 if( count
!= sys
->pool_size
)
184 vg_error( "Unexpected EOF reading database contents;"
185 " %lu records of %u were read\n", count
, sys
->pool_size
);
189 count
= fread( sys
->playerinfo_data
, sizeof(highscore_playerinfo
),
190 sys
->playerinfo_pool_size
, fp
);
192 if( count
!= sys
->playerinfo_pool_size
)
194 vg_error( "Unexpected EOF reading playerinfo contents;"
195 " %lu records of %u were read\n", count
,
196 sys
->playerinfo_pool_size
);
205 vg_low( "No existing database found (.aadb)\n" );
210 VG_STATIC
void highscores_init( u32 pool_size
, u32 playerinfo_pool_size
)
212 struct highscore_system
*sys
= &highscore_system
;
214 sys
->data
= vg_linear_alloc( vg_mem
.rtmemory
,
215 pool_size
*sizeof(highscore_record
) );
217 sys
->playerinfo_data
=
218 vg_linear_alloc( vg_mem
.rtmemory
,
219 playerinfo_pool_size
* sizeof(highscore_playerinfo
) );
221 memset( sys
->data
, 0, pool_size
*sizeof(highscore_record
) );
222 memset( sys
->playerinfo_data
, 0,
223 playerinfo_pool_size
*sizeof(highscore_playerinfo
) );
226 /* This is ugly.. too bad! */
227 sys
->aainfo
.base
= highscore_system
.data
;
228 sys
->aainfo
.stride
= sizeof(highscore_record
);
229 sys
->aainfo
.offset
= offsetof(highscore_record
,pool
);
230 sys
->aainfo
.p_cmp
= NULL
;
232 sys
->aainfo_datetime
.base
= highscore_system
.data
;
233 sys
->aainfo_datetime
.stride
= sizeof(highscore_record
);
234 sys
->aainfo_datetime
.offset
= offsetof(highscore_record
,aa
.datetime
);
235 sys
->aainfo_datetime
.p_cmp
= highscore_cmp_datetime
;
237 sys
->aainfo_points
.base
= highscore_system
.data
;
238 sys
->aainfo_points
.stride
= sizeof(highscore_record
);
239 sys
->aainfo_points
.offset
= offsetof(highscore_record
,aa
.points
);
240 sys
->aainfo_points
.p_cmp
= highscore_cmp_points
;
242 sys
->aainfo_time
.base
= highscore_system
.data
;
243 sys
->aainfo_time
.stride
= sizeof(highscore_record
);
244 sys
->aainfo_time
.offset
= offsetof(highscore_record
,aa
.time
);
245 sys
->aainfo_time
.p_cmp
= highscore_cmp_time
;
247 sys
->aainfo_playerid
.base
= highscore_system
.data
;
248 sys
->aainfo_playerid
.stride
= sizeof(highscore_record
);
249 sys
->aainfo_playerid
.offset
= offsetof(highscore_record
,aa
.playerid
);
250 sys
->aainfo_playerid
.p_cmp
= highscore_cmp_playerid
;
252 sys
->aainfo_playerinfo_playerid
.base
= highscore_system
.playerinfo_data
;
253 sys
->aainfo_playerinfo_playerid
.stride
= sizeof(highscore_playerinfo
);
254 sys
->aainfo_playerinfo_playerid
.offset
=
255 offsetof(highscore_playerinfo
,aa_playerid
);
256 sys
->aainfo_playerinfo_playerid
.p_cmp
= highscore_cmp_playerinfo_playerid
;
258 sys
->aainfo_playerinfo
.base
= highscore_system
.playerinfo_data
;
259 sys
->aainfo_playerinfo
.stride
= sizeof(highscore_playerinfo
);
260 sys
->aainfo_playerinfo
.offset
= offsetof(highscore_playerinfo
,aapn
);
261 sys
->aainfo_playerinfo
.p_cmp
= NULL
;
263 sys
->playerinfo_pool_size
= playerinfo_pool_size
;
264 sys
->pool_size
= pool_size
;
267 VG_STATIC
int highscores_serialize_all(void)
269 struct highscore_system
*sys
= &highscore_system
;
270 vg_info( "Serializing database\n" );
272 FILE *fp
= fopen( ".aadb", "wb" );
276 vg_error( "Could not open .aadb\n" );
280 fwrite( &sys
->dbheader
, sizeof(highscore_database
), 1, fp
);
281 fwrite( sys
->data
, sizeof(highscore_record
),
282 sys
->dbheader
.entry_capacity
, fp
);
283 fwrite( sys
->playerinfo_data
, sizeof(highscore_playerinfo
),
284 sys
->dbheader
.playerinfo_capacity
, fp
);
290 VG_STATIC highscore_record
*highscore_find_user_record( u64 playerid
, u32 trackid
)
292 struct highscore_system
*sys
= &highscore_system
;
294 highscore_track_table
*table
= &sys
->dbheader
.tracks
[trackid
];
295 highscore_record temp
;
296 temp
.playerid
= playerid
;
299 aatree_find( &sys
->aainfo_playerid
, table
->root_playerid
, &temp
);
301 if( find
== AATREE_PTR_NIL
)
304 return aatree_get_data( &sys
->aainfo_playerid
, find
);
307 VG_STATIC aatree_ptr
highscores_push_record( highscore_record
*record
)
309 struct highscore_system
*sys
= &highscore_system
;
311 vg_low( "Inserting record into database for track %hu\n",record
->trackid
);
313 if( record
->trackid
>= vg_list_size(sys
->dbheader
.tracks
) )
315 vg_error( "TrackID out of range (%hu>=%d)\n", record
->trackid
,
316 vg_list_size(sys
->dbheader
.tracks
) );
318 return AATREE_PTR_NIL
;
321 /* Search for existing record on this track */
322 highscore_track_table
*table
= &sys
->dbheader
.tracks
[record
->trackid
];
323 aatree_ptr existing
= aatree_find( &sys
->aainfo_playerid
,
324 table
->root_playerid
,
327 if( existing
!= AATREE_PTR_NIL
)
329 highscore_record
*crecord
= aatree_get_data( &sys
->aainfo_playerid
,
332 if( crecord
->time
< record
->time
||
333 (crecord
->time
== record
->time
&& crecord
->points
> record
->points
))
335 vg_low( "Not overwriting better score\n" );
339 vg_low( "Freeing existing record for player %lu\n", record
->playerid
);
340 table
->root_playerid
= aatree_del( &sys
->aainfo_playerid
, existing
);
341 table
->root_datetime
= aatree_del( &sys
->aainfo_datetime
, existing
);
342 table
->root_points
= aatree_del( &sys
->aainfo_points
, existing
);
343 table
->root_time
= aatree_del( &sys
->aainfo_time
, existing
);
345 aatree_pool_free( &sys
->aainfo
, existing
, &sys
->dbheader
.pool_head
);
349 aatree_pool_alloc( &sys
->aainfo
, &sys
->dbheader
.pool_head
);
351 if( index
== AATREE_PTR_NIL
)
353 vg_error( "Database records are over capacity!\n" );
357 highscore_record
*dst
= aatree_get_data( &sys
->aainfo
, index
);
358 memset( dst
, 0, sizeof(highscore_record
) );
360 dst
->trackid
= record
->trackid
;
361 dst
->datetime
= record
->datetime
;
362 dst
->playerid
= record
->playerid
;
363 dst
->points
= record
->points
;
364 dst
->time
= record
->time
;
367 aatree_insert( &sys
->aainfo_time
, table
->root_time
, index
);
368 table
->root_datetime
=
369 aatree_insert( &sys
->aainfo_datetime
, table
->root_datetime
, index
);
370 table
->root_playerid
=
371 aatree_insert( &sys
->aainfo_playerid
, table
->root_playerid
, index
);
373 aatree_insert( &sys
->aainfo_points
, table
->root_points
, index
);
378 VG_STATIC aatree_ptr
highscore_set_user_nickname( u64 steamid
, char nick
[16] )
381 for( int i
=0; i
<16; i
++ )
385 vg_low( "Updating %lu's nickname -> %s\n", steamid
, name
);
387 struct highscore_system
*sys
= &highscore_system
;
389 highscore_playerinfo temp
;
390 temp
.playerid
= steamid
;
392 aatree_ptr record
= aatree_find( &sys
->aainfo_playerinfo_playerid
,
393 sys
->dbheader
.playerinfo_root
,
395 highscore_playerinfo
*info
;
397 if( record
!= AATREE_PTR_NIL
)
399 info
= aatree_get_data( &sys
->aainfo_playerinfo
, record
);
403 record
= aatree_pool_alloc( &sys
->aainfo_playerinfo
,
404 &sys
->dbheader
.playerinfo_head
);
406 if( record
== AATREE_PTR_NIL
)
408 vg_error( "Player info database is over capacity!\n" );
409 return AATREE_PTR_NIL
;
412 info
= aatree_get_data( &sys
->aainfo_playerinfo
, record
);
413 memset( info
, 0, sizeof(highscore_playerinfo
) );
415 info
->playerid
= steamid
;
416 sys
->dbheader
.playerinfo_root
= aatree_insert(
417 &sys
->aainfo_playerinfo_playerid
,
418 sys
->dbheader
.playerinfo_root
, record
);
421 for( int i
=0; i
<16; i
++ )
422 info
->nickname
[i
] = nick
[i
];
424 return AATREE_PTR_NIL
;
427 /* Get the length of a string, bounded by '\0' or len, whichever is first */
428 VG_STATIC
int highscore_strlen( const char *str
, int len
)
431 for( str_length
=0; str_length
<len
; str_length
++ )
432 if( !str
[str_length
] )
438 /* Print the string(max length:len) centered into buf (has width:width) */
439 VG_STATIC
void highscore_strc( char *buf
, const char *str
, int len
, int width
)
441 int str_length
= highscore_strlen( str
, len
),
442 offs
= (width
-str_length
)/2;
444 for( int i
=0; i
<str_length
; i
++ )
455 /* Print the string(max length:len) left aligned into buf */
456 VG_STATIC
void highscore_strl( char *buf
, const char *str
, int len
)
458 for( int i
=0; i
<len
; i
++ )
467 /* Print the string (max length:len) right aligned into buf (has width:width) */
468 VG_STATIC
void highscore_strr( char *buf
, const char *str
, int len
, int width
)
470 int str_length
= highscore_strlen( str
, len
);
472 for( int i
=0; i
<len
; i
++ )
477 buf
[width
-str_length
+i
] = str
[i
];
481 /* Print integer (padded with: alt), right aligned into buf(width: len) */
482 VG_STATIC
void highscore_intr( char *buf
, int value
, int len
, char alt
)
490 buf
[ len
-1 - (i
++) ] = '0' + (value
% 10);
495 buf
[ len
-1 - i
] = alt
;
498 /* Print integer into buffer with max length len */
499 VG_STATIC
void highscore_intl( char *buf
, int value
, int len
)
509 temp
[ i
++ ] = '0' + (value
% 10);
516 for( int j
=0; j
<i
; j
++ )
518 buf
[j
] = temp
[ i
-1-j
];
522 /* Clear buffer with length using clr character */
523 VG_STATIC
void highscore_clear( char *buf
, char clr
, int length
)
525 for( int i
=0; i
<length
; i
++ )
531 --------------------------
532 #| Player | Time | Pts
533 1|aaaabbbbcc 5:23.32 30000
535 3|aaabbbcccl 2:30.45 20000
545 /* Generate a highscores board in text form, the width is always 27. Buffer
546 * must be (count+3)*27 in size. */
547 VG_STATIC
void highscores_board_generate( char *buf
, u32 id
, u32 count
)
550 highscore_clear( buf
, ' ', (count
+3)*w
);
552 struct track_info
*inf
= &track_infos
[id
];
553 struct highscore_system
*sys
= &highscore_system
;
555 highscore_track_table
*table
= &sys
->dbheader
.tracks
[ id
];
556 aatree_ptr it
= aatree_kth( &sys
->aainfo_time
, table
->root_time
, 0 );
558 highscore_strc ( buf
+w
*0, inf
->name
, w
,w
);
559 highscore_clear( buf
+w
*1, '-', w
);
560 highscore_strl ( buf
+w
*2, " #| Player | Time ", 27 );
562 for( int i
=0; i
<count
; i
++ )
564 char *line
= buf
+w
*(3+i
);
565 highscore_intr( line
, i
+1, 2, ' ' );
568 if( it
== AATREE_PTR_NIL
)
571 highscore_record
*record
= aatree_get_data( &sys
->aainfo_time
, it
);
572 highscore_playerinfo temp
;
573 temp
.playerid
= record
->playerid
;
575 aatree_ptr info_ptr
= aatree_find( &sys
->aainfo_playerinfo_playerid
,
576 sys
->dbheader
.playerinfo_root
,
580 if( info_ptr
== AATREE_PTR_NIL
)
581 highscore_strl( line
+3, "unknown", 16 );
584 highscore_playerinfo
*inf
= aatree_get_data(
585 &sys
->aainfo_playerinfo_playerid
, info_ptr
);
587 highscore_strl( line
+3, inf
->nickname
, 16 );
590 u16 centiseconds
= record
->time
,
591 seconds
= centiseconds
/ 100,
592 minutes
= seconds
/ 60;
598 if( minutes
> 9 ) minutes
= 9;
601 highscore_intr( line
+20, minutes
, 1, '0' );
603 highscore_intr( line
+22, seconds
, 2, '0' );
605 highscore_intr( line
+25, centiseconds
, 2, '0' );
609 highscore_intl( line
+22, record
->points
, 5 );
611 it
= aatree_next( &sys
->aainfo_time
, it
);
615 /* Print string out to file using newlines. Count is number of records
616 * ( this requires a buffer of (count+3)*27 size */
617 VG_STATIC
void highscores_board_printf( FILE *fp
, const char *buf
, u32 count
)
621 for( int i
=0; i
<count
+3; i
++ )
622 fprintf( fp
, "%.27s\n", &buf
[i
*w
] );
625 #endif /* HIGHSCORES_H */