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.
This commit is contained in:
Eric House 2023-11-05 11:52:48 -08:00
parent 0528e5b5fd
commit 259357a818
7 changed files with 102 additions and 5 deletions

View file

@ -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 )
{

View file

@ -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 )

View file

@ -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 );
}

View file

@ -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 );

View file

@ -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

View file

@ -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 );

View file

@ -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 );