diff --git a/xwords4/common/comms.c b/xwords4/common/comms.c index 255427227..91ca5572e 100644 --- a/xwords4/common/comms.c +++ b/xwords4/common/comms.c @@ -35,6 +35,10 @@ #ifndef XWFEATURE_STANDALONE_ONLY +#ifndef INITIAL_CLIENT_VERS +# define INITIAL_CLIENT_VERS 1 +#endif + #ifdef COMMS_HEARTBEAT /* It might make sense for this to be a parameter or somehow tied to the platform and transport. But in that case it'd have to be passed across @@ -2009,6 +2013,7 @@ msg_to_stream( CommsCtxt* comms, XWRELAY_Cmd cmd, XWHostID destID, break; case XWRELAY_GAME_CONNECT: stream_putU8( stream, XWRELAY_PROTO_VERSION ); + stream_putU16( stream, INITIAL_CLIENT_VERS ); stringToStream( stream, addr.u.ip_relay.invite ); stream_putU8( stream, addr.u.ip_relay.seeksPublicRoom ); stream_putU8( stream, addr.u.ip_relay.advertiseRoom ); @@ -2027,6 +2032,7 @@ msg_to_stream( CommsCtxt* comms, XWRELAY_Cmd cmd, XWHostID destID, case XWRELAY_GAME_RECONNECT: stream_putU8( stream, XWRELAY_PROTO_VERSION ); + stream_putU16( stream, INITIAL_CLIENT_VERS ); stringToStream( stream, addr.u.ip_relay.invite ); stream_putU8( stream, addr.u.ip_relay.seeksPublicRoom ); stream_putU8( stream, addr.u.ip_relay.advertiseRoom ); diff --git a/xwords4/relay/cref.cpp b/xwords4/relay/cref.cpp index 9e1ecb05f..5d22a4866 100644 --- a/xwords4/relay/cref.cpp +++ b/xwords4/relay/cref.cpp @@ -179,7 +179,7 @@ CookieRef::Unlock() { } bool -CookieRef::_Connect( int socket, int nPlayersH, int nPlayersS, int seed, +CookieRef::_Connect( int socket, int clientVersion, int nPlayersH, int nPlayersS, int seed, bool seenSeed, in_addr& addr ) { bool connected = false; @@ -199,7 +199,7 @@ CookieRef::_Connect( int socket, int nPlayersH, int nPlayersS, int seed, if ( !connected ) { set sockets = GetSockets(); if ( sockets.end() == sockets.find( socket ) ) { - pushConnectEvent( socket, nPlayersH, nPlayersS, seed, addr ); + pushConnectEvent( socket, clientVersion, nPlayersH, nPlayersS, seed, addr ); handleEvents(); connected = HasSocket_locked( socket ); } else { @@ -210,7 +210,8 @@ CookieRef::_Connect( int socket, int nPlayersH, int nPlayersS, int seed, } bool -CookieRef::_Reconnect( int socket, HostID hid, int nPlayersH, int nPlayersS, +CookieRef::_Reconnect( int socket, int clientVersion, HostID hid, + int nPlayersH, int nPlayersS, int seed, in_addr& addr, bool gameDead ) { bool spotTaken = false; @@ -222,7 +223,7 @@ CookieRef::_Reconnect( int socket, HostID hid, int nPlayersH, int nPlayersS, logf( XW_LOGINFO, "%s: dropping because already here", __func__ ); } else { - pushReconnectEvent( socket, hid, nPlayersH, nPlayersS, seed, addr ); + pushReconnectEvent( socket, clientVersion, hid, nPlayersH, nPlayersS, seed, addr ); } if ( gameDead ) { pushGameDead( socket ); @@ -512,12 +513,13 @@ CookieRef::_Remove( int socket ) } /* Forward */ void -CookieRef::pushConnectEvent( int socket, int nPlayersH, int nPlayersS, +CookieRef::pushConnectEvent( int socket, int clientVersion, int nPlayersH, int nPlayersS, int seed, in_addr& addr ) { CRefEvent evt; evt.type = XWE_DEVCONNECT; evt.u.con.socket = socket; + evt.u.con.clientVersion = clientVersion; evt.u.con.srcID = HOST_ID_NONE; evt.u.con.nPlayersH = nPlayersH; evt.u.con.nPlayersS = nPlayersS; @@ -527,11 +529,13 @@ CookieRef::pushConnectEvent( int socket, int nPlayersH, int nPlayersS, } /* pushConnectEvent */ void -CookieRef::pushReconnectEvent( int socket, HostID srcID, int nPlayersH, - int nPlayersS, int seed, in_addr& addr ) +CookieRef::pushReconnectEvent( int socket, int clientVersion, HostID srcID, + int nPlayersH, int nPlayersS, int seed, + in_addr& addr ) { CRefEvent evt( XWE_RECONNECT ); evt.u.con.socket = socket; + evt.u.con.clientVersion = clientVersion; evt.u.con.srcID = srcID; evt.u.con.nPlayersH = nPlayersH; evt.u.con.nPlayersS = nPlayersS; @@ -872,9 +876,10 @@ CookieRef::increasePlayerCounts( CRefEvent* evt, bool reconn, HostID* hidp ) assert( m_nPlayersHere <= m_nPlayersSought ); } - evt->u.con.srcID = DBMgr::Get()->AddDevice( ConnName(), evt->u.con.srcID, - nPlayersH, seed, - evt->u.con.addr, reconn ); + evt->u.con.srcID = + DBMgr::Get()->AddDevice( ConnName(), evt->u.con.srcID, + evt->u.con.clientVersion, nPlayersH, seed, + evt->u.con.addr, reconn ); HostID hostid = evt->u.con.srcID; if ( NULL != hidp ) { diff --git a/xwords4/relay/cref.h b/xwords4/relay/cref.h index 65618fe40..d5949f82c 100644 --- a/xwords4/relay/cref.h +++ b/xwords4/relay/cref.h @@ -119,9 +119,10 @@ class CookieRef { static void Delete( CookieID cid ); static void Delete( const char* name ); - bool _Connect( int socket, int nPlayersH, int nPlayersS, int seed, - bool seenSeed, in_addr& addr ); - bool _Reconnect( int socket, HostID srcID, int nPlayersH, int nPlayersS, + bool _Connect( int socket, int clientVersion, int nPlayersH, int nPlayersS, + int seed, bool seenSeed, in_addr& addr ); + bool _Reconnect( int socket, int clientVersion, HostID srcID, + int nPlayersH, int nPlayersS, int seed, in_addr& addr, bool gameDead ); void _HandleAck( HostID hostID ); void _PutMsg( HostID srcID, in_addr& addr, HostID destID, unsigned char* buf, int buflen ); @@ -155,6 +156,7 @@ class CookieRef { } fwd; struct { int socket; + int clientVersion; int nPlayersH; int nPlayersS; int seed; @@ -193,9 +195,9 @@ class CookieRef { bool cascade ); void send_msg( int socket, HostID id, XWRelayMsg msg, XWREASON why, bool cascade ); - void pushConnectEvent( int socket, int nPlayersH, int nPlayersS, + void pushConnectEvent( int socket, int clientVersion, int nPlayersH, int nPlayersS, int seed, in_addr& addr ); - void pushReconnectEvent( int socket, HostID srcID, + void pushReconnectEvent( int socket, int clientVersion, HostID srcID, int nPlayersH, int nPlayersS, int seed, in_addr& addr ); void pushHeartbeatEvent( HostID id, int socket ); diff --git a/xwords4/relay/crefmgr.cpp b/xwords4/relay/crefmgr.cpp index e1f2888e5..b7a18ba8b 100644 --- a/xwords4/relay/crefmgr.cpp +++ b/xwords4/relay/crefmgr.cpp @@ -589,11 +589,13 @@ CookieMapIterator::Next() ////////////////////////////////////////////////////////////////////////////// /* connect case */ -SafeCref::SafeCref( const char* cookie, int socket, int nPlayersH, int nPlayersS, +SafeCref::SafeCref( const char* cookie, int socket, int clientVersion, + int nPlayersH, int nPlayersS, unsigned short gameSeed, int langCode, bool wantsPublic, bool makePublic ) : m_cinfo( NULL ) , m_mgr( CRefMgr::Get() ) + , m_clientVersion( clientVersion ) , m_isValid( false ) , m_seenSeed( false ) { @@ -613,11 +615,12 @@ SafeCref::SafeCref( const char* cookie, int socket, int nPlayersH, int nPlayersS /* REconnect case */ SafeCref::SafeCref( const char* connName, const char* cookie, HostID hid, - int socket, int nPlayersH, int nPlayersS, + int socket, int clientVersion, int nPlayersH, int nPlayersS, unsigned short gameSeed, int langCode, bool wantsPublic, bool makePublic ) : m_cinfo( NULL ) , m_mgr( CRefMgr::Get() ) + , m_clientVersion( clientVersion ) , m_isValid( false ) { CidInfo* cinfo; diff --git a/xwords4/relay/crefmgr.h b/xwords4/relay/crefmgr.h index 8596a1d5c..cc19b0663 100644 --- a/xwords4/relay/crefmgr.h +++ b/xwords4/relay/crefmgr.h @@ -171,12 +171,13 @@ class SafeCref { public: /* for connect */ - SafeCref( const char* cookie, int socket, int nPlayersH, int nPlayersS, + SafeCref( const char* cookie, int socket, int clientVersion, + int nPlayersH, int nPlayersS, unsigned short gameSeed, int langCode, bool wantsPublic, bool makePublic ); /* for reconnect */ SafeCref( const char* connName, const char* cookie, HostID hid, - int socket, int nPlayersH, int nPlayersS, + int socket, int clientVersion, int nPlayersH, int nPlayersS, unsigned short gameSeed, int langCode, bool wantsPublic, bool makePublic ); SafeCref( const char* const connName ); @@ -209,7 +210,7 @@ class SafeCref { if ( IsValid() ) { CookieRef* cref = m_cinfo->GetRef(); assert( 0 != cref->GetCid() ); - return cref->_Connect( socket, nPlayersH, nPlayersS, seed, + return cref->_Connect( socket, m_clientVersion, nPlayersH, nPlayersS, seed, m_seenSeed, addr ); } else { return false; @@ -225,7 +226,7 @@ class SafeCref { if ( m_dead ) { *errp = XWRELAY_ERROR_DEADGAME; } else { - success = cref->_Reconnect( socket, srcID, nPlayersH, + success = cref->_Reconnect( socket, m_clientVersion, srcID, nPlayersH, nPlayersS, seed, addr, m_dead ); } } @@ -385,6 +386,7 @@ class SafeCref { private: CidInfo* m_cinfo; CRefMgr* m_mgr; + int m_clientVersion; bool m_isValid; bool m_locked; bool m_dead; diff --git a/xwords4/relay/dbmgr.cpp b/xwords4/relay/dbmgr.cpp index 38dab2781..63a536bc3 100644 --- a/xwords4/relay/dbmgr.cpp +++ b/xwords4/relay/dbmgr.cpp @@ -238,7 +238,7 @@ DBMgr::AllDevsAckd( const char* const connName ) } HostID -DBMgr::AddDevice( const char* connName, HostID curID, int nToAdd, +DBMgr::AddDevice( const char* connName, HostID curID, int clientVersion, int nToAdd, unsigned short seed, const in_addr& addr, bool ackd ) { HostID newID = curID; @@ -255,11 +255,12 @@ DBMgr::AddDevice( const char* connName, HostID curID, int nToAdd, assert( newID <= 4 ); const char* fmt = "UPDATE " GAMES_TABLE " SET nPerDevice[%d] = %d," + " clntVers[%d] = %d," " seeds[%d] = %d, addrs[%d] = \'%s\', mtimes[%d]='now', ack[%d]=\'%c\'" " WHERE connName = '%s'"; char query[256]; - snprintf( query, sizeof(query), fmt, newID, nToAdd, newID, seed, - newID, inet_ntoa(addr), newID, + snprintf( query, sizeof(query), fmt, newID, nToAdd, newID, clientVersion, + newID, seed, newID, inet_ntoa(addr), newID, newID, ackd?'A':'a', connName ); logf( XW_LOGINFO, "%s: query: %s", __func__, query ); diff --git a/xwords4/relay/dbmgr.h b/xwords4/relay/dbmgr.h index 8aad5bc88..0b0fb6abc 100644 --- a/xwords4/relay/dbmgr.h +++ b/xwords4/relay/dbmgr.h @@ -55,7 +55,7 @@ class DBMgr { char* connNameBuf, int bufLen, int* nPlayersHP ); bool AllDevsAckd( const char* const connName ); - HostID AddDevice( const char* const connName, HostID curID, + HostID AddDevice( const char* const connName, HostID curID, int clientVersion, int nToAdd, unsigned short seed, const in_addr& addr, bool unAckd ); void NoteAckd( const char* const connName, HostID id ); HostID HIDForSeed( const char* const connName, unsigned short seed ); diff --git a/xwords4/relay/scripts/showinplay.sh b/xwords4/relay/scripts/showinplay.sh index 70918f7aa..97eb64766 100755 --- a/xwords4/relay/scripts/showinplay.sh +++ b/xwords4/relay/scripts/showinplay.sh @@ -26,7 +26,7 @@ QUERY="WHERE NOT -NTOTAL = sum_array(nperdevice)" echo "Device (pid) count: $(pidof xwords | wc | awk '{print $2}')" echo "Row count:" $(psql -t xwgames -c "select count(*) FROM games $QUERY;") -echo "SELECT dead,connname,cid,room,lang,ntotal,nperdevice,seeds,ack,nsent "\ +echo "SELECT dead,connname,cid,room,lang,ntotal,clntVers,nperdevice,seeds,ack,nsent "\ "FROM games $QUERY ORDER BY NOT dead, connname LIMIT $LIMIT;" \ | psql xwgames diff --git a/xwords4/relay/xwrelay.cpp b/xwords4/relay/xwrelay.cpp index 77a943a90..a94a157f4 100644 --- a/xwords4/relay/xwrelay.cpp +++ b/xwords4/relay/xwrelay.cpp @@ -267,10 +267,27 @@ readStr( unsigned char** bufp, const unsigned char* end, } /* readStr */ static XWREASON -flagsOK( unsigned char flags ) +flagsOK( unsigned char** bufp, unsigned char const* end, + unsigned short* clientVersion ) { - return flags == XWRELAY_PROTO_VERSION ? - XWRELAY_ERROR_NONE : XWRELAY_ERROR_OLDFLAGS; + XWREASON err = XWRELAY_ERROR_OLDFLAGS; + unsigned char flags; + if ( getNetByte( bufp, end, &flags ) ) { + switch ( flags ) { + case XWRELAY_PROTO_VERSION_CLIENTVERS: + if ( getNetShort( bufp, end, clientVersion ) ) { + err = XWRELAY_ERROR_NONE; + } + break; + case XWRELAY_PROTO_VERSION_NOCLIENT: + *clientVersion = 0; + err = XWRELAY_ERROR_NONE; + break; + default: + break; + } + } + return err; } /* flagsOK */ void @@ -328,8 +345,8 @@ processConnect( unsigned char* bufp, int bufLen, int socket, in_addr& addr ) cookie[0] = '\0'; - unsigned char flags = *bufp++; - XWREASON err = flagsOK( flags ); + unsigned short clientVersion; + XWREASON err = flagsOK( &bufp, end, &clientVersion ); if ( err == XWRELAY_ERROR_NONE ) { /* HostID srcID; */ unsigned char nPlayersH; @@ -354,7 +371,7 @@ processConnect( unsigned char* bufp, int bufLen, int socket, in_addr& addr ) static pthread_mutex_t s_newCookieLock = PTHREAD_MUTEX_INITIALIZER; MutexLock ml( &s_newCookieLock ); - SafeCref scr( cookie, socket, nPlayersH, nPlayersT, + SafeCref scr( cookie, socket, clientVersion, nPlayersH, nPlayersT, seed, langCode, wantsPublic, makePublic ); /* nPlayersT etc could be slots in SafeCref to avoid passing here */ @@ -378,8 +395,8 @@ processReconnect( unsigned char* bufp, int bufLen, int socket, in_addr& addr ) logf( XW_LOGINFO, "%s()", __func__ ); - unsigned char flags = *bufp++; - XWREASON err = flagsOK( flags ); + unsigned short clientVersion; + XWREASON err = flagsOK( &bufp, end, &clientVersion ); if ( err == XWRELAY_ERROR_NONE ) { char cookie[MAX_INVITE_LEN+1]; char connName[MAX_CONNNAME_LEN+1] = {0}; @@ -401,7 +418,7 @@ processReconnect( unsigned char* bufp, int bufLen, int socket, in_addr& addr ) && readStr( &bufp, end, connName, sizeof(connName) ) ) { SafeCref scr( connName[0]? connName : NULL, - cookie, srcID, socket, nPlayersH, + cookie, srcID, socket, clientVersion, nPlayersH, nPlayersT, gameSeed, langCode, wantsPublic, makePublic ); success = scr.Reconnect( socket, srcID, nPlayersH, nPlayersT, diff --git a/xwords4/relay/xwrelay.h b/xwords4/relay/xwrelay.h index 4bad2a6ee..312bf2b7c 100644 --- a/xwords4/relay/xwrelay.h +++ b/xwords4/relay/xwrelay.h @@ -120,7 +120,8 @@ typedef unsigned char XWRELAY_Cmd; #define XWRELAY_PROTO_VERSION_LATE_NAME 0x02 #define XWRELAY_PROTO_VERSION_LATE_COOKIEID 0x03 #define XWRELAY_PROTO_VERSION_NOCLIENT 0x04 -#define XWRELAY_PROTO_VERSION XWRELAY_PROTO_VERSION_NOCLIENT +#define XWRELAY_PROTO_VERSION_CLIENTVERS 0x05 +#define XWRELAY_PROTO_VERSION XWRELAY_PROTO_VERSION_CLIENTVERS /* Errors passed with denied */ #ifndef CANT_DO_TYPEDEF diff --git a/xwords4/relay/xwrelay.sh b/xwords4/relay/xwrelay.sh index f58caf835..01e8d2df3 100755 --- a/xwords4/relay/xwrelay.sh +++ b/xwords4/relay/xwrelay.sh @@ -50,6 +50,7 @@ cid integer ,pub BOOLEAN ,connName VARCHAR(64) UNIQUE PRIMARY KEY ,nTotal INTEGER +,clntVers INTEGER[] ,nPerDevice INTEGER[] ,seeds INTEGER[] ,ack VARCHAR(1)[]