diff --git a/xwords4/common/comtypes.h b/xwords4/common/comtypes.h index aa352d168..dc0abf2d3 100644 --- a/xwords4/common/comtypes.h +++ b/xwords4/common/comtypes.h @@ -83,6 +83,7 @@ #define STREAM_VERS_41B4 0x02 #define STREAM_VERS_405 0x01 +/* search for FIX_NEXT_VERSION_CHANGE next time this is changed */ #define CUR_STREAM_VERS STREAM_VERS_COMMSBACKOFF typedef struct XP_Rect { diff --git a/xwords4/common/server.c b/xwords4/common/server.c index de12b6d97..ed7e594a1 100644 --- a/xwords4/common/server.c +++ b/xwords4/common/server.c @@ -76,6 +76,7 @@ typedef struct ServerVolatiles { void* turnChangeData; GameOverListener gameOverListener; void* gameOverData; + XP_U16 pendingRegistrations; XP_Bool showPrevMove; } ServerVolatiles; @@ -86,7 +87,6 @@ typedef struct ServerNonvolatiles { XW_State stateAfterShow; XP_S8 currentTurn; /* invalid when game is over */ XP_S8 quitter; /* -1 unless somebody resigned */ - XP_U8 pendingRegistrations; XP_Bool showRobotScores; XP_Bool sortNewTiles; #ifdef STREAM_VERS_BIGBOARD @@ -206,21 +206,23 @@ logNewState( XW_State old, XW_State newst ) ****************************************************************************/ #ifndef XWFEATURE_STANDALONE_ONLY static void -syncPlayers( ServerCtxt* server ) +figureMissing( ServerCtxt* server ) { + XP_U16 pendingRegistrations = 0; XP_U16 ii; CurGameInfo* gi = server->vol.gi; LocalPlayer* lp = gi->players; ServerPlayer* player = server->players; for ( ii = 0; ii < gi->nPlayers; ++ii, ++lp, ++player ) { - if ( !lp->isLocal/* && !lp->name */ ) { - ++server->nv.pendingRegistrations; + if ( !lp->isLocal && !lp->name ) { + ++pendingRegistrations; } player->deviceIndex = lp->isLocal? SERVER_DEVICE : UNKNOWN_DEVICE; } + server->vol.pendingRegistrations = pendingRegistrations; } #else -# define syncPlayers( server ) +# define figureMissing( server ) #endif static XP_Bool @@ -243,7 +245,7 @@ initServer( ServerCtxt* server ) SETSTATE( server, XWSTATE_BEGIN ); } - syncPlayers( server ); + figureMissing( server ); server->nv.nDevices = 1; /* local device (0) is always there */ #ifdef STREAM_VERS_BIGBOARD @@ -302,7 +304,8 @@ getNV( XWStreamCtxt* stream, ServerNonvolatiles* nv, XP_U16 nPlayers ) if ( STREAM_VERS_DICTNAME <= version ) { nv->quitter = (XP_S8)stream_getBits( stream, NPLAYERS_NBITS ) - 1; } - nv->pendingRegistrations = (XP_U8)stream_getBits( stream, NPLAYERS_NBITS ); + /* FIX_NEXT_VERSION_CHANGE */ + /*nv->pendingRegistrations = (XP_U8)*/ stream_getBits( stream, NPLAYERS_NBITS ); for ( ii = 0; ii < nPlayers; ++ii ) { nv->addresses[ii].channelNo = @@ -337,7 +340,8 @@ putNV( XWStreamCtxt* stream, const ServerNonvolatiles* nv, XP_U16 nPlayers ) /* +1: make -1 (NOTURN) into a positive number */ stream_putBits( stream, NPLAYERS_NBITS, nv->currentTurn+1 ); stream_putBits( stream, NPLAYERS_NBITS, nv->quitter+1 ); - stream_putBits( stream, NPLAYERS_NBITS, nv->pendingRegistrations ); + /* FIX_NEXT_VERSION_CHANGE */ + stream_putBits( stream, NPLAYERS_NBITS, 0 /*nv->pendingRegistrations*/ ); for ( ii = 0; ii < nPlayers; ++ii ) { stream_putBits( stream, 16, nv->addresses[ii].channelNo ); @@ -416,9 +420,11 @@ server_makeFromStream( MPFORMAL XWStreamCtxt* stream, ModelCtxt* model, server->nv.prevWordsStream = readStreamIf( server, stream ); } + figureMissing( server ); + util_informMissing( util, server->vol.gi->serverRole == SERVER_ISSERVER, comms_getConType( comms ), - server->nv.pendingRegistrations ); + server->vol.pendingRegistrations ); return server; } /* server_makeFromStream */ @@ -699,7 +705,7 @@ handleRegistrationMsg( ServerCtxt* server, XWStreamCtxt* stream ) playersInMsg = (XP_U16)stream_getBits( stream, NPLAYERS_NBITS ); XP_ASSERT( playersInMsg > 0 ); - if ( server->nv.pendingRegistrations < playersInMsg ) { + if ( server->vol.pendingRegistrations < playersInMsg ) { util_userError( server->vol.util, ERR_REG_UNEXPECTED_USER ); success = XP_FALSE; } else { @@ -738,7 +744,7 @@ handleRegistrationMsg( ServerCtxt* server, XWStreamCtxt* stream ) } #endif - if ( server->nv.pendingRegistrations == 0 ) { + if ( server->vol.pendingRegistrations == 0 ) { XP_ASSERT( ii == playersInMsg ); /* otherwise malformed */ setStreamVersion( server ); checkResizeBoard( server ); @@ -1031,7 +1037,7 @@ server_do( ServerCtxt* server ) switch( server->nv.gameState ) { case XWSTATE_BEGIN: - if ( server->nv.pendingRegistrations == 0 ) { /* all players on + if ( server->vol.pendingRegistrations == 0 ) { /* all players on device */ assignTilesToAll( server ); SETSTATE( server, XWSTATE_INTURN ); @@ -1126,7 +1132,7 @@ findFirstPending( ServerCtxt* server, ServerPlayer** playerP ) LocalPlayer* lp; CurGameInfo* gi = server->vol.gi; XP_U16 nPlayers = gi->nPlayers; - XP_U16 nPending = server->nv.pendingRegistrations; + XP_U16 nPending = server->vol.pendingRegistrations; XP_ASSERT( nPlayers > 0 ); lp = gi->players + nPlayers; @@ -1160,7 +1166,7 @@ registerRemotePlayer( ServerCtxt* server, XWStreamCtxt* stream ) /* The player must already be there with a null name, or it's an error. Take the first empty slot. */ - XP_ASSERT( server->nv.pendingRegistrations > 0 ); + XP_ASSERT( server->vol.pendingRegistrations > 0 ); /* find the slot to use */ lp = findFirstPending( server, &player ); @@ -1179,7 +1185,7 @@ registerRemotePlayer( ServerCtxt* server, XWStreamCtxt* stream ) channelNo = stream_getAddress( stream ); deviceIndex = getIndexForDevice( server, channelNo ); - --server->nv.pendingRegistrations; + --server->vol.pendingRegistrations; if ( deviceIndex == -1 ) { RemoteAddress* addr; @@ -1350,7 +1356,7 @@ client_readInitialMessage( ServerCtxt* server, XWStreamCtxt* stream ) sortTilesIf( server, ii ); } - syncPlayers( server ); + figureMissing( server ); SETSTATE( server, XWSTATE_INTURN ); @@ -2427,7 +2433,7 @@ server_getMissingPlayers( const ServerCtxt* server ) } break; case SERVER_ISSERVER: - if ( 0 < server->nv.pendingRegistrations ) { + if ( 0 < server->vol.pendingRegistrations ) { XP_U16 nPlayers = server->vol.gi->nPlayers; const ServerPlayer* players = server->players; for ( ii = 0; ii < nPlayers; ++ii ) { @@ -2550,6 +2556,7 @@ tileCountsOk( const ServerCtxt* server ) static void setTurn( ServerCtxt* server, XP_S16 turn ) { + XP_ASSERT( -1 == turn || 0 == server->vol.pendingRegistrations ); if ( server->nv.currentTurn != turn ) { server->nv.currentTurn = turn; server->nv.lastMoveTime = util_getCurSeconds( server->vol.util ); diff --git a/xwords4/linux/Makefile b/xwords4/linux/Makefile index a2544a37e..31364fa8b 100644 --- a/xwords4/linux/Makefile +++ b/xwords4/linux/Makefile @@ -145,6 +145,7 @@ endif DEFINES += ${BLUETOOTH} DEFINES += -DXWFEATURE_RELAY DEFINES += -DXWFEATURE_SMS -DXWFEATURE_BASE64 +DEFINES += -DXWFEATURE_DIRECTIP # Robot can be made to think, to simulate for relay mostly DEFINES += -DXWFEATURE_SLOW_ROBOT diff --git a/xwords4/linux/gtkconnsdlg.c b/xwords4/linux/gtkconnsdlg.c index 507848227..e83a29e8c 100644 --- a/xwords4/linux/gtkconnsdlg.c +++ b/xwords4/linux/gtkconnsdlg.c @@ -35,6 +35,8 @@ typedef struct _GtkConnsState { GtkWidget* bthost; GtkWidget* smsphone; GtkWidget* smsport; + GtkWidget* iphost; + GtkWidget* ipport; GtkWidget* bgScanButton; GtkWidget* notebook; @@ -80,7 +82,15 @@ handle_ok( GtkWidget* XP_UNUSED(widget), gpointer closure ) CommsConnType conType = pageNoToConnType( state, page ); switch ( conType ) { +#ifdef XWFEATURE_DIRECTIP case COMMS_CONN_IP_DIRECT: + txt = gtk_entry_get_text( GTK_ENTRY(state->iphost) ); + XP_STRNCPY( state->addr->u.ip.hostName_ip, txt, + sizeof(state->addr->u.ip.hostName_ip) ); + txt = gtk_entry_get_text( GTK_ENTRY(state->ipport) ); + state->addr->u.ip.port_ip = atoi( txt ); + break; +#endif #ifdef XWFEATURE_RELAY case COMMS_CONN_RELAY: txt = gtk_entry_get_text( GTK_ENTRY(state->invite) ); @@ -224,6 +234,32 @@ makeBTPage( GtkConnsState* state ) return vbox; } /* makeBTPage */ +#ifdef XWFEATURE_DIRECTIP +static GtkWidget* +makeIPDirPage( GtkConnsState* state ) +{ + GtkWidget* vbox = gtk_vbox_new( FALSE, 0 ); + + /* XP_UCHAR hostName_ip[MAX_HOSTNAME_LEN + 1]; */ + /* XP_U16 port_ip; */ + + const gchar* name = COMMS_CONN_IP_DIRECT == state->addr->conType ? + state->addr->u.ip.hostName_ip : state->globals->cGlobals.params->connInfo.ip.hostName; + GtkWidget* hbox = makeLabeledField( "Hostname", &state->iphost, name ); + gtk_box_pack_start( GTK_BOX(vbox), hbox, FALSE, TRUE, 0 ); + + hbox = makeLabeledField( "Relay port", &state->ipport, NULL ); + if ( COMMS_CONN_IP_DIRECT == state->addr->conType ) { + char buf[16]; + snprintf( buf, sizeof(buf), "%d", state->addr->u.ip.port_ip ); + gtk_entry_set_text( GTK_ENTRY(state->ipport), buf ); + } + gtk_box_pack_start( GTK_BOX(vbox), hbox, FALSE, TRUE, 0 ); + + return vbox; +} +#endif + static GtkWidget* makeSMSPage( GtkConnsState* state ) { @@ -279,6 +315,12 @@ gtkConnsDlg( GtkGameGlobals* globals, CommsAddrRec* addr, DeviceRole role, (void)gtk_notebook_append_page( GTK_NOTEBOOK(state.notebook), makeBTPage(&state), gtk_label_new( "Bluetooth" ) ); +#endif +#ifdef XWFEATURE_DIRECTIP + state.pageTypes[nTypes++] = COMMS_CONN_IP_DIRECT; + (void)gtk_notebook_append_page( GTK_NOTEBOOK(state.notebook), + makeIPDirPage(&state), + gtk_label_new( "Direct" ) ); #endif state.pageTypes[nTypes++] = COMMS_CONN_SMS; (void)gtk_notebook_append_page( GTK_NOTEBOOK(state.notebook), diff --git a/xwords4/linux/main.h b/xwords4/linux/main.h index 0cfd99501..3274e549b 100644 --- a/xwords4/linux/main.h +++ b/xwords4/linux/main.h @@ -137,7 +137,7 @@ typedef struct LaunchParams { bdaddr_t hostAddr; /* unused if a host */ } bt; #endif -#ifdef XWFEATURE_IP_DIRECT +#if defined XWFEATURE_IP_DIRECT || defined XWFEATURE_DIRECTIP struct { const char* hostName; int port; diff --git a/xwords4/linux/scripts/discon_ok2.sh b/xwords4/linux/scripts/discon_ok2.sh index 0b95ef55c..257d07f0e 100755 --- a/xwords4/linux/scripts/discon_ok2.sh +++ b/xwords4/linux/scripts/discon_ok2.sh @@ -315,6 +315,9 @@ close_device() { unset ROOMS[$ID] unset APPS[$ID] unset ARGS_DEVID[$ID] + + COUNT=${#ARGS[*]} + echo "$COUNT devices left playing..." } OBITS=""