address bug where message was getting ACK'd by a device that failed to

save what it had ACK'd leaving the game permanently broken.  Do that
by adding a new method game_saveSucceeded() called after the client
claims to have committed bytes returned by game_writeToStream() to
disk.  In that method comms updates the value it'll use in subseqent
ACKs.
This commit is contained in:
Eric House 2012-09-10 07:31:45 -07:00
parent 45de62818b
commit 0ccc2331b5
12 changed files with 97 additions and 43 deletions

View file

@ -211,7 +211,7 @@ Java_org_eehouse_android_xw4_jni_XwJNI_gi_1to_1stream
XWStreamCtxt* stream = mem_stream_make( MPPARM(mpool) vtMgr,
NULL, 0, NULL );
game_saveToStream( NULL, gi, stream );
game_saveToStream( NULL, gi, stream, 0 );
destroyGI( MPPARM(mpool) &gi );
int nBytes = stream_getSize( stream );
@ -344,6 +344,7 @@ typedef struct _JNIState {
XWGame game;
JNIEnv* env;
AndGlobals globals;
XP_U16 curSaveCount;
#ifdef DEBUG
const char* envSetterFunc;
#endif
@ -536,7 +537,7 @@ Java_org_eehouse_android_xw4_jni_XwJNI_game_1saveToStream
XWStreamCtxt* stream = mem_stream_make( MPPARM(mpool) globals->vtMgr,
NULL, 0, NULL );
game_saveToStream( &state->game, gi, stream );
game_saveToStream( &state->game, gi, stream, ++state->curSaveCount );
if ( NULL != jgi ) {
destroyGI( MPPARM(mpool) &gi );
@ -553,6 +554,15 @@ Java_org_eehouse_android_xw4_jni_XwJNI_game_1saveToStream
return result;
}
JNIEXPORT void JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_game_1saveSucceeded
( JNIEnv* env, jclass C, jint gamePtr )
{
XWJNI_START();
game_saveSucceeded( &state->game, state->curSaveCount );
XWJNI_END();
}
JNIEXPORT void JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_board_1invalAll
( JNIEnv *env, jclass C, jint gamePtr )

View file

@ -277,6 +277,8 @@ public class JNIThread extends Thread {
byte[] state = XwJNI.game_saveToStream( m_jniGamePtr, null );
GameUtils.saveGame( m_context, state, m_lock, false );
DBUtils.saveSummary( m_context, m_lock, summary );
// There'd better be no way for saveGame above to fail!
XwJNI.game_saveSucceeded( m_jniGamePtr );
}
@SuppressWarnings("fallthrough")
@ -357,7 +359,6 @@ public class JNIThread extends Thread {
(byte[])args[0],
(CommsAddrRec)args[1]);
handle( JNICmd.CMD_DO );
handle( JNICmd.CMD_ACKANY );
if ( draw ) {
handle( JNICmd.CMD_SAVE );
}

View file

@ -127,6 +127,7 @@ public class XwJNI {
public static native void game_summarize( int gamePtr, GameSummary summary );
public static native byte[] game_saveToStream( int gamePtr,
CurGameInfo gi );
public static native void game_saveSucceeded( int gamePtr );
public static native void game_getGi( int gamePtr, CurGameInfo gi );
public static native void game_getState( int gamePtr,
JNIThread.GameStateInfo gsi );

View file

@ -66,8 +66,15 @@ typedef struct AddressRecord {
struct AddressRecord* next;
CommsAddrRec addr;
MsgID nextMsgID; /* on a per-channel basis */
MsgID lastMsgRcd; /* on a per-channel basis */
MsgID lastMsgAckd; /* on a per-channel basis */
/* lastMsgRcd is the numerically highest MsgID we've seen. Because once
* it's sent in message as an ACK the other side will delete messages
* based on it, we don't send a number higher than has actually been
* written out successfully. lastMsgSaved is that number.
*/
MsgID lastMsgRcd;
MsgID lastMsgSaved;
/* only used if COMMS_HEARTBEAT set except for serialization (to_stream) */
XP_PlayerAddr channelNo;
struct {
@ -109,6 +116,7 @@ struct CommsCtxt {
XP_Bool hbTimerPending;
XP_Bool reconTimerPending;
#endif
XP_U16 lastSaveToken;
/* The following fields, down to isServer, are only used if
XWFEATURE_RELAY is defined, but I'm leaving them in here so apps built
@ -575,7 +583,7 @@ comms_makeFromStream( MPFORMAL XWStreamCtxt* stream, XW_UtilCtxt* util,
addrFromStream( &rec->addr, stream );
rec->nextMsgID = stream_getU16( stream );
rec->lastMsgRcd = stream_getU16( stream );
rec->lastMsgSaved = rec->lastMsgRcd = stream_getU16( stream );
if ( version >= STREAM_VERS_BLUETOOTH2 ) {
rec->lastMsgAckd = stream_getU16( stream );
}
@ -713,7 +721,8 @@ addrToStream( XWStreamCtxt* stream, const CommsAddrRec* addrP )
} /* addrToStream */
void
comms_writeToStream( const CommsCtxt* comms, XWStreamCtxt* stream )
comms_writeToStream( CommsCtxt* comms, XWStreamCtxt* stream,
XP_U16 saveToken )
{
XP_U16 nAddrRecs;
AddressRecord* rec;
@ -762,8 +771,26 @@ comms_writeToStream( const CommsCtxt* comms, XWStreamCtxt* stream )
stream_putBytes( stream, msg->msg, msg->len );
}
comms->lastSaveToken = saveToken;
} /* comms_writeToStream */
void
comms_saveSucceeded( CommsCtxt* comms, XP_U16 saveToken )
{
XP_LOGF( "%s(saveToken=%d)", __func__, saveToken );
XP_ASSERT( !!comms );
if ( saveToken == comms->lastSaveToken ) {
XP_LOGF( "%s: lastSave matches", __func__ );
AddressRecord* rec;
for ( rec = comms->recs; !!rec; rec = rec->next ) {
rec->lastMsgSaved = rec->lastMsgRcd;
}
#ifdef XWFEATURE_COMMSACK
comms_ackAny( comms ); /* might not want this for all transports */
#endif
}
}
void
comms_getAddr( const CommsCtxt* comms, CommsAddrRec* addr )
{
@ -902,7 +929,7 @@ makeElemWithID( CommsCtxt* comms, MsgID msgID, AddressRecord* rec,
{
XP_U16 headerLen;
XP_U16 streamSize = NULL == stream? 0 : stream_getSize( stream );
MsgID lastMsgRcd = (!!rec)? rec->lastMsgRcd : 0;
MsgID lastMsgSaved = (!!rec)? rec->lastMsgSaved : 0;
MsgQueueElem* newMsgElem;
XWStreamCtxt* msgStream;
@ -924,10 +951,10 @@ makeElemWithID( CommsCtxt* comms, MsgID msgID, AddressRecord* rec,
stream_putU16( msgStream, channelNo );
stream_putU32( msgStream, msgID );
XP_LOGF( "put lastMsgRcd: %ld", lastMsgRcd );
stream_putU32( msgStream, lastMsgRcd );
XP_LOGF( "put lastMsgSaved: %ld", lastMsgSaved );
stream_putU32( msgStream, lastMsgSaved );
if ( !!rec ) {
rec->lastMsgAckd = lastMsgRcd;
rec->lastMsgAckd = lastMsgSaved;
}
headerLen = stream_getSize( msgStream );
@ -1734,6 +1761,7 @@ comms_checkIncomingStream( CommsCtxt* comms, XWStreamCtxt* stream,
XP_LOGF( "%s: got channelNo=%d;msgID=%ld;len=%d", __func__,
channelNo & CHANNEL_MASK, msgID, payloadSize );
rec->lastMsgRcd = msgID;
comms->lastSaveToken = 0; /* lastMsgRcd no longer valid */
stream_setAddress( stream, channelNo );
messageValid = payloadSize > 0;
}

View file

@ -198,7 +198,9 @@ CommsCtxt* comms_makeFromStream( MPFORMAL XWStreamCtxt* stream,
XW_UtilCtxt* util,
const TransportProcs* procs );
void comms_start( CommsCtxt* comms );
void comms_writeToStream( const CommsCtxt* comms, XWStreamCtxt* stream );
void comms_writeToStream( CommsCtxt* comms, XWStreamCtxt* stream,
XP_U16 saveToken );
void comms_saveSucceeded( CommsCtxt* comms, XP_U16 saveToken );
XP_S16 comms_send( CommsCtxt* comms, XWStreamCtxt* stream );
XP_Bool comms_resendAll( CommsCtxt* comms );

View file

@ -259,8 +259,9 @@ game_makeFromStream( MPFORMAL XWStreamCtxt* stream, XWGame* game,
void
game_saveToStream( const XWGame* game, const CurGameInfo* gi,
XWStreamCtxt* stream )
XWStreamCtxt* stream, XP_U16 saveToken )
{
XP_ASSERT( 0 != saveToken );
stream_putU8( stream, CUR_STREAM_VERS );
stream_setVersion( stream, CUR_STREAM_VERS );
@ -272,7 +273,7 @@ game_saveToStream( const XWGame* game, const CurGameInfo* gi,
XP_ASSERT( !game->comms );
#endif
if ( !!game->comms ) {
comms_writeToStream( game->comms, stream );
comms_writeToStream( game->comms, stream, saveToken );
}
model_writeToStream( game->model, stream );
@ -281,6 +282,14 @@ game_saveToStream( const XWGame* game, const CurGameInfo* gi,
}
} /* game_saveToStream */
void
game_saveSucceeded( const XWGame* game, XP_U16 saveToken )
{
if ( !!game->comms ) {
comms_saveSucceeded( game->comms, saveToken );
}
}
void
game_getState( const XWGame* game, GameStateInfo* gsi )
{

View file

@ -103,7 +103,8 @@ XP_Bool game_makeFromStream( MPFORMAL XWStreamCtxt* stream, XWGame* game,
const TransportProcs* procs );
void game_saveToStream( const XWGame* game, const CurGameInfo* gi,
XWStreamCtxt* stream );
XWStreamCtxt* stream, XP_U16 saveToken );
void game_saveSucceeded( const XWGame* game, XP_U16 saveToken );
void game_dispose( XWGame* game );
void game_getState( const XWGame* game, GameStateInfo* gsi );

View file

@ -1893,22 +1893,7 @@ cursesmain( XP_Bool isServer, LaunchParams* params )
}
#endif
}
if ( !!g_globals.cGlobals.params->fileName ) {
XWStreamCtxt* outStream;
outStream =
mem_stream_make( MPPARM(g_globals.cGlobals.params->util->mpool)
g_globals.cGlobals.params->vtMgr,
&g_globals.cGlobals, 0, writeToFile );
stream_open( outStream );
game_saveToStream( &g_globals.cGlobals.game,
&g_globals.cGlobals.params->gi,
outStream );
stream_destroy( outStream );
sync();
}
saveGame( &g_globals.cGlobals );
game_dispose( &g_globals.cGlobals.game ); /* takes care of the dict */
gi_disposePlayerInfo( MEMPOOL &g_globals.cGlobals.params->gi );

View file

@ -680,19 +680,7 @@ quit( void )
static void
cleanup( GtkAppGlobals* globals )
{
if ( !!globals->cGlobals.params->fileName ) {
XWStreamCtxt* outStream;
outStream = mem_stream_make( MEMPOOL globals->cGlobals.params->vtMgr,
globals, 0, writeToFile );
stream_open( outStream );
game_saveToStream( &globals->cGlobals.game,
&globals->cGlobals.params->gi,
outStream );
stream_destroy( outStream );
}
saveGame( &globals->cGlobals );
game_dispose( &globals->cGlobals.game ); /* takes care of the dict */
gi_disposePlayerInfo( MEMPOOL &globals->cGlobals.params->gi );
@ -2088,6 +2076,9 @@ newConnectionInput( GIOChannel *source,
redraw =
server_receiveMessage(globals->cGlobals.game.server,
inboundS );
if ( redraw ) {
saveGame( &globals->cGlobals );
}
}
stream_destroy( inboundS );
}

View file

@ -175,6 +175,8 @@ writeToFile( XWStreamCtxt* stream, void* closure )
}
free( buf );
game_saveSucceeded( &cGlobals->game, cGlobals->curSaveToken );
} /* writeToFile */
void
@ -233,6 +235,27 @@ strFromStream( XWStreamCtxt* stream )
return buf;
} /* strFromStream */
void
saveGame( CommonGlobals* cGlobals )
{
if ( !!cGlobals->params->fileName ) {
XWStreamCtxt* outStream;
outStream = mem_stream_make( cGlobals->params->util->mpool,
cGlobals->params->vtMgr,
cGlobals, 0, writeToFile );
stream_open( outStream );
game_saveToStream( &cGlobals->game,
&cGlobals->params->gi,
outStream, ++cGlobals->curSaveToken );
stream_destroy( outStream );
game_saveSucceeded( &cGlobals->game, cGlobals->curSaveToken );
}
}
static void
handle_messages_from( CommonGlobals* cGlobals, const TransportProcs* procs,
int fdin )

View file

@ -66,6 +66,7 @@ XWStreamCtxt* streamFromFile( CommonGlobals* cGlobals, char* name,
void* closure );
XWStreamCtxt* streamFromDB( CommonGlobals* cGlobals, void* closure );
void writeToFile( XWStreamCtxt* stream, void* closure );
void saveGame( CommonGlobals* cGlobals );
int blocking_read( int fd, unsigned char* buf, int len );

View file

@ -193,6 +193,8 @@ struct CommonGlobals {
#endif
TimerInfo timerInfo[NUM_TIMERS_PLUS_ONE];
XP_U16 curSaveToken;
};
#endif