--- /dev/null
+#ifndef HIGHSCORES_H
+#define HIGHSCORES_H
+
+#include "vg/vg_store.h"
+#include "vg/vg_stdint.h"
+
+/*
+ * Designed to be used across client and server,
+ * for the client its only storing the local users records, for server its
+ * storing many.
+ */
+
+typedef struct highscore highscore;
+typedef struct highscore_record highscore_record;
+typedef struct highscore_track_table highscore_track_table;
+typedef struct highscore_database highscore_database;
+
+#pragma pack(push,1)
+struct highscore_record
+{
+ u16 trackid, points, time, reserved0;
+ u64 playerid;
+ u32 datetime;
+
+ u32 reserved[7];
+
+ union
+ {
+ struct
+ {
+ aatree_node points,
+ time,
+ playerid,
+ datetime;
+ }
+ aa;
+
+ struct
+ {
+ /* TODO pool allocator */
+ u32 next, prev;
+ }
+ pool;
+ };
+};
+
+struct highscore_track_table
+{
+ aatree_ptr root_points,
+ root_time,
+ root_playerid,
+ root_datetime;
+
+ u32 reserved[12];
+};
+
+struct highscore_database
+{
+ highscore_track_table tracks[ 128 ];
+
+ aatree_ptr pool_head;
+ u32 reserved[63];
+};
+
+#pragma pack(pop)
+
+static struct highscore_system
+{
+ highscore_database dbheader;
+ aatree aainfo,
+ aainfo_points,
+ aainfo_time,
+ aainfo_playerid,
+ aainfo_datetime;
+
+ void *data;
+}
+highscore_system;
+
+static int highscore_cmp_points( void *a, void *b )
+{
+ highscore_record *pa = a, *pb = b;
+ return (int)pa->points - (int)pb->points;
+}
+
+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;
+}
+
+static int highscore_cmp_time( void *a, void *b )
+{
+ highscore_record *pa = a, *pb = b;
+ return (int)pb->time - (int)pa->time;
+}
+
+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;
+}
+
+static int highscores_init( u32 pool_size )
+{
+ struct highscore_system *sys = &highscore_system;
+
+ size_t requested_mem = pool_size * sizeof(highscore_record);
+ sys->data = malloc( requested_mem );
+
+ requested_mem /= 1024;
+ requested_mem /= 1024;
+
+ if( !highscore_system.data )
+ {
+ vg_error( "Could not allocated %dmb of memory for database\n",
+ requested_mem );
+ return 0;
+ }
+ else
+ vg_success( "Allocated %dmb for database\n", requested_mem );
+
+ /* 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;
+
+
+ /* TODO: Load from disk if avalible */
+ if( 0 )
+ {
+
+ }
+ else
+ {
+ vg_info( "Initializing database nodes\n" );
+ sys->dbheader.pool_head = aatree_init_pool( &sys->aainfo, pool_size );
+
+ for( int i=0; i<vg_list_size(sys->dbheader.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;
+ }
+ }
+
+ return 1;
+}
+
+static void highscores_free(void)
+{
+ free( highscore_system.data );
+}
+
+static aatree_ptr highscores_push_record( highscore_record *record )
+{
+ struct highscore_system *sys = &highscore_system;
+
+ /* TODO: Verify steam ID */
+ vg_log( "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 )
+ {
+ vg_log( "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 )
+ 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;
+}
+
+static void _highscore_showtime( void *data )
+{
+ highscore_record *record = data;
+ printf( "%hu", record->time );
+}
+
+static void highscores_print_track( u32 trackid, u32 count )
+{
+ struct highscore_system *sys = &highscore_system;
+
+ highscore_track_table *table = &sys->dbheader.tracks[ trackid ];
+ aatree_ptr it = aatree_kth( &sys->aainfo_time, table->root_time, 0 );
+
+ vg_info( "Highscores, top %u records for track %u\n", count, trackid );
+ vg_info( "==============================================\n" );
+
+ int i=0;
+ while( it != AATREE_PTR_NIL && i < 10 )
+ {
+ highscore_record *record = aatree_get_data( &sys->aainfo_time, it );
+ vg_info( " [%d]: player(%lu), time: %hu, score: %hu, track:%hu\n",
+ i+1, record->playerid, record->time, record->points,
+ record->trackid );
+
+ i++;
+ it = aatree_next( &sys->aainfo_time, it );
+ }
+
+ vg_info( "==============================================\n" );
+}
+
+#endif /* HIGHSCORES_H */
*/
static void network_submit_highscore( u32 trackid, u16 points, u16 time );
-
/*
* Game endpoints are provided with the same names to allow running without a
* network connection.
hSteamNetworkingSockets, &remoteAddr, 0, NULL );
}
+static void send_auth_ticket(void)
+{
+ u32 size = sizeof(netmsg_auth) + steam_app_ticket_length;
+ netmsg_auth *auth = malloc(size);
+
+ auth.inetmsg_id = k_inetmsg_auth;
+ auth.ticket_length = steam_app_ticket_length;
+ for( int i=0; i<steam_app_ticket_length; i++ )
+ auth.ticket[i] = steam_app_ticket[i];
+
+ SteamAPI_ISteamNetworkingSockets_SendMessageToConnection(
+ hSteamNetworkingSockets, cremote, auth, size,
+ k_nSteamNetworkingSend_Reliable, NULL );
+
+ free( auth );
+}
+
static void scores_update(void)
{
vg_log( "scores_update()\n" );
if( cremote_state == k_ESteamNetworkingConnectionState_Connected )
{
/*
- * request updated scores
+ * request updated scores, this does not require any authentication.
*/
netmsg_scores_request req;
req.inetmsg_id = k_inetmsg_scores_request;
hSteamNetworkingSockets, cremote, &req,
sizeof(netmsg_scores_request),
k_nSteamNetworkingSend_Reliable, NULL );
+
+ /*
+ * Send record update, it requires authentication
+ */
+ if( steam_app_ticket_length )
+ {
+ }
}
else
{
--- /dev/null
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stddef.h>
+#include <math.h>
+
+#include "vg/vg_platform.h"
+#include "vg/vg_stdint.h"
+#include "vg/vg_store.h"
+#include "vg/vg_io.h"
+#include "vg/vg_m.h"
+
+#include "highscores.h"
+
+int main(int argc, const char *argv[])
+{
+ vg_info( "Database test\n" );
+
+ if( !highscores_init( 200000 ) )
+ return 0;
+
+ vg_log( "Inserting test records...\n" );
+ for( int i=0; i<200000; i++ )
+ {
+ highscore_record entry;
+ entry.trackid = vg_randf() * 129.0f;
+ entry.points = vg_randf() * 10000.0f;
+ entry.time = vg_randf() * 20000.0f;
+ entry.playerid = rand() % 800;
+ entry.datetime = vg_randf() * 100000.0f;
+
+ highscores_push_record( &entry );
+ }
+ vg_log( "Done.\n" );
+
+ highscores_print_track( 2, 10 );
+ highscores_free();
+ return 0;
+}
+
+#if 0
+typedef struct yoyo_t yoyo_t;
+struct yoyo_t
+{
+ int my_data;
+ aatree_node anode;
+};
+
+static void yoyo_t_show( void *_data )
+{
+ yoyo_t *data = _data;
+ printf( "%d ", data->my_data );
+}
+
+static int yoyo_t_cmp( void *_a, void *_b )
+{
+ yoyo_t *a = _a, *b = _b;
+ return b->my_data - a->my_data;
+}
+
+int main(int argc, const char *argv[])
+{
+ yoyo_t *allsorts = malloc( sizeof(yoyo_t) * 10000 );
+
+ aatree test;
+ test.base = allsorts;
+ test.offset = offsetof( yoyo_t, anode );
+ test.stride = sizeof( yoyo_t );
+ test.p_cmp = yoyo_t_cmp;
+
+ for( int i=0; i<30; i++ ) vg_randf();
+
+ for( int j=0; j<1000; j++ )
+ {
+ int spam_amt = 100;
+ aatree_ptr root = AATREE_PTR_NIL;
+ for( int i=0; i<spam_amt; i++ )
+ {
+ yoyo_t *rando = &allsorts[i];
+ rando->my_data = vg_randf() * 563.0f;
+ root = aatree_insert( &test, root, i );
+ }
+
+ int ln=0, err=0;
+ int drawting = 1;
+ aatree_show_counts( &test, root, 0, &ln, &err, yoyo_t_show, drawting );
+
+#if 0
+ int value = 3;
+ vg_info( "Ptr of %d: %u\n", value, aatree_find( &test, root, &value ) );
+
+ for( int i=0; i<20; i++ )
+ {
+ yoyo_t *v = aatree_get_data(&test,aatree_kth( &test, root, i ));
+ vg_info( "Value of [%d]: %d\n", i, v->my_data );
+ }
+#endif
+ if( ln != spam_amt || err != 0 )
+ {
+ vg_error( "ADJAWIUDWAJD\n" );
+ break;
+ }
+
+ aatree_ptr traverser = aatree_kth( &test, root, 0 );
+
+ while( traverser != AATREE_PTR_NIL )
+ {
+ yoyo_t *v = aatree_get_data( &test, traverser );
+ vg_info( "... %d\n", v->my_data );
+
+ traverser = aatree_next( &test, traverser );
+ }
+
+ int orig = spam_amt;
+ for( int i=0; i<orig; i++ )
+ {
+ int remover = vg_min( (int)(vg_randf() * spam_amt), spam_amt-1 );
+ aatree_ptr kremove = aatree_kth( &test, root, remover );
+
+ vg_info( "Removing K %d\n", remover );
+ vg_info( "id: %d\n", kremove );
+
+ if( drawting )
+ vg_info( "AND NOW REMOVE K %d (id: %d, value: %d)\n", remover, kremove,
+ *((int*)aatree_get_data( &test, kremove )) );
+
+ root = aatree_del( &test, kremove );
+
+ ln=0;
+ err=0;
+ aatree_show_counts( &test, root, 0, &ln, &err, yoyo_t_show, drawting );
+
+ if( ln != spam_amt-1 || err != 0 )
+ {
+ vg_error( "ADJAWIUDWAJD ( %d %d // %d, %d)\n",
+ j, ln, spam_amt, err );
+ free( allsorts );
+ return 0;
+ }
+ vg_success( "%d\n", j );
+ spam_amt --;
+ }
+ }
+
+ free( allsorts );
+ return 0;
+}
+
+#endif