mirror of
git://xwords.git.sourceforge.net/gitroot/xwords/xwords
synced 2025-01-15 15:41:24 +01:00
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:
parent
45de62818b
commit
0ccc2331b5
12 changed files with 97 additions and 43 deletions
|
@ -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 )
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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 )
|
||||
{
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
|
|
|
@ -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 )
|
||||
|
|
|
@ -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 );
|
||||
|
||||
|
|
|
@ -193,6 +193,8 @@ struct CommonGlobals {
|
|||
#endif
|
||||
|
||||
TimerInfo timerInfo[NUM_TIMERS_PLUS_ONE];
|
||||
|
||||
XP_U16 curSaveToken;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in a new issue