mirror of
git://xwords.git.sourceforge.net/gitroot/xwords/xwords
synced 2025-01-08 05:24:39 +01:00
43bd3c018a
Works for large numbers of relay games in test, but will not yet actually hold more than one value. Should be safe to merge to main branch once stream upgrade is verified.
354 lines
11 KiB
C
354 lines
11 KiB
C
/* -*- compile-command: "make MEMDEBUG=TRUE -j3"; -*- */
|
|
/*
|
|
* Copyright 2000-2013 by Eric House (xwords@eehouse.org). All rights
|
|
* reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
#include "gamesdb.h"
|
|
#include "main.h"
|
|
|
|
static void getColumnText( sqlite3_stmt *ppStmt, int iCol, XP_UCHAR* buf,
|
|
int len );
|
|
|
|
|
|
sqlite3*
|
|
openGamesDB( const char* dbName )
|
|
{
|
|
int result = sqlite3_initialize();
|
|
XP_ASSERT( SQLITE_OK == result );
|
|
|
|
sqlite3* pDb = NULL;
|
|
result = sqlite3_open( dbName, &pDb );
|
|
XP_ASSERT( SQLITE_OK == result );
|
|
|
|
const char* createGamesStr =
|
|
"CREATE TABLE games ( "
|
|
"rowid INTEGER PRIMARY KEY AUTOINCREMENT"
|
|
",game BLOB"
|
|
",room VARCHAR(32)"
|
|
",connvia VARCHAR(32)"
|
|
",ended INT(1)"
|
|
",turn INT(2)"
|
|
",nmoves INT"
|
|
",seed INT"
|
|
",gameid INT"
|
|
",nmissing INT(2)"
|
|
")";
|
|
result = sqlite3_exec( pDb, createGamesStr, NULL, NULL, NULL );
|
|
|
|
const char* createValuesStr =
|
|
"CREATE TABLE pairs ( key TEXT UNIQUE,value TEXT )";
|
|
result = sqlite3_exec( pDb, createValuesStr, NULL, NULL, NULL );
|
|
XP_LOGF( "sqlite3_exec=>%d", result );
|
|
XP_USE( result );
|
|
|
|
return pDb;
|
|
}
|
|
|
|
void
|
|
closeGamesDB( sqlite3* pDb )
|
|
{
|
|
sqlite3_close( pDb );
|
|
XP_LOGF( "%s finished", __func__ );
|
|
}
|
|
|
|
void
|
|
writeToDB( XWStreamCtxt* stream, void* closure )
|
|
{
|
|
int result;
|
|
CommonGlobals* cGlobals = (CommonGlobals*)closure;
|
|
sqlite3_int64 selRow = cGlobals->selRow;
|
|
sqlite3* pDb = cGlobals->pDb;
|
|
XP_U16 len = stream_getSize( stream );
|
|
char buf[256];
|
|
char* query;
|
|
|
|
sqlite3_stmt* stmt = NULL;
|
|
XP_Bool newGame = -1 == selRow;
|
|
if ( newGame ) { /* new row; need to insert blob first */
|
|
query = "INSERT INTO games (game) VALUES (?)";
|
|
} else {
|
|
const char* fmt = "UPDATE games SET game=? where rowid=%lld";
|
|
snprintf( buf, sizeof(buf), fmt, selRow );
|
|
query = buf;
|
|
}
|
|
|
|
result = sqlite3_prepare_v2( pDb, query, -1, &stmt, NULL );
|
|
XP_ASSERT( SQLITE_OK == result );
|
|
result = sqlite3_bind_zeroblob( stmt, 1 /*col 0 ??*/, len );
|
|
XP_ASSERT( SQLITE_OK == result );
|
|
result = sqlite3_step( stmt );
|
|
XP_ASSERT( SQLITE_DONE == result );
|
|
XP_USE( result );
|
|
|
|
if ( newGame ) { /* new row; need to insert blob first */
|
|
selRow = sqlite3_last_insert_rowid( pDb );
|
|
XP_LOGF( "%s: new rowid: %lld", __func__, selRow );
|
|
cGlobals->selRow = selRow;
|
|
}
|
|
|
|
sqlite3_blob* blob;
|
|
result = sqlite3_blob_open( pDb, "main", "games", "game",
|
|
selRow, 1 /*flags: writeable*/, &blob );
|
|
XP_ASSERT( SQLITE_OK == result );
|
|
const XP_U8* ptr = stream_getPtr( stream );
|
|
result = sqlite3_blob_write( blob, ptr, len, 0 );
|
|
XP_ASSERT( SQLITE_OK == result );
|
|
result = sqlite3_blob_close( blob );
|
|
XP_ASSERT( SQLITE_OK == result );
|
|
if ( !!stmt ) {
|
|
sqlite3_finalize( stmt );
|
|
}
|
|
|
|
(*cGlobals->onSave)( cGlobals->onSaveClosure, selRow, newGame );
|
|
}
|
|
|
|
void
|
|
summarize( CommonGlobals* cGlobals )
|
|
{
|
|
XP_S16 nMoves = model_getNMoves( cGlobals->game.model );
|
|
XP_Bool gameOver = server_getGameIsOver( cGlobals->game.server );
|
|
XP_S16 turn = server_getCurrentTurn( cGlobals->game.server );
|
|
XP_U16 seed = 0;
|
|
XP_S16 nMissing = 0;
|
|
XP_U32 gameID = cGlobals->gi->gameID;
|
|
XP_ASSERT( 0 != gameID );
|
|
CommsAddrRec addr = {0};
|
|
gchar* room = "";
|
|
|
|
gchar* connvia = "local";
|
|
|
|
if ( !!cGlobals->game.comms ) {
|
|
nMissing = server_getMissingPlayers( cGlobals->game.server );
|
|
comms_getAddr( cGlobals->game.comms, &addr );
|
|
switch( addr_getType( &addr ) ) {
|
|
case COMMS_CONN_RELAY:
|
|
room = addr.u.ip_relay.invite;
|
|
connvia = "Relay";
|
|
break;
|
|
case COMMS_CONN_SMS:
|
|
connvia = "SMS";
|
|
break;
|
|
case COMMS_CONN_BT:
|
|
connvia = "Bluetooth";
|
|
break;
|
|
default:
|
|
// XP_ASSERT(0);
|
|
break;
|
|
}
|
|
seed = comms_getChannelSeed( cGlobals->game.comms );
|
|
}
|
|
|
|
const char* fmt = "UPDATE games "
|
|
" SET room='%s', ended=%d, turn=%d, nmissing=%d, nmoves=%d, seed=%d, gameid=%d, connvia='%s'"
|
|
" WHERE rowid=%lld";
|
|
XP_UCHAR buf[256];
|
|
snprintf( buf, sizeof(buf), fmt, room, gameOver?1:0, turn, nMissing, nMoves,
|
|
seed, gameID, connvia, cGlobals->selRow );
|
|
XP_LOGF( "query: %s", buf );
|
|
sqlite3_stmt* stmt = NULL;
|
|
int result = sqlite3_prepare_v2( cGlobals->pDb, buf, -1, &stmt, NULL );
|
|
XP_ASSERT( SQLITE_OK == result );
|
|
result = sqlite3_step( stmt );
|
|
XP_ASSERT( SQLITE_DONE == result );
|
|
sqlite3_finalize( stmt );
|
|
XP_USE( result );
|
|
}
|
|
|
|
GSList*
|
|
listGames( sqlite3* pDb )
|
|
{
|
|
GSList* list = NULL;
|
|
|
|
sqlite3_stmt *ppStmt;
|
|
int result = sqlite3_prepare_v2( pDb,
|
|
"SELECT rowid FROM games ORDER BY rowid",
|
|
-1, &ppStmt, NULL );
|
|
XP_ASSERT( SQLITE_OK == result );
|
|
XP_USE( result );
|
|
while ( NULL != ppStmt ) {
|
|
switch( sqlite3_step( ppStmt ) ) {
|
|
case SQLITE_ROW: /* have data */
|
|
{
|
|
sqlite3_int64* data = g_malloc( sizeof( *data ) );
|
|
*data = sqlite3_column_int64( ppStmt, 0 );
|
|
XP_LOGF( "%s: got a row; id=%lld", __func__, *data );
|
|
list = g_slist_append( list, data );
|
|
}
|
|
break;
|
|
case SQLITE_DONE:
|
|
sqlite3_finalize( ppStmt );
|
|
ppStmt = NULL;
|
|
break;
|
|
default:
|
|
XP_ASSERT( 0 );
|
|
break;
|
|
}
|
|
}
|
|
return list;
|
|
}
|
|
|
|
XP_Bool
|
|
getGameInfo( sqlite3* pDb, sqlite3_int64 rowid, GameInfo* gib )
|
|
{
|
|
XP_Bool success = XP_FALSE;
|
|
const char* fmt = "SELECT room, ended, turn, nmoves, nmissing, seed, connvia, gameid "
|
|
"FROM games WHERE rowid = %lld";
|
|
XP_UCHAR query[256];
|
|
snprintf( query, sizeof(query), fmt, rowid );
|
|
|
|
sqlite3_stmt* ppStmt;
|
|
int result = sqlite3_prepare_v2( pDb, query, -1, &ppStmt, NULL );
|
|
XP_ASSERT( SQLITE_OK == result );
|
|
result = sqlite3_step( ppStmt );
|
|
if ( SQLITE_ROW == result ) {
|
|
success = XP_TRUE;
|
|
getColumnText( ppStmt, 0, gib->room, sizeof(gib->room) );
|
|
gib->gameOver = 1 == sqlite3_column_int( ppStmt, 1 );
|
|
gib->turn = sqlite3_column_int( ppStmt, 2 );
|
|
gib->nMoves = sqlite3_column_int( ppStmt, 3 );
|
|
gib->nMissing = sqlite3_column_int( ppStmt, 4 );
|
|
gib->seed = sqlite3_column_int( ppStmt, 5 );
|
|
getColumnText( ppStmt, 6, gib->conn, sizeof(gib->conn) );
|
|
gib->gameID = sqlite3_column_int( ppStmt, 7 );
|
|
snprintf( gib->name, sizeof(gib->name), "Game %lld", rowid );
|
|
}
|
|
sqlite3_finalize( ppStmt );
|
|
return success;
|
|
}
|
|
|
|
void
|
|
getRowsForGameID( sqlite3* pDb, XP_U32 gameID, sqlite3_int64* rowids,
|
|
int* nRowIDs )
|
|
{
|
|
int maxRowIDs = *nRowIDs;
|
|
*nRowIDs = 0;
|
|
|
|
char buf[256];
|
|
snprintf( buf, sizeof(buf), "SELECT rowid from games WHERE gameid = %d LIMIT %d",
|
|
gameID, maxRowIDs );
|
|
sqlite3_stmt *ppStmt;
|
|
int result = sqlite3_prepare_v2( pDb, buf, -1, &ppStmt, NULL );
|
|
XP_ASSERT( SQLITE_OK == result );
|
|
int ii;
|
|
for ( ii = 0; ii < maxRowIDs; ++ii ) {
|
|
result = sqlite3_step( ppStmt );
|
|
if ( SQLITE_ROW != result ) {
|
|
break;
|
|
}
|
|
rowids[ii] = sqlite3_column_int64( ppStmt, 0 );
|
|
++*nRowIDs;
|
|
}
|
|
sqlite3_finalize( ppStmt );
|
|
}
|
|
|
|
XP_Bool
|
|
loadGame( XWStreamCtxt* stream, sqlite3* pDb, sqlite3_int64 rowid )
|
|
{
|
|
char buf[256];
|
|
snprintf( buf, sizeof(buf), "SELECT game from games WHERE rowid = %lld", rowid );
|
|
|
|
sqlite3_stmt *ppStmt;
|
|
int result = sqlite3_prepare_v2( pDb, buf, -1, &ppStmt, NULL );
|
|
XP_ASSERT( SQLITE_OK == result );
|
|
result = sqlite3_step( ppStmt );
|
|
XP_Bool success = SQLITE_ROW == result;
|
|
if ( success ) {
|
|
const void* ptr = sqlite3_column_blob( ppStmt, 0 );
|
|
int size = sqlite3_column_bytes( ppStmt, 0 );
|
|
stream_putBytes( stream, ptr, size );
|
|
}
|
|
sqlite3_finalize( ppStmt );
|
|
return success;
|
|
}
|
|
|
|
void
|
|
deleteGame( sqlite3* pDb, sqlite3_int64 rowid )
|
|
{
|
|
char query[256];
|
|
snprintf( query, sizeof(query), "DELETE FROM games WHERE rowid = %lld", rowid );
|
|
sqlite3_stmt* ppStmt;
|
|
int result = sqlite3_prepare_v2( pDb, query, -1, &ppStmt, NULL );
|
|
XP_ASSERT( SQLITE_OK == result );
|
|
result = sqlite3_step( ppStmt );
|
|
XP_ASSERT( SQLITE_DONE == result );
|
|
XP_USE( result );
|
|
sqlite3_finalize( ppStmt );
|
|
}
|
|
|
|
void
|
|
db_store( sqlite3* pDb, const gchar* key, const gchar* value )
|
|
{
|
|
char buf[256];
|
|
snprintf( buf, sizeof(buf),
|
|
"INSERT OR REPLACE INTO pairs (key, value) VALUES ('%s', '%s')",
|
|
key, value );
|
|
sqlite3_stmt *ppStmt;
|
|
int result = sqlite3_prepare_v2( pDb, buf, -1, &ppStmt, NULL );
|
|
XP_ASSERT( SQLITE_OK == result );
|
|
result = sqlite3_step( ppStmt );
|
|
XP_ASSERT( SQLITE_DONE == result );
|
|
XP_USE( result );
|
|
sqlite3_finalize( ppStmt );
|
|
}
|
|
|
|
XP_Bool
|
|
db_fetch( sqlite3* pDb, const gchar* key, gchar* buf, gint buflen )
|
|
{
|
|
char query[256];
|
|
snprintf( query, sizeof(query),
|
|
"SELECT value from pairs where key = '%s'", key );
|
|
sqlite3_stmt *ppStmt;
|
|
int result = sqlite3_prepare_v2( pDb, query, -1, &ppStmt, NULL );
|
|
XP_Bool found = SQLITE_OK == result;
|
|
if ( found ) {
|
|
result = sqlite3_step( ppStmt );
|
|
found = SQLITE_ROW == result;
|
|
if ( found ) {
|
|
getColumnText( ppStmt, 0, buf, buflen );
|
|
} else {
|
|
buf[0] = '\0';
|
|
}
|
|
}
|
|
sqlite3_finalize( ppStmt );
|
|
return found;
|
|
}
|
|
|
|
void
|
|
db_remove( sqlite3* pDb, const gchar* key )
|
|
{
|
|
char query[256];
|
|
snprintf( query, sizeof(query), "DELETE FROM pairs WHERE key = '%s'", key );
|
|
sqlite3_stmt *ppStmt;
|
|
int result = sqlite3_prepare_v2( pDb, query, -1, &ppStmt, NULL );
|
|
XP_ASSERT( SQLITE_OK == result );
|
|
result = sqlite3_step( ppStmt );
|
|
XP_ASSERT( SQLITE_DONE == result );
|
|
XP_USE( result );
|
|
sqlite3_finalize( ppStmt );
|
|
}
|
|
|
|
static void
|
|
getColumnText( sqlite3_stmt *ppStmt, int iCol, XP_UCHAR* buf,
|
|
int XP_UNUSED_DBG(len) )
|
|
{
|
|
const unsigned char* txt = sqlite3_column_text( ppStmt, iCol );
|
|
int needLen = sqlite3_column_bytes( ppStmt, iCol );
|
|
XP_ASSERT( needLen < len );
|
|
XP_MEMCPY( buf, txt, needLen );
|
|
buf[needLen] = '\0';
|
|
}
|