From 259357a8180c58bf991e452de4e263c35d262e13 Mon Sep 17 00:00:00 2001 From: Eric House Date: Sun, 5 Nov 2023 11:52:48 -0800 Subject: [PATCH] reduce sending of dead-game mqtt messages Two cases dealt with here. First, if my opponent deletes the game when I have an un-ack'd message, I'll keep sending it forever. Fix is to flip a bit in comms in response to a game_gone event so no more sending will happen. (Better than emptying the queue, as it leaves open the possibility of resurrecting the game with code changes later.) Second, if there's a retained message from a dead game I'll keep receiving it until it's replaced, and if the game's dead it never will be. Fix is to add a new api endpoint noSuchGame() to the relay2 server and to call it on receiving a message for which I have no game to deliver it to. The endpoint "unretains" the message so I won't get it again unless it's resent. --- .../org/eehouse/android/xw4/GameUtils.java | 32 +++++++++++++++++++ .../org/eehouse/android/xw4/MQTTUtils.java | 24 ++++++++++++++ .../eehouse/android/xw4/jni/DUtilCtxt.java | 2 ++ .../org/eehouse/android/xw4/jni/XwJNI.java | 3 +- xwords4/android/jni/xwjni.c | 14 ++++++++ xwords4/common/comms.c | 31 +++++++++++++++--- xwords4/common/comms.h | 1 + 7 files changed, 102 insertions(+), 5 deletions(-) diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/GameUtils.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/GameUtils.java index ac227edca..7e6e0adb4 100644 --- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/GameUtils.java +++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/GameUtils.java @@ -1404,6 +1404,38 @@ public class GameUtils { body, rowid ); } + // PENDING This -- finding or opening game, doing something, then saving + // and closing if it was opened -- gets done a lot. Try refactoring. + public static void onGameGone( Context context, int gameID ) + { + long[] rowids = DBUtils.getRowIDsFor( context, gameID ); + if ( null == rowids || 0 == rowids.length ) { + Log.d( TAG, "onGameGone(): no rows for game %X", gameID ); + } else { + for ( long rowid: rowids ) { + try ( JNIThread thread = JNIThread.getRetained( rowid ) ) { + if ( null != thread ) { + XwJNI.comms_setQuashed( thread.getGamePtr() ); + // JNIThread saves automatically on release + } else { + try ( GameLock lock = GameLock.lock( rowid, 300 ) ) { + if ( null != lock ) { + CurGameInfo gi = new CurGameInfo( context ); + try ( GamePtr gamePtr = loadMakeGame( context, gi, lock ) ) { + if ( null != gamePtr ) { + if ( XwJNI.comms_setQuashed( gamePtr ) ) { + saveGame( context, gamePtr, gi, lock, false ); + } + } + } + } + } + } + } + } + } + } + private static void tellDied( Context context, GameLock lock, boolean informNow ) { diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/MQTTUtils.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/MQTTUtils.java index 75dceea56..2c0d88244 100644 --- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/MQTTUtils.java +++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/MQTTUtils.java @@ -562,11 +562,34 @@ public class MQTTUtils extends Thread clearInstance( this ); } + private static void postNoSuchGame( final Context context, final int gameID ) + { + // Run in thread in case we're called on main thread + new Thread( new Runnable() { + @Override + public void run() { + JSONObject params = new JSONObject(); + try { + String devid = XwJNI.dvc_getMQTTDevID(); + params.put( "devid", devid ); + params.put( "gameid", gameID ); + HttpsURLConnection conn + = NetUtils.makeHttpsMQTTConn( context, "noSuchGame" ); + /*(void*)*/NetUtils.runConn( conn, params, true ); + } catch ( JSONException jex ) { + Log.e( TAG, "notifyNotHere() ex: %s", jex ); + } + } + } ).start(); + } + private static void notifyNotHere( Context context, String addressee, int gameID ) { TopicsAndPackets tap = XwJNI.dvc_makeMQTTNoSuchGames( addressee, gameID ); addToSendQueue( context, tap ); + + postNoSuchGame( context, gameID ); } public static int send( Context context, TopicsAndPackets tap ) @@ -775,6 +798,7 @@ public class MQTTUtils extends Thread new MQTTServiceHelper( context, from ) .postEvent( MultiService.MultiEvent.MESSAGE_NOGAME, gameID, expl ); + postNoSuchGame( context, gameID ); } public static void fcmConfirmed( Context context, boolean working ) diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/DUtilCtxt.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/DUtilCtxt.java index e4a0abcd1..6c09c435f 100644 --- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/DUtilCtxt.java +++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/DUtilCtxt.java @@ -348,6 +348,8 @@ public class DUtilCtxt { public void onGameGoneReceived( int gameID, CommsAddrRec from ) { + GameUtils.onGameGone( m_context, gameID ); + Assert.assertTrueNR( from.contains( CommsAddrRec.CommsConnType.COMMS_CONN_MQTT ) ); MQTTUtils.handleGameGone( m_context, from, gameID ); } diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/XwJNI.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/XwJNI.java index 0dc095648..eb8f06ef8 100644 --- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/XwJNI.java +++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/XwJNI.java @@ -173,7 +173,7 @@ public class XwJNI { public static TopicsAndPackets dvc_makeMQTTNoSuchGames( String addressee, int gameID ) { Log.d( TAG, "dvc_makeMQTTNoSuchGames(to: %s, gameID: %X)", addressee, gameID ); - DbgUtils.printStack( TAG ); + // DbgUtils.printStack( TAG ); return dvc_makeMQTTNoSuchGames( getJNI().m_ptrGlobals, addressee, gameID ); } @@ -549,6 +549,7 @@ public class XwJNI { public static native CommsAddrRec comms_getHostAddr( GamePtr gamePtr ); public static native CommsAddrRec[] comms_getAddrs( GamePtr gamePtr ); public static native void comms_dropHostAddr( GamePtr gamePtr, CommsConnType typ ); + public static native boolean comms_setQuashed( GamePtr gamePtr ); public static native int comms_resendAll( GamePtr gamePtr, boolean force, CommsConnType filter, boolean andAck ); diff --git a/xwords4/android/jni/xwjni.c b/xwords4/android/jni/xwjni.c index ef32f4f8f..915fefdee 100644 --- a/xwords4/android/jni/xwjni.c +++ b/xwords4/android/jni/xwjni.c @@ -2664,6 +2664,20 @@ Java_org_eehouse_android_xw4_jni_XwJNI_comms_1dropHostAddr LOG_RETURN_VOID(); } +JNIEXPORT jboolean JNICALL +Java_org_eehouse_android_xw4_jni_XwJNI_comms_1setQuashed +( JNIEnv* env, jclass C, GamePtrType gamePtr ) +{ + jboolean result = false; + XWJNI_START(gamePtr); + XP_ASSERT( !!state->game.comms ); + if ( NULL != state->game.comms ) { + result = comms_setQuashed( state->game.comms, XP_TRUE ); + } + XWJNI_END(); + return result; +} + #ifdef DEBUG JNIEXPORT void JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_comms_1setAddrDisabled diff --git a/xwords4/common/comms.c b/xwords4/common/comms.c index ed212f33e..d0ab1e57a 100644 --- a/xwords4/common/comms.c +++ b/xwords4/common/comms.c @@ -238,6 +238,8 @@ struct CommsCtxt { }; #define FLAG_HARVEST_DONE 1 +#define FLAG_QUASHED 2 +#define QUASHED(COMMS) (0 != ((COMMS)->flags & FLAG_QUASHED)) #if defined XWFEATURE_IP_DIRECT || defined XWFEATURE_DIRECTIP typedef enum { @@ -2082,7 +2084,9 @@ sendMsg( const CommsCtxt* comms, XWEnv xwe, MsgQueueElem* elem, addrP = &comms->recs->addr; } } - if ( NULL == addrP ) { + if ( QUASHED(comms) ) { + // XP_LOGFF( "not sending; comms is quashed" ); + } else if ( NULL == addrP ) { XP_LOGFF( TAGFMT() "no addr for channel %x; dropping!'", TAGPRMS, channelNo ); // XP_ASSERT(0); /* firing */ } else { @@ -2235,7 +2239,9 @@ comms_resendAll( CommsCtxt* comms, XWEnv xwe, CommsConnType filter, XP_Bool forc XP_ASSERT( !!comms ); XP_U32 now = dutil_getCurSeconds( comms->dutil, xwe ); - if ( !force && (now < comms->nextResend) ) { + if ( QUASHED(comms) ) { + // XP_LOGFF( "not sending; comms is quashed" ); + } else if ( !force && (now < comms->nextResend) ) { XP_LOGFF( "aborting: %d seconds left in backoff", comms->nextResend - now ); } else { @@ -3256,6 +3262,23 @@ comms_isConnected( const CommsCtxt* const comms ) return result; } +XP_Bool +comms_setQuashed( CommsCtxt* comms, XP_Bool quashed ) +{ + XP_U8 flags = comms->flags; + if ( quashed ) { + flags |= FLAG_QUASHED; + } else { + flags &= ~FLAG_QUASHED; + } + XP_Bool changed = flags != comms->flags; + if ( changed ) { + comms->flags = flags; + XP_LOGFF( "(quashed=%s): changing state", boolToStr(quashed) ); + } + return changed; +} + #ifdef XWFEATURE_KNOWNPLAYERS void comms_gatherPlayers( CommsCtxt* comms, XWEnv xwe, XP_U32 created ) @@ -3459,9 +3482,9 @@ comms_getStats( const CommsCtxt* comms, XWStreamCtxt* stream ) XP_UCHAR buf[100]; XP_SNPRINTF( (XP_UCHAR*)buf, sizeof(buf), - (XP_UCHAR*)"role: %s; msg queue len: %d\n", + (XP_UCHAR*)"role: %s; msg queue len: %d; quashed: %s;\n", comms->isServer ? "host" : "guest", - comms->queueLen ); + comms->queueLen, boolToStr(QUASHED(comms)) ); stream_catString( stream, buf ); forEachElem( (CommsCtxt*)comms, statsProc, stream ); diff --git a/xwords4/common/comms.h b/xwords4/common/comms.h index 810cd7f0e..61582f268 100644 --- a/xwords4/common/comms.h +++ b/xwords4/common/comms.h @@ -244,6 +244,7 @@ XP_Bool comms_checkComplete( const CommsAddrRec* const addr ); XP_Bool comms_canChat( const CommsCtxt* comms ); XP_Bool comms_isConnected( const CommsCtxt* const comms ); +XP_Bool comms_setQuashed( CommsCtxt* comms, XP_Bool quashed ); #ifdef RELAY_VIA_HTTP void comms_gameJoined( CommsCtxt* comms, const XP_UCHAR* connname, XWHostID hid );