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:
ehouse 2005-10-02 15:39:38 +00:00
parent e098e6f7b2
commit 2b58da4cdf
13 changed files with 478 additions and 132 deletions

View file

@ -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;

View file

@ -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 );

View file

@ -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

View file

@ -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 )
{

View file

@ -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

View file

@ -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;

View file

@ -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;

View file

@ -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 :-) */

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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 )

View file

@ -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.