mirror of
git://xwords.git.sourceforge.net/gitroot/xwords/xwords
synced 2025-01-29 08:34:37 +01:00
show host as part of each game display
This commit is contained in:
parent
7b4f9ede41
commit
2936869b45
4 changed files with 200 additions and 48 deletions
|
@ -603,7 +603,7 @@ enableDraw( CursesBoardGlobals* bGlobals, const cb_dims* dims )
|
|||
setupBoard( bGlobals );
|
||||
}
|
||||
|
||||
static CursesBoardGlobals*
|
||||
CursesBoardGlobals*
|
||||
findOrOpenForGameID( CursesBoardState* cbState, XP_U32 gameID,
|
||||
const CurGameInfo* gi, const CommsAddrRec* returnAddr )
|
||||
{
|
||||
|
@ -617,6 +617,15 @@ findOrOpenForGameID( CursesBoardState* cbState, XP_U32 gameID,
|
|||
return result;
|
||||
}
|
||||
|
||||
const CommonGlobals*
|
||||
cb_getForGameID( CursesBoardState* cbState, XP_U32 gameID )
|
||||
{
|
||||
CursesBoardGlobals* cbg = findOrOpenForGameID( cbState, gameID, NULL, NULL );
|
||||
CommonGlobals* result = &cbg->cGlobals;
|
||||
// XP_LOGFF( "(%X) => %p", gameID, result );
|
||||
return result;
|
||||
}
|
||||
|
||||
static CursesBoardGlobals*
|
||||
findOrOpen( CursesBoardState* cbState, sqlite3_int64 rowid,
|
||||
const CurGameInfo* gi, const CommsAddrRec* returnAddr )
|
||||
|
|
|
@ -56,6 +56,8 @@ void cb_addInvite( CursesBoardState* cbState, XP_U32 gameID, XP_U16 forceChannel
|
|||
XP_Bool cb_makeRematch( CursesBoardState* cbState, XP_U32 gameID, XP_U32* newGameID );
|
||||
XP_Bool cb_makeMoveIf( CursesBoardState* cbState, XP_U32 gameID );
|
||||
|
||||
const CommonGlobals* cb_getForGameID( CursesBoardState* cbState, XP_U32 gameID );
|
||||
|
||||
void cb_closeAll( CursesBoardState* cbState );
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1472,13 +1472,18 @@ makeObjIfNot( cJSON** objp )
|
|||
}
|
||||
|
||||
static void
|
||||
addGIDToObject( cJSON** objp, XP_U32 gid, const char* key )
|
||||
addStringToObject( cJSON** objp, const char* key, const char* value )
|
||||
{
|
||||
makeObjIfNot( objp );
|
||||
cJSON_AddStringToObject( *objp, key, value );
|
||||
}
|
||||
|
||||
static void
|
||||
addGIDToObject( cJSON** objp, XP_U32 gid, const char* key )
|
||||
{
|
||||
char buf[16];
|
||||
sprintf( buf, "%08X", gid );
|
||||
cJSON_AddStringToObject( *objp, key, buf );
|
||||
addStringToObject( objp, key, buf );
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -1603,14 +1608,18 @@ rematchFromArgs( CursesAppGlobals* aGlobals, cJSON* args )
|
|||
return result;
|
||||
}
|
||||
|
||||
static cJSON*
|
||||
getGamesStateForArgs( CursesAppGlobals* aGlobals, cJSON* args )
|
||||
static XP_Bool
|
||||
getGamesStateForArgs( CursesAppGlobals* aGlobals, cJSON* args,
|
||||
cJSON** states, cJSON** orders )
|
||||
{
|
||||
cJSON* result = cJSON_CreateArray();
|
||||
|
||||
LOG_FUNC();
|
||||
LaunchParams* params = aGlobals->cag.params;
|
||||
cJSON* gids = cJSON_GetObjectItem(args, "gids" );
|
||||
for ( int ii = 0 ; ii < cJSON_GetArraySize(gids) ; ++ii ) {
|
||||
|
||||
*states = cJSON_CreateArray();
|
||||
|
||||
cJSON* gids = cJSON_GetObjectItem( args, "gids" );
|
||||
XP_Bool success = !!gids;
|
||||
for ( int ii = 0 ; success && ii < cJSON_GetArraySize(gids) ; ++ii ) {
|
||||
XP_U32 gameID = castGid( cJSON_GetArrayItem( gids, ii ) );
|
||||
|
||||
GameInfo gib;
|
||||
|
@ -1622,9 +1631,88 @@ getGamesStateForArgs( CursesAppGlobals* aGlobals, cJSON* args )
|
|||
cJSON_AddNumberToObject( item, "nMoves", gib.nMoves );
|
||||
cJSON_AddNumberToObject( item, "nTiles", gib.nTiles );
|
||||
|
||||
cJSON_AddItemToArray( result, item );
|
||||
cJSON_AddItemToArray( *states, item );
|
||||
}
|
||||
}
|
||||
|
||||
XP_LOGFF( "done with states" ); /* got here */
|
||||
|
||||
if ( success && !!orders ) {
|
||||
cJSON* gids = cJSON_GetObjectItem( args, "orders" );
|
||||
if ( !gids ) {
|
||||
*orders = NULL;
|
||||
} else {
|
||||
*orders = cJSON_CreateArray();
|
||||
for ( int ii = 0 ; ii < cJSON_GetArraySize(gids) ; ++ii ) {
|
||||
XP_U32 gameID = castGid( cJSON_GetArrayItem( gids, ii ) );
|
||||
|
||||
const CommonGlobals* cg = cb_getForGameID( aGlobals->cbState, gameID );
|
||||
if ( !cg ) {
|
||||
continue;
|
||||
}
|
||||
const XWGame* game = &cg->game;
|
||||
if ( server_getGameIsConnected( game->server ) ) {
|
||||
const CurGameInfo* gi = cg->gi;
|
||||
LOGGI( gi, __func__ );
|
||||
cJSON* order = NULL;
|
||||
addGIDToObject( &order, gameID, "gid" );
|
||||
cJSON* players = cJSON_CreateArray();
|
||||
for ( int jj = 0; jj < gi->nPlayers; ++jj ) {
|
||||
XP_LOGFF( "looking at player %d", jj );
|
||||
const LocalPlayer* lp = &gi->players[jj];
|
||||
XP_LOGFF( "adding player %d: %s", jj, lp->name );
|
||||
cJSON* cName = cJSON_CreateString( lp->name );
|
||||
cJSON_AddItemToArray( players, cName);
|
||||
}
|
||||
cJSON_AddItemToObject( order, "players", players );
|
||||
cJSON_AddItemToArray( *orders, order );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LOG_RETURNF( "%s", boolToStr(success) );
|
||||
return success;
|
||||
}
|
||||
|
||||
/* Return for each gid and array of player names, in play order, and including
|
||||
for each whether it's the host and if a robot. For now let's try by opening
|
||||
each game (yeah! yuck!) to read the info directly. Later add to a the db
|
||||
accessed by gamesdb.c
|
||||
*/
|
||||
static cJSON*
|
||||
getPlayersForArgs( CursesAppGlobals* aGlobals, cJSON* args )
|
||||
{
|
||||
cJSON* result = cJSON_CreateArray();
|
||||
cJSON* gids = cJSON_GetObjectItem( args, "gids" );
|
||||
for ( int ii = 0 ; ii < cJSON_GetArraySize(gids) ; ++ii ) {
|
||||
XP_U32 gameID = castGid( cJSON_GetArrayItem( gids, ii ) );
|
||||
|
||||
const CommonGlobals* cg = cb_getForGameID( aGlobals->cbState, gameID );
|
||||
const CurGameInfo* gi = cg->gi;
|
||||
LOGGI( gi, __func__ );
|
||||
const XWGame* game = &cg->game;
|
||||
|
||||
cJSON* players = cJSON_CreateArray();
|
||||
for ( int jj = 0; jj < gi->nPlayers; ++jj ) {
|
||||
cJSON* playerObj = NULL;
|
||||
const LocalPlayer* lp = &gi->players[jj];
|
||||
XP_LOGFF( "adding player %d: %s", jj, lp->name );
|
||||
addStringToObject( &playerObj, "name", lp->name );
|
||||
XP_Bool isLocal = lp->isLocal;
|
||||
cJSON_AddBoolToObject( playerObj, "isLocal", isLocal );
|
||||
|
||||
/* Roles: I don't think a guest in a 3- or 4-device game knows
|
||||
which of the other players is host. Host is who it sends its
|
||||
moves to, but is there an order there? */
|
||||
XP_Bool isHost = game_getIsHost( game );
|
||||
isHost = isHost && isLocal;
|
||||
cJSON_AddBoolToObject( playerObj, "isHost", isHost );
|
||||
|
||||
cJSON_AddItemToArray( players, playerObj );
|
||||
}
|
||||
cJSON_AddItemToArray( result, players );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -1649,7 +1737,7 @@ on_incoming_signal( GSocketService* XP_UNUSED(service),
|
|||
if ( 0 <= nread ) {
|
||||
XP_ASSERT( nread == len );
|
||||
buf[nread] = '\0';
|
||||
XP_LOGFF( "Message was: \"%s\"\n", buf );
|
||||
XP_LOGFF( "Message: \"%s\"\n", buf );
|
||||
|
||||
cJSON* reply = cJSON_CreateArray();
|
||||
|
||||
|
@ -1666,8 +1754,10 @@ on_incoming_signal( GSocketService* XP_UNUSED(service),
|
|||
XP_Bool success = XP_TRUE;
|
||||
|
||||
if ( 0 == strcmp( cmdStr, "quit" ) ) {
|
||||
cJSON* gids = getGamesStateForArgs( aGlobals, args );
|
||||
addObjectToObject( &response, "states", gids );
|
||||
cJSON* gids;
|
||||
if ( getGamesStateForArgs( aGlobals, args, &gids, NULL ) ) {
|
||||
addObjectToObject( &response, "states", gids );
|
||||
}
|
||||
handleQuit( aGlobals, 0 );
|
||||
} else if ( 0 == strcmp( cmdStr, "getMQTTDevID" ) ) {
|
||||
MQTTDevID devID;
|
||||
|
@ -1693,8 +1783,16 @@ on_incoming_signal( GSocketService* XP_UNUSED(service),
|
|||
addGIDToObject( &response, newGameID, "newGid" );
|
||||
}
|
||||
} else if ( 0 == strcmp( cmdStr, "getStates" ) ) {
|
||||
cJSON* gids = getGamesStateForArgs( aGlobals, args );
|
||||
addObjectToObject( &response, "states", gids );
|
||||
cJSON* gids;
|
||||
cJSON* orders;
|
||||
success = getGamesStateForArgs( aGlobals, args, &gids, &orders );
|
||||
if ( success ) {
|
||||
addObjectToObject( &response, "states", gids );
|
||||
addObjectToObject( &response, "orders", orders );
|
||||
}
|
||||
} else if ( 0 == strcmp( cmdStr, "getPlayers" ) ) {
|
||||
cJSON* players = getPlayersForArgs( aGlobals, args );
|
||||
addObjectToObject( &response, "players", players );
|
||||
} else {
|
||||
success = XP_FALSE;
|
||||
XP_ASSERT(0);
|
||||
|
|
|
@ -38,7 +38,8 @@ def chooseNames(nPlayers):
|
|||
return result
|
||||
|
||||
class GameInfo():
|
||||
def __init__(self, gid, rematchLevel):
|
||||
def __init__(self, device, gid, rematchLevel):
|
||||
self.device = device
|
||||
self.gid = gid
|
||||
self.state = {}
|
||||
assert isinstance(rematchLevel, int)
|
||||
|
@ -52,34 +53,50 @@ class GameInfo():
|
|||
def gameOver(self):
|
||||
return self.state.get('gameOver', False)
|
||||
|
||||
def getDevice(self): return self.device
|
||||
|
||||
class GuestGameInfo(GameInfo):
|
||||
def __init__(self, gid, rematchLevel):
|
||||
super().__init__(gid, rematchLevel)
|
||||
def __init__(self, device, gid, rematchLevel):
|
||||
super().__init__(device, gid, rematchLevel)
|
||||
|
||||
class HostGameInfo(GameInfo):
|
||||
def __init__(self, guestNames, **kwargs):
|
||||
super().__init__(kwargs.get('gid'), kwargs.get('rematchLevel'))
|
||||
def __init__(self, device, guestNames, **kwargs):
|
||||
super().__init__(device, kwargs.get('gid'), kwargs.get('rematchLevel'))
|
||||
self.guestNames = guestNames
|
||||
self.needsInvite = kwargs.get('needsInvite', True)
|
||||
self.orderedPlayers = None
|
||||
global gGamesMade;
|
||||
gGamesMade += 1
|
||||
|
||||
def haveOrder(self): return self.orderedPlayers is not None
|
||||
|
||||
def setOrder(self, orderedPlayers): self.orderedPlayers = orderedPlayers
|
||||
|
||||
def __str__(self):
|
||||
return 'gid: {}, guests: {}'.format(self.gid, self.guestNames)
|
||||
|
||||
class GameStatus():
|
||||
_N_LINES = 3 # could be lower if all games have 2 players
|
||||
_statuses = None
|
||||
|
||||
def __init__(self, gid):
|
||||
self.gid = gid
|
||||
self.players = []
|
||||
self.allOver = True
|
||||
self.hostPlayerName = None
|
||||
self.hostName = None
|
||||
|
||||
def harvest(self, dev):
|
||||
self.players.append(dev.host)
|
||||
self.allOver = self.allOver and dev.gameOver(self.gid)
|
||||
|
||||
def sortPlayers(self):
|
||||
game = Device.getHostGame(self.gid)
|
||||
orderedPlayers = game.orderedPlayers
|
||||
if orderedPlayers:
|
||||
assert len(orderedPlayers) == len(self.players)
|
||||
self.players = orderedPlayers
|
||||
self.hostName = game.getDevice().host
|
||||
|
||||
# Build a gid->status map for each game, querying each device in
|
||||
# the game for details
|
||||
@staticmethod
|
||||
|
@ -95,11 +112,18 @@ class GameStatus():
|
|||
for gid in list(statuses.keys()):
|
||||
if statuses[gid].allOver: del statuses[gid]
|
||||
|
||||
for status in statuses.values():
|
||||
status.sortPlayers()
|
||||
|
||||
GameStatus._statuses = statuses
|
||||
|
||||
@staticmethod
|
||||
def numLines():
|
||||
return GameStatus._N_LINES
|
||||
maxPlayers = 0
|
||||
for status in GameStatus._statuses.values():
|
||||
nPlayers = len(status.players)
|
||||
if nPlayers > maxPlayers: maxPlayers = nPlayers
|
||||
return maxPlayers + 1
|
||||
|
||||
# For all games, print the proper line of status for that game in
|
||||
# exactly 8 chars
|
||||
|
@ -109,25 +133,22 @@ class GameStatus():
|
|||
for gid in sorted(GameStatus._statuses.keys()):
|
||||
if indx == 0:
|
||||
results.append(gid)
|
||||
continue
|
||||
players = indx == 1 and g_NAMES[:2] or g_NAMES[2:]
|
||||
lineTxt = ''
|
||||
for player in players:
|
||||
if not player in GameStatus._statuses[gid].players:
|
||||
lineTxt += ' '
|
||||
else:
|
||||
dev = Device._devs.get(player)
|
||||
initial = player[0]
|
||||
arg2 = -1
|
||||
gameState = dev.gameFor(gid).state
|
||||
if gameState:
|
||||
if gameState.get('gameOver', False):
|
||||
initial = initial.lower()
|
||||
arg2 = gameState.get('nPending', 0)
|
||||
else:
|
||||
arg2 = gameState.get('nTiles')
|
||||
lineTxt += '{}{: 3}'.format(initial, arg2)
|
||||
results.append(lineTxt)
|
||||
elif indx <= len(GameStatus._statuses[gid].players):
|
||||
status = GameStatus._statuses[gid]
|
||||
player = status.players[indx-1]
|
||||
hostMarker = status.hostName == player and '*' or ' '
|
||||
initial = player[0]
|
||||
arg3 = -1
|
||||
dev = Device._devs.get(player)
|
||||
gameState = dev.gameFor(gid).state
|
||||
if gameState:
|
||||
if gameState.get('gameOver', False):
|
||||
initial = initial.lower()
|
||||
arg3 = gameState.get('nPending', 0)
|
||||
else:
|
||||
arg3 = gameState.get('nTiles')
|
||||
results.append('{}{}{: 3}'.format(hostMarker, initial, arg3).center(len(gid)))
|
||||
|
||||
return ' '.join(results)
|
||||
|
||||
class Device():
|
||||
|
@ -279,15 +300,15 @@ class Device():
|
|||
|
||||
rematchLevel = game.rematchLevel - 1
|
||||
assert rematchLevel >= 0 # fired
|
||||
self.hostedGames.append(HostGameInfo(guests, needsInvite=False, gid=newGid,
|
||||
self.hostedGames.append(HostGameInfo(self, guests, needsInvite=False, gid=newGid,
|
||||
rematchLevel=rematchLevel))
|
||||
for guest in guests:
|
||||
Device.getFor(guest).expectInvite(newGid, rematchLevel)
|
||||
Device.getForPlayer(guest).expectInvite(newGid, rematchLevel)
|
||||
|
||||
def invite(self, game):
|
||||
failed = False
|
||||
for ii in range(len(game.guestNames)):
|
||||
guestDev = Device.getFor(game.guestNames[ii])
|
||||
guestDev = Device.getForPlayer(game.guestNames[ii])
|
||||
|
||||
addr = {}
|
||||
if self.args.WITH_MQTT: addr['mqtt'] = guestDev.mqttDevID
|
||||
|
@ -302,7 +323,7 @@ class Device():
|
|||
if not failed: game.needsInvite = False
|
||||
|
||||
def expectInvite(self, gid, rematchLevel):
|
||||
self.guestGames.append(GuestGameInfo(gid, rematchLevel))
|
||||
self.guestGames.append(GuestGameInfo(self, gid, rematchLevel))
|
||||
self.launchIfNot()
|
||||
|
||||
# Return true only if all games I host are finished on all games.
|
||||
|
@ -349,7 +370,20 @@ class Device():
|
|||
def rematchOrQuit(self):
|
||||
if self.endTime:
|
||||
gids = [game.gid for game in self._allGames() if not self.gameOver(game.gid)]
|
||||
response = self._sendWaitReply('getStates', gids=gids)
|
||||
|
||||
orders = []
|
||||
for gid in gids:
|
||||
game = Device.getHostGame(gid)
|
||||
if game and not game.haveOrder():
|
||||
orders.append(gid)
|
||||
|
||||
response = self._sendWaitReply('getStates', gids=gids, orders=orders)
|
||||
|
||||
for order in response.get('orders', []):
|
||||
gid = order.get('gid')
|
||||
game = Device.getHostGame(gid)
|
||||
assert isinstance(game, HostGameInfo) # firing
|
||||
game.setOrder(order.get('players'))
|
||||
|
||||
anyRematched = False
|
||||
for obj in response.get('states', []):
|
||||
|
@ -449,7 +483,7 @@ class Device():
|
|||
return [dev for dev in Device._devs.values()]
|
||||
|
||||
@staticmethod
|
||||
def getFor(player):
|
||||
def getForPlayer(player):
|
||||
result = None
|
||||
for dev in Device.getAll():
|
||||
if dev.host == player:
|
||||
|
@ -458,8 +492,17 @@ class Device():
|
|||
assert result
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
def getHostGame(gid):
|
||||
devs = Device.devsWith(gid)
|
||||
for dev in devs:
|
||||
game = dev.gameFor(gid)
|
||||
if isinstance(game, HostGameInfo):
|
||||
return game
|
||||
assert False
|
||||
|
||||
def addGameWith(self, guests):
|
||||
self.hostedGames.append(HostGameInfo(guests, needsInvite=True,
|
||||
self.hostedGames.append(HostGameInfo(self, guests, needsInvite=True,
|
||||
rematchLevel=self.args.REMATCH_LEVEL))
|
||||
for guest in guests:
|
||||
Device.deviceFor(self.args, guest) # in case this device never hosts
|
||||
|
|
Loading…
Add table
Reference in a new issue