get rematch working on linux, at least for two-device games. (Handling

the case where one of several guests wants to rematch is a hard
problem for later.) Requires passing old-style relayIDs (connname plus
device index) when devIDs aren't available, which they may not always
be.
This commit is contained in:
Eric House 2015-07-06 20:28:16 -07:00
parent fd06bca151
commit 0f33228155
22 changed files with 484 additions and 185 deletions

View file

@ -560,6 +560,7 @@ comms_setConnID( CommsCtxt* comms, XP_U32 connID )
static void
addrFromStreamOne( CommsAddrRec* addrP, XWStreamCtxt* stream, CommsConnType typ )
{
XP_U16 version = stream_getVersion( stream );
switch( typ ) {
case COMMS_CONN_NONE:
break;
@ -584,8 +585,11 @@ addrFromStreamOne( CommsAddrRec* addrP, XWStreamCtxt* stream, CommsConnType typ
stringFromStreamHere( stream, addrP->u.ip_relay.hostName,
sizeof(addrP->u.ip_relay.hostName) );
addrP->u.ip_relay.ipAddr = stream_getU32( stream );
if ( version >= STREAM_VERS_DEVIDS ) {
addrP->u.ip_relay.devID = stream_getU32( stream );
}
addrP->u.ip_relay.port = stream_getU16( stream );
if ( stream_getVersion( stream ) >= STREAM_VERS_DICTLANG ) {
if ( version >= STREAM_VERS_DICTLANG ) {
addrP->u.ip_relay.seeksPublicRoom = stream_getBits( stream, 1 );
addrP->u.ip_relay.advertiseRoom = stream_getBits( stream, 1 );
}
@ -632,7 +636,8 @@ comms_makeFromStream( MPFORMAL XWStreamCtxt* stream, XW_UtilCtxt* util,
isServer = stream_getU8( stream );
addrFromStream( &addr, stream );
if ( addr_hasType( &addr, COMMS_CONN_RELAY ) ) {
if ( version >= STREAM_VERS_DEVIDS
|| addr_hasType( &addr, COMMS_CONN_RELAY ) ) {
nPlayersHere = (XP_U16)stream_getBits( stream, 4 );
nPlayersTotal = (XP_U16)stream_getBits( stream, 4 );
} else {
@ -810,6 +815,7 @@ addrToStreamOne( XWStreamCtxt* stream, CommsConnType typ, const CommsAddrRec* ad
stringToStream( stream, addrP->u.ip_relay.invite );
stringToStream( stream, addrP->u.ip_relay.hostName );
stream_putU32( stream, addrP->u.ip_relay.ipAddr );
stream_putU32( stream, addrP->u.ip_relay.devID );
stream_putU16( stream, addrP->u.ip_relay.port );
stream_putBits( stream, 1, addrP->u.ip_relay.seeksPublicRoom );
stream_putBits( stream, 1, addrP->u.ip_relay.advertiseRoom );
@ -827,6 +833,7 @@ addrToStreamOne( XWStreamCtxt* stream, CommsConnType typ, const CommsAddrRec* ad
void
addrToStream( XWStreamCtxt* stream, const CommsAddrRec* addrP )
{
stream_setVersion( stream, CUR_STREAM_VERS );
stream_putU8( stream, addrP->_conTypes );
CommsConnType typ;
@ -843,13 +850,13 @@ comms_writeToStream( CommsCtxt* comms, XWStreamCtxt* stream,
AddressRecord* rec;
MsgQueueElem* msg;
stream_setVersion( stream, CUR_STREAM_VERS );
stream_putU8( stream, (XP_U8)comms->isServer );
logAddr( comms, &comms->addr, __func__ );
addrToStream( stream, &comms->addr );
if ( addr_hasType( &comms->addr, COMMS_CONN_RELAY ) ) {
stream_putBits( stream, 4, comms->rr.nPlayersHere );
stream_putBits( stream, 4, comms->rr.nPlayersTotal );
}
stream_putBits( stream, 4, comms->rr.nPlayersHere );
stream_putBits( stream, 4, comms->rr.nPlayersTotal );
stream_putU32( stream, comms->connID );
stream_putU16( stream, comms->nextChannelNo );
@ -982,6 +989,20 @@ formatRelayID( const CommsCtxt* comms, XWHostID hostID,
return XP_TRUE;
}
XP_Bool
comms_formatRelayID( const CommsCtxt* comms, XP_U16 indx,
XP_UCHAR* buf, XP_U16* lenp )
{
XP_LOGF( "%s(indx=%d)", __func__, indx );
XWHostID hostID = HOST_ID_SERVER;
if ( comms->isServer ) {
hostID += 1 + indx;
}
XP_Bool success = formatRelayID( comms, hostID, buf, lenp );
XP_LOGF( "%s(%d) => %s", __func__, indx, buf );
return success;
}
/* Get *my* "relayID", a combo of connname and host id */
XP_Bool
comms_getRelayID( const CommsCtxt* comms, XP_UCHAR* buf, XP_U16* lenp )
@ -997,12 +1018,14 @@ comms_getInitialAddr( CommsAddrRec* addr
#ifdef XWFEATURE_RELAY
, const XP_UCHAR* relayName
, XP_U16 relayPort
, XP_U32 devID
#endif
)
{
#if defined XWFEATURE_RELAY
addr_setType( addr, COMMS_CONN_RELAY ); /* for temporary ease in debugging */
addr->u.ip_relay.ipAddr = 0L; /* force 'em to set it */
addr->u.ip_relay.devID = devID;
addr->u.ip_relay.port = relayPort;
{
const char* name = relayName;
@ -1051,7 +1074,7 @@ comms_getConTypes( const CommsCtxt* comms )
XP_LOGF( "%s: returning COMMS_CONN_NONE for null comms", __func__ );
}
return typ;
} /* comms_getConType */
} /* comms_getConTypes */
XP_Bool
comms_getIsServer( const CommsCtxt* comms )
@ -2629,14 +2652,8 @@ augmentChannelAddr( CommsCtxt* comms, AddressRecord * const rec,
switch( typ ) {
case COMMS_CONN_RELAY:
dest = &rec->addr.u.ip_relay;
src = &addr->u.ip_relay;
siz = sizeof( rec->addr.u.ip_relay );
/* Special case for relay: use comms' relay info if caller
didn't bother to fill it in */
if ( addr->u.ip_relay.invite[0] ) {
src = &addr->u.ip_relay;
} else {
src = &comms->addr.u.ip_relay;
}
if ( 0 != hostID ) {
rec->rr.hostID = hostID;
XP_LOGF( "%s: set hostID for rec %p to %d", __func__, rec, hostID );

View file

@ -90,6 +90,7 @@ typedef struct _CommsAddrRec {
XP_UCHAR invite[MAX_INVITE_LEN + 1];
XP_UCHAR hostName[MAX_HOSTNAME_LEN + 1];
XP_U32 ipAddr; /* looked up from above */
XP_U32 devID;
XP_U16 port;
XP_Bool seeksPublicRoom;
XP_Bool advertiseRoom;
@ -180,6 +181,7 @@ void comms_getInitialAddr( CommsAddrRec* addr
#ifdef XWFEATURE_RELAY
, const XP_UCHAR* relayName
, XP_U16 relayPort
, XP_U32 devID
#endif
);
XP_Bool comms_checkAddr( DeviceRole role, const CommsAddrRec* addr,
@ -189,6 +191,9 @@ void comms_getAddr( const CommsCtxt* comms, CommsAddrRec* addr );
void comms_setAddr( CommsCtxt* comms, const CommsAddrRec* addr );
void comms_getAddrs( const CommsCtxt* comms, CommsAddrRec addr[],
XP_U16* nRecs );
XP_Bool comms_formatRelayID( const CommsCtxt* comms, XP_U16 indx,
XP_UCHAR* buf, XP_U16* lenp );
XP_U16 comms_countPendingPackets( const CommsCtxt* comms );

View file

@ -47,6 +47,7 @@
#endif
#define MAX_COLS MAX_ROWS
#define STREAM_VERS_DEVIDS 0x19
#define STREAM_VERS_MULTIADDR 0x18
#define STREAM_VERS_MODELDIVIDER 0x17
#define STREAM_VERS_COMMSBACKOFF 0x16
@ -86,7 +87,7 @@
#define STREAM_VERS_405 0x01
/* search for FIX_NEXT_VERSION_CHANGE next time this is changed */
#define CUR_STREAM_VERS STREAM_VERS_MULTIADDR
#define CUR_STREAM_VERS STREAM_VERS_DEVIDS
typedef struct XP_Rect {
XP_S16 left;

View file

@ -49,7 +49,8 @@ static void gi_setDict( MPFORMAL CurGameInfo* gi, const DictionaryCtxt* dict );
#endif
static void
checkServerRole( CurGameInfo* gi, XP_U16* nPlayersHere, XP_U16* nPlayersTotal )
checkServerRole( CurGameInfo* gi, XP_U16* nPlayersHere,
XP_U16* nPlayersTotal )
{
if ( !!gi ) {
XP_U16 ii, remoteCount = 0;
@ -87,7 +88,7 @@ makeGameID( XW_UtilCtxt* util )
void
game_makeNewGame( MPFORMAL XWGame* game, CurGameInfo* gi,
XW_UtilCtxt* util, DrawCtx* draw,
CommonPrefs* cp, const TransportProcs* procs
const CommonPrefs* cp, const TransportProcs* procs
#ifdef SET_GAMESEED
,XP_U16 gameSeed
#endif
@ -280,6 +281,25 @@ game_makeFromStream( MPFORMAL XWStreamCtxt* stream, XWGame* game,
return success;
} /* game_makeFromStream */
void
game_saveNewGame( MPFORMAL const CurGameInfo* gi, XW_UtilCtxt* util,
const CommonPrefs* cp, XWStreamCtxt* out )
{
XWGame newGame = {0};
CurGameInfo newGI = {0};
gi_copy( MPPARM(mpool) &newGI, gi );
game_makeNewGame( MPPARM(mpool) &newGame, &newGI, util,
NULL, /* DrawCtx*, */
cp, NULL, /* TransportProcs* procs */
0 );
game_saveToStream( &newGame, &newGI, out, 1 );
game_saveSucceeded( &newGame, 1 );
game_dispose( &newGame );
gi_disposePlayerInfo( MPPARM(mpool) &newGI );
}
void
game_saveToStream( const XWGame* game, const CurGameInfo* gi,
XWStreamCtxt* stream, XP_U16 saveToken )

View file

@ -60,7 +60,7 @@ typedef struct XWGame {
void game_makeNewGame( MPFORMAL XWGame* game, CurGameInfo* gi,
XW_UtilCtxt* util, DrawCtx* draw,
CommonPrefs* cp, const TransportProcs* procs
const CommonPrefs* cp, const TransportProcs* procs
#ifdef SET_GAMESEED
,XP_U16 gameSeed
#endif
@ -76,6 +76,9 @@ XP_Bool game_makeFromStream( MPFORMAL XWStreamCtxt* stream, XWGame* game,
DrawCtx* draw, CommonPrefs* cp,
const TransportProcs* procs );
void game_saveNewGame( MPFORMAL const CurGameInfo* gi, XW_UtilCtxt* util,
const CommonPrefs* cp, XWStreamCtxt* out );
void game_saveToStream( const XWGame* game, const CurGameInfo* gi,
XWStreamCtxt* stream, XP_U16 saveToken );
void game_saveSucceeded( const XWGame* game, XP_U16 saveToken );

View file

@ -55,6 +55,7 @@ void
invit_setDevID( InviteInfo* invit, XP_U32 devID )
{
invit->devID = devID;
types_addType( &invit->_conTypes, COMMS_CONN_RELAY );
}
void

View file

@ -30,6 +30,12 @@
typedef enum {OSType_NONE, OSType_LINUX, OSType_ANDROID, } XP_OSType;
/* InviteInfo
*
* A representation of return addresses sent with an invitation so that the
* recipient has all it needs to create a game and connect back.
*/
typedef struct _InviteInfo {
XP_U8 version; /* struct version for backward compatibility */
XP_U16 _conTypes;

View file

@ -25,6 +25,7 @@ static void getColumnText( sqlite3_stmt *ppStmt, int iCol, XP_UCHAR* buf,
int len );
#ifdef DEBUG
static char* sqliteErr2str( int err );
static void assertPrintResult( sqlite3* pDb, int result, int expect );
#endif
sqlite3*
@ -41,6 +42,7 @@ openGamesDB( const char* dbName )
"CREATE TABLE games ( "
"rowid INTEGER PRIMARY KEY AUTOINCREMENT"
",game BLOB"
",inviteInfo BLOB"
",room VARCHAR(32)"
",connvia VARCHAR(32)"
",ended INT(1)"
@ -48,6 +50,7 @@ openGamesDB( const char* dbName )
",nmoves INT"
",seed INT"
",gameid INT"
",ntotal INT(2)"
",nmissing INT(2)"
")";
result = sqlite3_exec( pDb, createGamesStr, NULL, NULL, NULL );
@ -68,31 +71,32 @@ closeGamesDB( sqlite3* pDb )
XP_LOGF( "%s finished", __func__ );
}
void
writeToDB( XWStreamCtxt* stream, void* closure )
static sqlite3_int64
writeBlobColumn( XWStreamCtxt* stream, sqlite3* pDb, sqlite3_int64 curRow,
const char* column )
{
int result;
CommonGlobals* cGlobals = (CommonGlobals*)closure;
sqlite3_int64 selRow = cGlobals->selRow;
sqlite3* pDb = cGlobals->pDb;
/* size includes stream version as header */
XP_U16 len = stream_getSize( stream );
char buf[256];
char* query;
sqlite3_stmt* stmt = NULL;
XP_Bool newGame = -1 == selRow;
XP_Bool newGame = -1 == curRow;
if ( newGame ) { /* new row; need to insert blob first */
query = "INSERT INTO games (game) VALUES (?)";
const char* fmt = "INSERT INTO games (%s) VALUES (?)";
snprintf( buf, sizeof(buf), fmt, column );
query = buf;
} else {
const char* fmt = "UPDATE games SET game=? where rowid=%lld";
snprintf( buf, sizeof(buf), fmt, selRow );
const char* fmt = "UPDATE games SET %s=? where rowid=%lld";
snprintf( buf, sizeof(buf), fmt, column, curRow );
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_prepare_v2( pDb, query, -1, &stmt, NULL );
assertPrintResult( pDb, result, SQLITE_OK );
result = sqlite3_bind_zeroblob( stmt, 1 /*col 0 ??*/, sizeof(XP_U16) + len );
assertPrintResult( pDb, result, SQLITE_OK );
result = sqlite3_step( stmt );
if ( SQLITE_DONE != result ) {
XP_LOGF( "%s: sqlite3_step => %s", __func__, sqliteErr2str( result ) );
@ -101,24 +105,51 @@ writeToDB( XWStreamCtxt* stream, void* closure )
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;
curRow = sqlite3_last_insert_rowid( pDb );
XP_LOGF( "%s: new rowid: %lld", __func__, curRow );
}
sqlite3_blob* blob;
result = sqlite3_blob_open( pDb, "main", "games", "game",
selRow, 1 /*flags: writeable*/, &blob );
XP_ASSERT( SQLITE_OK == result );
result = sqlite3_blob_open( pDb, "main", "games", column,
curRow, 1 /*flags: writeable*/, &blob );
assertPrintResult( pDb, result, SQLITE_OK );
XP_U16 strVersion = stream_getVersion( stream );
XP_ASSERT( strVersion <= CUR_STREAM_VERS );
result = sqlite3_blob_write( blob, &strVersion, sizeof(strVersion), 0 );
assertPrintResult( pDb, result, SQLITE_OK );
const XP_U8* ptr = stream_getPtr( stream );
result = sqlite3_blob_write( blob, ptr, len, 0 );
XP_ASSERT( SQLITE_OK == result );
result = sqlite3_blob_write( blob, ptr, len, sizeof(strVersion) );
assertPrintResult( pDb, result, SQLITE_OK );
result = sqlite3_blob_close( blob );
XP_ASSERT( SQLITE_OK == result );
assertPrintResult( pDb, result, SQLITE_OK );
if ( !!stmt ) {
sqlite3_finalize( stmt );
}
return curRow;
}
sqlite3_int64
writeNewGameToDB( XWStreamCtxt* stream, sqlite3* pDb )
{
sqlite3_int64 newRow = writeBlobColumn( stream, pDb, -1, "game" );
return newRow;
}
void
writeToDB( XWStreamCtxt* stream, void* closure )
{
CommonGlobals* cGlobals = (CommonGlobals*)closure;
sqlite3_int64 selRow = cGlobals->selRow;
sqlite3* pDb = cGlobals->pDb;
XP_Bool newGame = -1 == selRow;
selRow = writeBlobColumn( stream, pDb, selRow, "game" );
if ( newGame ) { /* new row; need to insert blob first */
cGlobals->selRow = selRow;
}
(*cGlobals->onSave)( cGlobals->onSaveClosure, selRow, newGame );
}
@ -130,6 +161,7 @@ summarize( CommonGlobals* cGlobals )
XP_S16 turn = server_getCurrentTurn( cGlobals->game.server );
XP_U16 seed = 0;
XP_S16 nMissing = 0;
XP_U16 nTotal = cGlobals->gi->nPlayers;
XP_U32 gameID = cGlobals->gi->gameID;
XP_ASSERT( 0 != gameID );
CommsAddrRec addr = {0};
@ -171,15 +203,15 @@ summarize( CommonGlobals* cGlobals )
}
const char* fmt = "UPDATE games "
" SET room='%s', ended=%d, turn=%d, nmissing=%d, nmoves=%d, seed=%d, gameid=%d, connvia='%s'"
" SET room='%s', ended=%d, turn=%d, ntotal=%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 );
snprintf( buf, sizeof(buf), fmt, room, gameOver?1:0, turn, nTotal, 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 );
assertPrintResult( cGlobals->pDb, result, SQLITE_OK );
result = sqlite3_step( stmt );
if ( SQLITE_DONE != result ) {
XP_LOGF( "sqlite3_step=>%s", sqliteErr2str( result ) );
@ -198,7 +230,7 @@ listGames( sqlite3* pDb )
int result = sqlite3_prepare_v2( pDb,
"SELECT rowid FROM games ORDER BY rowid",
-1, &ppStmt, NULL );
XP_ASSERT( SQLITE_OK == result );
assertPrintResult( pDb, result, SQLITE_OK );
XP_USE( result );
while ( NULL != ppStmt ) {
switch( sqlite3_step( ppStmt ) ) {
@ -226,14 +258,15 @@ 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 "
const char* fmt = "SELECT room, ended, turn, nmoves, ntotal, 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 );
assertPrintResult( pDb, result, SQLITE_OK );
result = sqlite3_step( ppStmt );
if ( SQLITE_ROW == result ) {
success = XP_TRUE;
@ -241,10 +274,11 @@ getGameInfo( sqlite3* pDb, sqlite3_int64 rowid, GameInfo* gib )
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 );
gib->nTotal = sqlite3_column_int( ppStmt, 4 );
gib->nMissing = sqlite3_column_int( ppStmt, 5 );
gib->seed = sqlite3_column_int( ppStmt, 6 );
getColumnText( ppStmt, 7, gib->conn, sizeof(gib->conn) );
gib->gameID = sqlite3_column_int( ppStmt, 8 );
snprintf( gib->name, sizeof(gib->name), "Game %lld", rowid );
}
sqlite3_finalize( ppStmt );
@ -259,11 +293,11 @@ getRowsForGameID( sqlite3* pDb, XP_U32 gameID, sqlite3_int64* rowids,
*nRowIDs = 0;
char buf[256];
snprintf( buf, sizeof(buf), "SELECT rowid from games WHERE gameid = %d LIMIT %d",
gameID, maxRowIDs );
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 );
assertPrintResult( pDb, result, SQLITE_OK );
int ii;
for ( ii = 0; ii < maxRowIDs; ++ii ) {
result = sqlite3_step( ppStmt );
@ -276,26 +310,56 @@ getRowsForGameID( sqlite3* pDb, XP_U32 gameID, sqlite3_int64* rowids,
sqlite3_finalize( ppStmt );
}
XP_Bool
loadGame( XWStreamCtxt* stream, sqlite3* pDb, sqlite3_int64 rowid )
static XP_Bool
loadBlobColumn( XWStreamCtxt* stream, sqlite3* pDb, sqlite3_int64 rowid,
const char* column )
{
char buf[256];
snprintf( buf, sizeof(buf), "SELECT game from games WHERE rowid = %lld", rowid );
snprintf( buf, sizeof(buf), "SELECT %s from games WHERE rowid = %lld",
column, rowid );
sqlite3_stmt *ppStmt;
int result = sqlite3_prepare_v2( pDb, buf, -1, &ppStmt, NULL );
XP_ASSERT( SQLITE_OK == result );
assertPrintResult( pDb, result, SQLITE_OK );
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 );
success = 0 < size;
if ( success ) {
XP_U16 strVersion;
XP_MEMCPY( &strVersion, ptr, sizeof(strVersion) );
XP_ASSERT( strVersion <= CUR_STREAM_VERS );
stream_setVersion( stream, strVersion );
XP_ASSERT( size >= sizeof(strVersion) );
stream_putBytes( stream, ptr + sizeof(strVersion),
size - sizeof(strVersion) );
}
}
sqlite3_finalize( ppStmt );
return success;
}
XP_Bool
loadGame( XWStreamCtxt* stream, sqlite3* pDb, sqlite3_int64 rowid )
{
return loadBlobColumn( stream, pDb, rowid, "game" );
}
void
saveInviteAddrs( XWStreamCtxt* stream, sqlite3* pDb, sqlite3_int64 rowid )
{
sqlite3_int64 row = writeBlobColumn( stream, pDb, rowid, "inviteInfo" );
assert( row == rowid );
}
XP_Bool
loadInviteAddrs( XWStreamCtxt* stream, sqlite3* pDb, sqlite3_int64 rowid )
{
return loadBlobColumn( stream, pDb, rowid, "inviteInfo" );
}
void
deleteGame( sqlite3* pDb, sqlite3_int64 rowid )
{
@ -303,9 +367,9 @@ deleteGame( sqlite3* pDb, sqlite3_int64 rowid )
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 );
assertPrintResult( pDb, result, SQLITE_OK );
result = sqlite3_step( ppStmt );
XP_ASSERT( SQLITE_DONE == result );
assertPrintResult( pDb, result, SQLITE_DONE );
XP_USE( result );
sqlite3_finalize( ppStmt );
}
@ -319,9 +383,9 @@ db_store( sqlite3* pDb, const gchar* key, const gchar* value )
key, value );
sqlite3_stmt *ppStmt;
int result = sqlite3_prepare_v2( pDb, buf, -1, &ppStmt, NULL );
XP_ASSERT( SQLITE_OK == result );
assertPrintResult( pDb, result, SQLITE_OK );
result = sqlite3_step( ppStmt );
XP_ASSERT( SQLITE_DONE == result );
assertPrintResult( pDb, result, SQLITE_DONE );
XP_USE( result );
sqlite3_finalize( ppStmt );
}
@ -355,9 +419,9 @@ db_remove( sqlite3* pDb, const gchar* key )
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 );
assertPrintResult( pDb, result, SQLITE_OK );
result = sqlite3_step( ppStmt );
XP_ASSERT( SQLITE_DONE == result );
assertPrintResult( pDb, result, SQLITE_DONE );
XP_USE( result );
sqlite3_finalize( ppStmt );
}
@ -413,4 +477,17 @@ sqliteErr2str( int err )
}
return "<unknown>";
}
static void
assertPrintResult( sqlite3* pDb, int result, int expect )
{
int code = sqlite3_errcode( pDb );
XP_ASSERT( code == result ); /* do I need to pass it? */
if ( code != expect ) {
const char* msg = sqlite3_errmsg( pDb );
XP_LOGF( "sqlite3 error: %s", msg );
XP_ASSERT(0);
}
}
#endif

View file

@ -35,6 +35,7 @@ typedef struct _GameInfo {
XP_S16 nMoves;
XP_Bool gameOver;
XP_S16 turn;
XP_U16 nTotal;
XP_S16 nMissing;
XP_U16 seed;
} GameInfo;
@ -44,6 +45,8 @@ sqlite3* openGamesDB( const char* dbName );
void closeGamesDB( sqlite3* dbp );
void writeToDB( XWStreamCtxt* stream, void* closure );
sqlite3_int64 writeNewGameToDB( XWStreamCtxt* stream, sqlite3* pDb );
void summarize( CommonGlobals* cGlobals );
/* Return GSList whose data is (ptrs to) rowids */
@ -52,6 +55,10 @@ XP_Bool getGameInfo( sqlite3* dbp, sqlite3_int64 rowid, GameInfo* gib );
void getRowsForGameID( sqlite3* dbp, XP_U32 gameID, sqlite3_int64* rowids,
int* nRowIDs );
XP_Bool loadGame( XWStreamCtxt* stream, sqlite3* pDb, sqlite3_int64 rowid );
void saveInviteAddrs( XWStreamCtxt* stream, sqlite3* pDb,
sqlite3_int64 rowid );
XP_Bool loadInviteAddrs( XWStreamCtxt* stream, sqlite3* pDb,
sqlite3_int64 rowid );
void deleteGame( sqlite3* pDb, sqlite3_int64 rowid );
#define KEY_RDEVID "RDEVID"

View file

@ -77,8 +77,8 @@ gtkaskm( const gchar* message, AskMInfo* infos, int nInfos )
for ( ii = 0; ii < nInfos; ++ii ) {
AskMInfo* info = &infos[ii];
if ( !state.cancelled ) {
const gchar* txt = gtk_entry_get_text( GTK_ENTRY(fields[ii]) );
XP_LOGF( "%s: got text %s", __func__, txt );
XP_LOGF( "%s: got text %s", __func__,
gtk_entry_get_text( GTK_ENTRY(fields[ii]) ) );
} else {
*info->result = NULL;
}

View file

@ -54,6 +54,7 @@
#include "draw.h"
#include "game.h"
#include "movestak.h"
#include "strutils.h"
#include "gtkask.h"
#include "gtkinvit.h"
#include "gtkaskm.h"
@ -82,6 +83,10 @@ static GtkWidget* addButton( GtkWidget* hbox, gchar* label, GCallback func,
static void handle_invite_button( GtkWidget* widget, GtkGameGlobals* globals );
static void gtkShowFinalScores( const GtkGameGlobals* globals,
XP_Bool ignoreTimeout );
static void send_invites( CommonGlobals* cGlobals,
const CommsAddrRec* inviteAddr,
const XP_UCHAR* relayID,
XP_U16 nPlayers );
#define GTK_TRAY_HT_ROWS 3
@ -103,7 +108,7 @@ lookupClientStream( GtkGameGlobals* globals, int sock )
static void
rememberClient( GtkGameGlobals* globals, guint key, int sock,
XWStreamCtxt* stream )
XWStreamCtxt* stream )
{
short i;
for ( i = 0; i < MAX_NUM_PLAYERS; ++i ) {
@ -868,10 +873,39 @@ destroy_board_window( GtkWidget* XP_UNUSED(widget), GtkGameGlobals* globals )
static void
on_board_window_shown( GtkWidget* XP_UNUSED(widget), GtkGameGlobals* globals )
{
if ( server_getGameIsOver( globals->cGlobals.game.server ) ) {
LOG_FUNC();
CommonGlobals* cGlobals = &globals->cGlobals;
if ( server_getGameIsOver( cGlobals->game.server ) ) {
gtkShowFinalScores( globals, XP_TRUE );
}
}
CommsCtxt* comms = cGlobals->game.comms;
if ( !!comms /*&& COMMS_CONN_NONE == comms_getConTypes( comms )*/ ) {
/* If it has pending invite info, send the invitation! */
XWStreamCtxt* stream = mem_stream_make( MPPARM(cGlobals->util->mpool)
cGlobals->params->vtMgr,
cGlobals, CHANNEL_NONE, NULL );
if ( loadInviteAddrs( stream, cGlobals->pDb, cGlobals->selRow ) ) {
CommsAddrRec addr = {0};
addrFromStream( &addr, stream );
comms_setAddr( cGlobals->game.comms, &addr );
XP_U16 nRecs = stream_getU8( stream );
XP_LOGF( "%s: got invite info: %d records", __func__, nRecs );
for ( int ii = 0; ii < nRecs; ++ii ) {
XP_UCHAR relayID[32];
stringFromStreamHere( stream, relayID, sizeof(relayID) );
XP_LOGF( "%s: loaded relayID %s", __func__, relayID );
CommsAddrRec addr = {0};
addrFromStream( &addr, stream );
send_invites( cGlobals, &addr, relayID, 1 /*nPlayers*/ );
}
}
stream_destroy( stream );
}
} /* on_board_window_shown */
static void
cleanup( GtkGameGlobals* globals )
@ -986,7 +1020,9 @@ new_game_impl( GtkGameGlobals* globals, XP_Bool fireConnDlg )
if ( !!cGlobals->game.comms ) {
comms_getAddr( cGlobals->game.comms, &addr );
} else {
comms_getInitialAddr( &addr, RELAY_NAME_DEFAULT, RELAY_PORT_DEFAULT );
XP_U32 devID = linux_getDevIDRelay( cGlobals->params );
comms_getInitialAddr( &addr, RELAY_NAME_DEFAULT, RELAY_PORT_DEFAULT,
devID );
}
CurGameInfo* gi = cGlobals->gi;
@ -1572,7 +1608,7 @@ static void
handle_invite_button( GtkWidget* XP_UNUSED(widget), GtkGameGlobals* globals )
{
CommonGlobals* cGlobals = &globals->cGlobals;
const CurGameInfo* gi = cGlobals->gi;
/* const CurGameInfo* gi = cGlobals->gi; */
/* gchar* countStr; */
/* gchar* phone = NULL; */
@ -1595,46 +1631,52 @@ handle_invite_button( GtkWidget* XP_UNUSED(widget), GtkGameGlobals* globals )
CommsAddrRec inviteAddr = {0};
gint nPlayers = nMissing;
XP_U32 devID;
XP_Bool confirmed = gtkInviteDlg( globals, &inviteAddr, &nPlayers, &devID );
XP_Bool confirmed = gtkInviteDlg( globals, &inviteAddr, &nPlayers );
XP_LOGF( "%s: inviteDlg => %d", __func__, confirmed );
if ( confirmed ) {
gchar gameName[64];
snprintf( gameName, VSIZE(gameName), "Game %d", gi->gameID );
send_invites( cGlobals, &inviteAddr, NULL, nPlayers );
}
} /* handle_invite_button */
CommsAddrRec addr;
CommsCtxt* comms = cGlobals->game.comms;
XP_ASSERT( comms );
comms_getAddr( comms, &addr );
static void
send_invites( CommonGlobals* cGlobals, const CommsAddrRec* inviteAddr,
const XP_UCHAR* relayID, XP_U16 nPlayers )
{
CommsAddrRec addr = {0};
CommsCtxt* comms = cGlobals->game.comms;
XP_ASSERT( comms );
comms_getAddr( comms, &addr );
gint forceChannel = 0; /* PENDING */
gint forceChannel = 0; /* PENDING */
InviteInfo invit = {0};
invit_init( &invit, gi, &addr, nPlayers, forceChannel );
invit_setDevID( &invit, linux_getDevIDRelay( cGlobals->params ) );
InviteInfo invit = {0};
invit_init( &invit, cGlobals->gi, &addr, nPlayers, forceChannel );
invit_setDevID( &invit, linux_getDevIDRelay( cGlobals->params ) );
#ifdef DEBUG
{
XWStreamCtxt* stream = mem_stream_make( MPPARM(cGlobals->util->mpool)
cGlobals->params->vtMgr,
NULL, CHANNEL_NONE, NULL );
invit_saveToStream( &invit, stream );
InviteInfo tmp;
invit_makeFromStream( &tmp, stream );
stream_destroy( stream );
XP_ASSERT( 0 == memcmp( &invit, &tmp, sizeof(invit) ) );
}
{
XWStreamCtxt* stream = mem_stream_make( MPPARM(cGlobals->util->mpool)
cGlobals->params->vtMgr,
NULL, CHANNEL_NONE, NULL );
invit_saveToStream( &invit, stream );
InviteInfo tmp;
invit_makeFromStream( &tmp, stream );
stream_destroy( stream );
XP_ASSERT( 0 == memcmp( &invit, &tmp, sizeof(invit) ) );
}
#endif
if ( addr_hasType( &inviteAddr, COMMS_CONN_SMS ) ) {
linux_sms_invite( cGlobals->params, gi, &addr, gameName,
nPlayers, forceChannel,
inviteAddr.u.sms.phone, inviteAddr.u.sms.port );
}
if ( addr_hasType( &addr, COMMS_CONN_RELAY ) ) {
relaycon_invite( cGlobals->params, devID, &invit );
}
if ( addr_hasType( inviteAddr, COMMS_CONN_SMS ) ) {
XP_ASSERT( 0 ); /* not implemented */
/* linux_sms_invite( cGlobals->params, gi, &addr, gameName, */
/* nPlayers, forceChannel, */
/* inviteAddr.u.sms.phone, inviteAddr.u.sms.port ); */
}
if ( addr_hasType( inviteAddr, COMMS_CONN_RELAY ) ) {
XP_U32 devID = inviteAddr->u.ip_relay.devID;
XP_ASSERT( 0 != devID || (!!relayID && !!relayID[0]) );
relaycon_invite( cGlobals->params, devID, relayID, &invit );
}
/* while ( gtkaskm( "Invite how many and how?", infos, VSIZE(infos) ) ) { */
@ -1673,7 +1715,7 @@ handle_invite_button( GtkWidget* XP_UNUSED(widget), GtkGameGlobals* globals )
/* for ( int ii = 0; ii < VSIZE(infos); ++ii ) { */
/* g_free( *infos[ii].result ); */
/* } */
} /* handle_invite_button */
}
static void
gtkUserError( GtkGameGlobals* globals, const char* format, ... )
@ -1805,8 +1847,7 @@ gtkShowFinalScores( const GtkGameGlobals* globals, XP_Bool ignoreTimeout )
buttons, timeout );
free( text );
if ( 2 == chosen ) {
XP_LOGF( "%s: rematch chosen!", __func__ );
XP_ASSERT( 0 );
make_rematch( globals->apg, cGlobals );
}
} /* gtkShowFinalScores */
@ -1847,7 +1888,7 @@ gtk_util_notifyGameOver( XW_UtilCtxt* uc, XP_S16 quitter )
server_handleUndo( cGlobals->game.server, 0 );
board_draw( cGlobals->game.board );
} else if ( !cGlobals->params->skipGameOver ) {
gtkShowFinalScores( globals, XP_FALSE );
gtkShowFinalScores( globals, XP_TRUE );
}
} /* gtk_util_notifyGameOver */
@ -2785,7 +2826,8 @@ makeNewGame( GtkGameGlobals* globals )
if ( 0 == relayPort ) {
relayPort = RELAY_PORT_DEFAULT;
}
comms_getInitialAddr( &cGlobals->addr, relayName, relayPort );
comms_getInitialAddr( &cGlobals->addr, relayName, relayPort,
linux_getDevIDRelay( cGlobals->params ) );
}
CurGameInfo* gi = cGlobals->gi;

View file

@ -36,7 +36,6 @@ typedef struct _GtkInviteState {
GtkGameGlobals* globals;
CommsAddrRec* addr;
gint* nPlayersP;
XP_U32* devIDP;
gint maxPlayers;
XP_UCHAR devIDBuf[32];
@ -94,7 +93,7 @@ handle_ok( GtkWidget* XP_UNUSED(widget), gpointer closure )
#ifdef XWFEATURE_RELAY
case COMMS_CONN_RELAY:
txt = gtk_entry_get_text( GTK_ENTRY(state->devID) );
*(state->devIDP) = atoi( txt );
state->addr->u.ip_relay.devID = atoi( txt );
break;
#endif
#ifdef XWFEATURE_BLUETOOTH
@ -276,17 +275,15 @@ onPageChanged( GtkNotebook* XP_UNUSED(notebook), gpointer XP_UNUSED(arg1),
}
XP_Bool
gtkInviteDlg( GtkGameGlobals* globals, CommsAddrRec* addr, gint* nPlayers,
XP_U32* devIDP )
gtkInviteDlg( GtkGameGlobals* globals, CommsAddrRec* addr, gint* nPlayersP )
{
GtkInviteState state;
XP_MEMSET( &state, 0, sizeof(state) );
state.globals = globals;
state.addr = addr;
state.nPlayersP = nPlayers;
state.maxPlayers = *nPlayers;
state.devIDP = devIDP;
state.nPlayersP = nPlayersP;
state.maxPlayers = *nPlayersP;
GtkWidget* dialog;
GtkWidget* hbox;

View file

@ -24,7 +24,7 @@
/* return true if not cancelled */
XP_Bool gtkInviteDlg( GtkGameGlobals* globals, CommsAddrRec* addr,
/*inout*/ gint* nPlayers, XP_U32* devID );
/*inout*/ gint* nPlayers );
#endif

View file

@ -33,7 +33,6 @@
static void onNewData( GtkAppGlobals* apg, sqlite3_int64 rowid,
XP_Bool isNew );
static void updateButtons( GtkAppGlobals* apg );
static void open_row( GtkAppGlobals* apg, sqlite3_int64 row );
static void
recordOpened( GtkAppGlobals* apg, GtkGameGlobals* globals )
@ -77,7 +76,7 @@ findOpenGame( const GtkAppGlobals* apg, sqlite3_int64 rowid )
}
enum { ROW_ITEM, NAME_ITEM, ROOM_ITEM, GAMEID_ITEM, SEED_ITEM, CONN_ITEM, OVER_ITEM, TURN_ITEM,
NMOVES_ITEM, MISSING_ITEM, N_ITEMS };
NMOVES_ITEM, NTOTAL_ITEM, MISSING_ITEM, N_ITEMS };
static void
foreachProc( GtkTreeModel* model, GtkTreePath* XP_UNUSED(path),
@ -111,7 +110,7 @@ row_activated_cb( GtkTreeView* tree_view, GtkTreePath* path,
if ( gtk_tree_model_get_iter( model, &iter, path ) ) {
sqlite3_int64 rowid;
gtk_tree_model_get( model, &iter, ROW_ITEM, &rowid, -1 );
open_row( apg, rowid );
open_row( apg, rowid, XP_FALSE );
}
}
@ -158,6 +157,7 @@ init_games_list( GtkAppGlobals* apg )
addTextColumn( list, "Ended", OVER_ITEM );
addTextColumn( list, "Turn", TURN_ITEM );
addTextColumn( list, "NMoves", NMOVES_ITEM );
addTextColumn( list, "NTotal", NTOTAL_ITEM );
addTextColumn( list, "NMissing", MISSING_ITEM );
GtkListStore* store = gtk_list_store_new( N_ITEMS,
@ -170,6 +170,7 @@ init_games_list( GtkAppGlobals* apg )
G_TYPE_BOOLEAN, /* OVER_ITEM */
G_TYPE_INT, /* TURN_ITEM */
G_TYPE_INT, /* NMOVES_ITEM */
G_TYPE_INT, /* NTOTAL_ITEM */
G_TYPE_INT /* MISSING_ITEM */
);
gtk_tree_view_set_model( GTK_TREE_VIEW(list), GTK_TREE_MODEL(store) );
@ -217,6 +218,7 @@ add_to_list( GtkWidget* list, sqlite3_int64 rowid, XP_Bool isNew,
OVER_ITEM, gib->gameOver,
TURN_ITEM, gib->turn,
NMOVES_ITEM, gib->nMoves,
NTOTAL_ITEM, gib->nTotal,
MISSING_ITEM, gib->nMissing,
-1 );
XP_LOGF( "DONE adding" );
@ -226,7 +228,8 @@ static void updateButtons( GtkAppGlobals* apg )
{
guint count = apg->selRows->len;
gtk_widget_set_sensitive( apg->openButton, 1 == count );
gtk_widget_set_sensitive( apg->openButton, 1 <= count );
gtk_widget_set_sensitive( apg->rematchButton, 1 == count );
gtk_widget_set_sensitive( apg->deleteButton, 1 <= count );
}
@ -249,10 +252,14 @@ handle_newgame_button( GtkWidget* XP_UNUSED(widget), void* closure )
}
}
static void
open_row( GtkAppGlobals* apg, sqlite3_int64 row )
void
open_row( GtkAppGlobals* apg, sqlite3_int64 row, XP_Bool isNew )
{
if ( -1 != row && !gameIsOpen( apg, row ) ) {
if ( isNew ) {
onNewData( apg, row, XP_TRUE );
}
apg->params->needsNewGame = XP_FALSE;
GtkGameGlobals* globals = malloc( sizeof(*globals) );
initGlobals( globals, apg->params, NULL );
@ -267,8 +274,82 @@ static void
handle_open_button( GtkWidget* XP_UNUSED(widget), void* closure )
{
GtkAppGlobals* apg = (GtkAppGlobals*)closure;
sqlite3_int64 selRow = getSelRow( apg );
open_row( apg, selRow );
GArray* selRows = apg->selRows;
for ( int ii = 0; ii < selRows->len; ++ii ) {
sqlite3_int64 row = g_array_index( selRows, sqlite3_int64, ii );
open_row( apg, row, XP_FALSE );
}
}
void
make_rematch( GtkAppGlobals* apg, const CommonGlobals* cGlobals )
{
// LaunchParams* params = apg->params;
XWStreamCtxt* stream = mem_stream_make( MPPARM(cGlobals->util->mpool)
cGlobals->params->vtMgr,
NULL, CHANNEL_NONE, NULL );
/* Create new game. But has no addressing info, so need to set that
aside for later. */
CurGameInfo gi = {0};
gi_copy( MPPARM(cGlobals->util->mpool) &gi, cGlobals->gi );
gi.gameID = 0; /* clear so will get generated */
game_saveNewGame( MPPARM(cGlobals->util->mpool) &gi,
cGlobals->util, &cGlobals->cp, stream );
sqlite3_int64 rowID = writeNewGameToDB( stream, cGlobals->pDb );
stream_destroy( stream );
gi_disposePlayerInfo( MPPARM(cGlobals->util->mpool) &gi );
/* If it's a multi-device game, save enough information with it than when
opened it can invite the other device[s] join the rematch. */
const CommsCtxt* comms = cGlobals->game.comms;
if ( !!comms ) {
XWStreamCtxt* stream = mem_stream_make( MPPARM(cGlobals->util->mpool)
cGlobals->params->vtMgr,
NULL, CHANNEL_NONE, NULL );
CommsAddrRec addr;
comms_getAddr( comms, &addr );
addrToStream( stream, &addr );
CommsAddrRec addrs[4];
XP_U16 nRecs = VSIZE(addrs);
comms_getAddrs( comms, addrs, &nRecs );
stream_putU8( stream, nRecs );
for ( int ii = 0; ii < nRecs; ++ii ) {
XP_UCHAR relayID[32];
XP_U16 len = sizeof(relayID);
comms_formatRelayID( comms, ii, relayID, &len );
XP_LOGF( "%s: adding relayID: %s", __func__, relayID );
stringToStream( stream, relayID );
if ( addr_hasType( &addrs[ii], COMMS_CONN_RELAY ) ) {
/* copy over room name */
XP_STRCAT( addrs[ii].u.ip_relay.invite, addr.u.ip_relay.invite );
}
addrToStream( stream, &addrs[ii] );
}
saveInviteAddrs( stream, cGlobals->pDb, rowID );
stream_destroy( stream );
}
open_row( apg, rowID, XP_TRUE );
}
static void
handle_rematch_button( GtkWidget* XP_UNUSED(widget), void* closure )
{
GtkAppGlobals* apg = (GtkAppGlobals*)closure;
GArray* selRows = apg->selRows;
for ( int ii = 0; ii < selRows->len; ++ii ) {
sqlite3_int64 rowid = g_array_index( selRows, sqlite3_int64, ii );
GtkGameGlobals tmpGlobals;
if ( loadGameNoDraw( &tmpGlobals, apg->params, rowid ) ) {
make_rematch( apg, &tmpGlobals.cGlobals );
}
freeGlobals( &tmpGlobals );
}
}
static void
@ -396,6 +477,8 @@ makeGamesWindow( GtkAppGlobals* apg )
(void)addButton( "New game", hbox, G_CALLBACK(handle_newgame_button), apg );
apg->openButton = addButton( "Open", hbox,
G_CALLBACK(handle_open_button), apg );
apg->rematchButton = addButton( "Rematch", hbox,
G_CALLBACK(handle_rematch_button), apg );
apg->deleteButton = addButton( "Delete", hbox,
G_CALLBACK(handle_delete_button), apg );
(void)addButton( "Quit", hbox, G_CALLBACK(handle_quit_button), apg );
@ -479,29 +562,38 @@ relayInviteReceived( void* closure, InviteInfo* invite )
GtkAppGlobals* apg = (GtkAppGlobals*)closure;
LaunchParams* params = apg->params;
CurGameInfo gi = {0};
gi_copy( MPPARM(params->mpool) &gi, &params->pgi );
XP_U32 gameID = invite->gameID;
sqlite3_int64 rowids[1];
int nRowIDs = VSIZE(rowids);
getRowsForGameID( apg->params->pDb, gameID, rowids, &nRowIDs );
if ( 0 < nRowIDs ) {
gtktell( apg->window, "Duplicate invite rejected" );
} else {
CurGameInfo gi = {0};
gi_copy( MPPARM(params->mpool) &gi, &params->pgi );
gi_setNPlayers( &gi, invite->nPlayersT, invite->nPlayersH );
gi.gameID = invite->gameID;
gi.dictLang = invite->lang;
gi.forceChannel = invite->forceChannel;
replaceStringIfDifferent( params->mpool, &gi.dictName, invite->dict );
gi_setNPlayers( &gi, invite->nPlayersT, invite->nPlayersH );
gi.gameID = gameID;
gi.dictLang = invite->lang;
gi.forceChannel = invite->forceChannel;
replaceStringIfDifferent( params->mpool, &gi.dictName, invite->dict );
GtkGameGlobals* globals = malloc( sizeof(*globals) );
params->needsNewGame = XP_FALSE;
initGlobals( globals, params, &gi );
GtkGameGlobals* globals = malloc( sizeof(*globals) );
params->needsNewGame = XP_FALSE;
initGlobals( globals, params, &gi );
invit_makeAddrRec( invite, &globals->cGlobals.addr );
// globals->cGlobals.addr = *returnAddr;
invit_makeAddrRec( invite, &globals->cGlobals.addr );
// globals->cGlobals.addr = *returnAddr;
GtkWidget* gameWindow = globals->window;
globals->cGlobals.pDb = apg->params->pDb;
globals->cGlobals.selRow = -1;
recordOpened( apg, globals );
gtk_widget_show( gameWindow );
GtkWidget* gameWindow = globals->window;
globals->cGlobals.pDb = apg->params->pDb;
globals->cGlobals.selRow = -1;
recordOpened( apg, globals );
gtk_widget_show( gameWindow );
gi_disposePlayerInfo( MPPARM(params->mpool) &gi );
gi_disposePlayerInfo( MPPARM(params->mpool) &gi );
}
}
static void
@ -646,17 +738,6 @@ onGameSaved( void* closure, sqlite3_int64 rowid,
}
}
sqlite3_int64
getSelRow( const GtkAppGlobals* apg )
{
sqlite3_int64 result = -1;
guint len = apg->selRows->len;
if ( 1 == len ) {
result = g_array_index( apg->selRows, sqlite3_int64, 0 );
}
return result;
}
static GtkAppGlobals* g_globals_for_signal = NULL;
static void

View file

@ -26,5 +26,7 @@
int gtkmain( LaunchParams* params );
void windowDestroyed( GtkGameGlobals* globals );
void onGameSaved( void* closure, sqlite3_int64 rowid, XP_Bool firstTime );
void open_row( GtkAppGlobals* apg, sqlite3_int64 row, XP_Bool isNew );
void make_rematch( GtkAppGlobals* apg, const CommonGlobals* cGlobals );
#endif

View file

@ -918,6 +918,10 @@ linux_getDevID( LaunchParams* params, DevIDType* typ )
if ( !!params->lDevID ) {
result = params->lDevID;
*typ = ID_TYPE_LINUX;
} else if ( db_fetch( params->pDb, KEY_RDEVID, params->devIDStore,
sizeof(params->devIDStore) ) ) {
result = params->devIDStore;
*typ = '\0' == result[0] ? ID_TYPE_ANON : ID_TYPE_RELAY;
} else if ( db_fetch( params->pDb, KEY_LDEVID, params->devIDStore,
sizeof(params->devIDStore) ) ) {
result = params->devIDStore;

View file

@ -247,9 +247,8 @@ typedef struct _GtkAppGlobals {
GtkWidget* window;
GtkWidget* listWidget;
GtkWidget* openButton;
GtkWidget* rematchButton;
GtkWidget* deleteButton;
} GtkAppGlobals;
sqlite3_int64 getSelRow( const GtkAppGlobals* apg );
#endif

View file

@ -48,14 +48,14 @@ static ssize_t sendIt( RelayConStorage* storage, const XP_U8* msgbuf, XP_U16 len
static size_t addVLIStr( XP_U8* buf, size_t len, const XP_UCHAR* str );
static void getNetString( const XP_U8** ptr, XP_U16 len, XP_UCHAR* buf );
static XP_U16 getNetShort( const XP_U8** ptr );
#ifdef DEBUG
static XP_U32 getNetLong( const XP_U8** ptr );
#endif
static int writeHeader( RelayConStorage* storage, XP_U8* dest, XWRelayReg cmd );
static bool readHeader( const XP_U8** buf, MsgHeader* header );
static size_t writeDevID( XP_U8* buf, size_t len, const XP_UCHAR* str );
static size_t writeShort( XP_U8* buf, size_t len, XP_U16 shrt );
static size_t writeLong( XP_U8* buf, size_t len, XP_U32 lng );
static size_t writeBytes( XP_U8* buf, size_t len, const XP_U8* bytes,
size_t nBytes );
static size_t writeVLI( XP_U8* out, uint32_t nn );
static size_t un2vli( int nn, uint8_t* buf );
static bool vli2un( const uint8_t** inp, uint32_t* outp );
@ -113,7 +113,8 @@ relaycon_reg( LaunchParams* params, const XP_UCHAR* rDevID,
}
void
relaycon_invite( LaunchParams* params, XP_U32 dest, InviteInfo* invit )
relaycon_invite( LaunchParams* params, XP_U32 destDevID,
const XP_UCHAR* relayID, InviteInfo* invit )
{
XP_U8 tmpbuf[256];
int indx = 0;
@ -122,7 +123,17 @@ relaycon_invite( LaunchParams* params, XP_U32 dest, InviteInfo* invit )
indx += writeHeader( storage, tmpbuf, XWPDEV_INVITE );
XP_U32 me = linux_getDevIDRelay( params );
indx += writeLong( &tmpbuf[indx], sizeof(tmpbuf) - indx, me );
indx += writeLong( &tmpbuf[indx], sizeof(tmpbuf) - indx, dest );
/* write relayID <connname>/<hid>, or if we have an actual devID write a
null byte plus it. */
if ( 0 == destDevID ) {
XP_ASSERT( '\0' != relayID[0] );
indx += writeBytes( &tmpbuf[indx], sizeof(tmpbuf) - indx,
(XP_U8*)relayID, 1 + XP_STRLEN( relayID ) );
} else {
tmpbuf[indx++] = '\0'; /* null byte: zero-len str */
indx += writeLong( &tmpbuf[indx], sizeof(tmpbuf) - indx, destDevID );
}
XWStreamCtxt* stream = mem_stream_make( MPPARM(params->mpool)
params->vtMgr, params,
@ -132,8 +143,7 @@ relaycon_invite( LaunchParams* params, XP_U32 dest, InviteInfo* invit )
indx += writeShort( &tmpbuf[indx], sizeof(tmpbuf) - indx, len );
XP_ASSERT( indx + len < sizeof(tmpbuf) );
const XP_U8* ptr = stream_getPtr( stream );
XP_MEMCPY( &tmpbuf[indx], ptr, len );
indx += len;
indx += writeBytes( &tmpbuf[indx], sizeof(tmpbuf) - indx, ptr, len );
stream_destroy( stream );
sendIt( storage, tmpbuf, indx );
@ -151,11 +161,8 @@ relaycon_send( LaunchParams* params, const XP_U8* buf, XP_U16 buflen,
XP_U8 tmpbuf[1 + 4 + 1 + sizeof(gameToken) + buflen];
int indx = 0;
indx += writeHeader( storage, tmpbuf, XWPDEV_MSG );
XP_U32 inNBO = htonl(gameToken);
XP_MEMCPY( &tmpbuf[indx], &inNBO, sizeof(inNBO) );
indx += sizeof(inNBO);
XP_MEMCPY( &tmpbuf[indx], buf, buflen );
indx += buflen;
indx += writeLong( &tmpbuf[indx], sizeof(tmpbuf) - indx, gameToken );
indx += writeBytes( &tmpbuf[indx], sizeof(tmpbuf) - indx, buf, buflen );
nSent = sendIt( storage, tmpbuf, indx );
if ( nSent > buflen ) {
nSent = buflen;
@ -179,14 +186,12 @@ relaycon_sendnoconn( LaunchParams* params, const XP_U8* buf, XP_U16 buflen,
1 + idLen +
sizeof(gameToken) + buflen];
indx += writeHeader( storage, tmpbuf, XWPDEV_MSGNOCONN );
gameToken = htonl( gameToken );
XP_MEMCPY( &tmpbuf[indx], &gameToken, sizeof(gameToken) );
indx += sizeof(gameToken);
XP_MEMCPY( &tmpbuf[indx], relayID, idLen );
indx += idLen;
indx += writeLong( &tmpbuf[indx], sizeof(tmpbuf) - indx, gameToken );
indx += writeBytes( &tmpbuf[indx], sizeof(tmpbuf) - indx,
(const XP_U8*)relayID, idLen );
tmpbuf[indx++] = '\n';
XP_MEMCPY( &tmpbuf[indx], buf, buflen );
nSent = sendIt( storage, tmpbuf, sizeof(tmpbuf) );
indx += writeBytes( &tmpbuf[indx], sizeof(tmpbuf) - indx, buf, buflen );
nSent = sendIt( storage, tmpbuf, indx );
if ( nSent > buflen ) {
nSent = buflen;
}
@ -218,9 +223,7 @@ relaycon_deleted( LaunchParams* params, const XP_UCHAR* devID,
int indx = 0;
indx += writeHeader( storage, tmpbuf, XWPDEV_DELGAME );
indx += writeDevID( &tmpbuf[indx], sizeof(tmpbuf) - indx, devID );
gameToken = htonl( gameToken );
memcpy( &tmpbuf[indx], &gameToken, sizeof(gameToken) );
indx += sizeof( gameToken );
indx += writeLong( &tmpbuf[indx], sizeof(tmpbuf) - indx, gameToken );
sendIt( storage, tmpbuf, indx );
}
@ -330,7 +333,10 @@ relaycon_receive( GIOChannel* source, GIOCondition XP_UNUSED_DBG(condition), gpo
}
case XWPDEV_GOTINVITE: {
XP_LOGF( "%s(): got XWPDEV_GOTINVITE", __func__ );
XP_U32 sender = getNetLong( &ptr );
#ifdef DEBUG
XP_U32 sender =
#endif
getNetLong( &ptr );
XP_U16 len = getNetShort( &ptr );
XWStreamCtxt* stream = mem_stream_make( MPPARM(storage->params->mpool)
storage->params->vtMgr, storage,
@ -438,7 +444,7 @@ writeShort( XP_U8* buf, size_t len, XP_U16 shrt )
{
shrt = htons( shrt );
assert( sizeof( shrt ) <= len );
memcpy( buf, &shrt, sizeof(shrt) );
XP_MEMCPY( buf, &shrt, sizeof(shrt) );
return sizeof(shrt);
}
@ -451,6 +457,14 @@ writeLong( XP_U8* buf, size_t len, XP_U32 lng )
return sizeof(lng);
}
static size_t
writeBytes( XP_U8* buf, size_t len, const XP_U8* bytes, size_t nBytes )
{
assert( nBytes <= len );
XP_MEMCPY( buf, bytes, nBytes );
return nBytes;
}
static size_t
writeVLI( XP_U8* out, uint32_t nn )
{
@ -469,7 +483,6 @@ getNetShort( const XP_U8** ptr )
return ntohs( result );
}
#ifdef DEBUG
static XP_U32
getNetLong( const XP_U8** ptr )
{
@ -478,7 +491,6 @@ getNetLong( const XP_U8** ptr )
*ptr += sizeof(result);
return ntohl( result );
}
#endif
static void
getNetString( const XP_U8** ptr, XP_U16 len, XP_UCHAR* buf )

View file

@ -39,7 +39,10 @@ void relaycon_init( LaunchParams* params, const RelayConnProcs* procs,
void* procsClosure, const char* host, int port );
void relaycon_reg( LaunchParams* params, const XP_UCHAR* rDevID,
DevIDType typ, const XP_UCHAR* devID );
void relaycon_invite( LaunchParams* params, XP_U32 dest, InviteInfo* invite );
/* Need one of dest or relayID, with dest preferred. pass 0 for dest to use
relayID (formatted per comms::formatRelayID()) */
void relaycon_invite( LaunchParams* params, XP_U32 dest,
const XP_UCHAR* relayID, InviteInfo* invite );
XP_S16 relaycon_send( LaunchParams* params, const XP_U8* buf, XP_U16 buflen,
XP_U32 gameToken, const CommsAddrRec* addrRec );
XP_S16 relaycon_sendnoconn( LaunchParams* params, const XP_U8* buf,

View file

@ -873,6 +873,19 @@ DBMgr::readArray( const char* const connName, const char* column, int arr[] ) /
PQclear( result );
}
// parse something created by comms.c's formatRelayID
DevIDRelay
DBMgr::getDevID( string& relayID )
{
size_t pos = relayID.find_first_of( '/' );
string connName = relayID.substr( 0, pos );
int hid = relayID[pos + 1] - '0';
DevIDRelay result = getDevID( connName.c_str(), hid );
// Not an error. Remove or downlog when confirm working
logf( XW_LOGERROR, "%s(%s) => %d", __func__, relayID.c_str(), result );
return result;
}
DevIDRelay
DBMgr::getDevID( const char* connName, int hid )
{

View file

@ -146,6 +146,8 @@ class DBMgr {
void RemoveStoredMessage( const int msgID );
void RemoveStoredMessages( vector<int>& ids );
DevIDRelay getDevID( string& relayID );
private:
DBMgr();
bool execSql( const string& query );

View file

@ -1779,17 +1779,24 @@ handle_udp_packet( UdpThreadClosure* utc )
break;
}
case XWPDEV_INVITE:
case XWPDEV_INVITE: {
DevIDRelay sender;
DevIDRelay invitee;
if ( getNetLong( &ptr, end, &sender )
&& getNetLong( &ptr, end, &invitee) ) {
string relayID;
if ( getNetLong( &ptr, end, &sender )
&& getNetString( &ptr, end, relayID ) ) {
DevIDRelay invitee;
if ( 0 < relayID.size() ) {
invitee = DBMgr::Get()->getDevID( relayID );
} else if ( !getNetLong( &ptr, end, &invitee ) ) {
break; // failure
}
logf( XW_LOGVERBOSE0, "got invite from %d for %d",
sender, invitee );
post_invite( sender, invitee, ptr, end - ptr );
}
break;
}
case XWPDEV_KEEPALIVE:
case XWPDEV_RQSTMSGS: {
DevID devID( ID_TYPE_RELAY );