From fab39a5f87c78eb9dcc09c11a1fec3dd601c36e5 Mon Sep 17 00:00:00 2001 From: Andy2 Date: Wed, 16 Nov 2011 06:47:55 -0800 Subject: [PATCH] Trying to deal with old- and new-proto devices interacting. Fix clients to append their stream version to their inital connect message. (The format can't change, so detecting additional length was the only option. comm.c on existing clients won't allow more than one connect message per channel, so adding a new to be used in addition didn't work.) New servers detect this; old will ignore. Track the version (implicit or not) of all clients, and use the lowest any supports, so that new server and all new clients will use newer proto. --- xwords4/common/game.c | 10 +-- xwords4/common/game.h | 8 ++- xwords4/common/model.c | 32 ++++++--- xwords4/common/server.c | 148 ++++++++++++++++++++++++++++++++-------- 4 files changed, 155 insertions(+), 43 deletions(-) diff --git a/xwords4/common/game.c b/xwords4/common/game.c index 1e0473727..62e3e2178 100644 --- a/xwords4/common/game.c +++ b/xwords4/common/game.c @@ -426,16 +426,18 @@ gi_readFromStream( MPFORMAL XWStreamCtxt* stream, CurGameInfo* gi ) XP_U16 ii; XP_UCHAR* str; XP_U16 strVersion = stream_getVersion( stream ); +#ifdef STREAM_VERS_BIGBOARD + XP_U16 nColsNBits = STREAM_VERS_BIGBOARD > strVersion ? 4 : NUMCOLS_NBITS; +#else + XP_U16 nColsNBits = NUMCOLS_NBITS; +#endif str = stringFromStream( mpool, stream ); replaceStringIfDifferent( mpool, &gi->dictName, str ); XP_FREEP( mpool, &str ); gi->nPlayers = (XP_U8)stream_getBits( stream, NPLAYERS_NBITS ); - gi->boardSize = - (XP_U8)stream_getBits( stream, - strVersion < STREAM_VERS_BIGBOARD? 4 : - NUMCOLS_NBITS ); + gi->boardSize = (XP_U8)stream_getBits( stream, nColsNBits ); gi->serverRole = (DeviceRole)stream_getBits( stream, 2 ); gi->hintsNotAllowed = stream_getBits( stream, 1 ); if ( strVersion < STREAM_VERS_ROBOTIQ ) { diff --git a/xwords4/common/game.h b/xwords4/common/game.h index 61f4de9f3..15601a24a 100644 --- a/xwords4/common/game.h +++ b/xwords4/common/game.h @@ -33,8 +33,6 @@ extern "C" { #if MAX_COLS > 16 # define STREAM_VERS_BIGBOARD 0x12 -#else -# define STREAM_VERS_BIGBOARD 0x11 #endif #define STREAM_SAVE_PREVWORDS 0x11 #define STREAM_VERS_SERVER_SAVES_TOSHOW 0x10 @@ -58,7 +56,11 @@ extern "C" { #define STREAM_VERS_41B4 0x02 #define STREAM_VERS_405 0x01 -#define CUR_STREAM_VERS STREAM_VERS_BIGBOARD +#if MAX_COLS > 16 +# define CUR_STREAM_VERS STREAM_VERS_BIGBOARD +#else +# define CUR_STREAM_VERS STREAM_SAVE_PREVWORDS +#endif typedef struct LocalPlayer { XP_UCHAR* name; diff --git a/xwords4/common/model.c b/xwords4/common/model.c index f1d6ade68..13bebf0ab 100644 --- a/xwords4/common/model.c +++ b/xwords4/common/model.c @@ -118,10 +118,13 @@ model_makeFromStream( MPFORMAL XWStreamCtxt* stream, DictionaryCtxt* dict, short i; XP_Bool hasDict; XP_U16 nPlayers; + XP_U16 nColsNBits; XP_U16 version = stream_getVersion( stream ); - XP_U16 nColsNBits = - STREAM_VERS_BIGBOARD > version ? 4 : - NUMCOLS_NBITS; +#ifdef STREAM_VERS_BIGBOARD + nColsNBits = STREAM_VERS_BIGBOARD > version ? 4 : NUMCOLS_NBITS; +#else + nColsNBits = NUMCOLS_NBITS; +#endif XP_ASSERT( !!dict || !!dicts ); @@ -818,6 +821,12 @@ model_currentMoveToStream( ModelCtxt* model, XP_S16 turn, { PlayerCtxt* player; XP_S16 numTiles; +#ifdef STREAM_VERS_BIGBOARD + XP_U16 version = stream_getVersion( stream ); + XP_U16 nColsNBits = STREAM_VERS_BIGBOARD > version ? 4 : NUMCOLS_NBITS; +#else + XP_U16 nColsNBits = NUMCOLS_NBITS; +#endif XP_ASSERT( turn >= 0 ); player = &model->players[turn]; @@ -834,8 +843,8 @@ model_currentMoveToStream( ModelCtxt* model, XP_S16 turn, &col, &row, &isBlank ); XP_ASSERT( numTiles >= 0 ); stream_putBits( stream, TILE_NBITS, tile ); - stream_putBits( stream, NUMCOLS_NBITS, col ); - stream_putBits( stream, NUMCOLS_NBITS, row ); + stream_putBits( stream, nColsNBits, col ); + stream_putBits( stream, nColsNBits, row ); stream_putBits( stream, 1, isBlank ); } } /* model_currentMoveToStream */ @@ -852,8 +861,13 @@ model_makeTurnFromStream( ModelCtxt* model, XP_U16 playerNum, { XP_U16 numTiles, ii; Tile blank = dict_getBlankTile( model_getDictionary(model) ); +#ifdef STREAM_VERS_BIGBOARD XP_U16 version = stream_getVersion( stream ); XP_U16 nColsNBits = STREAM_VERS_BIGBOARD > version ? 4 : NUMCOLS_NBITS; +#else + XP_U16 nColsNBits = NUMCOLS_NBITS; +#endif + model_resetCurrentTurn( model, playerNum ); @@ -2220,9 +2234,11 @@ loadPlayerCtxt( XWStreamCtxt* stream, XP_U16 version, PlayerCtxt* pc ) { PendingTile* pt; XP_U16 nTiles; - XP_U16 nColsNBits = - STREAM_VERS_BIGBOARD > stream_getVersion( stream ) ? 4 : - NUMCOLS_NBITS; +#ifdef STREAM_VERS_BIGBOARD + XP_U16 nColsNBits = STREAM_VERS_BIGBOARD > version ? 4 : NUMCOLS_NBITS; +#else + XP_U16 nColsNBits = NUMCOLS_NBITS; +#endif pc->curMoveValid = stream_getBits( stream, 1 ); diff --git a/xwords4/common/server.c b/xwords4/common/server.c index a40960c3b..d8a38bfe7 100644 --- a/xwords4/common/server.c +++ b/xwords4/common/server.c @@ -60,6 +60,9 @@ typedef struct ServerPlayer { typedef struct RemoteAddress { XP_PlayerAddr channelNo; +#ifdef STREAM_VERS_BIGBOARD + XP_U8 streamVersion; +#endif } RemoteAddress; /* These are the parts of the server's state that needs to be preserved @@ -84,6 +87,9 @@ typedef struct ServerNonvolatiles { XP_U8 pendingRegistrations; XP_Bool showRobotScores; XP_Bool sortNewTiles; +#ifdef STREAM_VERS_BIGBOARD + XP_U8 streamVersion; +#endif #ifdef XWFEATURE_SLOW_ROBOT XP_U16 robotThinkMin, robotThinkMax; /* not saved (yet) */ #endif @@ -139,13 +145,14 @@ static XWStreamCtxt* messageStreamWithHeader( ServerCtxt* server, XP_U16 devIndex, XW_Proto code ); static XP_Bool handleRegistrationMsg( ServerCtxt* server, XWStreamCtxt* stream ); -static void registerRemotePlayer( ServerCtxt* server, XWStreamCtxt* stream ); +static XP_S8 registerRemotePlayer( ServerCtxt* server, XWStreamCtxt* stream ); static void server_sendInitialMessage( ServerCtxt* server ); static void sendBadWordMsgs( ServerCtxt* server ); static XP_Bool handleIllegalWord( ServerCtxt* server, XWStreamCtxt* incoming ); static void tellMoveWasLegal( ServerCtxt* server ); -static void writeProto( XWStreamCtxt* stream, XW_Proto proto ); +static void writeProto( const ServerCtxt* server, XWStreamCtxt* stream, + XW_Proto proto ); #endif #define PICK_NEXT -1 @@ -231,6 +238,9 @@ initServer( ServerCtxt* server ) syncPlayers( server ); server->nv.nDevices = 1; /* local device (0) is always there */ +#ifdef STREAM_VERS_BIGBOARD + server->nv.streamVersion = STREAM_SAVE_PREVWORDS; /* default to old */ +#endif } /* initServer */ ServerCtxt* @@ -281,7 +291,18 @@ getNV( XWStreamCtxt* stream, ServerNonvolatiles* nv, XP_U16 nPlayers ) for ( ii = 0; ii < nPlayers; ++ii ) { nv->addresses[ii].channelNo = (XP_PlayerAddr)stream_getBits( stream, 16 ); +#ifdef STREAM_VERS_BIGBOARD + if ( STREAM_VERS_BIGBOARD <= version ) { + nv->addresses[ii].streamVersion = stream_getBits( stream, 8 ); + } +#endif } +#ifdef STREAM_VERS_BIGBOARD + if ( STREAM_SAVE_PREVWORDS < version ) { + nv->streamVersion = stream_getU8 ( stream ); + } + XP_LOGF( "%s: read streamVersion: 0x%x", __func__, nv->streamVersion ); +#endif } /* getNV */ static void @@ -302,7 +323,14 @@ putNV( XWStreamCtxt* stream, ServerNonvolatiles* nv, XP_U16 nPlayers ) for ( ii = 0; ii < nPlayers; ++ii ) { stream_putBits( stream, 16, nv->addresses[ii].channelNo ); +#ifdef STREAM_VERS_BIGBOARD + stream_putBits( stream, 8, nv->addresses[ii].streamVersion ); +#endif } +#ifdef STREAM_VERS_BIGBOARD + stream_putU8( stream, nv->streamVersion ); + XP_LOGF( "%s: wrote streamVersion: 0x%x", __func__, nv->streamVersion ); +#endif } /* putNV */ static XWStreamCtxt* @@ -516,7 +544,7 @@ server_initClientConnection( ServerCtxt* server, XWStreamCtxt* stream ) if ( server->nv.gameState == XWSTATE_NONE ) { stream_open( stream ); - writeProto( stream, XWPROTO_DEVICE_REGISTRATION ); + writeProto( server, stream, XWPROTO_DEVICE_REGISTRATION ); nPlayers = gi->nPlayers; XP_ASSERT( nPlayers > 0 ); @@ -532,8 +560,8 @@ server_initClientConnection( ServerCtxt* server, XWStreamCtxt* stream ) continue; } - stream_putBits( stream, 1, LP_IS_ROBOT(lp) ); /* better not to send this */ - + stream_putBits( stream, 1, LP_IS_ROBOT(lp) ); /* better not to + send this */ /* The first nPlayers players are the ones we'll use. The local flag doesn't matter when for SERVER_ISCLIENT. */ name = emptyStringIfNull(lp->name); @@ -544,6 +572,10 @@ server_initClientConnection( ServerCtxt* server, XWStreamCtxt* stream ) stream_putBits( stream, NAME_LEN_NBITS, len ); stream_putBytes( stream, name, len ); } +#ifdef STREAM_VERS_BIGBOARD + stream_putU8( stream, CUR_STREAM_VERS ); +#endif + } else { XP_LOGF( "%s: wierd state %s; dropping message", __func__, getStateStr(server->nv.gameState) ); @@ -594,11 +626,32 @@ callTurnChangeListener( ServerCtxt* server ) } /* callTurnChangeListener */ #ifndef XWFEATURE_STANDALONE_ONLY +# ifdef STREAM_VERS_BIGBOARD +static void +setStreamVersion( ServerCtxt* server ) +{ + XP_U16 devIndex; + XP_U8 streamVersion = CUR_STREAM_VERS; + for ( devIndex = 1; devIndex < server->nv.nDevices; ++devIndex ) { + XP_U8 devVersion = server->nv.addresses[devIndex].streamVersion; + if ( devVersion < streamVersion ) { + streamVersion = devVersion; + } + } + XP_LOGF( "%s: setting streamVersion: %d", __func__, streamVersion ); + server->nv.streamVersion = streamVersion; +} +# else +# define setStreamVersion(s) +# endif + static XP_Bool handleRegistrationMsg( ServerCtxt* server, XWStreamCtxt* stream ) { XP_Bool success = XP_TRUE; - XP_U16 playersInMsg, i; + XP_U16 playersInMsg; + XP_S8 clientIndex; + XP_U16 ii = 0; LOG_FUNC(); /* code will have already been consumed */ @@ -609,21 +662,41 @@ handleRegistrationMsg( ServerCtxt* server, XWStreamCtxt* stream ) util_userError( server->vol.util, ERR_REG_UNEXPECTED_USER ); success = XP_FALSE; } else { - for ( i = 0; i < playersInMsg; ++i ) { - registerRemotePlayer( server, stream ); + XP_S8 prevIndex = -1; + for ( ; ii < playersInMsg; ++ii ) { + clientIndex = registerRemotePlayer( server, stream ); /* This is abusing the semantics of turn change -- at least in the case where there is another device yet to register -- but we need to let the board know to redraw the scoreboard with more players there. */ callTurnChangeListener( server ); + XP_ASSERT( ii == 0 || prevIndex == clientIndex ); + prevIndex = clientIndex; } - if ( server->nv.pendingRegistrations == 0 ) { - assignTilesToAll( server ); - SETSTATE( server, XWSTATE_RECEIVED_ALL_REG ); + } + +#ifdef STREAM_VERS_BIGBOARD + if ( 0 < stream_getSize(stream) ) { + XP_U8 streamVersion = stream_getU8( stream ); + if ( streamVersion >= STREAM_VERS_BIGBOARD ) { + XP_LOGF( "%s: upping device %d streamVersion to %d", + __func__, clientIndex, streamVersion ); + server->nv.addresses[clientIndex].streamVersion = streamVersion; } } +#endif + + if ( server->nv.pendingRegistrations == 0 ) { + XP_ASSERT( ii == playersInMsg ); /* otherwise malformed */ + setStreamVersion( server ); + assignTilesToAll( server ); + SETSTATE( server, XWSTATE_RECEIVED_ALL_REG ); + } +/* now set server's streamVersion if all remote players have the higher one. */ +/* But first need to pass it in the strramx */ + return success; } /* handleRegistrationMsg */ #endif @@ -1000,7 +1073,7 @@ findFirstPending( ServerCtxt* server, ServerPlayer** playerP ) return lp; } /* findFirstPending */ -static void +static XP_S8 registerRemotePlayer( ServerCtxt* server, XWStreamCtxt* stream ) { XP_S8 deviceIndex; @@ -1040,10 +1113,13 @@ registerRemotePlayer( ServerCtxt* server, XWStreamCtxt* stream ) XP_ASSERT( channelNo != 0 ); addr->channelNo = channelNo; +#ifdef STREAM_VERS_BIGBOARD + addr->streamVersion = STREAM_SAVE_PREVWORDS; +#endif } player->deviceIndex = deviceIndex; - + return deviceIndex; } /* registerRemotePlayer */ static void @@ -1096,10 +1172,9 @@ client_readInitialMessage( ServerCtxt* server, XWStreamCtxt* stream ) PoolContext* pool; /* version; any dependencies here? */ - if ( STREAM_SAVE_PREVWORDS >= stream_getVersion( stream ) ) { - XP_U8 streamVersion = stream_getU8( stream ); - stream_setVersion( stream, streamVersion ); - } + XP_U8 streamVersion = stream_getU8( stream ); + XP_LOGF( "%s: set streamVersion to %d", __func__, streamVersion ); + stream_setVersion( stream, streamVersion ); gameID = stream_getU32( stream ); XP_LOGF( "read gameID of %lx; calling comms_setConnID", gameID ); @@ -1237,7 +1312,9 @@ server_sendInitialMessage( ServerCtxt* server ) DictionaryCtxt* dict = model_getDictionary(model); XP_ASSERT( !!stream ); stream_open( stream ); - writeProto( stream, XWPROTO_CLIENT_SETUP ); + writeProto( server, stream, XWPROTO_CLIENT_SETUP ); + + stream_putU8( stream, CUR_STREAM_VERS ); XP_LOGF( "putting gameID %lx into msg", gameID ); stream_putU32( stream, gameID ); @@ -1341,9 +1418,11 @@ messageStreamWithHeader( ServerCtxt* server, XP_U16 devIndex, XW_Proto code ) printCode("making", code); stream = util_makeStreamFromAddr( server->vol.util, channelNo ); - +#ifdef STREAM_VERS_BIGBOARD + stream_setVersion( stream, server->nv.streamVersion ); +#endif stream_open( stream ); - writeProto( stream, code ); + writeProto( server, stream, code ); return stream; } /* messageStreamWithHeader */ @@ -2441,22 +2520,34 @@ server_handleUndo( ServerCtxt* server ) #ifndef XWFEATURE_STANDALONE_ONLY static void -writeProto( XWStreamCtxt* stream, XW_Proto proto ) +writeProto( const ServerCtxt* server, XWStreamCtxt* stream, XW_Proto proto ) { - stream_putBits( stream, XWPROTO_NBITS, XWPROTO_NEW_PROTO ); +#ifdef STREAM_VERS_BIGBOARD + XP_ASSERT( server->nv.streamVersion > 0 ); + if ( STREAM_SAVE_PREVWORDS < server->nv.streamVersion ) { + stream_putBits( stream, XWPROTO_NBITS, XWPROTO_NEW_PROTO ); + stream_putBits( stream, 8, CUR_STREAM_VERS ); + } +#else + XP_USE(server); +#endif stream_putBits( stream, XWPROTO_NBITS, proto ); - stream_putU8( stream, CUR_STREAM_VERS ); } static XW_Proto -readProto( XWStreamCtxt* stream ) +readProto( ServerCtxt* server, XWStreamCtxt* stream ) { - XP_U8 version = STREAM_SAVE_PREVWORDS; /* version prior to fmt change */ XW_Proto proto = (XW_Proto)stream_getBits( stream, XWPROTO_NBITS ); + XP_U8 version = STREAM_SAVE_PREVWORDS; /* version prior to fmt change */ +#ifdef STREAM_VERS_BIGBOARD if ( XWPROTO_NEW_PROTO == proto ) { + version = stream_getBits( stream, 8 ); proto = (XW_Proto)stream_getBits( stream, XWPROTO_NBITS ); - version = stream_getU8( stream ); } + server->nv.streamVersion = version; +#else + XP_USE(server); +#endif stream_setVersion( stream, version ); return proto; } @@ -2465,7 +2556,7 @@ XP_Bool server_receiveMessage( ServerCtxt* server, XWStreamCtxt* incoming ) { XP_Bool accepted = XP_FALSE; - XW_Proto code = readProto( incoming ); + XW_Proto code = readProto( server, incoming ); printCode( "Receiving", code ); @@ -2554,7 +2645,8 @@ server_receiveMessage( ServerCtxt* server, XWStreamCtxt* incoming ) accepted = XP_TRUE; break; default: - XP_WARNF( "Unknown code on incoming message: %d\n", code ); + XP_WARNF( "%s: Unknown code on incoming message: %d\n", + __func__, code ); break; } /* switch */ }