fix relay game failure to change roles

If a configured-as-host game joined an existing game the relay would
make it a guest. The android util_ callback for that change was only
implemented in BoardDelegate and so the change was dropped unless the
game was open/visible. Because comms recorded the change, though, the
callback would never be called again and so the game never learned to
behave as a guest and never registered: permanent failure to join game!
Implemented with a new server state so initClientConnection can be
called from server_do() instead of inside comms while processing an
incoming packet.
This commit is contained in:
Eric House 2020-08-25 14:47:56 -07:00
parent 15fde8044f
commit b0f7176b6c
14 changed files with 85 additions and 89 deletions

View file

@ -1828,22 +1828,6 @@ public class BoardDelegate extends DelegateBase
}
}
@Override
public void setIsServer( boolean isServer )
{
Log.d( TAG, "setIsServer(isServer=%b)", isServer );
DeviceRole newRole = isServer? DeviceRole.SERVER_ISSERVER
: DeviceRole.SERVER_ISCLIENT;
if ( newRole != m_gi.serverRole ) {
m_gi.serverRole = newRole;
if ( !isServer ) {
handleViaThread( JNICmd.CMD_SWITCHCLIENT );
}
} else {
Log.d( TAG, "setIsServer(): no change" );
}
}
@Override
public void bonusSquareHeld( int bonus )
{

View file

@ -60,7 +60,6 @@ public class JNIThread extends Thread implements AutoCloseable {
CMD_INVALALL,
CMD_LAYOUT,
CMD_START,
CMD_SWITCHCLIENT,
CMD_RESET,
CMD_SAVE,
CMD_DO,
@ -524,12 +523,6 @@ public class JNIThread extends Thread implements AutoCloseable {
draw = tryConnect( m_jniGamePtr, m_gi );
break;
case CMD_SWITCHCLIENT:
XwJNI.server_reset( m_jniGamePtr );
XwJNI.server_initClientConnection( m_jniGamePtr );
draw = XwJNI.server_do( m_jniGamePtr );
break;
case CMD_DO:
if ( nextSame( JNICmd.CMD_DO ) ) {
continue;

View file

@ -61,7 +61,6 @@ public interface UtilCtxt {
void requestTime();
void remSelected();
void timerSelected( boolean inDuplicateMode, boolean canPause );
void setIsServer( boolean isServer );
void informWordsBlocked( int nWords, String words, String dict );
void bonusSquareHeld( int bonus );

View file

@ -79,12 +79,14 @@ public class UtilCtxtImpl implements UtilCtxt {
@Override
public void setTimer( int why, int when, int handle )
{
Log.e( TAG, "setTimer(%d) not doing anything...", why );
subclassOverride( "setTimer" );
}
@Override
public void clearTimer( int why )
{
Log.e( TAG, "setTimer(%d) not doing anything...", why );
subclassOverride( "clearTimer" );
}
@ -100,13 +102,6 @@ public class UtilCtxtImpl implements UtilCtxt {
subclassOverride( "timerSelected" );
}
@Override
public void setIsServer( boolean isServer )
{
subclassOverride( "setIsServer" );
Assert.failDbg();
}
@Override
public void informWordsBlocked( int nWords, String words, String dict )
{

View file

@ -703,16 +703,6 @@ and_util_addrChange( XW_UtilCtxt* uc, XWEnv xwe,
// LOG_FUNC();
}
static void
and_util_setIsServer( XW_UtilCtxt* uc, XWEnv xwe, XP_Bool isServer )
{
/* Change both the C and Java structs, which need to stay in sync */
uc->gameInfo->serverRole = isServer? SERVER_ISSERVER : SERVER_ISCLIENT;
UTIL_CBK_HEADER( "setIsServer", "(Z)V" );
(*env)->CallVoidMethod( env, util->jutil, mid, isServer );
UTIL_CBK_TAIL();
}
static void
and_util_informWordsBlocked( XW_UtilCtxt* uc, XWEnv xwe, XP_U16 nBadWords,
XWStreamCtxt* words, const XP_UCHAR* dict )
@ -975,7 +965,6 @@ makeUtil( MPFORMAL JNIEnv* env,
#ifndef XWFEATURE_STANDALONE_ONLY
SET_PROC(informMissing);
SET_PROC(addrChange);
SET_PROC(setIsServer);
#endif
#ifdef XWFEATURE_SEARCHLIMIT
SET_PROC(getTraySearchLimits);

View file

@ -1221,6 +1221,7 @@ struct _JNIState {
#define XWJNI_START_GLOBALS() \
XWJNI_START() \
AndGameGlobals* globals = &state->globals; \
XP_USE(globals); /*no warnings */ \
#define XWJNI_END() \
} \

View file

@ -110,6 +110,10 @@ struct CommsCtxt {
AddressRecord* recs; /* return addresses */
TransportProcs procs;
RoleChangeProc rcProc;
void* rcClosure;
XP_U32 xportFlags;
#ifdef COMMS_HEARTBEAT
XP_U32 lastMsgRcd;
@ -355,7 +359,9 @@ CommsCtxt*
comms_make( MPFORMAL XWEnv xwe, XW_UtilCtxt* util, XP_Bool isServer,
XP_U16 XP_UNUSED_RELAY(nPlayersHere),
XP_U16 XP_UNUSED_RELAY(nPlayersTotal),
const TransportProcs* procs, XP_U16 forceChannel
const TransportProcs* procs,
RoleChangeProc rcp, void* rcClosure,
XP_U16 forceChannel
#ifdef SET_GAMESEED
, XP_U16 gameSeed
#endif
@ -379,6 +385,10 @@ comms_make( MPFORMAL XWEnv xwe, XW_UtilCtxt* util, XP_Bool isServer,
comms->xportFlags = comms->procs.flags;
#endif
}
XP_ASSERT( rcp );
comms->rcProc = rcp;
comms->rcClosure = rcClosure;
comms->dutil = util_getDevUtilCtxt( util, xwe );
comms->util = util;
comms->dutil = util_getDevUtilCtxt( util, xwe );
@ -648,8 +658,11 @@ addrFromStream( CommsAddrRec* addrP, XWStreamCtxt* stream )
}
CommsCtxt*
comms_makeFromStream( MPFORMAL XWEnv xwe, XWStreamCtxt* stream, XW_UtilCtxt* util,
const TransportProcs* procs, XP_U16 forceChannel )
comms_makeFromStream( MPFORMAL XWEnv xwe, XWStreamCtxt* stream,
XW_UtilCtxt* util,
const TransportProcs* procs,
RoleChangeProc rcp, void* rcClosure,
XP_U16 forceChannel )
{
XP_U16 nPlayersHere, nPlayersTotal;
AddressRecord** prevsAddrNext;
@ -670,8 +683,8 @@ comms_makeFromStream( MPFORMAL XWEnv xwe, XWStreamCtxt* stream, XW_UtilCtxt* uti
nPlayersTotal = 0;
}
CommsCtxt* comms = comms_make( MPPARM(mpool) xwe, util, isServer,
nPlayersHere, nPlayersTotal, procs,
forceChannel
nPlayersHere, nPlayersTotal, procs,
rcp, rcClosure, forceChannel
#ifdef SET_GAMESEED
, 0
#endif
@ -1768,8 +1781,11 @@ got_connect_cmd( CommsCtxt* comms, XWEnv xwe, XWStreamCtxt* stream,
if ( isServer != comms->isServer ) {
XP_LOGFF( "becoming%s a server", isServer ? "" : " NOT" );
comms->isServer = isServer;
util_setIsServer( comms->util, xwe, comms->isServer );
#ifdef DEBUG
XP_U16 queueLen = comms->queueLen;
#endif
(*comms->rcProc)( xwe, comms->rcClosure, !isServer );
XP_ASSERT( queueLen == comms->queueLen ); /* callback should not send!!! */
reset_internal( comms, xwe, isServer, comms->rr.nPlayersHere,
comms->rr.nPlayersTotal, XP_FALSE );
}
@ -2732,7 +2748,8 @@ comms_getStats( CommsCtxt* comms, XWStreamCtxt* stream )
}
XP_SNPRINTF( (XP_UCHAR*)buf, sizeof(buf),
(XP_UCHAR*)"msg queue len: %d; have %d channels\n",
(XP_UCHAR*)"role: %s; msg queue len: %d; have %d channels\n",
comms->isServer ? "host" : "guest",
comms->queueLen, nChannels );
stream_catString( stream, buf );

View file

@ -102,6 +102,7 @@ typedef void (*RelayRequestJoinProc)( XWEnv xwe, void* closure, const XP_UCHAR*
#endif
typedef void (*MsgCountChange)( XWEnv xwe, void* closure, XP_U16 msgCount );
typedef void (*RoleChangeProc)( XWEnv xwe, void* closure, XP_Bool amNowGuest );
typedef enum {
COMMS_XPORT_FLAGS_NONE = 0
@ -138,7 +139,9 @@ typedef struct _TransportProcs {
CommsCtxt* comms_make( MPFORMAL XWEnv xwe, XW_UtilCtxt* util,
XP_Bool isServer,
XP_U16 nPlayersHere, XP_U16 nPlayersTotal,
const TransportProcs* procs, XP_U16 forceChannel
const TransportProcs* procs,
RoleChangeProc rcp, void* rcClosure,
XP_U16 forceChannel
#ifdef SET_GAMESEED
,XP_U16 gameSeed
#endif
@ -187,6 +190,7 @@ XP_Bool comms_getIsServer( const CommsCtxt* comms );
CommsCtxt* comms_makeFromStream( MPFORMAL XWEnv xwe, XWStreamCtxt* stream,
XW_UtilCtxt* util,
const TransportProcs* procs,
RoleChangeProc rcp, void* rcClosure,
XP_U16 forceChannel );
void comms_start( CommsCtxt* comms, XWEnv xwe );
void comms_stop( CommsCtxt* comms, XWEnv xwe );

View file

@ -98,6 +98,14 @@ timerChangeListener( XWEnv xwe, void* data, const XP_U32 gameID,
gameID, oldVal, newVal );
}
static void
onRoleChanged( XWEnv xwe, void* closure, XP_Bool amNowGuest )
{
XP_ASSERT( amNowGuest );
XWGame* game = (XWGame*)closure;
server_onRoleChanged( game->server, xwe, amNowGuest );
}
static void
setListeners( XWGame* game, const CommonPrefs* cp )
{
@ -136,7 +144,8 @@ game_makeNewGame( MPFORMAL XWEnv xwe, XWGame* game, CurGameInfo* gi,
game->comms = comms_make( MPPARM(mpool) xwe, util,
gi->serverRole != SERVER_ISCLIENT,
nPlayersHere, nPlayersTotal,
procs, gi->forceChannel
procs, onRoleChanged, game,
gi->forceChannel
#ifdef SET_GAMESEED
, gameSeed
#endif
@ -191,6 +200,7 @@ game_reset( MPFORMAL XWGame* game, XWEnv xwe, CurGameInfo* gi, XW_UtilCtxt* util
game->comms = comms_make( MPPARM(mpool) xwe, util,
gi->serverRole != SERVER_ISCLIENT,
nPlayersHere, nPlayersTotal, procs,
onRoleChanged, game,
gi->forceChannel
#ifdef SET_GAMESEED
, 0
@ -281,7 +291,8 @@ game_makeFromStream( MPFORMAL XWEnv xwe, XWStreamCtxt* stream, XWGame* game,
if ( hasComms ) {
game->comms = comms_makeFromStream( MPPARM(mpool) xwe, stream, util,
procs, gi->forceChannel );
procs, onRoleChanged, game,
gi->forceChannel );
} else {
game->comms = NULL;
}
@ -301,6 +312,11 @@ game_makeFromStream( MPFORMAL XWEnv xwe, XWStreamCtxt* stream, XWGame* game,
success = XP_TRUE;
} while( XP_FALSE );
}
if ( success && !!game && !!game->comms ) {
XP_ASSERT( comms_getIsServer(game->comms) == server_getIsServer(game->server) );
}
return success;
} /* game_makeFromStream */

View file

@ -217,6 +217,7 @@ getStateStr( XW_State st )
switch( st ) {
CASESTR(XWSTATE_NONE);
CASESTR(XWSTATE_BEGIN);
CASESTR(XWSTATE_NEWCLIENT);
CASESTR(XWSTATE_NEED_SHOWSCORE);
CASESTR(XWSTATE_RECEIVED_ALL_REG);
CASESTR(XWSTATE_NEEDSEND_BADWORD_INFO);
@ -226,6 +227,7 @@ getStateStr( XW_State st )
CASESTR(XWSTATE_INTURN);
CASESTR(XWSTATE_GAMEOVER);
default:
XP_ASSERT(0);
return "unknown";
}
# undef CASESTR
@ -289,6 +291,10 @@ amServer( const ServerCtxt* server )
return result;
}
#ifdef DEBUG
XP_Bool server_getIsServer( const ServerCtxt* server ) { return amServer(server); }
#endif
static void
initServer( ServerCtxt* server, XWEnv xwe )
{
@ -558,6 +564,21 @@ server_writeToStream( const ServerCtxt* server, XWStreamCtxt* stream )
writeStreamIf( stream, server->nv.prevWordsStream );
} /* server_writeToStream */
void
server_onRoleChanged( ServerCtxt* server, XWEnv xwe, XP_Bool amNowGuest )
{
if ( amNowGuest == amServer(server) ) { /* do I need to change */
XP_ASSERT ( amNowGuest );
if ( amNowGuest ) {
server->vol.gi->serverRole = SERVER_ISCLIENT;
server_reset( server, xwe, server->vol.comms );
SETSTATE( server, XWSTATE_NEWCLIENT );
util_requestTime( server->vol.util, xwe );
}
}
}
static void
cleanupServer( ServerCtxt* server, XWEnv xwe )
{
@ -1630,6 +1651,12 @@ server_do( ServerCtxt* server, XWEnv xwe )
}
break;
case XWSTATE_NEWCLIENT:
XP_ASSERT( !amServer( server ) );
SETSTATE( server, XWSTATE_NONE ); /* server_initClientConnection expects this */
server_initClientConnection( server, xwe );
break;
case XWSTATE_NEEDSEND_BADWORD_INFO:
XP_ASSERT( server->vol.gi->serverRole == SERVER_ISSERVER );
badWordMoveUndoAndTellUser( server, xwe, &server->illegalWordInfo );

View file

@ -43,6 +43,7 @@ void server_reset( ServerCtxt* server, XWEnv xwe, CommsCtxt* comms );
void server_destroy( ServerCtxt* server, XWEnv xwe );
void server_prefsChanged( ServerCtxt* server, const CommonPrefs* cp );
void server_onRoleChanged( ServerCtxt* server, XWEnv xwe, XP_Bool amNowGuest );
typedef void (*TurnChangeListener)( XWEnv xwe, void* data );
void server_setTurnChangeListener( ServerCtxt* server, TurnChangeListener tl,
@ -134,6 +135,10 @@ void server_writeFinalScores( ServerCtxt* server, XWEnv xwe, XWStreamCtxt* strea
XP_U16 server_figureFinishBonus( const ServerCtxt* server, XP_U16 turn );
#endif
#ifdef DEBUG
XP_Bool server_getIsServer( const ServerCtxt* server );
#endif
#ifdef CPLUS
}
#endif

View file

@ -23,7 +23,7 @@
enum {
XWSTATE_NONE, /* 0 */
XWSTATE_BEGIN, /* 1 */
__UNUSED1, /* was XWSTATE_POOL_INITED */
XWSTATE_NEWCLIENT, /* was a host, now a reset client */
XWSTATE_NEED_SHOWSCORE, /* client-only */
__XWSTATE_WAITING_ALL_REG, /* 4 (unused) */
XWSTATE_RECEIVED_ALL_REG, /* includes waiting for dict from server */
@ -34,7 +34,7 @@ enum {
XWSTATE_INTURN, /* 10 */
XWSTATE_GAMEOVER, /* 11 */
XWSTATE_LAST /* for asserts only :-) */
XWSTATE_LAST, /* for asserts only :-) */
};
typedef XP_U8 XW_State;
#define XWSTATE_NBITS 4

View file

@ -171,7 +171,6 @@ typedef struct UtilVtable {
XP_U16 nMissing );
void (*m_util_addrChange)( XW_UtilCtxt* uc, XWEnv xwe, const CommsAddrRec* oldAddr,
const CommsAddrRec* newAddr );
void (*m_util_setIsServer)(XW_UtilCtxt* uc, XWEnv xwe, XP_Bool isServer );
#endif
void (*m_util_informWordsBlocked)( XW_UtilCtxt* uc, XWEnv xwe, XP_U16 nBadWords,
@ -306,8 +305,6 @@ struct XW_UtilCtxt {
(uc)->vtable->m_util_informMissing((uc), (e), (is), (ct), (nd), (nm) )
# define util_addrChange( uc,e, addro, addrn ) \
(uc)->vtable->m_util_addrChange((uc), (e), (addro), (addrn))
# define util_setIsServer( uc,e, is ) \
(uc)->vtable->m_util_setIsServer((uc), (e), (is))
# else
# define util_addrChange( uc,e, addro, addrn )
#endif

View file

@ -1865,36 +1865,6 @@ linux_util_addrChange( XW_UtilCtxt* uc, XWEnv XP_UNUSED(xwe),
}
}
static gint
changeRolesIdle( gpointer data )
{
CommonGlobals* cGlobals = (CommonGlobals*)data;
ServerCtxt* server = cGlobals->game.server;
server_reset( server, NULL_XWE, cGlobals->game.comms );
if ( SERVER_ISCLIENT == cGlobals->gi->serverRole ) {
XWStreamCtxt* stream =
mem_stream_make( MPPARM(cGlobals->util->mpool) cGlobals->params->vtMgr,
cGlobals, CHANNEL_NONE, sendOnClose );
(void)server_initClientConnection( server, NULL_XWE, stream );
}
(void)server_do( server, NULL_XWE );
return 0;
}
static void
linux_util_setIsServer( XW_UtilCtxt* uc, XWEnv XP_UNUSED(xwe),XP_Bool isServer )
{
XP_LOGF( "%s(isServer=%d)", __func__, isServer );
CommonGlobals* cGlobals = (CommonGlobals*)uc->closure;
DeviceRole newRole = isServer? SERVER_ISSERVER : SERVER_ISCLIENT;
cGlobals->params->serverRole = newRole;
cGlobals->gi->serverRole = newRole;
(void)ADD_ONETIME_IDLE( changeRolesIdle, cGlobals );
XP_ASSERT( isServer == game_getIsServer( &cGlobals->game ) );
}
#endif
unsigned int
@ -2552,7 +2522,6 @@ setupLinuxUtilCallbacks( XW_UtilCtxt* util )
#ifndef XWFEATURE_STANDALONE_ONLY
SET_PROC(informMissing);
SET_PROC(addrChange);
SET_PROC(setIsServer);
#endif
SET_PROC(formatPauseHistory);
SET_PROC(setTimer);