major mod to deal with devices that fail to receive ACK and then

reconnect.  I was putting both (i.e. the same device twice) in the
same game.  Now I detect this based on the seed being duplicated and
treat the device as having failed to ACK then proceed with the CONNECT
as if it were new.  Tested pretty heavily but only with two-device
games.
This commit is contained in:
Eric House 2011-06-20 18:13:15 -07:00
parent 44af266db6
commit 85d484a881
9 changed files with 333 additions and 134 deletions

View file

@ -86,14 +86,13 @@ CookieRef::ReInit( const char* cookie, const char* connName, CookieID id,
m_cookie = cookie==NULL?"":cookie;
m_connName = connName==NULL?"":connName;
m_cookieID = id;
m_curState = XWS_INITED;
m_curState = XWS_EMPTY;
m_nPlayersSought = nPlayers;
m_nPlayersHere = nAlreadyHere;
m_locking_thread = 0;
m_starttime = uptime();
m_in_handleEvents = false;
m_langCode = langCode;
m_nPendingAcks = 0;
if ( RelayConfigs::GetConfigs()->GetValueFor( "SEND_DELAY_MILLIS",
&m_delayMicros ) ) {
@ -104,8 +103,13 @@ CookieRef::ReInit( const char* cookie, const char* connName, CookieID id,
RelayConfigs::GetConfigs()->GetValueFor( "HEARTBEAT", &m_heatbeat );
logf( XW_LOGINFO, "initing cref for cookie %s, connName %s",
m_cookie.c_str(), m_connName.c_str() );
}
unsigned int ii;
for ( ii = 0; ii < sizeof(m_timers)/sizeof(m_timers[0]); ++ii ) {
m_timers[ii].m_this = NULL;
m_timers[ii].m_hid = ii + 1;
}
}
CookieRef::CookieRef( const char* cookie, const char* connName, CookieID id,
int langCode, int nPlayersT, int nAlreadyHere )
@ -176,19 +180,31 @@ CookieRef::Unlock() {
}
bool
CookieRef::_Connect( int socket, int nPlayersH, int nPlayersS, int seed )
CookieRef::_Connect( int socket, int nPlayersH, int nPlayersS, int seed,
bool seenSeed )
{
bool connected = false;
if ( AlreadyHere( seed, socket ) ) {
connected = true; /* but drop the packet */
/* } else if ( AlreadyHere( seed, -1 ) ) { */
/* dupe packet on different socket; need host record */
} else if ( CRefMgr::Get()->Associate( socket, this ) ) {
pushConnectEvent( socket, nPlayersH, nPlayersS, seed );
handleEvents();
connected = HasSocket_locked( socket );
} else {
logf( XW_LOGINFO, "dropping connect event; already connected" );
HostID prevHostID = HOST_ID_NONE;
bool alreadyHere = AlreadyHere( seed, socket, &prevHostID );
if ( alreadyHere ) {
if ( seenSeed ) { /* we need to get rid of the current entry, then
proceed as if this were a new connection */
assert( HOST_ID_NONE != prevHostID );
postDropDevice( prevHostID );
} else {
connected = true; /* but drop the packet */
}
}
if ( !connected ) {
if ( CRefMgr::Get()->Associate( socket, this ) ) {
pushConnectEvent( socket, nPlayersH, nPlayersS, seed );
handleEvents();
connected = HasSocket_locked( socket );
} else {
logf( XW_LOGINFO, "dropping connect event; already connected" );
}
}
return connected;
}
@ -212,7 +228,6 @@ CookieRef::_Reconnect( int socket, HostID hid, int nPlayersH, int nPlayersS,
void
CookieRef::_HandleAck( HostID hostID )
{
assert( m_nPendingAcks > 0 && m_nPendingAcks <= 4 );
CRefEvent evt( XWE_GOTONEACK );
evt.u.ack.srcID = hostID;
m_eventQueue.push_back( evt );
@ -288,22 +303,19 @@ CookieRef::SocketForHost( HostID dest )
}
bool
CookieRef::AlreadyHere( unsigned short seed, int socket )
CookieRef::AlreadyHere( unsigned short seed, int socket, HostID* prevHostID )
{
logf( XW_LOGINFO, "%s(seed=%x,socket=%d)", __func__, seed, socket );
bool here = false;
vector<HostRec>::iterator iter;
for ( iter = m_sockets.begin(); iter != m_sockets.end(); ++iter ) {
if ( iter->m_seed == seed ) { /* client already registered */
if ( iter->m_socket == socket ) {
/* dup packet */
here = true;
} else {
logf( XW_LOGINFO, "%s: seeds match; nuking existing record"
" for socket %d b/c assumed closed", __func__,
iter->m_socket );
m_sockets.erase( iter );
here = iter->m_seed == seed; /* client already registered */
if ( here ) {
if ( iter->m_socket != socket ) { /* not just a dupe packet */
logf( XW_LOGINFO, "%s: seeds match; socket %d assumed closed",
__func__, iter->m_socket );
*prevHostID = iter->m_hostID;
}
break;
}
@ -370,11 +382,10 @@ CookieRef::removeSocket( int socket )
if ( iter->m_socket == socket ) {
if ( iter->m_ackPending ) {
logf( XW_LOGINFO,
"Never got ack; removing %d players from DB",
iter->m_nPlayersH );
DBMgr::Get()->RmDevice( ConnName(), iter->m_hostID );
"Never got ack; removing hid %d from DB",
iter->m_hostID );
DBMgr::Get()->RmDeviceByHid( ConnName(), iter->m_hostID );
m_nPlayersHere -= iter->m_nPlayersH;
--m_nPendingAcks;
}
m_sockets.erase(iter);
--count;
@ -577,22 +588,24 @@ CookieRef::handleEvents()
switch( takeAction ) {
case XWA_SEND_CONNRSP:
if ( increasePlayerCounts( &evt, false ) ) {
setAllConnectedTimer();
sendResponse( &evt, takeAction != XWA_SEND_1ST_RERSP );
setAckTimer();
case XWA_SEND_CONNRSP:
{
HostID hid;
if ( increasePlayerCounts( &evt, false, &hid ) ) {
setAllConnectedTimer();
sendResponse( &evt, takeAction != XWA_SEND_1ST_RERSP );
setAckTimer( hid );
}
}
break;
case XWA_NOTEACKCHECK:
case XWA_NOTEACK:
modPending( &evt, true );
updateAck( evt.u.ack.srcID, true );
postCheckAllHere();
break;
case XWA_DROPDEVICE:
modPending( &evt, false );
updateAck( evt.u.ack.srcID, false );
break;
/* case XWA_SEND_1ST_RERSP: */
@ -603,7 +616,7 @@ CookieRef::handleEvents()
/* break; */
case XWA_SEND_RERSP:
increasePlayerCounts( &evt, true );
increasePlayerCounts( &evt, true, NULL );
sendResponse( &evt, false );
sendAnyStored( &evt );
postCheckAllHere();
@ -708,6 +721,16 @@ CookieRef::handleEvents()
}
m_curState = nextState;
#ifdef DEBUG
if ( XWS_EMPTY == m_curState ) {
assert( 0 == m_sockets.size() );
int nTotal, nHere;
GetPlayerCounts( ConnName(), &nTotal, &nHere );
assert( 0 == nHere );
}
#endif
} else {
logf( XW_LOGERROR, "Killing cref b/c unable to find transition "
"from %s on event %s", stateString(m_curState),
@ -779,7 +802,7 @@ CookieRef::send_stored_messages( HostID dest, int socket )
} /* send_stored_messages */
bool
CookieRef::increasePlayerCounts( CRefEvent* evt, bool reconn )
CookieRef::increasePlayerCounts( CRefEvent* evt, bool reconn, HostID* hidp )
{
int nPlayersH = evt->u.con.nPlayersH;
int socket = evt->u.con.socket;
@ -808,9 +831,12 @@ CookieRef::increasePlayerCounts( CRefEvent* evt, bool reconn )
}
evt->u.con.srcID = DBMgr::Get()->AddDevice( ConnName(), evt->u.con.srcID,
nPlayersH, seed );
nPlayersH, seed, reconn );
HostID hostid = evt->u.con.srcID;
if ( NULL != hidp ) {
*hidp = hostid;
}
/* first add the rec here, whether it'll get ack'd or not */
logf( XW_LOGINFO, "%s: remembering pair: hostid=%x, "
@ -831,36 +857,53 @@ CookieRef::increasePlayerCounts( CRefEvent* evt, bool reconn )
} /* increasePlayerCounts */
void
CookieRef::modPending( const CRefEvent* evt, bool keep )
CookieRef::updateAck( HostID hostID, bool keep )
{
HostID hostID = evt->u.ack.srcID;
assert( hostID >= HOST_ID_SERVER );
assert( hostID <= 4 );
int socket = 0;
cancelAckTimer( hostID );
vector<HostRec>::iterator iter;
for ( iter = m_sockets.begin(); iter != m_sockets.end(); ++iter ) {
if ( iter->m_ackPending && iter->m_hostID == hostID ) {
--m_nPendingAcks;
if ( keep ) {
iter->m_ackPending = false;
DBMgr::Get()->NoteAckd( ConnName(), hostID );
} else {
DBMgr::Get()->RmDevice( ConnName(), iter->m_hostID );
m_sockets.erase( iter );
socket = iter->m_socket;
}
break;
}
}
if ( 0 != socket ) {
removeSocket( socket );
}
printSeeds(__func__);
}
void
CookieRef::postCheckAllHere()
{
if ( m_nPendingAcks == 0 && DBMgr::Get()->GameFull( ConnName() ) ) {
/* && m_nPlayersHere == m_nPlayersSought ) { /\* complete! *\/ */
if ( DBMgr::Get()->AllDevsAckd( ConnName() ) ) {
CRefEvent evt( XWE_ALLHERE );
m_eventQueue.push_back( evt );
}
}
void
CookieRef::postDropDevice( HostID hostID )
{
CRefEvent evt( XWE_ACKTIMEOUT );
evt.u.ack.srcID = hostID;
m_eventQueue.push_back( evt );
handleEvents();
}
void
CookieRef::setAllConnectedTimer()
{
@ -872,23 +915,39 @@ CookieRef::setAllConnectedTimer()
}
void
CookieRef::setAckTimer( void )
CookieRef::setAckTimer( HostID hid )
{
logf( XW_LOGINFO, "%s()", __func__ );
ASSERT_LOCKED();
logf( XW_LOGINFO, "%s(%d)", __func__, hid );
assert( hid >= HOST_ID_SERVER );
assert( hid <= 4 );
--hid;
assert( NULL == m_timers[hid].m_this );
m_timers[hid].m_this = this;
time_t inHowLong;
if ( RelayConfigs::GetConfigs()->GetValueFor( "DEVACK", &inHowLong ) ) {
TimerMgr::GetTimerMgr()->SetTimer( inHowLong,
s_checkAck, this, 0 );
++m_nPendingAcks;
s_checkAck, &m_timers[hid], 0 );
} else {
logf( XW_LOGINFO, "not setting timer" );
}
}
void
CookieRef::cancelAckTimer( void )
CookieRef::cancelAckTimer( HostID hid )
{
TimerMgr::GetTimerMgr()->ClearTimer( s_checkAck, this );
ASSERT_LOCKED();
logf( XW_LOGINFO, "%s(%d)", __func__, hid );
assert( hid >= HOST_ID_SERVER );
assert( hid <= 4 );
--hid;
m_timers[hid].m_this = NULL;
// TimerMgr::GetTimerMgr()->ClearTimer( s_checkAck, this );
}
void
@ -1231,9 +1290,13 @@ CookieRef::s_checkAllConnected( void* closure )
/* static */ void
CookieRef::s_checkAck( void* closure )
{
CookieRef* self = (CookieRef*)closure;
SafeCref scr(self);
scr.CheckNotAcked();
AckTimer* at = (AckTimer*)closure;
CookieRef* self = at->m_this;
if ( NULL != self ) {
at->m_this = NULL;
SafeCref scr(self);
scr.CheckNotAcked( at->m_hid );
}
}
void
@ -1247,14 +1310,13 @@ CookieRef::_CheckAllConnected()
}
void
CookieRef::_CheckNotAcked()
CookieRef::_CheckNotAcked( HostID hid )
{
logf( XW_LOGINFO, "%s", __func__ );
if ( m_nPendingAcks > 0 ) {
CRefEvent newEvt( XWE_ACKTIMEOUT );
m_eventQueue.push_back( newEvt );
handleEvents();
}
logf( XW_LOGINFO, "%s(hid=%d)", __func__, hid );
CRefEvent newEvt( XWE_ACKTIMEOUT );
newEvt.u.ack.srcID = hid;
m_eventQueue.push_back( newEvt );
handleEvents();
}
void
@ -1264,10 +1326,11 @@ CookieRef::printSeeds( const char* caller )
char buf[64] = {0};
vector<HostRec>::iterator iter;
for ( iter = m_sockets.begin(); iter != m_sockets.end(); ++iter ) {
len += snprintf( &buf[len], sizeof(buf)-len, "%.4x/%d ",
iter->m_seed, iter->m_socket );
len += snprintf( &buf[len], sizeof(buf)-len, "%.4x/%d/%c ",
iter->m_seed, iter->m_socket,
iter->m_ackPending?'a':'A' );
}
logf( XW_LOGINFO, "seeds/sockets after %s(): %s", caller, buf );
logf( XW_LOGINFO, "seeds/sockets/ack'd after %s(): %s", caller, buf );
}
void

View file

@ -58,6 +58,12 @@ HostRec(HostID hostID, int socket, int nPlayersH, int seed, bool ackPending )
bool m_ackPending;
};
struct AckTimer {
public:
HostID m_hid;
class CookieRef* m_this;
};
class CookieRef {
private:
@ -95,7 +101,7 @@ class CookieRef {
HostID HostForSocket( int sock );
/* connect case */
bool AlreadyHere( unsigned short seed, int socket );
bool AlreadyHere( unsigned short seed, int socket, HostID* prevHostID );
/* reconnect case */
bool AlreadyHere( HostID hid, unsigned short seed, int socket );
@ -110,7 +116,8 @@ class CookieRef {
static void Delete( CookieID id );
static void Delete( const char* name );
bool _Connect( int socket, int nPlayersH, int nPlayersS, int seed );
bool _Connect( int socket, int nPlayersH, int nPlayersS, int seed,
bool seenSeed );
void _Reconnect( int socket, HostID srcID, int nPlayersH, int nPlayersS,
int seed, bool gameDead );
void _HandleAck( HostID hostID );
@ -122,9 +129,9 @@ class CookieRef {
void _Forward( HostID src, HostID dest, unsigned char* buf, int buflen );
void _Remove( int socket );
void _CheckAllConnected();
void _CheckNotAcked();
void _CheckNotAcked( HostID hid );
bool ShouldDie() { return m_curState == XWS_DEAD; }
bool ShouldDie() { return m_curState == XWS_EMPTY; }
XW_RELAY_STATE CurState() { return m_curState; }
void logf( XW_LogLevel level, const char* format, ... );
@ -202,18 +209,21 @@ class CookieRef {
void sendResponse( const CRefEvent* evt, bool initial );
void sendAnyStored( const CRefEvent* evt );
void initPlayerCounts( const CRefEvent* evt );
bool increasePlayerCounts( CRefEvent* evt, bool reconn );
void modPending( const CRefEvent* evt, bool keep );
bool increasePlayerCounts( CRefEvent* evt, bool reconn, HostID* hidp );
void updateAck( HostID hostID, bool keep );
void dropPending( int seed );
void postCheckAllHere();
void postDropDevice( HostID hostID );
bool hostAlreadyHere( int seed, int socket );
void reducePlayerCounts( int socket );
void setAllConnectedTimer();
void cancelAllConnectedTimer();
void setAckTimer();
void cancelAckTimer();
void setAckTimer( HostID hid );
void cancelAckTimer( HostID hid );
void forward_or_store( const CRefEvent* evt );
void send_denied( const CRefEvent* evt, XWREASON why );
@ -271,7 +281,8 @@ class CookieRef {
int m_langCode;
time_t m_starttime;
int m_nPendingAcks;
AckTimer m_timers[4];
pthread_mutex_t m_mutex;

View file

@ -180,7 +180,8 @@ CRefMgr::GetStats( CrefMgrInfo& mgrInfo )
info.m_langCode = cref->GetLangCode();
SafeCref sc(cref);
sc.GetHostsConnected( &info.m_hostsIds, &info.m_hostSeeds, &info.m_hostIps );
sc.GetHostsConnected( &info.m_hostsIds, &info.m_hostSeeds,
&info.m_hostIps );
mgrInfo.m_crefInfo.push_back( info );
}
@ -231,25 +232,31 @@ CRefMgr::getFromFreeList( void )
CookieRef*
CRefMgr::getMakeCookieRef( const char* cookie, HostID hid, int socket,
int nPlayersH, int nPlayersT, int langCode,
int gameSeed, bool wantsPublic,
bool makePublic )
int seed, bool wantsPublic,
bool makePublic, bool* seenSeed )
{
CookieRef* cref;
CookieID cid;
char connNameBuf[MAX_CONNNAME_LEN+1] = {0};
int alreadyHere = 0;
/* We have a cookie from a new connection or from a reconnect. This may
be the first time it's been seen, or there may be a game currently in
the XW_ST_CONNECTING state, or it may be a dupe of a connect packet.
If there's a game, cool. Otherwise add a new one. Pass the connName
which will be used if set, but if not set we'll be generating another
later when the game is complete.
the XW_ST_CONNECTING state, or it may be a dupe of a connect packet on
the same or a different socket. If there's a game, cool. Otherwise add
a new one. Pass the connName which will be used if set, but if not set
we'll be generating another later when the game is complete.
*/
char connNameBuf[MAX_CONNNAME_LEN+1] = {0};
int alreadyHere = 0;
CookieID cid = m_db->FindOpen( cookie, langCode, nPlayersT, nPlayersH,
wantsPublic,
connNameBuf, sizeof(connNameBuf),
&alreadyHere );
*seenSeed = m_db->SeenSeed( cookie, seed, langCode, nPlayersT,
wantsPublic, connNameBuf,
sizeof(connNameBuf), &alreadyHere, &cid );
if ( !*seenSeed ) {
cid = m_db->FindOpen( cookie, langCode, nPlayersT, nPlayersH,
wantsPublic, connNameBuf, sizeof(connNameBuf),
&alreadyHere );
}
if ( cid > 0 ) {
cref = getCookieRef_impl( cid );
} else {
@ -657,12 +664,14 @@ SafeCref::SafeCref( const char* cookie, int socket, int nPlayersH, int nPlayersS
: m_cref( NULL )
, m_mgr( CRefMgr::Get() )
, m_isValid( false )
, m_seenSeed( false )
{
CookieRef* cref;
cref = m_mgr->getMakeCookieRef( cookie, 0, socket,
nPlayersH, nPlayersS, langCode,
gameSeed, wantsPublic, makePublic );
gameSeed, wantsPublic, makePublic,
&m_seenSeed );
if ( cref != NULL ) {
m_locked = cref->Lock();
m_cref = cref;

View file

@ -138,7 +138,8 @@ class CRefMgr {
CookieRef* getMakeCookieRef( const char* cookie,
HostID hid, int socket, int nPlayersH,
int nPlayersS, int langCode, int seed,
bool wantsPublic, bool makePublic );
bool wantsPublic, bool makePublic,
bool* seenSeed );
/* reconnect case; just the stuff we don't have in db */
CookieRef* getMakeCookieRef( const char* connName, const char* cookie,
@ -219,7 +220,8 @@ class SafeCref {
bool Connect( int socket, int nPlayersH, int nPlayersS, int seed ) {
if ( IsValid() ) {
assert( 0 != m_cref->GetCookieID() );
return m_cref->_Connect( socket, nPlayersH, nPlayersS, seed );
return m_cref->_Connect( socket, nPlayersH, nPlayersS, seed,
m_seenSeed );
} else {
return false;
}
@ -300,9 +302,9 @@ class SafeCref {
m_cref->_CheckAllConnected();
}
}
void CheckNotAcked() {
void CheckNotAcked( HostID hid ) {
if ( IsValid() ) {
m_cref->_CheckNotAcked();
m_cref->_CheckNotAcked( hid );
}
}
const char* Cookie() {
@ -366,6 +368,7 @@ class SafeCref {
}
bool IsValid() { return m_isValid; }
bool SeenSeed() { return m_seenSeed; }
private:
CookieRef* m_cref;
@ -373,6 +376,7 @@ class SafeCref {
bool m_isValid;
bool m_locked;
bool m_dead;
bool m_seenSeed;
}; /* SafeCref class */

View file

@ -39,6 +39,8 @@ static DBMgr* s_instance = NULL;
static void formatParams( char* paramValues[], int nParams, const char* fmt,
char* buf, int bufLen, ... );
static int here_less_seed( const char* seeds, int perDeviceSum,
unsigned short seed );
/* static */ DBMgr*
DBMgr::Get()
@ -130,6 +132,48 @@ DBMgr::FindGame( const char* connName, char* cookieBuf, int bufLen,
logf( XW_LOGINFO, "%s(%s)=>%d", __func__, connName, cid );
return cid;
} /* FindGame */
bool
DBMgr::SeenSeed( const char* cookie, unsigned short seed,
int langCode, int nPlayersT, bool wantsPublic,
char* connNameBuf, int bufLen, int* nPlayersHP,
CookieID* cid )
{
int nParams = 5;
char* paramValues[nParams];
char buf[512];
formatParams( paramValues, nParams,
"%s"DELIM"%d"DELIM"%d"DELIM"%d"DELIM"%s", buf, sizeof(buf),
cookie, langCode, nPlayersT, seed,
wantsPublic?"TRUE":"FALSE" );
const char* cmd = "SELECT cid, connName, seeds, sum_array(nPerDevice) FROM "
GAMES_TABLE
" WHERE NOT dead"
" AND room ILIKE $1"
" AND lang = $2"
" AND nTotal = $3"
" AND $4 = ANY(seeds)"
" AND $5 = pub"
" ORDER BY ctime DESC"
" LIMIT 1";
PGresult* result = PQexecParams( getThreadConn(), cmd,
nParams, NULL,
paramValues,
NULL, NULL, 0 );
bool found = 1 == PQntuples( result );
if ( found ) {
*cid = atoi( PQgetvalue( result, 0, 0 ) );
*nPlayersHP = here_less_seed( PQgetvalue( result, 0, 2 ),
atoi( PQgetvalue( result, 0, 3 ) ),
seed );
snprintf( connNameBuf, bufLen, "%s", PQgetvalue( result, 0, 1 ) );
}
PQclear( result );
logf( XW_LOGINFO, "%s(%4X)=>%s", __func__, seed, found?"true":"false" );
return found;
}
CookieID
@ -174,9 +218,9 @@ DBMgr::FindOpen( const char* cookie, int lang, int nPlayersT, int nPlayersH,
} /* FindOpen */
bool
DBMgr::GameFull( const char* const connName )
DBMgr::AllDevsAckd( const char* const connName )
{
const char* cmd = "SELECT ntotal=sum_array(nperdevice) from " GAMES_TABLE
const char* cmd = "SELECT ntotal=sum_array(nperdevice) AND 'A'=ALL(ack) from " GAMES_TABLE
" WHERE connName='%s'";
char query[256];
snprintf( query, sizeof(query), cmd, connName );
@ -187,12 +231,13 @@ DBMgr::GameFull( const char* const connName )
assert( nTuples <= 1 );
bool full = 't' == PQgetvalue( result, 0, 0 )[0];
PQclear( result );
logf( XW_LOGINFO, "%s=>%d", __func__, full );
return full;
}
HostID
DBMgr::AddDevice( const char* connName, HostID curID, int nToAdd,
unsigned short seed )
unsigned short seed, bool ackd )
{
HostID newID = curID;
@ -208,29 +253,79 @@ DBMgr::AddDevice( const char* connName, HostID curID, int nToAdd,
assert( newID <= 4 );
const char* fmt = "UPDATE " GAMES_TABLE " SET nPerDevice[%d] = %d,"
" seeds[%d] = %d, mtimes[%d]='now'"
" seeds[%d] = %d, mtimes[%d]='now', ack[%d]=\'%c\'"
" WHERE connName = '%s'";
char query[256];
snprintf( query, sizeof(query), fmt, newID, nToAdd, newID, seed, newID, connName );
snprintf( query, sizeof(query), fmt, newID, nToAdd, newID, seed, newID,
newID, ackd?'A':'a', connName );
logf( XW_LOGINFO, "%s: query: %s", __func__, query );
execSql( query );
return newID;
} /* AddDevice */
void
DBMgr::NoteAckd( const char* const connName, HostID id )
{
char query[256];
const char* fmt = "UPDATE " GAMES_TABLE " SET ack[%d]='A'"
" WHERE connName = '%s'";
snprintf( query, sizeof(query), fmt, id, connName );
logf( XW_LOGINFO, "%s: query: %s", __func__, query );
execSql( query );
}
bool
DBMgr::RmDevice( const char* connName, HostID hid )
DBMgr::RmDeviceByHid( const char* connName, HostID hid )
{
const char* fmt = "UPDATE " GAMES_TABLE " SET nPerDevice[%d] = 0, "
"seeds[%d] = 0, mtimes[%d]='now' WHERE connName = '%s'";
"seeds[%d] = 0, ack[%d]='-', mtimes[%d]='now' WHERE connName = '%s'";
char query[256];
snprintf( query, sizeof(query), fmt, hid, hid, hid, connName );
snprintf( query, sizeof(query), fmt, hid, hid, hid, hid, connName );
logf( XW_LOGINFO, "%s: query: %s", __func__, query );
return execSql( query );
}
void
DBMgr::RmDeviceBySeed( const char* const connName, unsigned short seed )
{
char seeds[128] = {0};
const char* fmt = "SELECT seeds FROM " GAMES_TABLE
" WHERE connName = '%s'"
" AND %d = ANY(seeds)";
char query[256];
snprintf( query, sizeof(query), fmt, connName, seed );
logf( XW_LOGINFO, "%s: query: %s", __func__, query );
PGresult* result = PQexec( getThreadConn(), query );
if ( 1 == PQntuples( result ) ) {
snprintf( seeds, sizeof(seeds), "%s", PQgetvalue( result, 0, 0 ) );
}
PQclear( result );
if ( 0 != seeds[0] ) {
char *saveptr = NULL;
int ii;
char* str;
for ( str = seeds, ii = 0; ; str = NULL, ++ii ) {
char* tok = strtok_r( str, "{},", &saveptr );
if ( NULL == tok ) {
break;
} else {
int asint = atoi( tok );
if ( asint == seed ) {
RmDeviceByHid( connName, ii + 1 );
break;
}
}
}
} else {
assert(0); /* but don't ship with this!!!! */
}
} /* RmDeviceSeed */
bool
DBMgr::HaveDevice( const char* connName, HostID hid, int seed )
{
@ -537,7 +632,15 @@ formatParams( char* paramValues[], int nParams, const char* fmt, char* buf,
}
}
va_end(ap);
}
}
static int
here_less_seed( const char* seeds, int sumPerDevice, unsigned short seed )
{
logf( XW_LOGINFO, "%s: find %x in \"%s\", sub from \"%d\"", __func__,
seed, seeds, sumPerDevice );
return sumPerDevice - 1; /* FIXME */
}
static void
destr_function( void* conn )

View file

@ -43,14 +43,22 @@ class DBMgr {
CookieID FindGame( const char* connName, char* cookieBuf, int bufLen,
int* langP, int* nPlayersTP, int* nPlayersHP,
bool* isDead );
bool SeenSeed( const char* cookie, unsigned short seed,
int langCode, int nPlayersT, bool wantsPublic,
char* connNameBuf, int bufLen, int* nPlayersHP,
CookieID* cid );
CookieID FindOpen( const char* cookie, int lang, int nPlayersT,
int nPlayersH, bool wantsPublic,
char* connNameBuf, int bufLen, int* nPlayersHP );
bool GameFull( const char* const connName );
bool AllDevsAckd( const char* const connName );
HostID AddDevice( const char* const connName, HostID curID,
int nToAdd, unsigned short seed );
bool RmDevice( const char* const connName, HostID id );
int nToAdd, unsigned short seed, bool unAckd );
void NoteAckd( const char* const connName, HostID id );
bool RmDeviceByHid( const char* const connName, HostID id );
void RmDeviceBySeed( const char* const connName, unsigned short seed );
bool HaveDevice( const char* const connName, HostID id, int seed );
void AddCID( const char* const connName, CookieID cid );
void ClearCID( const char* connName );

View file

@ -61,7 +61,7 @@ typedef struct StateTable {
*/
static StateTable g_stateTable[] = {
{ XWS_INITED, XWE_DEVCONNECT, XWA_SEND_CONNRSP, XWS_WAITMORE },
{ XWS_EMPTY, XWE_DEVCONNECT, XWA_SEND_CONNRSP, XWS_WAITMORE },
{ XWS_WAITMORE, XWE_DEVCONNECT, XWA_SEND_CONNRSP, XWS_WAITMORE },
{ XWS_WAITMORE, XWE_GOTONEACK, XWA_NOTEACK, XWS_WAITMORE },
{ XWS_WAITMORE, XWE_ACKTIMEOUT, XWA_DROPDEVICE, XWS_WAITMORE },
@ -73,6 +73,7 @@ static StateTable g_stateTable[] = {
{ XWS_ALLCONND, XWE_RECONNECT, XWA_SEND_RERSP, XWS_ALLCONND },
{ XWS_ALLCONND, XWE_ALLHERE, XWA_NONE, XWS_ALLCONND },
{ XWS_ALLCONND, XWE_REMOVESOCKET, XWA_REMOVESOCK_1, XWS_ALLCONND },
{ XWS_ALLCONND, XWE_GOTONEACK, XWA_NONE, XWS_ALLCONND },
/* { XWS_WAITMORE, XWE_GAMEFULL, XWA_SENDALLHERE, XWS_ALLCONND }, */
/* { XWS_WAITMORE, XWE_CHECKFULL, XWA_, XWS_WAITMORE }, */
@ -107,12 +108,14 @@ static StateTable g_stateTable[] = {
/* { XWS_MSGONLY, XWE_NOMOREMSGS, XWA_NONE, XWS_DEAD }, */
/* { XWS_MSGONLY, XWE_NOMOREMSGS, XWA_NONE, XWS_DEAD }, */
{ XWS_ANY, XWE_NOMORESOCKETS, XWA_NONE, XWS_DEAD },
{ XWS_ANY, XWE_SHUTDOWN, XWA_SHUTDOWN, XWS_DEAD },
{ XWS_ANY, XWE_NOMORESOCKETS, XWA_NONE, XWS_EMPTY },
{ XWS_ANY, XWE_SHUTDOWN, XWA_SHUTDOWN, XWS_EMPTY },
/* drop timeout (unless we're in XWS_WAITMORE; see above) */
{ XWS_ANY, XWE_ACKTIMEOUT, XWA_NONE, XWS_SAME },
/* This doesn't make sense. Can't go straight to ALLCOND if don't have all
players/devices. */
{ XWS_INITED, XWE_RECONNECT, XWA_SEND_RERSP, XWS_WAITMORE },
{ XWS_EMPTY, XWE_RECONNECT, XWA_SEND_RERSP, XWS_WAITMORE },
{ XWS_MSGONLY, XWE_RECONNECT, XWA_SEND_RERSP, XWS_WAITMORE },
/* { XWS_MISSING, XWE_RECONNECT, XWA_SEND_RERSP, XWS_CHK_ALLHERE_2 }, */
@ -134,7 +137,7 @@ static StateTable g_stateTable[] = {
{ XWS_MISSING, XWE_HEARTRCVD, XWA_NOTEHEART, XWS_MISSING },
#endif
{ XWS_INITED, XWE_DEVGONE, XWA_RMDEV, XWS_DEAD },
{ XWS_EMPTY, XWE_DEVGONE, XWA_RMDEV, XWS_EMPTY },
{ XWS_WAITMORE, XWE_DEVGONE, XWA_RMDEV, XWS_WAITMORE },
/* This should be impossible unless device allows deleting an open/connected
game */
@ -143,20 +146,20 @@ static StateTable g_stateTable[] = {
{ XWS_ALLCONND, XWE_GAMEDEAD, XWA_TELLGAMEDEAD, XWS_ALLCONND },
/* Connect timer */
{ XWS_WAITMORE, XWE_CONNTIMER, XWA_TIMERDISCONN, XWS_DEAD },
{ XWS_WAITMORE, XWE_CONNTIMER, XWA_TIMERDISCONN, XWS_EMPTY },
/* { XWS_MISSING, XWE_CONNTIMER, XWA_NONE, XWS_MISSING }, */
{ XWS_ALLCONND, XWE_CONNTIMER, XWA_NONE, XWS_ALLCONND },
{ XWS_WAITMORE, XWE_NOTIFYDISCON, XWA_NOTIFYDISCON, XWS_WAITMORE },
/* { XWS_ALLCONND, XWE_NOTIFYDISCON, XWA_NOTIFYDISCON, XWS_MISSING }, */
/* { XWS_MISSING, XWE_NOTIFYDISCON, XWA_NOTIFYDISCON, XWS_MISSING }, */
{ XWS_DEAD, XWE_NOTIFYDISCON, XWA_NOTIFYDISCON, XWS_DEAD },
{ XWS_EMPTY, XWE_NOTIFYDISCON, XWA_NOTIFYDISCON, XWS_EMPTY },
/* This is our bread-n-butter */
{ XWS_ALLCONND, XWE_FORWARDMSG, XWA_FWD, XWS_ALLCONND },
/* { XWS_MISSING, XWE_FORWARDMSG, XWA_FWD, XWS_MISSING }, */
{ XWS_DEAD, XWE_REMOVESOCKET, XWA_REMOVESOCK_1, XWS_DEAD }
{ XWS_EMPTY, XWE_REMOVESOCKET, XWA_REMOVESOCK_1, XWS_EMPTY }
};
@ -172,7 +175,8 @@ getFromTable( XW_RELAY_STATE curState, XW_RELAY_EVENT curEvent,
if ( stp->stateStart == curState || stp->stateStart == XWS_ANY ) {
if ( stp->stateEvent == curEvent || stp->stateEvent == XWE_ANY ) {
*takeAction = stp->stateAction;
*nextState = stp->stateEnd;
*nextState =
stp->stateEnd == XWS_SAME? curState : stp->stateEnd;
found = true;
break;
}
@ -192,11 +196,10 @@ stateString( XW_RELAY_STATE state )
switch( state ) {
CASESTR(XWS_NONE);
CASESTR(XWS_ANY);
CASESTR(XWS_INITED);
CASESTR(XWS_EMPTY);
CASESTR(XWS_WAITMORE);
CASESTR(XWS_WAITING_ACKS);
CASESTR(XWS_ALLCONND);
CASESTR(XWS_DEAD);
/* CASESTR(XWS_MISSING); */
CASESTR(XWS_MSGONLY);
/* CASESTR(XWS_CHK_ALLHERE); */
@ -264,7 +267,6 @@ actString( XW_RELAY_ACTION act )
CASESTR(XWA_SEND_INITRSP);
CASESTR(XWA_SEND_CONNRSP);
CASESTR(XWA_NOTEACK);
CASESTR(XWA_NOTEACKCHECK);
/* CASESTR(XWA_ADDDEVICE); */
CASESTR(XWA_DROPDEVICE);
CASESTR(XWA_SNDALLHERE_2);

View file

@ -27,6 +27,7 @@ typedef
enum {
XWS_NONE
,XWS_ANY /* wildcard */
,XWS_SAME /* wildcard: means use same state as current */
/* ,XWS_CHKCOUNTS_INIT */ /* from initial state, check if all players
are here. Success should be an error,
@ -40,11 +41,13 @@ enum {
/* ,XWS_CHK_ALLHERE_2 */ /* same as above, but triggered by a reconnect
rather than a connect request */
,XWS_INITED /* Relay's running and the object's been
created, but nobody's signed up yet. This
is a very short-lived state since an
incoming connection is why the object was
created. */
,XWS_EMPTY /* Relay's running and the object's been
created, and nobody's signed up yet. The
object should never be in this state except
immediately after created, just before
deleted, or transitionally, as after a
device is removed prior to replacing its
record with another. */
,XWS_WAITMORE /* At least one device has connected, but no
packets have yet arrived to be
@ -63,8 +66,6 @@ enum {
/* ,XWS_ROOMCHK */ /* do we have room for as many players as are
being provided */
,XWS_DEAD /* About to kill the object */
} XW_RELAY_STATE;
@ -129,7 +130,6 @@ typedef enum {
// ,XWA_ADDDEVICE /* got ack, so device is in for sure */
,XWA_NOTEACK
,XWA_NOTEACKCHECK
,XWA_DROPDEVICE /* no ack; remove all traces of device */
,XWA_SEND_INITRSP /* response to first to connect */

View file

@ -314,8 +314,6 @@ processConnect( unsigned char* bufp, int bufLen, int socket )
unsigned char* end = bufp + bufLen;
bool success = false;
logf( XW_LOGINFO, "%s()", __func__ );
cookie[0] = '\0';
unsigned char flags = *bufp++;
@ -324,7 +322,7 @@ processConnect( unsigned char* bufp, int bufLen, int socket )
/* HostID srcID; */
unsigned char nPlayersH;
unsigned char nPlayersT;
unsigned short gameSeed;
unsigned short seed;
unsigned char langCode;
unsigned char makePublic, wantsPublic;
if ( readStr( &bufp, end, cookie, sizeof(cookie) )
@ -333,10 +331,11 @@ processConnect( unsigned char* bufp, int bufLen, int socket )
/* && getNetByte( &bufp, end, &srcID ) */
&& getNetByte( &bufp, end, &nPlayersH )
&& getNetByte( &bufp, end, &nPlayersT )
&& getNetShort( &bufp, end, &gameSeed )
&& getNetShort( &bufp, end, &seed )
&& getNetByte( &bufp, end, &langCode ) ) {
logf( XW_LOGINFO, "%s(): langCode=%d; nPlayersT=%d; wantsPublic=%d", __func__,
langCode, nPlayersT, wantsPublic );
logf( XW_LOGINFO, "%s(): langCode=%d; nPlayersT=%d; "
"wantsPublic=%d; seed=%.4X",
__func__, langCode, nPlayersT, wantsPublic, seed );
/* Make sure second thread can't create new cref for same cookie
this one just handled.*/
@ -344,10 +343,10 @@ processConnect( unsigned char* bufp, int bufLen, int socket )
MutexLock ml( &s_newCookieLock );
SafeCref scr( cookie, socket, nPlayersH, nPlayersT,
gameSeed, langCode, wantsPublic, makePublic );
seed, langCode, wantsPublic, makePublic );
/* nPlayersT etc could be slots in SafeCref to avoid passing
here */
success = scr.Connect( socket, nPlayersH, nPlayersT, gameSeed );
success = scr.Connect( socket, nPlayersH, nPlayersT, seed );
} else {
err = XWRELAY_ERROR_BADPROTO;
}
@ -442,7 +441,7 @@ processDisconnect( unsigned char* bufp, int bufLen, int socket )
static void
killSocket( int socket )
{
logf( XW_LOGINFO, "killSocket(%d)", socket );
logf( XW_LOGINFO, "%s(%d)", __func__, socket );
CRefMgr::Get()->RemoveSocketRefs( socket );
}