mirror of
git://xwords.git.sourceforge.net/gitroot/xwords/xwords
synced 2025-01-04 23:02:02 +01:00
4996d3fe89
Remove legacy relay-inspired logic around comms addressing. Now when a device creates a game it's required to provide its "self address," and if it's a client, the address of the host (which it presumably got through the invitation in response to which the game is being created.) Then as registration messages come in from clients, the host gathers their addresses as always.
398 lines
12 KiB
C
398 lines
12 KiB
C
/* -*- compile-command: "cd ../linux && make MEMDEBUG=TRUE -j3"; -*- */
|
|
/*
|
|
* Copyright 2020 by Eric House (xwords@eehouse.org). All rights reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
#include "knownplyr.h"
|
|
#include "strutils.h"
|
|
#include "comms.h"
|
|
#include "dbgutil.h"
|
|
|
|
#ifdef XWFEATURE_KNOWNPLAYERS
|
|
|
|
typedef struct _KnownPlayer {
|
|
struct _KnownPlayer* next;
|
|
XP_U32 newestMod;
|
|
XP_UCHAR* name;
|
|
CommsAddrRec addr;
|
|
} KnownPlayer;
|
|
|
|
typedef struct _KPState {
|
|
KnownPlayer* players;
|
|
XP_U16 nPlayers;
|
|
XP_Bool dirty;
|
|
XP_Bool inUse;
|
|
} KPState;
|
|
|
|
static void addPlayer( XW_DUtilCtxt* dutil, KPState* state,
|
|
const XP_UCHAR* name, const CommsAddrRec* addr,
|
|
XP_U32 newestMod );
|
|
static void getPlayersImpl( const KPState* state, const XP_UCHAR** players,
|
|
XP_U16* nFound );
|
|
|
|
static void
|
|
loadFromStream( XW_DUtilCtxt* dutil, KPState* state, XWStreamCtxt* stream )
|
|
{
|
|
while ( 0 < stream_getSize( stream ) ) {
|
|
XP_U32 newestMod = stream_getU32( stream );
|
|
XP_UCHAR buf[64];
|
|
stringFromStreamHere( stream, buf, VSIZE(buf) );
|
|
|
|
CommsAddrRec addr = {0};
|
|
addrFromStream( &addr, stream );
|
|
|
|
addPlayer( dutil, state, buf, &addr, newestMod );
|
|
}
|
|
}
|
|
|
|
static KPState*
|
|
loadState( XW_DUtilCtxt* dutil, XWEnv xwe )
|
|
{
|
|
// LOG_FUNC();
|
|
pthread_mutex_lock( &dutil->kpMutex );
|
|
|
|
KPState* state = (KPState*)dutil->kpCtxt;
|
|
if ( NULL == state ) {
|
|
dutil->kpCtxt = state = XP_CALLOC( dutil->mpool, sizeof(*state) );
|
|
XWStreamCtxt* stream = mem_stream_make_raw( MPPARM(dutil->mpool)
|
|
dutil_getVTManager(dutil) );
|
|
const XP_UCHAR* keys[] = { KNOWN_PLAYERS_KEY, NULL };
|
|
dutil_loadStream( dutil, xwe, keys, stream );
|
|
if ( 0 < stream_getSize( stream ) ) {
|
|
XP_U8 vers = stream_getU8( stream );
|
|
stream_setVersion( stream, vers );
|
|
loadFromStream( dutil, state, stream );
|
|
}
|
|
|
|
stream_destroy( stream, xwe );
|
|
}
|
|
XP_ASSERT( !state->inUse );
|
|
state->inUse = XP_TRUE;
|
|
return state;
|
|
}
|
|
|
|
static void
|
|
saveState( XW_DUtilCtxt* dutil, XWEnv xwe, KPState* state )
|
|
{
|
|
if ( state->dirty ) {
|
|
XWStreamCtxt* stream = mem_stream_make_raw( MPPARM(dutil->mpool)
|
|
dutil_getVTManager(dutil) );
|
|
stream_putU8( stream, CUR_STREAM_VERS );
|
|
for ( KnownPlayer* kp = state->players; !!kp; kp = kp->next ) {
|
|
stream_putU32( stream, kp->newestMod );
|
|
stringToStream( stream, kp->name );
|
|
addrToStream( stream, &kp->addr );
|
|
}
|
|
const XP_UCHAR* keys[] = { KNOWN_PLAYERS_KEY, NULL };
|
|
dutil_storeStream( dutil, xwe, keys, stream );
|
|
stream_destroy( stream, xwe );
|
|
state->dirty = XP_FALSE;
|
|
}
|
|
}
|
|
|
|
static void
|
|
releaseState( XW_DUtilCtxt* dutil, XWEnv xwe, KPState* state )
|
|
{
|
|
XP_ASSERT( state->inUse );
|
|
saveState( dutil, xwe, state );
|
|
state->inUse = XP_FALSE;
|
|
|
|
pthread_mutex_unlock( &dutil->kpMutex );
|
|
}
|
|
|
|
static const XP_UCHAR*
|
|
figureNameFor( XP_U16 posn, const CurGameInfo* gi )
|
|
{
|
|
const XP_UCHAR* result = NULL;
|
|
for ( int ii = 0, nthRemote = 0;
|
|
NULL == result && ii < gi->nPlayers;
|
|
++ii ) {
|
|
const LocalPlayer* lp = &gi->players[ii];
|
|
if ( lp->isLocal ) {
|
|
continue;
|
|
} else if ( nthRemote++ == posn ) {
|
|
result = lp->name;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static void
|
|
makeUniqueName( const KPState* state, const XP_UCHAR* name,
|
|
XP_UCHAR newName[], XP_U16 len )
|
|
{
|
|
XP_U16 nPlayers = state->nPlayers;
|
|
const XP_UCHAR* names[nPlayers];
|
|
getPlayersImpl( state, names, &nPlayers );
|
|
XP_ASSERT( nPlayers == state->nPlayers );
|
|
for ( int ii = 2; ; ++ii ) {
|
|
XP_SNPRINTF( newName, len, "%s %d", name, ii );
|
|
XP_Bool found = XP_FALSE;
|
|
for ( int jj = 0; !found && jj < nPlayers; ++jj ) {
|
|
found = 0 == XP_STRCMP( names[jj], newName );
|
|
}
|
|
if ( !found ) {
|
|
break;
|
|
}
|
|
}
|
|
XP_LOGFF( "created new name: %s", newName );
|
|
}
|
|
|
|
/* Adding players is the hard part. There will be a lot with the same name and
|
|
* representing the same device. That's easy: skip adding a new entry, but if
|
|
* there's a change or addition, make it. For changes, e.g. a different
|
|
* BlueTooth device name, the newer one wins.
|
|
*
|
|
* If two different names have the same mqtt devID, they're the same
|
|
* device!!. Change the name to be that of the newer of the two, making sure
|
|
* it's not a duplicate.
|
|
*
|
|
* For early testing, however, just make a new name.
|
|
*/
|
|
static void
|
|
addPlayer( XW_DUtilCtxt* XP_UNUSED_DBG(dutil), KPState* state, const XP_UCHAR* name,
|
|
const CommsAddrRec* addr, XP_U32 newestMod )
|
|
{
|
|
XP_LOGFF( "(name=%s)", name );
|
|
KnownPlayer* withSameDevID = NULL;
|
|
KnownPlayer* withSameName = NULL;
|
|
|
|
for ( KnownPlayer* kp = state->players;
|
|
!!kp && (!withSameDevID || !withSameName);
|
|
kp = kp->next ) {
|
|
if ( 0 == XP_STRCMP( kp->name, name ) ) {
|
|
withSameName = kp;
|
|
}
|
|
if ( addr->u.mqtt.devID == kp->addr.u.mqtt.devID ) {
|
|
withSameDevID = kp;
|
|
}
|
|
}
|
|
|
|
XP_UCHAR tmpName[64];
|
|
if ( !!withSameDevID ) { /* only one allowed */
|
|
XP_Bool isNewer = newestMod > withSameDevID->newestMod;
|
|
XP_Bool changed = augmentAddr( &withSameDevID->addr, addr, isNewer );
|
|
if ( isNewer ) {
|
|
withSameDevID->newestMod = newestMod;
|
|
}
|
|
state->dirty = changed || state->dirty;
|
|
} else {
|
|
if ( !!withSameName ) {
|
|
/* Same name but different devID? Create a unique name */
|
|
makeUniqueName( state, name, tmpName, VSIZE(tmpName) );
|
|
name = tmpName;
|
|
}
|
|
/* XP_LOGFF( "adding new player %s!", name ); */
|
|
KnownPlayer* newPlayer = XP_CALLOC( dutil->mpool, sizeof(*newPlayer) );
|
|
newPlayer->name = copyString( dutil->mpool, name );
|
|
newPlayer->addr = *addr;
|
|
newPlayer->next = state->players;
|
|
state->players = newPlayer;
|
|
state->dirty = XP_TRUE;
|
|
++state->nPlayers;
|
|
}
|
|
XP_LOGFF( "nPlayers now: %d", state->nPlayers );
|
|
}
|
|
|
|
XP_Bool
|
|
kplr_addAddrs( XW_DUtilCtxt* dutil, XWEnv xwe, const CurGameInfo* gi,
|
|
CommsAddrRec addrs[], XP_U16 nAddrs, XP_U32 modTime )
|
|
{
|
|
LOG_FUNC();
|
|
XP_Bool canUse = XP_TRUE;
|
|
for ( int ii = 0; ii < nAddrs && canUse; ++ii ) {
|
|
canUse = addr_hasType( &addrs[ii], COMMS_CONN_MQTT );
|
|
if ( !canUse ) {
|
|
XP_LOGFF( "addr %d has no mqqt id", ii );
|
|
}
|
|
}
|
|
|
|
if ( canUse ) {
|
|
KPState* state = loadState( dutil, xwe );
|
|
for ( int ii = 0; ii < nAddrs && canUse; ++ii ) {
|
|
const XP_UCHAR* name = figureNameFor( ii, gi );
|
|
if ( !!name ) {
|
|
addPlayer( dutil, state, name, &addrs[ii], modTime );
|
|
} else {
|
|
XP_LOGFF( "unable to find %dth name", ii );
|
|
}
|
|
}
|
|
releaseState( dutil, xwe, state );
|
|
}
|
|
|
|
return canUse;
|
|
}
|
|
|
|
XP_Bool
|
|
kplr_havePlayers( XW_DUtilCtxt* dutil, XWEnv xwe )
|
|
{
|
|
KPState* state = loadState( dutil, xwe );
|
|
XP_Bool result = 0 < state->nPlayers;
|
|
releaseState( dutil, xwe, state );
|
|
LOG_RETURNF( "%s", boolToStr(result) );
|
|
return result;
|
|
}
|
|
|
|
static void
|
|
getPlayersImpl( const KPState* state, const XP_UCHAR** players, XP_U16* nFound )
|
|
{
|
|
if ( state->nPlayers <= *nFound && !!players ) {
|
|
XP_U16 ii = 0;
|
|
for ( KnownPlayer* kp = state->players; !!kp; kp = kp->next ) {
|
|
players[ii++] = kp->name;
|
|
}
|
|
}
|
|
*nFound = state->nPlayers;
|
|
}
|
|
|
|
void
|
|
kplr_getNames( XW_DUtilCtxt* dutil, XWEnv xwe,
|
|
const XP_UCHAR** players, XP_U16* nFound )
|
|
{
|
|
KPState* state = loadState( dutil, xwe );
|
|
getPlayersImpl( state, players, nFound );
|
|
releaseState( dutil, xwe, state );
|
|
}
|
|
|
|
static KnownPlayer*
|
|
findByName( KPState* state, const XP_UCHAR* name )
|
|
{
|
|
KnownPlayer* result = NULL;
|
|
for ( KnownPlayer* kp = state->players; !!kp && !result; kp = kp->next ) {
|
|
if ( 0 == XP_STRCMP( kp->name, name ) ) {
|
|
result = kp;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
XP_Bool
|
|
kplr_getAddr( XW_DUtilCtxt* dutil, XWEnv xwe, const XP_UCHAR* name,
|
|
CommsAddrRec* addr, XP_U32* lastMod )
|
|
{
|
|
KPState* state = loadState( dutil, xwe );
|
|
XP_Bool found = XP_FALSE;
|
|
KnownPlayer* kp = findByName( state, name );
|
|
found = NULL != kp;
|
|
if ( found ) {
|
|
*addr = kp->addr;
|
|
if ( !!lastMod ) {
|
|
*lastMod = kp->newestMod;
|
|
}
|
|
}
|
|
releaseState( dutil, xwe, state );
|
|
LOG_RETURNF( "%s", boolToStr(found) );
|
|
return found;
|
|
}
|
|
|
|
const XP_UCHAR*
|
|
kplr_nameForMqttDev( XW_DUtilCtxt* dutil, XWEnv xwe, const XP_UCHAR* mqttDevID )
|
|
{
|
|
const XP_UCHAR* name = NULL;
|
|
MQTTDevID devID;
|
|
if ( strToMQTTCDevID( mqttDevID, &devID ) ) {
|
|
KPState* state = loadState( dutil, xwe );
|
|
for ( KnownPlayer* kp = state->players; !!kp && !name; kp = kp->next ) {
|
|
const CommsAddrRec* addr = &kp->addr;
|
|
if ( addr_hasType( addr, COMMS_CONN_MQTT ) ) {
|
|
if ( 0 == XP_MEMCMP( &addr->u.mqtt.devID, &devID, sizeof(devID) ) ) {
|
|
name = kp->name;
|
|
}
|
|
}
|
|
}
|
|
releaseState( dutil, xwe, state );
|
|
}
|
|
LOG_RETURNF( "%s", name );
|
|
return name;
|
|
}
|
|
|
|
static void
|
|
freeKP( XW_DUtilCtxt* XP_UNUSED_DBG(dutil), KnownPlayer* kp )
|
|
{
|
|
XP_FREEP( dutil->mpool, &kp->name );
|
|
XP_FREE( dutil->mpool, kp );
|
|
}
|
|
|
|
KP_Rslt
|
|
kplr_renamePlayer( XW_DUtilCtxt* dutil, XWEnv xwe, const XP_UCHAR* oldName,
|
|
const XP_UCHAR* newName )
|
|
{
|
|
KP_Rslt result;
|
|
KPState* state = loadState( dutil, xwe );
|
|
|
|
KnownPlayer* kp = findByName( state, oldName );
|
|
if ( !kp ) {
|
|
result = KP_NAME_NOT_FOUND;
|
|
} else if ( NULL != findByName( state, newName ) ) {
|
|
result = KP_NAME_IN_USE;
|
|
} else {
|
|
XP_FREEP( dutil->mpool, &kp->name );
|
|
kp->name = copyString( dutil->mpool, newName );
|
|
state->dirty = XP_TRUE;
|
|
result = KP_OK;
|
|
}
|
|
|
|
releaseState( dutil, xwe, state );
|
|
return result;
|
|
}
|
|
|
|
KP_Rslt
|
|
kplr_deletePlayer( XW_DUtilCtxt* dutil, XWEnv xwe, const XP_UCHAR* name )
|
|
{
|
|
KP_Rslt result = KP_NAME_NOT_FOUND;
|
|
KnownPlayer* doomed = NULL;
|
|
KPState* state = loadState( dutil, xwe );
|
|
|
|
KnownPlayer* prev = NULL;
|
|
for ( KnownPlayer* kp = state->players; !!kp && !doomed; kp = kp->next ) {
|
|
if ( 0 == XP_STRCMP( kp->name, name ) ) {
|
|
doomed = kp;
|
|
if ( NULL == prev ) { /* first time through? */
|
|
state->players = kp->next;
|
|
} else {
|
|
prev->next = kp->next;
|
|
}
|
|
--state->nPlayers;
|
|
state->dirty = XP_TRUE;
|
|
result = KP_OK;
|
|
}
|
|
prev = kp;
|
|
}
|
|
releaseState( dutil, xwe, state );
|
|
|
|
XP_ASSERT( !!doomed );
|
|
if ( !!doomed ) {
|
|
freeKP( dutil, doomed );
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void
|
|
kplr_cleanup( XW_DUtilCtxt* dutil )
|
|
{
|
|
KPState** state = (KPState**)&dutil->kpCtxt;
|
|
if ( !!*state ) {
|
|
XP_ASSERT( !(*state)->inUse );
|
|
KnownPlayer* next = NULL;
|
|
for ( KnownPlayer* kp = (*state)->players; !!kp; kp = next ) {
|
|
next = kp->next;
|
|
freeKP( dutil, kp );
|
|
}
|
|
XP_FREEP( dutil->mpool, state );
|
|
}
|
|
}
|
|
#endif
|