loadsa work
[carveJwlIkooP6JGAAIwe30JlM.git] / highscores.h
1 #ifndef HIGHSCORES_H
2 #define HIGHSCORES_H
3
4 #include "vg/vg_store.h"
5 #include "vg/vg_stdint.h"
6
7 /*
8 * Designed to be used across client and server,
9 * for the client its only storing the local users records, for server its
10 * storing many.
11 */
12
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
18 #pragma pack(push,1)
19 struct highscore_record
20 {
21 u16 trackid, points, time, reserved0;
22 u64 playerid;
23 u32 datetime;
24
25 u32 reserved[7];
26
27 union
28 {
29 struct
30 {
31 aatree_node points,
32 time,
33 playerid,
34 datetime;
35 }
36 aa;
37
38 struct
39 {
40 /* TODO pool allocator */
41 u32 next, prev;
42 }
43 pool;
44 };
45 };
46
47 struct highscore_track_table
48 {
49 aatree_ptr root_points,
50 root_time,
51 root_playerid,
52 root_datetime;
53
54 u32 reserved[12];
55 };
56
57 struct highscore_database
58 {
59 highscore_track_table tracks[ 128 ];
60
61 aatree_ptr pool_head;
62 u32 reserved[63];
63 };
64
65 #pragma pack(pop)
66
67 static struct highscore_system
68 {
69 highscore_database dbheader;
70 aatree aainfo,
71 aainfo_points,
72 aainfo_time,
73 aainfo_playerid,
74 aainfo_datetime;
75
76 void *data;
77 }
78 highscore_system;
79
80 static int highscore_cmp_points( void *a, void *b )
81 {
82 highscore_record *pa = a, *pb = b;
83 return (int)pa->points - (int)pb->points;
84 }
85
86 static int highscore_cmp_datetime( void *a, void *b )
87 {
88 highscore_record *pa = a, *pb = b;
89
90 if( pa->datetime == pb->datetime ) return 0;
91 return pa->datetime < pb->datetime? 1: -1;
92 }
93
94 static int highscore_cmp_time( void *a, void *b )
95 {
96 highscore_record *pa = a, *pb = b;
97 return (int)pb->time - (int)pa->time;
98 }
99
100 static int highscore_cmp_playerid( void *a, void *b )
101 {
102 highscore_record *pa = a, *pb = b;
103 if( pa->playerid == pb->playerid ) return 0;
104 return pa->playerid < pb->playerid? -1: 1;
105 }
106
107 static int highscores_init( u32 pool_size )
108 {
109 struct highscore_system *sys = &highscore_system;
110
111 size_t requested_mem = pool_size * sizeof(highscore_record);
112 sys->data = malloc( requested_mem );
113
114 requested_mem /= 1024;
115 requested_mem /= 1024;
116
117 if( !highscore_system.data )
118 {
119 vg_error( "Could not allocated %dmb of memory for database\n",
120 requested_mem );
121 return 0;
122 }
123 else
124 vg_success( "Allocated %dmb for database\n", requested_mem );
125
126 /* This is ugly.. too bad! */
127 sys->aainfo.base = highscore_system.data;
128 sys->aainfo.stride = sizeof(highscore_record);
129 sys->aainfo.offset = offsetof(highscore_record,pool);
130 sys->aainfo.p_cmp = NULL;
131
132 sys->aainfo_datetime.base = highscore_system.data;
133 sys->aainfo_datetime.stride = sizeof(highscore_record);
134 sys->aainfo_datetime.offset = offsetof(highscore_record,aa.datetime);
135 sys->aainfo_datetime.p_cmp = highscore_cmp_datetime;
136
137 sys->aainfo_points.base = highscore_system.data;
138 sys->aainfo_points.stride = sizeof(highscore_record);
139 sys->aainfo_points.offset = offsetof(highscore_record,aa.points);
140 sys->aainfo_points.p_cmp = highscore_cmp_points;
141
142 sys->aainfo_time.base = highscore_system.data;
143 sys->aainfo_time.stride = sizeof(highscore_record);
144 sys->aainfo_time.offset = offsetof(highscore_record,aa.time);
145 sys->aainfo_time.p_cmp = highscore_cmp_time;
146
147 sys->aainfo_playerid.base = highscore_system.data;
148 sys->aainfo_playerid.stride = sizeof(highscore_record);
149 sys->aainfo_playerid.offset = offsetof(highscore_record,aa.playerid);
150 sys->aainfo_playerid.p_cmp = highscore_cmp_playerid;
151
152
153 /* TODO: Load from disk if avalible */
154 if( 0 )
155 {
156
157 }
158 else
159 {
160 vg_info( "Initializing database nodes\n" );
161 sys->dbheader.pool_head = aatree_init_pool( &sys->aainfo, pool_size );
162
163 for( int i=0; i<vg_list_size(sys->dbheader.tracks); i++ )
164 {
165 highscore_track_table *table = &sys->dbheader.tracks[i];
166 table->root_points = AATREE_PTR_NIL;
167 table->root_playerid = AATREE_PTR_NIL;
168 table->root_time = AATREE_PTR_NIL;
169 table->root_datetime = AATREE_PTR_NIL;
170 }
171 }
172
173 return 1;
174 }
175
176 static void highscores_free(void)
177 {
178 free( highscore_system.data );
179 }
180
181 static aatree_ptr highscores_push_record( highscore_record *record )
182 {
183 struct highscore_system *sys = &highscore_system;
184
185 /* TODO: Verify steam ID */
186 vg_log( "Inserting record into database for track %hu\n",record->trackid );
187
188 if( record->trackid >= vg_list_size(sys->dbheader.tracks) )
189 {
190 vg_error( "TrackID out of range (%hu>=%d)\n", record->trackid,
191 vg_list_size(sys->dbheader.tracks) );
192
193 return AATREE_PTR_NIL;
194 }
195
196 /* Search for existing record on this track */
197 highscore_track_table *table = &sys->dbheader.tracks[record->trackid];
198 aatree_ptr existing = aatree_find( &sys->aainfo_playerid,
199 table->root_playerid,
200 record );
201
202 if( existing != AATREE_PTR_NIL )
203 {
204 vg_log( "Freeing existing record for player %lu\n", record->playerid );
205 table->root_playerid = aatree_del( &sys->aainfo_playerid, existing );
206 table->root_datetime = aatree_del( &sys->aainfo_datetime, existing );
207 table->root_points = aatree_del( &sys->aainfo_points, existing );
208 table->root_time = aatree_del( &sys->aainfo_time, existing );
209
210 aatree_pool_free( &sys->aainfo, existing, &sys->dbheader.pool_head );
211 }
212
213 aatree_ptr index =
214 aatree_pool_alloc( &sys->aainfo, &sys->dbheader.pool_head );
215
216 if( index == AATREE_PTR_NIL )
217 return index;
218
219 highscore_record *dst = aatree_get_data( &sys->aainfo, index );
220 memset( dst, 0, sizeof(highscore_record) );
221
222 dst->trackid = record->trackid;
223 dst->datetime = record->datetime;
224 dst->playerid = record->playerid;
225 dst->points = record->points;
226 dst->time = record->time;
227
228 table->root_time =
229 aatree_insert( &sys->aainfo_time, table->root_time, index );
230 table->root_datetime =
231 aatree_insert( &sys->aainfo_datetime, table->root_datetime, index );
232 table->root_playerid =
233 aatree_insert( &sys->aainfo_playerid, table->root_playerid, index );
234 table->root_points =
235 aatree_insert( &sys->aainfo_points, table->root_points, index );
236
237 return index;
238 }
239
240 static void _highscore_showtime( void *data )
241 {
242 highscore_record *record = data;
243 printf( "%hu", record->time );
244 }
245
246 static void highscores_print_track( u32 trackid, u32 count )
247 {
248 struct highscore_system *sys = &highscore_system;
249
250 highscore_track_table *table = &sys->dbheader.tracks[ trackid ];
251 aatree_ptr it = aatree_kth( &sys->aainfo_time, table->root_time, 0 );
252
253 vg_info( "Highscores, top %u records for track %u\n", count, trackid );
254 vg_info( "==============================================\n" );
255
256 int i=0;
257 while( it != AATREE_PTR_NIL && i < 10 )
258 {
259 highscore_record *record = aatree_get_data( &sys->aainfo_time, it );
260 vg_info( " [%d]: player(%lu), time: %hu, score: %hu, track:%hu\n",
261 i+1, record->playerid, record->time, record->points,
262 record->trackid );
263
264 i++;
265 it = aatree_next( &sys->aainfo_time, it );
266 }
267
268 vg_info( "==============================================\n" );
269 }
270
271 #endif /* HIGHSCORES_H */