From 3bdfda6548b4a53e2e047f199ccbd4f6ad0749b8 Mon Sep 17 00:00:00 2001 From: ehouse Date: Mon, 2 Nov 2009 01:01:47 +0000 Subject: [PATCH] Make it possible for multiple games to connect using the same room name. All new connections are stored together, and after each connection an attempt is made to build one complete game with a host and however many guests. All remaining devices are moved into a new pending record in the same state, and the completed game is treated as always. Seems to work, though nearly 20% of linux instances are failing to connect the relay run from the new test script samename.sh. Need to figure out why. Also added logging of seed and connname to comms.c since games launched together can no longer be certain to connect on the relay. This allows the test script to identify joined games from their logs and detect success or failure. This checkin changes the relay protocol, so relay and clients will both need to be upgraded. --- xwords4/common/comms.c | 11 +- xwords4/relay/cref.cpp | 292 ++++++++++++++++++++++++++++++++------ xwords4/relay/cref.h | 7 +- xwords4/relay/crefmgr.cpp | 55 +++++-- xwords4/relay/crefmgr.h | 7 +- xwords4/relay/states.cpp | 6 + xwords4/relay/states.h | 6 + xwords4/relay/xwrelay.h | 15 +- 8 files changed, 334 insertions(+), 65 deletions(-) diff --git a/xwords4/common/comms.c b/xwords4/common/comms.c index c0a639e65..acb508160 100644 --- a/xwords4/common/comms.c +++ b/xwords4/common/comms.c @@ -820,6 +820,7 @@ getChannelSeed( CommsCtxt* comms ) { while ( comms->channelSeed == 0 ) { comms->channelSeed = XP_RANDOM(); + XP_LOGF( "%s: channelSeed: %.4X", __func__, comms->channelSeed ); } return comms->channelSeed; } @@ -1082,6 +1083,7 @@ relayPreProcess( CommsCtxt* comms, XWStreamCtxt* stream, XWHostID* senderID ) XWHostID destID, srcID; CookieID cookieID; XP_U8 relayErr; + XP_U16 nHere, nSought; /* nothing for us to do here if not using relay */ XWRELAY_Cmd cmd = stream_getU8( stream ); @@ -1092,8 +1094,10 @@ relayPreProcess( CommsCtxt* comms, XWStreamCtxt* stream, XWHostID* senderID ) case XWRELAY_RECONNECT_RESP: set_relay_state( comms, COMMS_RELAYSTATE_CONNECTED ); comms->r.heartbeat = stream_getU16( stream ); - comms->r.cookieID = stream_getU16( stream ); - XP_LOGF( "set cookieID = %d", comms->r.cookieID ); + nHere = (XP_U16)stream_getU8( stream ); + nSought = (XP_U16)stream_getU8( stream ); + /* This may belong as an alert to user so knows has connected. */ + XP_LOGF( "%s: have %d of %d players", __func__, nHere, nSought ); setHeartbeatTimer( comms ); break; @@ -1103,6 +1107,8 @@ relayPreProcess( CommsCtxt* comms, XWStreamCtxt* stream, XWHostID* senderID ) || comms->r.myHostID == srcID ); comms->r.myHostID = srcID; XP_LOGF( "set hostid: %x", comms->r.myHostID ); + comms->r.cookieID = stream_getU16( stream ); + XP_LOGF( "set cookieID = %d", comms->r.cookieID ); #ifdef DEBUG { @@ -1111,6 +1117,7 @@ relayPreProcess( CommsCtxt* comms, XWStreamCtxt* stream, XWHostID* senderID ) XP_ASSERT( comms->r.connName[0] == '\0' || 0 == XP_STRCMP( comms->r.connName, connName ) ); XP_MEMCPY( comms->r.connName, connName, sizeof(comms->r.connName) ); + XP_LOGF( "%s: connName: \"%s\"", __func__, connName ); } #else stringFromStreamHere( stream, comms->r.connName, diff --git a/xwords4/relay/cref.cpp b/xwords4/relay/cref.cpp index c011e6872..4d2c35766 100644 --- a/xwords4/relay/cref.cpp +++ b/xwords4/relay/cref.cpp @@ -265,14 +265,7 @@ CookieRef::GameOpen( const char* cookie, int nPlayersH, bool isNew, /* do nothing; reject */ logf( XW_LOGINFO, "reject: bad state %s", stateString(m_curState) ); } else { - if ( m_nPlayersSought == 0 ) { - accept = true; - } else if ( nPlayersH + m_nPlayersHere <= m_nPlayersSought ) { - accept = true; - } else { - logf( XW_LOGINFO, "reject: m_nPlayersSought=%d, m_nPlayersHere=%d", - m_nPlayersSought, m_nPlayersHere ); - } + accept = true; } /* Error to connect if cookie doesn't match. */ @@ -570,7 +563,11 @@ CookieRef::handleEvents() cancelAllConnectedTimer(); assignHostIds(); assignConnName(); - sendAllHere( ); + sendAllHere(); + break; + + case XWA_POSTCLONE: + moveSockets(); break; case XWA_REJECT: @@ -579,7 +576,6 @@ CookieRef::handleEvents() /* nothing to do for these */ break; - default: assert(0); break; @@ -615,31 +611,230 @@ putNetShort( unsigned char** bufpp, unsigned short s ) *bufpp += sizeof(s); } +/* static bool */ +/* hostRecComp( const HostRec& aa, const HostRec& bb ) */ +/* { */ +/* /\* first order, hosts go before guests; second order, by m_nPlayersHere *\/ */ +/* /\* if ( aa.m_nPlayersS *\/ */ + +/* return aa.m_nPlayersH < bb.m_nPlayersH; */ +/* } */ + +static void +print_sockets( const char* caller, vector& sockets ) +{ + logf( XW_LOGINFO, " %s from %s", __func__, caller ); + vector::iterator iter; + for ( iter = sockets.begin(); iter != sockets.end(); ++iter ) { + logf( XW_LOGINFO, "isHost: %d; nPlayersH: %d; seed=%.4X; socket: %d", + iter->m_nPlayersS > 0, iter->m_nPlayersH, iter->m_seed, + iter->m_socket ); + } +} /* print_sockets */ + +#define MAX_PER_SIZE 16 /* need to enforce this when connections arrive */ +bool +CookieRef::tryMakeGame( vector& remaining ) +{ + assert( remaining.size() == 0 ); + int nHosts = 0; + int nGuests; + bool complete = false; + unsigned int nRecords = m_sockets.size(); + int ii; + + /* m_sockets is sorted with hosts first, guests after, and within each in + descending order by number of players provided. Start by finding where + the host/guest break is. */ + vector::iterator iter; + for ( iter = m_sockets.begin(); iter != m_sockets.end(); ++iter ) { + if ( iter->m_nPlayersS == 0 ) { + break; + } + ++nHosts; + } + logf( XW_LOGINFO, "%s: there are %d hosts", __func__, nHosts ); + + nGuests = m_sockets.size() - nHosts; + int inUse[nGuests]; + int nUsed; + + /* Now for each host, try to give him a set of guests. Assumption: + there's no way we can find more than one complete game here since we + try after every host addition. The algorithm, which assumes guests are + sorted from most-players-provided down, is to try the largest numbers + first, skipping each that won't fit. If we fail, we try again starting + from the next host. + */ + int hostIndex; + for ( hostIndex = 0; hostIndex < nHosts; ++hostIndex ) { + unsigned int firstGuest; + for ( firstGuest = nHosts; firstGuest < nRecords; ++firstGuest ) { + int sought = m_sockets[hostIndex].m_nPlayersS - m_sockets[hostIndex].m_nPlayersH; + + nUsed = 0; + for ( ii = (int)firstGuest; ii < (int)nRecords; ++ii ) { + int one = m_sockets[ii].m_nPlayersH; + if ( one <= sought ) { /* not too big? */ + sought -= one; + inUse[nUsed++] = ii; + if ( sought == 0 ) { + complete = true; + goto loop_end; + } + } + } + } + } + loop_end: + + /* If we have a full compliment of devices now, remove all the others into + remaining */ + if ( complete ) { + int nRemaining = nRecords-nUsed-1; /* -1 for host */ + int lastUsed = nUsed-1; + + /* guest[s] first */ + for ( ii = nRecords - 1; ii >= nHosts; --ii ) { + if ( ii == inUse[lastUsed] ) { + --lastUsed; + } else { + assert( nRemaining > 0 ); + HostRec hr = m_sockets[ii]; + m_nPlayersHere -= hr.m_nPlayersH; + assert( hr.m_nPlayersS == 0 ); + remaining.insert( remaining.begin(), hr ); /* insert at start */ + --nRemaining; + m_sockets.erase( m_sockets.begin() + ii ); + } + } + + /* now remove the host we chose */ + for ( ii = nHosts - 1; ii >= 0; --ii ) { + if ( ii != hostIndex ) { + assert( nRemaining > 0 ); + HostRec hr = m_sockets[ii]; + m_nPlayersHere -= hr.m_nPlayersH; + m_nPlayersSought -= hr.m_nPlayersS; + remaining.insert( remaining.begin(), hr ); /* insert at start */ + --nRemaining; + m_sockets.erase( m_sockets.begin() + ii ); + } + } + + assert( 0 == nRemaining ); + } + + print_sockets( __func__, m_sockets ); + print_sockets( __func__, remaining ); + + assert( remaining.size() + m_sockets.size() == nRecords ); + + logf( XW_LOGINFO, "%s => %d", __func__, complete ); + return complete; +} /* tryMakeGame */ + +/* Maintain order first by whether is host or not, and then by number of + players provided */ +void +CookieRef::insertSorted( HostRec hr ) +{ + bool newIsHost = hr.m_nPlayersS > 0; + int newPlayersH = hr.m_nPlayersH; + + vector::iterator iter; + for ( iter = m_sockets.begin(); iter != m_sockets.end(); ++iter ) { + bool curIsHost = iter->m_nPlayersS > 0; + bool belongsBySort = newPlayersH <= iter->m_nPlayersH; + + /* We're done when we're inserting a host and have found a guest; or + when we're in the right region (host or guest) and the secondary + sort says do-it.*/ + + if ( newIsHost && !curIsHost ) { + break; + } else if ( (newIsHost == curIsHost) && belongsBySort ) { + break; + } + } + + m_sockets.insert( iter, hr ); + logf( XW_LOGINFO, "m_sockets.size() now %d", m_sockets.size() ); + print_sockets( __func__, m_sockets ); + + m_nPlayersHere += hr.m_nPlayersH; + m_nPlayersSought += hr.m_nPlayersS; +} /* insertSorted */ + +void +CookieRef::populate( vector hosts ) +{ + /* copy enough state that it can live on own */ + m_sockets = hosts; + m_curState = XWS_CLONED; + + vector::iterator iter; + for ( iter = m_sockets.begin(); iter != m_sockets.end(); ++iter ) { + m_nPlayersSought += iter->m_nPlayersS; + m_nPlayersHere += iter->m_nPlayersH; + } + print_sockets( __func__, m_sockets ); +} + void CookieRef::increasePlayerCounts( const CRefEvent* evt ) { int nPlayersH = evt->u.con.nPlayersH; int nPlayersS = evt->u.con.nPlayersS; + int socket = evt->u.con.socket; HostID hid = evt->u.con.srcID; + int seed = evt->u.con.seed; assert( hid <= 4 ); - logf( XW_LOGINFO, "%s: hid=%d, nPlayersH=%d, ", __func__, - "nPlayersS=%d", hid, nPlayersH, nPlayersS ); + logf( XW_LOGINFO, "%s: hid=%d, nPlayersH=%d, " + "nPlayersS=%d", __func__, hid, nPlayersH, nPlayersS ); - if ( hid == HOST_ID_SERVER ) { - assert( m_nPlayersSought == 0 ); - m_nPlayersSought = nPlayersS; - } else { - assert( nPlayersS == 0 ); /* should catch this earlier!!! */ - assert( m_nPlayersSought == 0 || m_nPlayersHere <= m_nPlayersSought ); + /* Up to this point we should just be adding host records to this cref. + Maybe even including multiple hosts. Now we go through what we have + and try to build a game. sendResponse() is where new hostrecs are + actually added. That seems broken! */ + + ASSERT_LOCKED(); + + /* first add the rec here, whether it'll stay for not */ + logf( XW_LOGINFO, "%s: remembering pair: hostid=%x, socket=%d (size=%d)", + __func__, hid, socket, m_sockets.size()); + HostRec hr( hid, socket, nPlayersH, nPlayersS, seed ); + + insertSorted( hr ); + + vector remaining; + bool gameComplete = tryMakeGame( remaining ); + + /* If we built a game but had leftover HostRecs, they're now in remaining. + Build a new cref for them, and process its first event now. It'll then + be ready to receive new messages, e.g. new connections. */ + if ( remaining.size() > 0 ) { + CookieRef* clone = CRefMgr::Get()->Clone( this ); + assert( !!clone ); + clone->Lock(); + + clone->populate( remaining ); + + assert( clone->m_eventQueue.size() == 0 ); + CRefEvent evt; + evt.type = XWE_CLONECHKMSG; + clone->m_eventQueue.push_back( evt ); + clone->handleEvents(); + + clone->Unlock(); } - m_nPlayersHere += nPlayersH; logf( XW_LOGVERBOSE1, "%s: here=%d; total=%d", __func__, m_nPlayersHere, m_nPlayersSought ); CRefEvent newevt; - if ( m_nPlayersHere == m_nPlayersSought ) { + if ( gameComplete ) { newevt.type = XWE_ALLHERE; m_gameFull = true; } else { @@ -657,7 +852,7 @@ CookieRef::reducePlayerCounts( int socket ) for ( iter = m_sockets.begin(); iter != m_sockets.end(); ++iter ) { if ( iter->m_socket == socket ) { if ( iter->m_hostID == HOST_ID_SERVER ) { - m_nPlayersSought = 0; + m_nPlayersSought -= iter->m_nPlayersS; } else { assert( iter->m_nPlayersS == 0 ); } @@ -677,12 +872,16 @@ void CookieRef::checkCounts( const CRefEvent* evt ) { int nPlayersH = evt->u.con.nPlayersH; + int nPlayersS = evt->u.con.nPlayersS; HostID hid = evt->u.con.srcID; bool success; - logf( XW_LOGVERBOSE1, "checkCounts: hid=%d, nPlayers=%d, m_nPlayersSought = %d, " - "m_nPlayersHere = %d", - hid, nPlayersH, m_nPlayersSought, m_nPlayersHere ); + logf( XW_LOGVERBOSE1, "%s: hid:%d; nPlayersS:%d; nPlayersH:%d; " + "m_nPlayersSought:%d; m_nPlayersHere:%d", __func__, + hid, nPlayersS, nPlayersH, m_nPlayersSought, m_nPlayersHere ); + + /* increasePlayerCounts() is where we actually increase the numbers. Is + that right? */ if ( hid == HOST_ID_SERVER ) { success = m_nPlayersSought == 0; @@ -694,7 +893,7 @@ CookieRef::checkCounts( const CRefEvent* evt ) CRefEvent newevt; if ( success ) { - newevt = *evt; + newevt = *evt; /* this is a gross hack!!! */ newevt.type = XWE_OKTOSEND; } else { newevt.type = XWE_COUNTSBAD; @@ -721,30 +920,20 @@ void CookieRef::sendResponse( const CRefEvent* evt, bool initial ) { int socket = evt->u.con.socket; - HostID hid = evt->u.con.srcID; - int nPlayersH = evt->u.con.nPlayersH; - int nPlayersS = evt->u.con.nPlayersS; - int seed = evt->u.con.seed; - - ASSERT_LOCKED(); - - logf( XW_LOGINFO, "%s: remembering pair: hostid=%x, socket=%d (size=%d)", - __func__, hid, socket, m_sockets.size()); - HostRec hr(hid, socket, nPlayersH, nPlayersS, seed ); - m_sockets.push_back( hr ); - logf( XW_LOGINFO, "m_sockets.size() now %d", m_sockets.size() ); /* Now send the response */ - unsigned char buf[1 + /* cmd */ - sizeof(short) + /* heartbeat */ - sizeof(CookieID) + unsigned char buf[1 /* cmd */ + + sizeof(short) /* heartbeat */ + + sizeof(short) /* total here */ + + sizeof(short) /* total expected */ ]; unsigned char* bufp = buf; *bufp++ = initial ? XWRELAY_CONNECT_RESP : XWRELAY_RECONNECT_RESP; putNetShort( &bufp, GetHeartbeat() ); - putNetShort( &bufp, GetCookieID() ); + *bufp++ = GetPlayersHere(); + *bufp++ = GetPlayersSought(); send_with_length( socket, buf, bufp - buf, true ); logf( XW_LOGVERBOSE0, "sent %s", cmdToStr( XWRELAY_Cmd(buf[0]) ) ); @@ -814,16 +1003,35 @@ CookieRef::notifyOthers( int socket, XWRelayMsg msg, XWREASON why ) } } /* notifyOthers */ +void +CookieRef::moveSockets( void ) +{ + ASSERT_LOCKED(); + + vector sockets; + vector::iterator iter; + for ( iter = m_sockets.begin(); iter != m_sockets.end(); ++iter ) { + sockets.push_back( iter->m_socket ); + } + + CRefMgr::Get()->MoveSockets( sockets, this ); +} + void CookieRef::sendAllHere( void ) { - unsigned char buf[1 + 1 + 1 + MAX_CONNNAME_LEN]; + unsigned char buf[1 + 1 /* hostID */ + + sizeof(CookieID) + + 1 + MAX_CONNNAME_LEN]; + unsigned char* bufp = buf; unsigned char* idLoc; *bufp++ = XWRELAY_ALLHERE; idLoc = bufp++; /* space for hostId, remembering address */ + putNetShort( &bufp, GetCookieID() ); + const char* connName = ConnName(); assert( !!connName && connName[0] ); int len = strlen( connName ); diff --git a/xwords4/relay/cref.h b/xwords4/relay/cref.h index 7a3d3ddd9..c0d16524b 100644 --- a/xwords4/relay/cref.h +++ b/xwords4/relay/cref.h @@ -82,7 +82,7 @@ class CookieRef { int CountSockets() { return m_sockets.size(); } bool HasSocket( int socket ); bool HasSocket_locked( int socket ); - const char* Cookie() { return m_cookie.c_str(); } + const char* Cookie() const { return m_cookie.c_str(); } const char* ConnName() { return m_connName.c_str(); } int GetHeartbeat() { return m_heatbeat; } @@ -185,6 +185,7 @@ class CookieRef { void handleEvents(); void sendResponse( const CRefEvent* evt, bool initial ); + void populate( vector hosts ); void increasePlayerCounts( const CRefEvent* evt ); void reducePlayerCounts( int socket ); void checkCounts( const CRefEvent* evt ); @@ -201,6 +202,7 @@ class CookieRef { void notifyDisconn(const CRefEvent* evt); void removeSocket( int socket ); void sendAllHere( void ); + void moveSockets( void ); bool SeedBelongs( int gameSeed ); bool SeedsBelong( const char* connName ); void assignConnName( void ); @@ -212,6 +214,9 @@ class CookieRef { bool notInUse(void) { return m_cookieID == 0; } + bool tryMakeGame( vector& remaining ); + void insertSorted( HostRec hr ); + /* timer callback */ static void s_checkAllConnected( void* closure ); diff --git a/xwords4/relay/crefmgr.cpp b/xwords4/relay/crefmgr.cpp index eb794c794..c7d176fc3 100644 --- a/xwords4/relay/crefmgr.cpp +++ b/xwords4/relay/crefmgr.cpp @@ -130,8 +130,9 @@ CRefMgr::FindOpenGameFor( const char* cookie, const char* connName, HostID hid, int socket, int nPlayersH, int nPlayersT, int gameSeed, bool* alreadyHere ) { - logf( XW_LOGINFO, "%s(cookie=%s,connName=%s,hid=%d,seed=%x,socket=%d)", __func__, - cookie, connName, hid, gameSeed, socket ); + logf( XW_LOGINFO, "%s(cookie=%s,connName=%s,hid=%d,seed=%x,socket=%d," + "here=%d,total=%d)", __func__, cookie, connName, hid, gameSeed, + socket, nPlayersH, nPlayersT ); CookieRef* found = NULL; if ( !!cookie || !!connName ) { /* drop if both are null */ @@ -149,7 +150,8 @@ CRefMgr::FindOpenGameFor( const char* cookie, const char* connName, if ( cref->Lock() ) { assert( !cookie || 0 == strcmp( cookie, cref->Cookie() ) ); if ( cref->SeedBelongs( gameSeed ) ) { - logf( XW_LOGINFO, "%s: SeedBelongs: dup packet?", __func__ ); + logf( XW_LOGINFO, "%s: SeedBelongs: dup packet?", + __func__ ); *alreadyHere = true; found = cref; } else if ( cref->GameOpen( cookie, nPlayersH, @@ -312,7 +314,7 @@ CRefMgr::getMakeCookieRef_locked( const char* cookie, const char* connName, cref = FindOpenGameFor( cookie, connName, hid, socket, nPlayersH, nPlayersT, gameSeed, &alreadyHere ); if ( cref == NULL && !alreadyHere ) { - cref = AddNew( cookie, connName, gameSeed, nextCID( NULL ) ); + cref = AddNew( cookie, connName, nextCID( NULL ) ); } return cref; @@ -321,8 +323,14 @@ CRefMgr::getMakeCookieRef_locked( const char* cookie, const char* connName, bool CRefMgr::Associate( int socket, CookieRef* cref ) { - bool isNew = false; MutexLock ml( &m_SocketStuffMutex ); + return Associate_locked( socket, cref ); +} + +bool +CRefMgr::Associate_locked( int socket, CookieRef* cref ) +{ + bool isNew = false; SocketMap::iterator iter = m_SocketStuff.find( socket ); /* This isn't enough. Must provide a way to reuse sockets should a genuinely different connection appear. Now maybe we already remove @@ -341,9 +349,8 @@ CRefMgr::Associate( int socket, CookieRef* cref ) } void -CRefMgr::Disassociate( int socket, CookieRef* cref ) +CRefMgr::Disassociate_locked( int socket, CookieRef* cref ) { - MutexLock ml( &m_SocketStuffMutex ); SocketMap::iterator iter = m_SocketStuff.find( socket ); if ( iter == m_SocketStuff.end() ) { logf( XW_LOGERROR, "can't find SocketStuff for socket %d", socket ); @@ -355,6 +362,32 @@ CRefMgr::Disassociate( int socket, CookieRef* cref ) } } +void +CRefMgr::Disassociate( int socket, CookieRef* cref ) +{ + MutexLock ml( &m_SocketStuffMutex ); + Disassociate_locked( socket, cref ); +} + +void +CRefMgr::MoveSockets( vector sockets, CookieRef* cref ) +{ + MutexLock ml( &m_SocketStuffMutex ); + vector::iterator iter; + for ( iter = sockets.begin(); iter != sockets.end(); ++iter ) { + Disassociate_locked( *iter, NULL ); + Associate_locked( *iter, cref ); + } +} + +CookieRef* +CRefMgr::Clone( const CookieRef* parent ) +{ + const char* cookie = parent->Cookie(); + CookieRef* cref = AddNew( cookie, NULL, nextCID( NULL ) ); + return cref; +} + #if 0 pthread_mutex_t* CRefMgr::GetWriteMutexForSocket( int socket ) @@ -441,11 +474,11 @@ CRefMgr::heartbeatProc( void* closure ) #endif CookieRef* -CRefMgr::AddNew( const char* cookie, const char* connName, int seed, - CookieID id ) +CRefMgr::AddNew( const char* cookie, const char* connName, CookieID id ) { - logf( XW_LOGINFO, "%s( cookie=%s, connName=%s, seed=%.4X, cid=%d)", __func__, - cookie, connName, seed, id ); + /* PENDING: should this return a locked cref? */ + logf( XW_LOGINFO, "%s( cookie=%s, connName=%s, cid=%d)", __func__, + cookie, connName, id ); CookieRef* ref = getFromFreeList(); diff --git a/xwords4/relay/crefmgr.h b/xwords4/relay/crefmgr.h index 6eb6ba65e..c20893080 100644 --- a/xwords4/relay/crefmgr.h +++ b/xwords4/relay/crefmgr.h @@ -97,7 +97,11 @@ class CRefMgr { /* Track sockets independent of cookie refs */ bool Associate( int socket, CookieRef* cref ); + bool Associate_locked( int socket, CookieRef* cref ); void Disassociate( int socket, CookieRef* cref ); + void Disassociate_locked( int socket, CookieRef* cref ); + void MoveSockets( vector sockets, CookieRef* cref ); + CookieRef* Clone( const CookieRef* parent ); pthread_mutex_t* GetWriteMutexForSocket( int socket ); void RemoveSocketRefs( int socket ); void PrintSocketInfo( int socket, string& out ); @@ -133,8 +137,7 @@ class CRefMgr { CookieRef* getCookieRef( int socket ); bool checkCookieRef_locked( CookieRef* cref ); CookieRef* getCookieRef_impl( CookieID cookieID ); - CookieRef* AddNew( const char* cookie, const char* connName, int seed, - CookieID id ); + CookieRef* AddNew( const char* cookie, const char* connName, CookieID id ); CookieRef* FindOpenGameFor( const char* cookie, const char* connName, HostID hid, int socket, int nPlayersH, int nPlayersS, int gameSeed, diff --git a/xwords4/relay/states.cpp b/xwords4/relay/states.cpp index 410bf9656..44c75e5aa 100644 --- a/xwords4/relay/states.cpp +++ b/xwords4/relay/states.cpp @@ -83,6 +83,9 @@ StateTable g_stateTable[] = { { XWS_CONNECTING, XWE_DISCONNMSG, XWA_DISCONNECT, XWS_CONNECTING }, { XWS_MISSING, XWE_DISCONNMSG, XWA_DISCONNECT, XWS_MISSING }, +/* Cloned state is added via code, not via table actions; new initial state */ +{ XWS_CLONED, XWE_CLONECHKMSG, XWA_POSTCLONE, XWS_CONNECTING }, + /* I'm seeing this but not sure how to handle. Might disconnect be needed now */ { XWS_MISSING, XWE_FORWARDMSG, XWA_DISCONNECT, XWS_MISSING }, @@ -178,6 +181,7 @@ stateString( XW_RELAY_STATE state ) CASESTR(XWS_CHKCOUNTS_INIT); CASESTR(XWS_CHKCOUNTS_MISS); CASESTR(XWS_CHKCOUNTS); + CASESTR(XWS_CLONED); default: assert(0); } @@ -212,6 +216,7 @@ eventString( XW_RELAY_EVENT evt ) CASESTR(XWE_OKTOSEND); CASESTR(XWE_COUNTSBAD); CASESTR(XWE_SHUTDOWN); + CASESTR(XWE_CLONECHKMSG); default: assert(0); } @@ -240,6 +245,7 @@ actString( XW_RELAY_ACTION act ) CASESTR(XWA_REMOVESOCKET); CASESTR(XWA_HEARTDISCONN); CASESTR(XWA_SHUTDOWN); + CASESTR(XWA_POSTCLONE); default: assert(0); } diff --git a/xwords4/relay/states.h b/xwords4/relay/states.h index a67776606..460443af9 100644 --- a/xwords4/relay/states.h +++ b/xwords4/relay/states.h @@ -35,6 +35,8 @@ enum { ,XWS_CHKCOUNTS_MISS /* from the missing state */ ,XWS_CHKCOUNTS /* check from any other state */ + ,XWS_CLONED /* just got duplicated */ + ,XWS_CHK_ALLHERE /* Need to see if all expected devices/players are on board. */ @@ -93,6 +95,8 @@ typedef enum { ,XWE_RECONNECTMSG /* A device is re-connecting using the connID for this object */ + ,XWE_CLONECHKMSG /* We've cloned; now clean up */ + ,XWE_DISCONNMSG /* disconnect socket from this game/cref */ ,XWE_FORWARDMSG /* A message needs forwarding */ @@ -132,6 +136,8 @@ typedef enum { ,XWA_SENDALLHERE /* Let all devices know we're in business */ ,XWA_SNDALLHERE_2 /* Ditto, but for a reconnect */ + ,XWA_POSTCLONE + ,XWA_FWD /* Forward a message */ ,XWA_NOTEHEART /* Record heartbeat received */ diff --git a/xwords4/relay/xwrelay.h b/xwords4/relay/xwrelay.h index 9aa2bb7bd..28055c313 100644 --- a/xwords4/relay/xwrelay.h +++ b/xwords4/relay/xwrelay.h @@ -51,19 +51,19 @@ enum { XWRELAY_NONE /* 0 is an illegal value */ , XWRELAY_CONNECT_RESP /* Sent from relay to device in response to XWRELAY_CONNECT. Format: - heartbeat_seconds: 2; connectionID: 2; connNameLen: 1; - connName*/ + heartbeat_seconds: 2; players_here: 1; players_sought: 1; */ , XWRELAY_RECONNECT_RESP /* Sent from relay to device in response to XWRELAY_RECONNECT. Format: - heartbeat_seconds: 2; */ + same as for XWRELAY_CONNECT_RESP */ , XWRELAY_ALLHERE /* Sent from relay when it enters the state where all expected devices are here (at start of new game or after having been gone for a while). Devices should not attempt to forward messages before this message is received or after XWRELAY_DISCONNECT_OTHER is received. - Format: hostID: 1; */ + Format: hostID: 1; connectionID: 2; connNameLen: 1; + connName; */ , XWRELAY_DISCONNECT_YOU /* Sent from relay when existing connection is terminated. @@ -103,9 +103,10 @@ typedef unsigned char XWRELAY_Cmd; #define MAX_MSG_LEN 256 /* 100 is more like it */ #define MAX_CONNNAME_LEN 48 /* host ID, boot time, and seeds as hex? */ -#define XWRELAY_PROTO_VERSION_ORIG 0x01 -#define XWRELAY_PROTO_VERSION_LATE_NAME 0x02 -#define XWRELAY_PROTO_VERSION XWRELAY_PROTO_VERSION_LATE_NAME +#define XWRELAY_PROTO_VERSION_ORIG 0x01 +#define XWRELAY_PROTO_VERSION_LATE_NAME 0x02 +#define XWRELAY_PROTO_VERSION_LATE_COOKIEID 0x03 +#define XWRELAY_PROTO_VERSION XWRELAY_PROTO_VERSION_LATE_COOKIEID /* Errors passed with denied */ #ifndef CANT_DO_TYPEDEF