X-Git-Url: https://harrygodden.com/git/?a=blobdiff_plain;f=gameserver_db.h;fp=gameserver_db.h;h=cffa2dc069a546b04fd5a64d72ee417265e5ad7b;hb=524c05104673b95ef0841d6ee90bcd24f9b829dc;hp=0000000000000000000000000000000000000000;hpb=603805f69c5484316a52ee099d03254c5a1096f3;p=carveJwlIkooP6JGAAIwe30JlM.git diff --git a/gameserver_db.h b/gameserver_db.h new file mode 100644 index 0000000..cffa2dc --- /dev/null +++ b/gameserver_db.h @@ -0,0 +1,255 @@ +#ifndef GAMESERVER_DB_H +#define GAMESERVER_DB_H + +#include "vg/vg_log.h" +#include "network_common.h" +#include "dep/sqlite3/sqlite3.h" +#include "highscores.h" + +#define DB_COURSE_UID_MAX 32 +#define DB_TABLE_UID_MAX (ADDON_UID_MAX+DB_COURSE_UID_MAX+32) +#define DB_CRASH_ON_SQLITE_ERROR +#define DB_LOG_SQL_STATEMENTS + +struct { + sqlite3 *db; +} +static database; + +/* + * Log the error code (or carry on if its OK). + */ +static void log_sqlite3( int code ){ + if( code == SQLITE_OK ) return; + vg_print_backtrace(); + vg_error( "sqlite3(%d): %s\n", code, sqlite3_errstr(code) ); + +#ifdef DB_CRASH_ON_SQLITE_ERROR + int crash = *((int*)2); +#endif +} + +/* + * Perpare statement and auto throw away if fails. Returns NULL on failure. + */ +static sqlite3_stmt *db_stmt( const char *code ){ +#ifdef DB_LOG_SQL_STATEMENTS + vg_low( code ); +#endif + + sqlite3_stmt *stmt; + int fc = sqlite3_prepare_v2( database.db, code, -1, &stmt, NULL ); + + if( fc != SQLITE_OK ){ + log_sqlite3( fc ); + sqlite3_finalize( stmt ); + return NULL; + } + + return stmt; +} + +/* + * bind zero terminated string + */ +static int db_sqlite3_bind_sz( sqlite3_stmt *stmt, int pos, const char *sz ){ + return sqlite3_bind_text( stmt, pos, sz, -1, SQLITE_STATIC ); +} + +/* + * Allowed characters in sqlite table names. We use "" as delimiters. + */ +static int db_verify_charset( const char *str, int mincount ){ + for( int i=0; ; i++ ){ + char c = str[i]; + if( c == '\0' ){ + if( i < mincount ) return 0; + else return 1; + } + + if( !((c==' ')||(c=='!')||(c>='#'&&c<='~')) ) return 0; + } + + return 0; +} + +/* + * Find table name from mod UID and course UID, plus the week number + */ +static int db_get_highscore_table_name( char mod_uid[ADDON_UID_MAX], + char run_uid[DB_COURSE_UID_MAX], + u32 week, + char table_name[DB_TABLE_UID_MAX] ){ + if( !db_verify_charset( mod_uid, 13 ) || + !db_verify_charset( run_uid, 1 ) ) return 0; + + vg_str a; + vg_strnull( &a, table_name, DB_TABLE_UID_MAX ); + vg_strcat( &a, mod_uid ); + vg_strcat( &a, ":" ); + vg_strcat( &a, run_uid ); + + if( week ){ + vg_strcat( &a, "#" ); + vg_strcati32( &a, week ); + } + + return vg_strgood( &a ); +} + +/* + * Read value from highscore table. If not found or error, returns 0 + */ +static i32 db_readusertime( char table[DB_TABLE_UID_MAX], u64 steamid ){ + char buf[ 512 ]; + vg_str q; + vg_strnull( &q, buf, 512 ); + vg_strcat( &q, "SELECT time FROM \"" ); + vg_strcat( &q, table ); + vg_strcat( &q, "\" WHERE steamid = ?;" ); + if( !vg_strgood(&q) ) return 0; + + sqlite3_stmt *stmt = db_stmt( q.buffer ); + sqlite3_bind_int64( stmt, 1, *((i64 *)&steamid) ); + + if( stmt ){ + int fc = sqlite3_step( stmt ); + + i32 result = 0; + + if( fc == SQLITE_ROW ) + result = sqlite3_column_int( stmt, 0 ); + else if( fc != SQLITE_DONE ) + log_sqlite3(fc); + + sqlite3_finalize( stmt ); + return result; + } + else return 0; +} + +/* + * Write to highscore table + */ +static int db_writeusertime( char table[DB_TABLE_UID_MAX], u64 steamid, + i32 score, int only_if_faster ){ + /* auto create table + * ------------------------------------------*/ + char buf[ 512 ]; + vg_str q; + vg_strnull( &q, buf, 512 ); + vg_strcat( &q, "CREATE TABLE IF NOT EXISTS \n \"" ); + vg_strcat( &q, table ); + vg_strcat( &q, "\"\n (steamid BIGINT PRIMARY KEY, time INT);" ); + if( !vg_strgood(&q) ) return 0; + + vg_str str; + sqlite3_stmt *create_table = db_stmt( q.buffer ); + + if( create_table ){ + db_sqlite3_bind_sz( create_table, 1, table ); + + int fc = sqlite3_step( create_table ); + sqlite3_finalize( create_table ); + if( fc != SQLITE_DONE ) + return 0; + } + else return 0; + + if( only_if_faster ){ + i32 current = db_readusertime( table, steamid ); + if( (current != 0) && (score > current) ) + return 1; + } + + /* insert score + * -------------------------------------------------*/ + vg_strnull( &q, buf, 512 ); + vg_strcat( &q, "REPLACE INTO \"" ); + vg_strcat( &q, table ); + vg_strcat( &q, "\"(steamid,time)\n VALUES (?,?);" ); + if( !vg_strgood(&q) ) return 0; + + sqlite3_stmt *stmt = db_stmt( q.buffer ); + + if( stmt ){ + sqlite3_bind_int64( stmt, 1, *((i64 *)&steamid) ); + sqlite3_bind_int( stmt, 2, score ); + + int fc = sqlite3_step( stmt ); + sqlite3_finalize( stmt ); + if( fc != SQLITE_DONE ) + return 0; + else + return 1; + } + else return 0; +} + +/* + * Set username and type + */ +static int db_updateuser( u64 steamid, const char *username, int admin ){ + sqlite3_stmt *stmt = db_stmt( + "INSERT OR REPLACE INTO users (steamid, name, type) " + "VALUES (?,?,?);" ); + + if( stmt ){ + sqlite3_bind_int64( stmt, 1, *((i64*)(&steamid)) ); + db_sqlite3_bind_sz( stmt, 2, username ); + sqlite3_bind_int( stmt, 3, admin ); + + int fc = sqlite3_step( stmt ); + sqlite3_finalize(stmt); + + if( fc == SQLITE_DONE ){ + vg_success( "Inserted %lu (%s), type: %d\n", + steamid, username, admin ); + return 1; + } + else{ + log_sqlite3( fc ); + return 0; + } + } + else return 0; +} + +/* + * Create database connection and users table + */ +static int db_init(void){ + int rc = sqlite3_open( "highscores.db", &database.db ); + + if( rc ){ + vg_error( "database failure: %s\n", sqlite3_errmsg(database.db) ); + sqlite3_close( database.db ); + return 0; + } + + sqlite3_stmt *stmt = db_stmt( + "CREATE TABLE IF NOT EXISTS \n" + " users(steamid BIGINT PRIMARY KEY, name VARCHAR(128), type INT);" ); + + if( stmt ){ + int fc = sqlite3_step( stmt ); + sqlite3_finalize(stmt); + + if( fc == SQLITE_DONE ){ + vg_success( "Created users table\n" ); + db_updateuser( 76561198072130043, "harry", 2 ); + return 1; + } + else{ + log_sqlite3( fc ); + return 0; + } + } + else return 0; +} + +static void db_free(void){ + sqlite3_close( database.db ); +} + +#endif /* GAMESERVER_DB_H */