mirror of
git://xwords.git.sourceforge.net/gitroot/xwords/xwords
synced 2025-01-29 08:34:37 +01:00
New scheme for accepting and limiting reconnections. Now device must
send player counts, local and expected. Based on these the relay accepts connections, declares the game full and ready for message forwarding, and decides whether to accept a reconnect.
This commit is contained in:
parent
e098e6f7b2
commit
2b58da4cdf
13 changed files with 478 additions and 132 deletions
|
@ -64,6 +64,7 @@ typedef enum {
|
|||
COMMS_RELAYSTATE_UNCONNECTED
|
||||
, COMMS_RELAYSTATE_CONNECT_PENDING
|
||||
, COMMS_RELAYSTATE_CONNECTED
|
||||
, COMMS_RELAYSTATE_ALLCONNECTED
|
||||
} CommsRelaystate;
|
||||
|
||||
struct CommsCtxt {
|
||||
|
@ -98,6 +99,8 @@ struct CommsCtxt {
|
|||
on requires them. Not saved since only valid when connected, and we
|
||||
reconnect for every game and after restarting. */
|
||||
XP_U16 heartbeat;
|
||||
XP_U16 nPlayersHere;
|
||||
XP_U16 nPlayersTotal;
|
||||
#endif
|
||||
XP_Bool isServer;
|
||||
XP_Bool connecting;
|
||||
|
@ -132,6 +135,7 @@ static void setHeartbeatTimer( CommsCtxt* comms );
|
|||
****************************************************************************/
|
||||
CommsCtxt*
|
||||
comms_make( MPFORMAL XW_UtilCtxt* util, XP_Bool isServer,
|
||||
XP_U16 nPlayersHere, XP_U16 nPlayersTotal,
|
||||
TransportSend sendproc, void* closure )
|
||||
{
|
||||
CommsCtxt* result = (CommsCtxt*)XP_MALLOC( mpool, sizeof(*result) );
|
||||
|
@ -148,7 +152,8 @@ comms_make( MPFORMAL XW_UtilCtxt* util, XP_Bool isServer,
|
|||
XP_LOGF( "set myHostID to %d", result->myHostID );
|
||||
|
||||
result->relayState = COMMS_RELAYSTATE_UNCONNECTED;
|
||||
|
||||
result->nPlayersHere = nPlayersHere;
|
||||
result->nPlayersTotal = nPlayersTotal;
|
||||
return result;
|
||||
} /* comms_make */
|
||||
|
||||
|
@ -181,7 +186,8 @@ cleanupAddrRecs( CommsCtxt* comms )
|
|||
} /* cleanupAddrRecs */
|
||||
|
||||
void
|
||||
comms_reset( CommsCtxt* comms, XP_Bool isServer )
|
||||
comms_reset( CommsCtxt* comms, XP_Bool isServer,
|
||||
XP_U16 nPlayersHere, XP_U16 nPlayersTotal )
|
||||
{
|
||||
#ifdef BEYOND_IR
|
||||
relayDisconnect( comms );
|
||||
|
@ -197,6 +203,8 @@ comms_reset( CommsCtxt* comms, XP_Bool isServer )
|
|||
comms->connID = CONN_ID_NONE;
|
||||
#ifdef BEYOND_IR
|
||||
comms->cookieID = COOKIE_ID_NONE;
|
||||
comms->nPlayersHere = nPlayersHere;
|
||||
comms->nPlayersTotal = nPlayersTotal;
|
||||
relayConnect( comms );
|
||||
#endif
|
||||
} /* comms_reset */
|
||||
|
@ -260,13 +268,17 @@ comms_makeFromStream( MPFORMAL XWStreamCtxt* stream, XW_UtilCtxt* util,
|
|||
{
|
||||
CommsCtxt* comms;
|
||||
XP_Bool isServer;
|
||||
XP_U16 nAddrRecs;
|
||||
XP_U16 nAddrRecs, nPlayersHere, nPlayersTotal;
|
||||
AddressRecord** prevsAddrNext;
|
||||
MsgQueueElem** prevsQueueNext;
|
||||
short i;
|
||||
|
||||
isServer = stream_getU8( stream );
|
||||
comms = comms_make( MPPARM(mpool) util, isServer, sendproc, closure );
|
||||
nPlayersHere = (XP_U16)stream_getBits( stream, 4 );
|
||||
nPlayersTotal = (XP_U16)stream_getBits( stream, 4 );
|
||||
comms = comms_make( MPPARM(mpool) util, isServer,
|
||||
nPlayersHere, nPlayersTotal,
|
||||
sendproc, closure );
|
||||
|
||||
comms->connID = stream_getU32( stream );
|
||||
comms->nextChannelNo = stream_getU16( stream );
|
||||
|
@ -381,6 +393,8 @@ comms_writeToStream( CommsCtxt* comms, XWStreamCtxt* stream )
|
|||
MsgQueueElem* msg;
|
||||
|
||||
stream_putU8( stream, (XP_U8)comms->isServer );
|
||||
stream_putBits( stream, 4, comms->nPlayersHere );
|
||||
stream_putBits( stream, 4, comms->nPlayersTotal );
|
||||
|
||||
stream_putU32( stream, comms->connID );
|
||||
stream_putU16( stream, comms->nextChannelNo );
|
||||
|
@ -634,7 +648,7 @@ sendMsg( CommsCtxt* comms, MsgQueueElem* elem )
|
|||
if ( 0 ) {
|
||||
#ifdef BEYOND_IR
|
||||
} else if ( comms_getConType( comms ) == COMMS_CONN_RELAY ) {
|
||||
if ( comms->relayState == COMMS_RELAYSTATE_CONNECTED ) {
|
||||
if ( comms->relayState == COMMS_RELAYSTATE_ALLCONNECTED ) {
|
||||
XWHostID destID = getDestID( comms, channelNo );
|
||||
result = send_via_relay( comms, XWRELAY_MSG_TORELAY, destID,
|
||||
elem->msg, elem->len );
|
||||
|
@ -680,6 +694,7 @@ relayPreProcess( CommsCtxt* comms, XWStreamCtxt* stream, XWHostID* senderID )
|
|||
XWHostID destID, srcID;
|
||||
CookieID cookieID;
|
||||
XP_U8 relayErr;
|
||||
XP_U8 hasName;
|
||||
|
||||
if ( comms->addr.conType != COMMS_CONN_RELAY ) {
|
||||
consumed = XP_FALSE; /* nothing for us to do here! */
|
||||
|
@ -697,18 +712,24 @@ relayPreProcess( CommsCtxt* comms, XWStreamCtxt* stream, XWHostID* senderID )
|
|||
XP_LOGF( "got XWRELAY_CONNECTRESP; set cookieID = %d; "
|
||||
"set hostid: %x",
|
||||
comms->cookieID, comms->myHostID );
|
||||
if ( cmd == XWRELAY_CONNECT_RESP ) {
|
||||
setHeartbeatTimer( comms );
|
||||
break;
|
||||
|
||||
case XWRELAY_ALLHERE:
|
||||
comms->relayState = COMMS_RELAYSTATE_ALLCONNECTED;
|
||||
hasName = stream_getU8( stream );
|
||||
if ( hasName ) {
|
||||
stringFromStreamHere( stream, comms->connName,
|
||||
sizeof(comms->connName) );
|
||||
XP_LOGF( "read connName: %s", comms->connName );
|
||||
} else {
|
||||
XP_ASSERT( comms->connName[0] != '\0' );
|
||||
}
|
||||
|
||||
/* We're [re-]connected now. Send any pending messages. This may
|
||||
need to be done later since we're inside the platform's socket
|
||||
read proc now. */
|
||||
comms_resendAll( comms );
|
||||
|
||||
setHeartbeatTimer( comms );
|
||||
break;
|
||||
case XWRELAY_MSG_FROMRELAY:
|
||||
cookieID = stream_getU16( stream );
|
||||
|
@ -735,24 +756,12 @@ relayPreProcess( CommsCtxt* comms, XWStreamCtxt* stream, XWHostID* senderID )
|
|||
consumed = XP_TRUE;
|
||||
break;
|
||||
|
||||
case XWRELAY_OTHERCONNECT:
|
||||
/* If I'm a client connecting before the server, resend initial
|
||||
message to server now that it may be available. This is a bit
|
||||
of a hack. */
|
||||
comms_resendAll( comms );
|
||||
consumed = XP_TRUE;
|
||||
/* util_userError is synchronous, and so prevents a couple of
|
||||
robots from going at it until the user taps "ok". That sucks.
|
||||
For now let's see if the user can deduce the connection from
|
||||
the activity on the board. */
|
||||
/* util_userError( comms->util, INFO_REMOTE_CONNECTED ); */
|
||||
break;
|
||||
|
||||
case XWRELAY_DISCONNECT_YOU: /* Close socket for this? */
|
||||
case XWRELAY_CONNECTDENIED: /* Close socket for this? */
|
||||
XP_LOGF( "XWRELAY_DISCONNECT_YOU|XWRELAY_CONNECTDENIED" );
|
||||
relayErr = stream_getU8( stream );
|
||||
util_userError( comms->util, ERR_RELAY_BASE + relayErr );
|
||||
comms->relayState = COMMS_RELAYSTATE_UNCONNECTED;
|
||||
/* fallthru */
|
||||
default:
|
||||
consumed = XP_TRUE; /* drop it */
|
||||
|
@ -840,7 +849,8 @@ comms_checkIncomingStream( CommsCtxt* comms, XWStreamCtxt* stream,
|
|||
/* messageID for an incomming message should be one greater than
|
||||
* the id most recently used for that channel. */
|
||||
if ( !!recs && (msgID != recs->lastMsgReceived + 1) ) {
|
||||
XP_DEBUGF( "unex msgID " XP_LD " (expt " XP_LD ")",
|
||||
XP_DEBUGF( "on channel %d, old msgID " XP_LD
|
||||
" (next should be " XP_LD ")", channelNo,
|
||||
msgID, recs->lastMsgReceived+1 );
|
||||
validMessage = XP_FALSE;
|
||||
}
|
||||
|
@ -1060,6 +1070,8 @@ send_via_relay( CommsCtxt* comms, XWRELAY_Cmd cmd, XWHostID destID,
|
|||
stream_putU8( tmpStream, XWRELAY_PROTO_VERSION );
|
||||
stringToStream( tmpStream, addr.u.ip_relay.cookie );
|
||||
stream_putU8( tmpStream, comms->myHostID );
|
||||
stream_putU8( tmpStream, comms->nPlayersHere );
|
||||
stream_putU8( tmpStream, comms->nPlayersTotal );
|
||||
|
||||
comms->relayState = COMMS_RELAYSTATE_CONNECT_PENDING;
|
||||
break;
|
||||
|
@ -1067,6 +1079,8 @@ send_via_relay( CommsCtxt* comms, XWRELAY_Cmd cmd, XWHostID destID,
|
|||
case XWRELAY_GAME_RECONNECT:
|
||||
stream_putU8( tmpStream, XWRELAY_PROTO_VERSION );
|
||||
stream_putU8( tmpStream, comms->myHostID );
|
||||
stream_putU8( tmpStream, comms->nPlayersHere );
|
||||
stream_putU8( tmpStream, comms->nPlayersTotal );
|
||||
stringToStream( tmpStream, comms->connName );
|
||||
|
||||
comms->relayState = COMMS_RELAYSTATE_CONNECT_PENDING;
|
||||
|
|
|
@ -29,7 +29,7 @@ EXTERN_C_START
|
|||
#define CHANNEL_NONE ((XP_PlayerAddr)0)
|
||||
#define CONN_ID_NONE 0L
|
||||
|
||||
typedef XP_U32 MsgID;
|
||||
typedef XP_U32 MsgID; /* this is too big!!! PENDING */
|
||||
typedef XP_U8 XWHostID;
|
||||
|
||||
typedef enum {
|
||||
|
@ -74,10 +74,12 @@ typedef XP_S16 (*TransportSend)( XP_U8* buf, XP_U16 len,
|
|||
void* closure );
|
||||
|
||||
CommsCtxt* comms_make( MPFORMAL XW_UtilCtxt* util,
|
||||
XP_Bool isServer, TransportSend sendproc,
|
||||
void* closure );
|
||||
XP_Bool isServer,
|
||||
XP_U16 nPlayersHere, XP_U16 nPlayersTotal,
|
||||
TransportSend sendproc, void* closure );
|
||||
|
||||
void comms_reset( CommsCtxt* comms, XP_Bool isServer );
|
||||
void comms_reset( CommsCtxt* comms, XP_Bool isServer,
|
||||
XP_U16 nPlayersHere, XP_U16 nPlayersTotal );
|
||||
void comms_destroy( CommsCtxt* comms );
|
||||
|
||||
void comms_setConnID( CommsCtxt* comms, XP_U32 connID );
|
||||
|
|
|
@ -41,26 +41,30 @@ assertUtilOK( XW_UtilCtxt* util )
|
|||
#endif
|
||||
|
||||
static void
|
||||
checkServerRole( CurGameInfo* gi )
|
||||
checkServerRole( CurGameInfo* gi, XP_U16* nPlayersHere, XP_U16* nPlayersTotal )
|
||||
{
|
||||
if ( !!gi ) {
|
||||
XP_Bool standAlone = gi->serverRole == SERVER_STANDALONE;
|
||||
XP_U16 i, remoteCount = 0;
|
||||
|
||||
if ( gi->serverRole != SERVER_ISCLIENT ) {
|
||||
XP_Bool standAlone = gi->serverRole == SERVER_STANDALONE;
|
||||
XP_U16 i, remoteCount = 0;
|
||||
|
||||
for ( i = 0; i < gi->nPlayers; ++i ) {
|
||||
LocalPlayer* player = &gi->players[i];
|
||||
if ( !player->isLocal ) {
|
||||
++remoteCount;
|
||||
if ( standAlone ) {
|
||||
player->isLocal = XP_TRUE;
|
||||
}
|
||||
for ( i = 0; i < gi->nPlayers; ++i ) {
|
||||
LocalPlayer* player = &gi->players[i];
|
||||
if ( !player->isLocal ) {
|
||||
++remoteCount;
|
||||
if ( standAlone ) {
|
||||
player->isLocal = XP_TRUE;
|
||||
}
|
||||
}
|
||||
if ( remoteCount == 0 ) {
|
||||
gi->serverRole = SERVER_STANDALONE;
|
||||
}
|
||||
}
|
||||
if ( remoteCount == 0 && gi->serverRole != SERVER_ISCLIENT ) {
|
||||
gi->serverRole = SERVER_STANDALONE;
|
||||
}
|
||||
|
||||
*nPlayersHere = gi->nPlayers - remoteCount;
|
||||
if ( gi->serverRole == SERVER_ISCLIENT ) {
|
||||
*nPlayersTotal = 0;
|
||||
} else {
|
||||
*nPlayersTotal = gi->nPlayers;
|
||||
}
|
||||
}
|
||||
} /* checkServerRole */
|
||||
|
@ -71,8 +75,10 @@ game_makeNewGame( MPFORMAL XWGame* game, CurGameInfo* gi,
|
|||
XP_U16 gameID, CommonPrefs* cp,
|
||||
TransportSend sendproc, void* closure )
|
||||
{
|
||||
XP_U16 nPlayersHere, nPlayersTotal;
|
||||
|
||||
assertUtilOK( util );
|
||||
checkServerRole( gi );
|
||||
checkServerRole( gi, &nPlayersHere, &nPlayersTotal );
|
||||
|
||||
gi->gameID = gameID;
|
||||
|
||||
|
@ -83,6 +89,7 @@ game_makeNewGame( MPFORMAL XWGame* game, CurGameInfo* gi,
|
|||
if ( !!sendproc && gi->serverRole != SERVER_STANDALONE ) {
|
||||
game->comms = comms_make( MPPARM(mpool) util,
|
||||
gi->serverRole != SERVER_ISCLIENT,
|
||||
nPlayersHere, nPlayersTotal,
|
||||
sendproc, closure );
|
||||
} else {
|
||||
game->comms = (CommsCtxt*)NULL;
|
||||
|
@ -108,11 +115,12 @@ game_reset( MPFORMAL XWGame* game, CurGameInfo* gi, XW_UtilCtxt* util,
|
|||
void* closure )
|
||||
{
|
||||
XP_U16 i;
|
||||
XP_U16 nPlayersHere, nPlayersTotal;
|
||||
|
||||
XP_ASSERT( !!game->model );
|
||||
XP_ASSERT( !!gi );
|
||||
|
||||
checkServerRole( gi );
|
||||
checkServerRole( gi, &nPlayersHere, &nPlayersTotal );
|
||||
gi->gameID = gameID;
|
||||
|
||||
#ifndef XWFEATURE_STANDALONE_ONLY
|
||||
|
@ -121,11 +129,13 @@ game_reset( MPFORMAL XWGame* game, CurGameInfo* gi, XW_UtilCtxt* util,
|
|||
comms_destroy( game->comms );
|
||||
game->comms = NULL;
|
||||
} else {
|
||||
comms_reset( game->comms, gi->serverRole != SERVER_ISCLIENT );
|
||||
comms_reset( game->comms, gi->serverRole != SERVER_ISCLIENT,
|
||||
nPlayersHere, nPlayersTotal );
|
||||
}
|
||||
} else if ( gi->serverRole != SERVER_STANDALONE ) {
|
||||
game->comms = comms_make( MPPARM(mpool) util,
|
||||
gi->serverRole != SERVER_ISCLIENT,
|
||||
nPlayersHere, nPlayersTotal,
|
||||
sendproc, closure );
|
||||
}
|
||||
#endif
|
||||
|
|
217
relay/cref.cpp
217
relay/cref.cpp
|
@ -78,6 +78,8 @@ CookieRef::CookieRef( const char* cookie, const char* connName, CookieID id )
|
|||
, m_nextState(XW_ST_INITED)
|
||||
, m_eventQueue()
|
||||
, m_nextHostID(HOST_ID_SERVER)
|
||||
, m_nPlayersTotal(0)
|
||||
, m_nPlayersHere(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -105,23 +107,23 @@ CookieRef::~CookieRef()
|
|||
} /* ~CookieRef */
|
||||
|
||||
void
|
||||
CookieRef::_Connect( int socket, HostID srcID )
|
||||
CookieRef::_Connect( int socket, HostID hid, int nPlayersH, int nPlayersT )
|
||||
{
|
||||
CRefMgr::Get()->Associate( socket, this );
|
||||
if ( srcID == HOST_ID_NONE ) {
|
||||
srcID = nextHostID();
|
||||
logf( "assigned host id: %x", srcID );
|
||||
if ( hid == HOST_ID_NONE ) {
|
||||
hid = nextHostID();
|
||||
logf( "assigned host id: %x", hid );
|
||||
}
|
||||
pushConnectEvent( socket, srcID );
|
||||
pushConnectEvent( socket, hid, nPlayersH, nPlayersT );
|
||||
handleEvents();
|
||||
}
|
||||
|
||||
void
|
||||
CookieRef::_Reconnect( int socket, HostID srcID )
|
||||
CookieRef::_Reconnect( int socket, HostID hid, int nPlayersH, int nPlayersT )
|
||||
{
|
||||
CRefMgr::Get()->Associate( socket, this );
|
||||
/* MutexLock ml( &m_EventsMutex ); */
|
||||
pushReconnectEvent( socket, srcID );
|
||||
pushReconnectEvent( socket, hid, nPlayersH, nPlayersT );
|
||||
handleEvents();
|
||||
}
|
||||
|
||||
|
@ -169,12 +171,32 @@ CookieRef::NeverFullyConnected()
|
|||
}
|
||||
|
||||
int
|
||||
CookieRef::AcceptingConnections()
|
||||
CookieRef::AcceptingReconnections( HostID hid, int nPlayersH, int nPlayersT )
|
||||
{
|
||||
return m_curState == XW_ST_INITED
|
||||
|| m_curState == XW_ST_CONNECTING
|
||||
|| m_curState == XW_ST_MISSING;
|
||||
}
|
||||
int accept = 0;
|
||||
/* First, do we have room. Second, are we missing this guy? */
|
||||
|
||||
if ( m_curState != XW_ST_INITED
|
||||
&& m_curState != XW_ST_CONNECTING
|
||||
&& m_curState != XW_ST_MISSING ) {
|
||||
/* do nothing; reject */
|
||||
logf( "reject: bad state %s", stateString(m_curState) );
|
||||
} else if ( HostKnown( hid ) ) {
|
||||
logf( "reject: known hid" );
|
||||
/* do nothing: reject */
|
||||
} else {
|
||||
if ( m_nPlayersTotal == 0 ) {
|
||||
accept = 1;
|
||||
} else if ( nPlayersH + m_nPlayersHere <= m_nPlayersTotal ) {
|
||||
accept = 1;
|
||||
} else {
|
||||
logf( "reject: m_nPlayersTotal=%d, m_nPlayersHere=%d",
|
||||
m_nPlayersTotal, m_nPlayersHere );
|
||||
}
|
||||
}
|
||||
|
||||
return accept;
|
||||
} /* AcceptingReconnections */
|
||||
|
||||
void
|
||||
CookieRef::notifyDisconn( const CRefEvent* evt )
|
||||
|
@ -275,22 +297,28 @@ CookieRef::_Remove( int socket )
|
|||
} /* Forward */
|
||||
|
||||
void
|
||||
CookieRef::pushConnectEvent( int socket, HostID srcID )
|
||||
CookieRef::pushConnectEvent( int socket, HostID srcID,
|
||||
int nPlayersH, int nPlayersT )
|
||||
{
|
||||
CRefEvent evt;
|
||||
evt.type = XW_EVT_CONNECTMSG;
|
||||
evt.u.con.socket = socket;
|
||||
evt.u.con.srcID = srcID;
|
||||
evt.u.con.nPlayersH = nPlayersH;
|
||||
evt.u.con.nPlayersT = nPlayersT;
|
||||
m_eventQueue.push_back( evt );
|
||||
} /* pushConnectEvent */
|
||||
|
||||
void
|
||||
CookieRef::pushReconnectEvent( int socket, HostID srcID )
|
||||
CookieRef::pushReconnectEvent( int socket, HostID srcID,
|
||||
int nPlayersH, int nPlayersT )
|
||||
{
|
||||
CRefEvent evt;
|
||||
evt.type = XW_EVT_RECONNECTMSG;
|
||||
evt.u.con.socket = socket;
|
||||
evt.u.con.srcID = srcID;
|
||||
evt.u.con.nPlayersH = nPlayersH;
|
||||
evt.u.con.nPlayersT = nPlayersT;
|
||||
m_eventQueue.push_back( evt );
|
||||
} /* pushReconnectEvent */
|
||||
|
||||
|
@ -317,6 +345,7 @@ void
|
|||
CookieRef::pushForwardEvent( HostID src, HostID dest,
|
||||
unsigned char* buf, int buflen )
|
||||
{
|
||||
logf( "pushForwardEvent: %d -> %d", src, dest );
|
||||
CRefEvent evt;
|
||||
evt.type = XW_EVT_FORWARDMSG;
|
||||
evt.u.fwd.src = src;
|
||||
|
@ -399,21 +428,27 @@ CookieRef::handleEvents()
|
|||
XW_RELAY_ACTION takeAction;
|
||||
if ( getFromTable( m_curState, evt.type, &takeAction, &m_nextState ) ) {
|
||||
|
||||
logf( "cid %d: moving from state %s to state %s for event %s",
|
||||
m_cookieID, stateString(m_curState),
|
||||
stateString(m_nextState), eventString(evt.type) );
|
||||
logf( "cid %d: moving from state %s to state %s", m_cookieID,
|
||||
stateString(m_curState), stateString(m_nextState) );
|
||||
logf( "event = %s, action = %s", eventString(evt.type),
|
||||
actString(takeAction) );
|
||||
|
||||
switch( takeAction ) {
|
||||
|
||||
case XW_ACT_CHKCOUNTS:
|
||||
checkCounts( &evt );
|
||||
break;
|
||||
|
||||
case XW_ACT_SEND_1ST_RSP:
|
||||
case XW_ACT_SEND_1ST_RERSP:
|
||||
setAllConnectedTimer();
|
||||
increasePlayerCounts( &evt );
|
||||
sendResponse( &evt, takeAction == XW_ACT_SEND_1ST_RSP );
|
||||
break;
|
||||
|
||||
case XW_ACT_SEND_RSP:
|
||||
case XW_ACT_SEND_RERSP:
|
||||
notifyOthers( evt.u.con.socket, XWRELAY_OTHERCONNECT,
|
||||
XWRELAY_ERROR_NONE );
|
||||
increasePlayerCounts( &evt );
|
||||
sendResponse( &evt, takeAction == XW_ACT_SEND_RSP );
|
||||
break;
|
||||
|
||||
|
@ -440,6 +475,7 @@ CookieRef::handleEvents()
|
|||
break;
|
||||
|
||||
case XW_ACT_DISCONNECT:
|
||||
reducePlayerCounts( evt.u.discon.socket );
|
||||
removeSocket( evt.u.discon.socket );
|
||||
/* Don't notify. This is a normal part of a game ending. */
|
||||
break;
|
||||
|
@ -453,15 +489,26 @@ CookieRef::handleEvents()
|
|||
break;
|
||||
|
||||
case XW_ACT_REMOVESOCKET:
|
||||
reducePlayerCounts( evt.u.rmsock.socket );
|
||||
notifyOthers( evt.u.rmsock.socket, XWRELAY_DISCONNECT_OTHER,
|
||||
XWRELAY_ERROR_LOST_OTHER );
|
||||
removeSocket( evt.u.rmsock.socket );
|
||||
break;
|
||||
|
||||
case XW_ACT_SENDALLHERE:
|
||||
sendAllHere( 1 );
|
||||
break;
|
||||
case XW_ACT_2ND_SNDALLHERE:
|
||||
sendAllHere( 0 );
|
||||
break;
|
||||
|
||||
case XW_ACT_REJECT:
|
||||
|
||||
case XW_ACT_NONE:
|
||||
/* nothing to do for these */
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
break;
|
||||
|
@ -495,6 +542,92 @@ putNetShort( unsigned char** bufpp, unsigned short s )
|
|||
*bufpp += sizeof(s);
|
||||
}
|
||||
|
||||
void
|
||||
CookieRef::increasePlayerCounts( const CRefEvent* evt )
|
||||
{
|
||||
int nPlayersH = evt->u.con.nPlayersH;
|
||||
int nPlayersT = evt->u.con.nPlayersT;
|
||||
HostID hid = evt->u.con.srcID;
|
||||
|
||||
logf( "increasePlayerCounts: hid=%d, nPlayersH=%d, nPlayersT=%d",
|
||||
hid, nPlayersH, nPlayersT );
|
||||
|
||||
if ( hid == HOST_ID_SERVER ) {
|
||||
assert( m_nPlayersTotal == 0 );
|
||||
m_nPlayersTotal = nPlayersT;
|
||||
} else {
|
||||
assert( nPlayersT == 0 ); /* should catch this earlier!!! */
|
||||
assert( m_nPlayersTotal == 0 || m_nPlayersHere <= m_nPlayersTotal );
|
||||
}
|
||||
m_nPlayersHere += nPlayersH;
|
||||
|
||||
logf( "increasePlayerCounts: here=%d; total=%d",
|
||||
m_nPlayersHere, m_nPlayersTotal );
|
||||
|
||||
CRefEvent newevt;
|
||||
newevt.type = (m_nPlayersHere == m_nPlayersTotal) ?
|
||||
XW_EVT_ALLHERE : XW_EVT_SOMEMISSING;
|
||||
m_eventQueue.push_back( newevt );
|
||||
} /* increasePlayerCounts */
|
||||
|
||||
void
|
||||
CookieRef::reducePlayerCounts( int socket )
|
||||
{
|
||||
logf( "reducePlayerCounts on socket %d", socket );
|
||||
map<HostID,HostRec>::iterator iter = m_sockets.begin();
|
||||
while ( iter != m_sockets.end() ) {
|
||||
|
||||
if ( iter->second.m_socket == socket ) {
|
||||
assert( iter->first != 0 );
|
||||
|
||||
if ( iter->first == HOST_ID_SERVER ) {
|
||||
m_nPlayersTotal = 0;
|
||||
} else {
|
||||
assert( iter->second.m_nPlayersT == 0 );
|
||||
}
|
||||
m_nPlayersHere -= iter->second.m_nPlayersH;
|
||||
|
||||
logf( "reducePlayerCounts: m_nPlayersHere=%d; m_nPlayersTotal=%d",
|
||||
m_nPlayersHere, m_nPlayersTotal );
|
||||
|
||||
break;
|
||||
}
|
||||
++iter;
|
||||
}
|
||||
} /* reducePlayerCounts */
|
||||
|
||||
/* Determine if adding this device to the game would give us too many
|
||||
players. */
|
||||
void
|
||||
CookieRef::checkCounts( const CRefEvent* evt )
|
||||
{
|
||||
int nPlayersH = evt->u.con.nPlayersH;
|
||||
/* int nPlayersT = evt->u.con.nPlayersT; */
|
||||
HostID hid = evt->u.con.srcID;
|
||||
int success;
|
||||
|
||||
logf( "checkCounts: hid=%d, nPlayers=%d, m_nPlayersTotal = %d, "
|
||||
"m_nPlayersHere = %d",
|
||||
hid, nPlayersH, m_nPlayersTotal, m_nPlayersHere );
|
||||
|
||||
if ( hid == HOST_ID_SERVER ) {
|
||||
success = m_nPlayersTotal == 0;
|
||||
} else {
|
||||
success = (m_nPlayersTotal == 0) /* if no server present yet */
|
||||
|| (m_nPlayersTotal >= m_nPlayersHere + nPlayersH);
|
||||
}
|
||||
logf( "success = %d", success );
|
||||
|
||||
CRefEvent newevt;
|
||||
if ( success ) {
|
||||
newevt = *evt;
|
||||
newevt.type = XW_EVT_OKTOSEND;
|
||||
} else {
|
||||
newevt.type = XW_EVT_COUNTSBAD;
|
||||
}
|
||||
m_eventQueue.push_back( newevt );
|
||||
} /* checkCounts */
|
||||
|
||||
void
|
||||
CookieRef::setAllConnectedTimer()
|
||||
{
|
||||
|
@ -515,18 +648,21 @@ CookieRef::sendResponse( const CRefEvent* evt, int initial )
|
|||
{
|
||||
int socket = evt->u.con.socket;
|
||||
HostID id = evt->u.con.srcID;
|
||||
int nPlayersH = evt->u.con.nPlayersH;
|
||||
int nPlayersT = evt->u.con.nPlayersT;
|
||||
|
||||
assert( id != HOST_ID_NONE );
|
||||
logf( "remembering pair: hostid=%x, socket=%d", id, socket );
|
||||
HostRec hr(socket);
|
||||
HostRec hr(socket, nPlayersH, nPlayersT);
|
||||
m_sockets.insert( pair<HostID,HostRec>(id,hr) );
|
||||
|
||||
/* Now send the response */
|
||||
unsigned char buf[1 + /* cmd */
|
||||
sizeof(short) + /* heartbeat */
|
||||
sizeof(CookieID) +
|
||||
1 + /* hostID */
|
||||
MAX_CONNNAME_LEN+1];
|
||||
sizeof(CookieID) +
|
||||
1 /* hostID */
|
||||
];
|
||||
|
||||
unsigned char* bufp = buf;
|
||||
|
||||
*bufp++ = initial ? XWRELAY_CONNECT_RESP : XWRELAY_RECONNECT_RESP;
|
||||
|
@ -534,14 +670,6 @@ CookieRef::sendResponse( const CRefEvent* evt, int initial )
|
|||
putNetShort( &bufp, GetCookieID() );
|
||||
logf( "writing hostID of %d into mgs", id );
|
||||
*bufp++ = (char)id;
|
||||
if ( initial ) {
|
||||
const char* connName = ConnName();
|
||||
int len = strlen( connName );
|
||||
assert( len < MAX_CONNNAME_LEN );
|
||||
*bufp++ = (char)len;
|
||||
memcpy( bufp, connName, len );
|
||||
bufp += len;
|
||||
}
|
||||
|
||||
send_with_length( socket, buf, bufp - buf );
|
||||
logf( "sent XWRELAY_CONNECTRESP" );
|
||||
|
@ -607,8 +735,6 @@ CookieRef::send_msg( int socket, HostID id, XWRelayMsg msg, XWREASON why )
|
|||
memcpy( &buf[len], &tmp, 2 );
|
||||
len += 2;
|
||||
break;
|
||||
case XWRELAY_OTHERCONNECT:
|
||||
break;
|
||||
default:
|
||||
logf( "not handling message %d", msg );
|
||||
assert(0);
|
||||
|
@ -634,6 +760,31 @@ CookieRef::notifyOthers( int socket, XWRelayMsg msg, XWREASON why )
|
|||
}
|
||||
} /* notifyOthers */
|
||||
|
||||
void
|
||||
CookieRef::sendAllHere( int includeName )
|
||||
{
|
||||
unsigned char buf[1 + 1 + MAX_CONNNAME_LEN];
|
||||
unsigned char* bufp = buf;
|
||||
|
||||
*bufp++ = XWRELAY_ALLHERE;
|
||||
*bufp++ = includeName? 1 : 0;
|
||||
|
||||
if ( includeName ) {
|
||||
const char* connName = ConnName();
|
||||
int len = strlen( connName );
|
||||
assert( len < MAX_CONNNAME_LEN );
|
||||
*bufp++ = (char)len;
|
||||
memcpy( bufp, connName, len );
|
||||
bufp += len;
|
||||
}
|
||||
|
||||
map<HostID,HostRec>::iterator iter = m_sockets.begin();
|
||||
while ( iter != m_sockets.end() ) {
|
||||
send_with_length( iter->second.m_socket, buf, bufp-buf );
|
||||
++iter;
|
||||
}
|
||||
} /* sendAllHere */
|
||||
|
||||
void
|
||||
CookieRef::disconnectSockets( int socket, XWREASON why )
|
||||
{
|
||||
|
|
30
relay/cref.h
30
relay/cref.h
|
@ -37,9 +37,16 @@ class CookieMapIterator; /* forward */
|
|||
|
||||
class HostRec {
|
||||
public:
|
||||
HostRec(int socket) : m_socket(socket), m_lastHeartbeat(now()) {}
|
||||
HostRec(int socket, int nPlayersH, int nPlayersT)
|
||||
: m_socket(socket)
|
||||
, m_nPlayersH(nPlayersH)
|
||||
, m_nPlayersT(nPlayersT)
|
||||
, m_lastHeartbeat(now())
|
||||
{}
|
||||
~HostRec() {}
|
||||
int m_socket;
|
||||
int m_nPlayersH;
|
||||
int m_nPlayersT;
|
||||
time_t m_lastHeartbeat;
|
||||
};
|
||||
|
||||
|
@ -69,7 +76,7 @@ class CookieRef {
|
|||
int SocketForHost( HostID dest );
|
||||
|
||||
int NeverFullyConnected();
|
||||
int AcceptingConnections();
|
||||
int AcceptingReconnections( HostID hid, int nPlayersH, int nPlayersT );
|
||||
|
||||
/* for console */
|
||||
void _PrintCookieInfo( string& out );
|
||||
|
@ -81,8 +88,8 @@ class CookieRef {
|
|||
static void Delete( CookieID id );
|
||||
static void Delete( const char* name );
|
||||
|
||||
void _Connect( int socket, HostID srcID );
|
||||
void _Reconnect( int socket, HostID srcID );
|
||||
void _Connect( int socket, HostID srcID, int nPlayersH, int nPlayersT );
|
||||
void _Reconnect( int socket, HostID srcID, int nPlayersH, int nPlayersT );
|
||||
void _Disconnect(int socket, HostID hostID );
|
||||
void _HandleHeartbeat( HostID id, int socket );
|
||||
void _CheckHeartbeats( time_t now );
|
||||
|
@ -103,6 +110,8 @@ class CookieRef {
|
|||
} fwd;
|
||||
struct {
|
||||
int socket;
|
||||
int nPlayersH;
|
||||
int nPlayersT;
|
||||
HostID srcID;
|
||||
} con;
|
||||
struct {
|
||||
|
@ -134,8 +143,10 @@ class CookieRef {
|
|||
m_totalSent += nBytes;
|
||||
}
|
||||
|
||||
void pushConnectEvent( int socket, HostID srcID );
|
||||
void pushReconnectEvent( int socket, HostID srcID );
|
||||
void pushConnectEvent( int socket, HostID srcID,
|
||||
int nPlayersH, int nPlayersT );
|
||||
void pushReconnectEvent( int socket, HostID srcID,
|
||||
int nPlayersH, int nPlayersT );
|
||||
void pushHeartbeatEvent( HostID id, int socket );
|
||||
void pushHeartFailedEvent( int socket );
|
||||
|
||||
|
@ -153,6 +164,10 @@ class CookieRef {
|
|||
void handleEvents();
|
||||
|
||||
void sendResponse( const CRefEvent* evt, int initial );
|
||||
void increasePlayerCounts( const CRefEvent* evt );
|
||||
void reducePlayerCounts( int socket );
|
||||
void checkCounts( const CRefEvent* evt );
|
||||
|
||||
void setAllConnectedTimer();
|
||||
void cancelAllConnectedTimer();
|
||||
|
||||
|
@ -165,6 +180,7 @@ class CookieRef {
|
|||
void noteHeartbeat(const CRefEvent* evt);
|
||||
void notifyDisconn(const CRefEvent* evt);
|
||||
void removeSocket( int socket );
|
||||
void sendAllHere( int includeName );
|
||||
|
||||
HostID nextHostID() { return ++m_nextHostID; }
|
||||
/* timer callback */
|
||||
|
@ -190,6 +206,8 @@ class CookieRef {
|
|||
deque<CRefEvent> m_eventQueue;
|
||||
|
||||
HostID m_nextHostID;
|
||||
int m_nPlayersTotal;
|
||||
int m_nPlayersHere;
|
||||
}; /* CookieRef */
|
||||
|
||||
#endif
|
||||
|
|
|
@ -61,8 +61,10 @@ CRefMgr::~CRefMgr()
|
|||
}
|
||||
|
||||
CookieRef*
|
||||
CRefMgr::FindOpenGameFor( const char* cORn, int isCookie )
|
||||
CRefMgr::FindOpenGameFor( const char* cORn, int isCookie,
|
||||
HostID hid, int nPlayersH, int nPlayersT )
|
||||
{
|
||||
logf( "FindOpenGameFor with %s", cORn );
|
||||
CookieRef* cref = NULL;
|
||||
RWReadLock rwl( &m_cookieMapRWLock );
|
||||
|
||||
|
@ -70,14 +72,15 @@ CRefMgr::FindOpenGameFor( const char* cORn, int isCookie )
|
|||
while ( iter != m_cookieMap.end() ) {
|
||||
cref = iter->second;
|
||||
if ( isCookie ) {
|
||||
if ( cref->NeverFullyConnected() ) {
|
||||
if ( 0 == strcmp( cref->Name(), cORn ) ) {
|
||||
if ( 0 == strcmp( cref->Name(), cORn ) ) {
|
||||
if ( cref->NeverFullyConnected() ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ( cref->AcceptingConnections() ) {
|
||||
if ( 0 == strcmp( cref->ConnName(), cORn ) ) {
|
||||
if ( 0 == strcmp( cref->ConnName(), cORn ) ) {
|
||||
if ( cref->AcceptingReconnections( hid,
|
||||
nPlayersH, nPlayersH ) ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -119,7 +122,8 @@ CRefMgr::cookieIDForConnName( const char* connName )
|
|||
} /* cookieIDForConnName */
|
||||
|
||||
CookieRef*
|
||||
CRefMgr::getMakeCookieRef_locked( const char* cORn, int isCookie, HostID hid )
|
||||
CRefMgr::getMakeCookieRef_locked( const char* cORn, int isCookie, HostID hid,
|
||||
int nPlayersH, int nPlayersT )
|
||||
{
|
||||
CookieRef* cref;
|
||||
|
||||
|
@ -130,13 +134,14 @@ CRefMgr::getMakeCookieRef_locked( const char* cORn, int isCookie, HostID hid )
|
|||
XW_ST_CONNECTING state. So we need to look up the cookie first, then
|
||||
generate new connName and cookieIDs if it's not found. */
|
||||
|
||||
cref = FindOpenGameFor( cORn, isCookie );
|
||||
cref = FindOpenGameFor( cORn, isCookie, hid, nPlayersH, nPlayersT );
|
||||
if ( cref == NULL ) {
|
||||
string s;
|
||||
const char* connName;
|
||||
const char* cookie = NULL;
|
||||
if ( isCookie ) {
|
||||
cookie = cORn;
|
||||
string s = PermID::GetNextUniqueID();
|
||||
s = PermID::GetNextUniqueID();
|
||||
connName = s.c_str();
|
||||
} else {
|
||||
connName = cORn;
|
||||
|
@ -162,7 +167,7 @@ CRefMgr::Associate( int socket, CookieRef* cref )
|
|||
{
|
||||
MutexLock ml( &m_SocketStuffMutex );
|
||||
SocketMap::iterator iter = m_SocketStuff.find( socket );
|
||||
if ( iter == m_SocketStuff.end() ) {
|
||||
if ( iter != m_SocketStuff.end() ) {
|
||||
logf( "replacing existing cref/threadID pair for socket %d", socket );
|
||||
}
|
||||
|
||||
|
@ -312,6 +317,9 @@ CRefMgr::UnlockCref( CookieRef* cref )
|
|||
CookieRef*
|
||||
CRefMgr::AddNew( const char* cookie, const char* connName, CookieID id )
|
||||
{
|
||||
CookieRef* exists = getCookieRef_impl( id );
|
||||
assert( exists == NULL );
|
||||
|
||||
RWWriteLock rwl( &m_cookieMapRWLock );
|
||||
logf( "making new cref: %d", id );
|
||||
CookieRef* ref = new CookieRef( cookie, connName, id );
|
||||
|
@ -433,13 +441,15 @@ CookieMapIterator::Next()
|
|||
// SafeCref
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
SafeCref::SafeCref( const char* cORn, int isCookie, HostID hid )
|
||||
SafeCref::SafeCref( const char* cORn, int isCookie, HostID hid,
|
||||
int nPlayersH, int nPlayersT )
|
||||
: m_cref( NULL )
|
||||
, m_mgr( CRefMgr::Get() )
|
||||
{
|
||||
CookieRef* cref;
|
||||
|
||||
cref = m_mgr->getMakeCookieRef_locked( cORn, isCookie, hid );
|
||||
cref = m_mgr->getMakeCookieRef_locked( cORn, isCookie, hid,
|
||||
nPlayersH, nPlayersT );
|
||||
if ( cref != NULL ) {
|
||||
if ( m_mgr->LockCref( cref ) ) {
|
||||
m_cref = cref;
|
||||
|
|
|
@ -77,13 +77,16 @@ class CRefMgr {
|
|||
|
||||
private:
|
||||
friend class SafeCref;
|
||||
CookieRef* getMakeCookieRef_locked( const char* cORn, int isCookie, HostID hid );
|
||||
CookieRef* getMakeCookieRef_locked( const char* cORn, int isCookie,
|
||||
HostID hid,
|
||||
int nPlayersH, int nPlayersT );
|
||||
CookieRef* getCookieRef_locked( CookieID cookieID );
|
||||
CookieRef* getCookieRef_locked( int socket );
|
||||
int checkCookieRef_locked( CookieRef* cref );
|
||||
CookieRef* getCookieRef_impl( CookieID cookieID );
|
||||
CookieRef* AddNew( const char* cookie, const char* connName, CookieID id );
|
||||
CookieRef* FindOpenGameFor( const char* cORn, int isCookie );
|
||||
CookieRef* FindOpenGameFor( const char* cORn, int isCookie,
|
||||
HostID hid, int nPlayersH, int nPlayersT );
|
||||
|
||||
CookieID cookieIDForConnName( const char* connName );
|
||||
CookieID nextCID( const char* connName );
|
||||
|
@ -112,7 +115,8 @@ class SafeCref {
|
|||
CookieRef instance at a time. */
|
||||
|
||||
public:
|
||||
SafeCref( const char* cookieOrConnName, int cookie, HostID hid );
|
||||
SafeCref( const char* cookieOrConnName, int cookie, HostID hid,
|
||||
int nPlayersH, int nPlayersT );
|
||||
SafeCref( CookieID cid );
|
||||
SafeCref( int socket );
|
||||
SafeCref( CookieRef* cref );
|
||||
|
@ -126,17 +130,17 @@ class SafeCref {
|
|||
return 0;
|
||||
}
|
||||
}
|
||||
int Connect( int socket, HostID srcID ) {
|
||||
int Connect( int socket, HostID srcID, int nPlayersH, int nPlayersT ) {
|
||||
if ( IsValid() ) {
|
||||
m_cref->_Connect( socket, srcID );
|
||||
m_cref->_Connect( socket, srcID, nPlayersH, nPlayersT );
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
int Reconnect( int socket, HostID srcID ) {
|
||||
int Reconnect( int socket, HostID srcID, int nPlayersH, int nPlayersT ) {
|
||||
if ( IsValid() ) {
|
||||
m_cref->_Reconnect( socket, srcID );
|
||||
m_cref->_Reconnect( socket, srcID, nPlayersH, nPlayersT );
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
|
|
|
@ -34,6 +34,7 @@ PermID::GetNextUniqueID()
|
|||
MutexLock ml( &s_guard );
|
||||
|
||||
string s = s_serverName;
|
||||
assert( s.length() > 0 );
|
||||
s += ":";
|
||||
|
||||
char buf[32]; /* should last for a while :-) */
|
||||
|
|
124
relay/states.cpp
124
relay/states.cpp
|
@ -39,22 +39,84 @@ typedef struct StateTable {
|
|||
best if we can put the game in a state where others can't connect, if the
|
||||
window where new devices can sign in using a given cookie is fairly small.
|
||||
|
||||
Perhaps a better algorithm is needed for determining when the game is
|
||||
closed. It's not when the first XW_EVT_FORWARDMSG comes along, since that
|
||||
can happen before all hosts have arrived (e.g. if a client signs up before
|
||||
the server.) The goal has been to avoid having the relay know about the
|
||||
xwords protocol. But it already knows about hostID 1. So maybe when that
|
||||
host sends a message for forwarding we know we're set? Yes: the server
|
||||
sends only after all clients have reported in, at least now.
|
||||
New rules on accepting connections and reconnections:
|
||||
|
||||
The problem we want to avoid is bogus connections. Two are playing using
|
||||
cookie FOO. Somebody looks over a shoulder and sees FOO, and tries to
|
||||
connect. The relay must not start forwarding packets from the interloper
|
||||
into the game associated with FOO.
|
||||
- Connect and reconnect messages contain nPlayersHere (local) and
|
||||
nPlayersTotal params.
|
||||
|
||||
- On connect action, we either note the total expected, or increase the
|
||||
total we have. Ditto on reconnect. On disconnect, we take the departing
|
||||
hosts marbles away from the game.
|
||||
|
||||
- We only accept [re]connections when we're missing players, and we only
|
||||
forward messages when all devices/players are accounted for. There's a
|
||||
notification sent when the last shows up, so devices can send pending
|
||||
messages at that time.
|
||||
|
||||
- There's at least one bug with this scheme: we don't know how many players
|
||||
to expect until the server shows up, so we'll keep accepting clients
|
||||
until that happens. Probably need a config variable that sets an upper
|
||||
bound on the number of players.
|
||||
*/
|
||||
|
||||
StateTable g_stateTable[] = {
|
||||
|
||||
{ XW_ST_INITED, XW_EVT_CONNECTMSG, XW_ACT_CHKCOUNTS, XW_ST_CHKCOUNTS_INIT },
|
||||
{ XW_ST_CHKCOUNTS_INIT, XW_EVT_OKTOSEND, XW_ACT_SEND_1ST_RSP, XW_ST_CHK_ALLHERE },
|
||||
{ XW_ST_CHKCOUNTS_INIT, XW_EVT_COUNTSBAD, XW_ACT_REJECT, XW_ST_INITED },
|
||||
|
||||
{ XW_ST_CONNECTING, XW_EVT_CONNECTMSG, XW_ACT_CHKCOUNTS, XW_ST_CHKCOUNTS },
|
||||
{ XW_ST_CHKCOUNTS, XW_EVT_OKTOSEND, XW_ACT_SEND_1ST_RSP, XW_ST_CHK_ALLHERE },
|
||||
{ XW_ST_CHKCOUNTS, XW_EVT_COUNTSBAD, XW_ACT_REJECT, XW_ST_CONNECTING },
|
||||
|
||||
{ XW_ST_MISSING, XW_EVT_CONNECTMSG, XW_ACT_CHKCOUNTS, XW_ST_CHKCOUNTS_MISS },
|
||||
{ XW_ST_CHKCOUNTS_MISS, XW_EVT_OKTOSEND, XW_ACT_SEND_1ST_RSP, XW_ST_CHK_2ND_ALLHERE },
|
||||
{ XW_ST_CHKCOUNTS_MISS, XW_EVT_COUNTSBAD, XW_ACT_REJECT, XW_ST_MISSING },
|
||||
|
||||
|
||||
{ XW_ST_CONNECTING, XW_EVT_CONNECTMSG, XW_ACT_SEND_RSP, XW_ST_CHK_ALLHERE },
|
||||
{ XW_ST_CHK_ALLHERE, XW_EVT_ALLHERE, XW_ACT_SENDALLHERE, XW_ST_ALLCONNECTED },
|
||||
{ XW_ST_CHK_ALLHERE, XW_EVT_SOMEMISSING, XW_ACT_NONE, XW_ST_CONNECTING },
|
||||
|
||||
{ XW_ST_ALLCONNECTED, XW_EVT_DISCONNECTMSG, XW_ACT_DISCONNECT, XW_ST_MISSING },
|
||||
{ XW_ST_CONNECTING, XW_EVT_DISCONNECTMSG, XW_ACT_DISCONNECT, XW_ST_CONNECTING },
|
||||
{ XW_ST_MISSING, XW_EVT_DISCONNECTMSG, XW_ACT_DISCONNECT, XW_ST_MISSING },
|
||||
{ XW_ST_MISSING, XW_EVT_NOMORESOCKETS, XW_ACT_NONE, XW_ST_DEAD },
|
||||
{ XW_ST_CONNECTING, XW_EVT_NOMORESOCKETS, XW_ACT_NONE, XW_ST_DEAD },
|
||||
|
||||
{ XW_ST_INITED, XW_EVT_RECONNECTMSG, XW_ACT_SEND_RERSP, XW_ST_CHK_2ND_ALLHERE },
|
||||
{ XW_ST_MISSING, XW_EVT_RECONNECTMSG, XW_ACT_SEND_RERSP, XW_ST_CHK_2ND_ALLHERE },
|
||||
{ XW_ST_CHK_2ND_ALLHERE, XW_EVT_ALLHERE, XW_ACT_2ND_SNDALLHERE,XW_ST_ALLCONNECTED },
|
||||
{ XW_ST_CHK_2ND_ALLHERE, XW_EVT_SOMEMISSING, XW_ACT_NONE, XW_ST_MISSING },
|
||||
|
||||
{ XW_ST_CONNECTING, XW_EVT_REMOVESOCKET, XW_ACT_REMOVESOCKET, XW_ST_CONNECTING },
|
||||
{ XW_ST_ALLCONNECTED, XW_EVT_REMOVESOCKET, XW_ACT_REMOVESOCKET, XW_ST_MISSING },
|
||||
{ XW_ST_MISSING, XW_EVT_REMOVESOCKET, XW_ACT_REMOVESOCKET, XW_ST_MISSING },
|
||||
|
||||
{ XW_ST_ALLCONNECTED, XW_EVT_HEARTFAILED, XW_ACT_HEARTDISCONNECT, XW_ST_MISSING },
|
||||
{ XW_ST_CONNECTING, XW_EVT_HEARTFAILED, XW_ACT_HEARTDISCONNECT, XW_ST_CONNECTING },
|
||||
{ XW_ST_MISSING, XW_EVT_HEARTFAILED, XW_ACT_HEARTDISCONNECT, XW_ST_MISSING },
|
||||
|
||||
/* Heartbeat arrived */
|
||||
{ XW_ST_CONNECTING, XW_EVT_HEARTRCVD, XW_ACT_NOTEHEART, XW_ST_CONNECTING },
|
||||
{ XW_ST_ALLCONNECTED, XW_EVT_HEARTRCVD, XW_ACT_NOTEHEART, XW_ST_ALLCONNECTED },
|
||||
{ XW_ST_MISSING, XW_EVT_HEARTRCVD, XW_ACT_NOTEHEART, XW_ST_MISSING },
|
||||
|
||||
/* Connect timer */
|
||||
{ XW_ST_CONNECTING, XW_EVT_CONNTIMER, XW_ACT_TIMERDISCONNECT, XW_ST_DEAD },
|
||||
{ XW_ST_ALLCONNECTED, XW_EVT_CONNTIMER, XW_ACT_NONE, XW_ST_ALLCONNECTED },
|
||||
|
||||
{ XW_ST_CONNECTING, XW_EVT_NOTIFYDISCON,XW_ACT_NOTIFYDISCON, XW_ST_CONNECTING },
|
||||
{ XW_ST_ALLCONNECTED, XW_EVT_NOTIFYDISCON,XW_ACT_NOTIFYDISCON, XW_ST_MISSING },
|
||||
{ XW_ST_MISSING, XW_EVT_NOTIFYDISCON,XW_ACT_NOTIFYDISCON, XW_ST_DEAD },
|
||||
{ XW_ST_DEAD, XW_EVT_NOTIFYDISCON,XW_ACT_NOTIFYDISCON, XW_ST_DEAD },
|
||||
|
||||
/* This is our bread-n-butter */
|
||||
{ XW_ST_ALLCONNECTED, XW_EVT_FORWARDMSG, XW_ACT_FWD, XW_ST_ALLCONNECTED },
|
||||
|
||||
{ XW_ST_DEAD, XW_EVT_REMOVESOCKET, XW_ACT_REMOVESOCKET, XW_ST_DEAD },
|
||||
|
||||
#if 0
|
||||
/* Initial msg comes in. Managing object created in init state, sends response */
|
||||
{ XW_ST_INITED, XW_EVT_CONNECTMSG, XW_ACT_SEND_1ST_RSP, XW_ST_CONNECTING },
|
||||
{ XW_ST_INITED, XW_EVT_RECONNECTMSG, XW_ACT_SEND_1ST_RERSP,XW_ST_CONNECTING },
|
||||
|
@ -83,7 +145,6 @@ StateTable g_stateTable[] = {
|
|||
{ XW_ST_CHECKINGDEST, XW_EVT_DESTBAD, XW_ACT_NONE, XW_ST_CONNECTING },
|
||||
|
||||
/* Timeout before all connected */
|
||||
{ XW_ST_CONNECTING, XW_EVT_CONNTIMER, XW_ACT_TIMERDISCONNECT,XW_ST_DEAD },
|
||||
|
||||
{ XW_ST_ALLCONNECTED, XW_EVT_HEARTFAILED, XW_ACT_HEARTDISCONNECT, XW_ST_MISSING },
|
||||
{ XW_ST_CONNECTING, XW_EVT_HEARTFAILED, XW_ACT_HEARTDISCONNECT, XW_ST_CONNECTING },
|
||||
|
@ -98,7 +159,6 @@ StateTable g_stateTable[] = {
|
|||
|
||||
/* This is the entry we'll use most of the time */
|
||||
{ XW_ST_ALLCONNECTED, XW_EVT_FORWARDMSG, XW_ACT_FWD, XW_ST_ALLCONNECTED },
|
||||
{ XW_ST_ALLCONNECTED, XW_EVT_CONNTIMER, XW_ACT_NONE, XW_ST_ALLCONNECTED },
|
||||
|
||||
/* Heartbeat arrived */
|
||||
{ XW_ST_CONNECTING, XW_EVT_HEARTRCVD, XW_ACT_NOTEHEART, XW_ST_CONNECTING },
|
||||
|
@ -112,7 +172,7 @@ StateTable g_stateTable[] = {
|
|||
{ XW_ST_MISSING, XW_EVT_NOTIFYDISCON, XW_ACT_NOTIFYDISCON, XW_ST_DEAD },
|
||||
{ XW_ST_DEAD, XW_EVT_NOTIFYDISCON, XW_ACT_NOTIFYDISCON, XW_ST_DEAD },
|
||||
{ XW_ST_DEAD, XW_EVT_REMOVESOCKET, XW_ACT_REMOVESOCKET, XW_ST_DEAD },
|
||||
|
||||
#endif
|
||||
// { XW_ST_DEAD, XW_EVT_ANY, XW_ACT_NONE, XW_ST_DEAD },
|
||||
|
||||
/* Reconnect. Just like a connect but cookieID is supplied. Can it
|
||||
|
@ -161,6 +221,11 @@ stateString( XW_RELAY_STATE state )
|
|||
CASESTR(XW_ST_CHECKINGDEST);
|
||||
CASESTR(XW_ST_CHECKING_CAN_LOCK);
|
||||
CASESTR(XW_ST_MISSING);
|
||||
CASESTR(XW_ST_CHK_ALLHERE);
|
||||
CASESTR(XW_ST_CHK_2ND_ALLHERE);
|
||||
CASESTR(XW_ST_CHKCOUNTS_INIT);
|
||||
CASESTR(XW_ST_CHKCOUNTS_MISS);
|
||||
CASESTR(XW_ST_CHKCOUNTS);
|
||||
}
|
||||
assert(0);
|
||||
return "";
|
||||
|
@ -186,9 +251,40 @@ eventString( XW_RELAY_EVENT evt )
|
|||
CASESTR(XW_EVT_REMOVESOCKET);
|
||||
CASESTR(XW_EVT_NOMORESOCKETS);
|
||||
CASESTR(XW_EVT_NOTIFYDISCON);
|
||||
CASESTR(XW_EVT_ALLHERE);
|
||||
CASESTR(XW_EVT_SOMEMISSING);
|
||||
CASESTR(XW_EVT_OKTOSEND);
|
||||
CASESTR(XW_EVT_COUNTSBAD);
|
||||
}
|
||||
assert(0);
|
||||
return "";
|
||||
}
|
||||
|
||||
char*
|
||||
actString( XW_RELAY_ACTION act )
|
||||
{
|
||||
switch ( act ) {
|
||||
CASESTR(XW_ACT_NONE);
|
||||
CASESTR(XW_ACT_SEND_1ST_RSP);
|
||||
CASESTR(XW_ACT_SEND_1ST_RERSP);
|
||||
CASESTR(XW_ACT_CHKCOUNTS);
|
||||
CASESTR(XW_ACT_REJECT);
|
||||
CASESTR(XW_ACT_SEND_RSP);
|
||||
CASESTR(XW_ACT_SEND_RERSP);
|
||||
CASESTR(XW_ACT_SENDALLHERE);
|
||||
CASESTR(XW_ACT_2ND_SNDALLHERE);
|
||||
CASESTR(XW_ACT_FWD);
|
||||
CASESTR(XW_ACT_NOTEHEART);
|
||||
CASESTR(XW_ACT_DISCONNECTALL);
|
||||
CASESTR(XW_ACT_TIMERDISCONNECT);
|
||||
CASESTR(XW_ACT_CHECKDEST);
|
||||
CASESTR(XW_ACT_DISCONNECT);
|
||||
CASESTR(XW_ACT_NOTIFYDISCON);
|
||||
CASESTR(XW_ACT_REMOVESOCKET);
|
||||
CASESTR(XW_ACT_CHECK_CAN_LOCK);
|
||||
CASESTR(XW_ACT_HEARTDISCONNECT);
|
||||
}
|
||||
assert(0);
|
||||
return "";
|
||||
}
|
||||
#undef CASESTR
|
||||
|
|
|
@ -26,6 +26,19 @@ typedef
|
|||
enum {
|
||||
XW_ST_NONE
|
||||
|
||||
,XW_ST_CHKCOUNTS_INIT /* from initial state, check if all players
|
||||
are here. Success should be an error,
|
||||
actually: 1-device game. */
|
||||
|
||||
,XW_ST_CHKCOUNTS_MISS /* from the missing state */
|
||||
,XW_ST_CHKCOUNTS /* check from any other state */
|
||||
|
||||
,XW_ST_CHK_ALLHERE /* Need to see if all expected devices/players
|
||||
are on board. */
|
||||
|
||||
,XW_ST_CHK_2ND_ALLHERE /* same as above, but triggered by a reconnect
|
||||
rather than a connect request */
|
||||
|
||||
,XW_ST_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
|
||||
|
@ -66,6 +79,12 @@ enum {
|
|||
typedef enum {
|
||||
XW_EVT_NONE
|
||||
|
||||
,XW_EVT_OKTOSEND
|
||||
,XW_EVT_COUNTSBAD
|
||||
|
||||
,XW_EVT_ALLHERE /* notify that all expected players are arrived */
|
||||
,XW_EVT_SOMEMISSING /* notify that some expected players are still missing */
|
||||
|
||||
,XW_EVT_CONNECTMSG /* A device is connecting using the cookie for
|
||||
this object */
|
||||
|
||||
|
@ -107,12 +126,19 @@ typedef enum {
|
|||
,XW_ACT_SEND_1ST_RSP
|
||||
,XW_ACT_SEND_1ST_RERSP
|
||||
|
||||
,XW_ACT_SEND_RSP /* Send a connection response */
|
||||
,XW_ACT_CHKCOUNTS
|
||||
|
||||
,XW_ACT_REJECT
|
||||
|
||||
,XW_ACT_SEND_RSP /* Send a connection response */
|
||||
,XW_ACT_SEND_RERSP
|
||||
|
||||
,XW_ACT_FWD /* Forward a message */
|
||||
,XW_ACT_SENDALLHERE /* Let all devices know we're in business */
|
||||
,XW_ACT_2ND_SNDALLHERE
|
||||
|
||||
,XW_ACT_NOTEHEART /* Record heartbeat received */
|
||||
,XW_ACT_FWD /* Forward a message */
|
||||
|
||||
,XW_ACT_NOTEHEART /* Record heartbeat received */
|
||||
|
||||
,XW_ACT_DISCONNECTALL
|
||||
,XW_ACT_TIMERDISCONNECT /* disconnect all because of a timer */
|
||||
|
@ -138,5 +164,6 @@ int getFromTable( XW_RELAY_STATE curState, XW_RELAY_EVENT curEvent,
|
|||
|
||||
char* stateString( XW_RELAY_STATE state );
|
||||
char* eventString( XW_RELAY_EVENT evt );
|
||||
char* actString( XW_RELAY_ACTION act );
|
||||
|
||||
#endif
|
||||
|
|
|
@ -4,12 +4,12 @@
|
|||
# Format: var=value. No spaces between var and value at this point.
|
||||
|
||||
# Heartbeat timer. Sent to clients.
|
||||
HEARTBEAT=6000
|
||||
HEARTBEAT=60
|
||||
|
||||
# How long after the first connection on a cookie until we need to
|
||||
# have heard from all players in the game? After this long passes we
|
||||
# kill the connection after notifying all that have connected.
|
||||
ALLCONN=12000
|
||||
ALLCONN=300
|
||||
|
||||
# How many worker threads in the thread pool? Default is five.
|
||||
NTHREADS=5
|
||||
|
|
|
@ -203,11 +203,15 @@ processConnect( unsigned char* bufp, int bufLen, int socket )
|
|||
unsigned char flags = *bufp++;
|
||||
if ( flagsOK( flags ) ) {
|
||||
HostID srcID;
|
||||
unsigned char nPlayersH;
|
||||
unsigned char nPlayersT;
|
||||
if ( readStr( &bufp, end, cookie, sizeof(cookie) )
|
||||
&& getNetByte( &bufp, end, &srcID ) ) {
|
||||
&& getNetByte( &bufp, end, &srcID )
|
||||
&& getNetByte( &bufp, end, &nPlayersH )
|
||||
&& getNetByte( &bufp, end, &nPlayersT ) ) {
|
||||
|
||||
SafeCref scr( cookie, 1, srcID );
|
||||
success = scr.Connect( socket, srcID );
|
||||
SafeCref scr( cookie, 1, srcID, nPlayersH, nPlayersT );
|
||||
success = scr.Connect( socket, srcID, nPlayersH, nPlayersT );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -230,18 +234,22 @@ processReconnect( unsigned char* bufp, int bufLen, int socket )
|
|||
unsigned char flags = *bufp++;
|
||||
if ( flagsOK( flags ) ) {
|
||||
HostID srcID;
|
||||
unsigned char nPlayersH;
|
||||
unsigned char nPlayersT;
|
||||
if ( getNetByte( &bufp, end, &srcID )
|
||||
&& getNetByte( &bufp, end, &nPlayersH )
|
||||
&& getNetByte( &bufp, end, &nPlayersT )
|
||||
&& readStr( &bufp, end, connName, sizeof(connName) ) ) {
|
||||
|
||||
SafeCref scr( connName, 0, srcID );
|
||||
success = scr.Connect( socket, srcID );
|
||||
SafeCref scr( connName, 0, srcID, nPlayersH, nPlayersT );
|
||||
success = scr.Reconnect( socket, srcID, nPlayersH, nPlayersT );
|
||||
}
|
||||
}
|
||||
|
||||
if ( !success ) {
|
||||
denyConnection( socket );
|
||||
}
|
||||
}
|
||||
} /* processReconnect */
|
||||
|
||||
static void
|
||||
processDisconnect( unsigned char* bufp, int bufLen, int socket )
|
||||
|
|
|
@ -31,13 +31,15 @@ enum { XWRELAY_NONE /* 0 is an illegal value */
|
|||
|
||||
, XWRELAY_GAME_CONNECT
|
||||
/* Sent from device to relay to establish connection to relay. Format:
|
||||
flags: 1; cookieLen: 1; cookie: <cookieLen>; hostID: 1. */
|
||||
flags: 1; cookieLen: 1; cookie: <cookieLen>; hostID: 1; nPlayers: 1;
|
||||
nPlayersTotal: 1 */
|
||||
|
||||
, XWRELAY_GAME_RECONNECT
|
||||
/* Connect using connName rather than cookie. Used by a device that's
|
||||
lost its connection to a game in progress. Once a game is locked
|
||||
this is the only way a host can get (back) in. Format: flags: 1;
|
||||
hostID: 1; connNameLen: 1; connName<connNameLen>*/
|
||||
hostID: 1; nPlayers: 1; nPlayersTotal: 1; connNameLen: 1;
|
||||
connName<connNameLen>*/
|
||||
|
||||
, XWRELAY_GAME_DISCONNECT
|
||||
/* Tell the relay that we're gone for this game. After this message is
|
||||
|
@ -46,15 +48,18 @@ 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; assignedHostID: 1;
|
||||
connNameLen: 1; connName: <connNameLen>; */
|
||||
heartbeat_seconds: 2; connectionID: 2; assignedHostID: 1 */
|
||||
|
||||
, XWRELAY_RECONNECT_RESP
|
||||
/* Sent from relay to device in response to XWRELAY_RECONNECT. Format:
|
||||
heartbeat_seconds: 2; connectionID: 2; */
|
||||
|
||||
, XWRELAY_OTHERCONNECT /* Another device has [re]joined your game.
|
||||
Format: ??? */
|
||||
, 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: hasName: 1; [nameLen: 1; connName: <nameLen> */
|
||||
|
||||
, XWRELAY_DISCONNECT_YOU
|
||||
/* Sent from relay when existing connection is terminated.
|
||||
|
|
Loading…
Add table
Reference in a new issue