add sqlite and use it
[carveJwlIkooP6JGAAIwe30JlM.git] / gameserver_db.h
diff --git a/gameserver_db.h b/gameserver_db.h
new file mode 100644 (file)
index 0000000..cffa2dc
--- /dev/null
@@ -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 */