add more info to game "buttons"

Toward a games-list substitute. New game_summarize method will get
fleshed out and eventually replace what's in jni?
This commit is contained in:
Eric House 2021-03-08 18:38:02 -08:00
parent 8c920d6537
commit f3b76da8e1
5 changed files with 124 additions and 35 deletions

View file

@ -545,6 +545,30 @@ game_getState( const XWGame* game, XWEnv xwe, GameStateInfo* gsi )
gsi->canUnpause = server_canUnpause( server ); gsi->canUnpause = server_canUnpause( server );
} }
void
game_summarize( XWGame* game, CurGameInfo* gi, GameSummary* summary )
{
XP_MEMSET( summary, 0, sizeof(*summary) );
ServerCtxt* server = game->server;
summary->turn = server_getCurrentTurn( server, &summary->turnIsLocal );
summary->lastMoveTime = server_getLastMoveTime(server);
summary->lang = gi->dictLang;
for ( int ii = 0; ii < gi->nPlayers; ++ii ) {
LocalPlayer* lp = &gi->players[ii];
if ( LP_IS_ROBOT(lp) || !LP_IS_LOCAL(lp) ) {
if ( '\0' != summary->opponents[0] ) {
XP_STRCAT( summary->opponents, ", " );
}
XP_STRCAT( summary->opponents, lp->name );
}
}
if ( !!game->comms ) {
CommsCtxt* comms = game->comms;
summary->missingPlayers = server_getMissingPlayers( server );
summary->nPacketsPending = comms_countPendingPackets( comms );
}
}
XP_Bool XP_Bool
game_getIsServer( const XWGame* game ) game_getIsServer( const XWGame* game )
{ {

View file

@ -51,6 +51,17 @@ typedef struct _GameStateInfo {
XP_Bool canUnpause; /* duplicate-mode only */ XP_Bool canUnpause; /* duplicate-mode only */
} GameStateInfo; } GameStateInfo;
typedef struct _GameSummary {
XP_Bool turnIsLocal;
XP_Bool inPlay;
XP_S8 turn;
XP_U32 lastMoveTime;
XP_U8 missingPlayers;
XP_U8 nPacketsPending;
XP_LangCode lang;
XP_UCHAR opponents[64];
} GameSummary;
typedef struct _XWGame { typedef struct _XWGame {
XW_UtilCtxt* util; XW_UtilCtxt* util;
BoardCtxt* board; BoardCtxt* board;
@ -97,6 +108,7 @@ XP_Bool game_receiveMessage( XWGame* game, XWEnv xwe, XWStreamCtxt* stream,
void game_dispose( XWGame* game, XWEnv xwe ); void game_dispose( XWGame* game, XWEnv xwe );
void game_summarize( XWGame* game, CurGameInfo* gi, GameSummary* summary );
void game_getState( const XWGame* game, XWEnv xwe, GameStateInfo* gsi ); void game_getState( const XWGame* game, XWEnv xwe, GameStateInfo* gsi );
XP_Bool game_getIsServer( const XWGame* game ); XP_Bool game_getIsServer( const XWGame* game );

View file

@ -62,6 +62,7 @@
#define KEY_LAST_GID "cur_game" #define KEY_LAST_GID "cur_game"
#define KEY_PLAYER_NAME "player_name" #define KEY_PLAYER_NAME "player_name"
#define KEY_GAMES "games" #define KEY_GAMES "games"
#define KEY_SUMMARY "summary"
#define KEY_GAME "game_data" #define KEY_GAME "game_data"
#define KEY_NAME "game_name" #define KEY_NAME "game_name"
#define KEY_NEXT_GAME "next_game" #define KEY_NEXT_GAME "next_game"
@ -84,7 +85,7 @@
#define BUTTON_INVITE "Invite" #define BUTTON_INVITE "Invite"
#define BUTTON_GAME_NEW "New Game" #define BUTTON_GAME_NEW "New Game"
#define BUTTON_GAME_OPEN "Open Game" #define BUTTON_GAME_OPEN "Games"
#define BUTTON_GAME_RENAME "Rename Game" #define BUTTON_GAME_RENAME "Rename Game"
#define BUTTON_GAME_DELETE "Delete Game" #define BUTTON_GAME_DELETE "Delete Game"
#define BUTTON_NAME "My Name" #define BUTTON_NAME "My Name"
@ -144,8 +145,10 @@ EM_JS(void, show_name, (const char* name), {
}); });
EM_JS(void, show_pool, (int cur, int max), { EM_JS(void, show_pool, (int cur, int max), {
let msg = 'cur: ' + cur + 'b; max: ' + max + 'b'; const msg = 'cur: ' + cur + 'b; max: ' + max + 'b';
document.getElementById('mempool').textContent = msg; const div = document.getElementById('mempool');
div.textContent = msg;
div.parentNode.hidden = 0;
}); });
EM_JS(void, call_dialog, (const char* str, const char** but_strs, EM_JS(void, call_dialog, (const char* str, const char** but_strs,
@ -497,6 +500,16 @@ updateGameButtons( Globals* globals )
setButtons( BUTTONS_ID_GAME, buttons, onGameButton, gs ); setButtons( BUTTONS_ID_GAME, buttons, onGameButton, gs );
} }
static bool
langNameFor( Globals* globals, XP_LangCode code, char buf[], size_t buflen )
{
const char* lc = lcToLocale( code );
const XP_UCHAR* keys[] = { KEY_DICTS, lc, KEY_LANG_NAME, NULL };
XP_U32 len = buflen;
dutil_loadPtr( globals->dutil, NULL, keys, buf, &len );
return 0 < len;
}
static void static void
showName( GameState* gs ) showName( GameState* gs )
{ {
@ -507,14 +520,10 @@ showName( GameState* gs )
char buf[64]; char buf[64];
if ( true || 1 < countDicts( globals ) ) { if ( true || 1 < countDicts( globals ) ) {
char langName[32]; char langName[32];
const char* lc = lcToLocale(gs->gi.dictLang); if ( !langNameFor( globals, gs->gi.dictLang, langName, sizeof(langName) ) ) {
const XP_UCHAR* keys[] = {KEY_DICTS, lc, KEY_LANG_NAME, NULL }; strcpy( langName, "??" );
XP_U32 len = sizeof(langName);
dutil_loadPtr( globals->dutil, NULL, keys, langName, &len );
if ( 0 != len ) {
lc = langName;
} }
sprintf( buf, "%s (%s)", title, lc ); sprintf( buf, "%s (%s)", title, langName );
title = buf; title = buf;
} }
} }
@ -541,8 +550,7 @@ typedef struct _NameIterState {
static void static void
onGameChosen( void* closure, const char* key ) onGameChosen( void* closure, const char* key )
{ {
NameIterState* nis = (NameIterState*)closure; CAST_GLOB(Globals*, globals, closure);
Globals* globals = nis->globals;
/* To be safe, let's make sure the game exists. We don't want to create /* To be safe, let's make sure the game exists. We don't want to create
* another if somehow it doesn't */ * another if somehow it doesn't */
@ -552,13 +560,41 @@ onGameChosen( void* closure, const char* key )
loadAndDraw( globals, NULL, gameID, NULL ); loadAndDraw( globals, NULL, gameID, NULL );
} }
XP_FREE( globals->mpool, nis->names );
XP_FREE( globals->mpool, nis->ids );
XP_FREE( globals->mpool, nis );
updateDeviceButtons( globals ); updateDeviceButtons( globals );
} }
static char*
formatForGame(Globals* globals, bool multiLangs, const XP_UCHAR* gameKey )
{
const XP_UCHAR* keys[] = {KEY_GAMES, gameKey, KEY_NAME, NULL};
char gameName[32];
XP_U32 len = sizeof(gameName);
dutil_loadPtr( globals->dutil, NULL, keys, gameName, &len );
char buf[256];
int offset = snprintf( buf, sizeof(buf), "%s", gameName );
GameSummary summary;
len = sizeof(summary);
keys[2] = KEY_SUMMARY;
dutil_loadPtr( globals->dutil, NULL, keys, &summary, &len );
if ( len == sizeof(summary) ) {
if ( multiLangs ) {
char langName[32];
if ( langNameFor( globals, summary.lang, langName, sizeof(langName) ) ) {
offset += snprintf( buf+offset, sizeof(buf)-offset, " (in %s)", langName );
}
}
offset += snprintf( buf+offset, sizeof(buf)-offset, " My turn: %s",
summary.turnIsLocal ? "YES" : "NO" );
offset += snprintf( buf+offset, sizeof(buf)-offset, " Opponent: %s",
summary.opponents );
}
char* result = NULL;
replaceStringIfDifferent( globals->mpool, &result, buf );
return result;
}
static XP_Bool static XP_Bool
onOneGameName( void* closure, const XP_UCHAR* keys[] ) onOneGameName( void* closure, const XP_UCHAR* keys[] )
{ {
@ -566,6 +602,7 @@ onOneGameName( void* closure, const XP_UCHAR* keys[] )
if ( 0 != strcmp( gameIDStr, "0" ) ) { /* temporary */ if ( 0 != strcmp( gameIDStr, "0" ) ) { /* temporary */
NameIterState* nis = (NameIterState*)closure; NameIterState* nis = (NameIterState*)closure;
Globals* globals = nis->globals; Globals* globals = nis->globals;
bool multiLangs = 1 < countDicts(globals);
/* Make sure game exists. This may be unnecessary later */ /* Make sure game exists. This may be unnecessary later */
const XP_UCHAR* dataKeys[] = { keys[0], keys[1], KEY_GAME, NULL }; const XP_UCHAR* dataKeys[] = { keys[0], keys[1], KEY_GAME, NULL };
@ -575,12 +612,7 @@ onOneGameName( void* closure, const XP_UCHAR* keys[] )
int cur = nis->count++; int cur = nis->count++;
nis->names = XP_REALLOC( globals->mpool, nis->names, nis->names = XP_REALLOC( globals->mpool, nis->names,
nis->count * sizeof(nis->names[0]) ); nis->count * sizeof(nis->names[0]) );
nis->names[cur] = formatForGame( globals, multiLangs, keys[1] );
XP_U32 valLen;
dutil_loadPtr( globals->dutil, NULL, keys, NULL, &valLen );
nis->names[cur] = XP_MALLOC( globals->mpool, valLen );
dutil_loadPtr( globals->dutil, NULL, keys, nis->names[cur], &valLen );
nis->ids = XP_REALLOC( globals->mpool, nis->ids, nis->ids = XP_REALLOC( globals->mpool, nis->ids,
nis->count * sizeof(nis->ids[0]) ); nis->count * sizeof(nis->ids[0]) );
nis->ids[cur] = XP_MALLOC( globals->mpool, 1 + strlen(gameIDStr) ); nis->ids[cur] = XP_MALLOC( globals->mpool, 1 + strlen(gameIDStr) );
@ -594,14 +626,16 @@ static void
pickGame( Globals* globals ) pickGame( Globals* globals )
{ {
XW_DUtilCtxt* dutil = globals->dutil; XW_DUtilCtxt* dutil = globals->dutil;
NameIterState* nis = XP_CALLOC( globals->mpool, sizeof(*nis) ); NameIterState nis = { .globals = globals, };
nis->globals = globals;
const XP_UCHAR* keys[] = {KEY_GAMES, KEY_WILDCARD, KEY_NAME, NULL}; const XP_UCHAR* keys[] = {KEY_GAMES, KEY_WILDCARD, KEY_NAME, NULL};
dutil_forEach( dutil, NULL, keys, onOneGameName, nis ); dutil_forEach( dutil, NULL, keys, onOneGameName, &nis );
const char* msg = "Choose game to open:"; const char* msg = "Choose game to open:";
call_pickGame(msg, nis->ids, nis->names, nis->count, onGameChosen, nis); call_pickGame(msg, nis.ids, nis.names, nis.count, onGameChosen, globals);
XP_FREE( globals->mpool, nis.names );
XP_FREE( globals->mpool, nis.ids );
} }
static void static void
@ -1066,6 +1100,7 @@ getSavedGame( Globals* globals, int gameID )
{ {
GameState* gs; GameState* gs;
/* Is it already open? */
for ( gs = globals->games; !!gs; gs = gs->next ) { for ( gs = globals->games; !!gs; gs = gs->next ) {
if ( gameID == gs->gi.gameID ) { if ( gameID == gs->gi.gameID ) {
break; break;
@ -1100,6 +1135,7 @@ getSavedGame( Globals* globals, int gameID )
updateScreen( gs, false ); updateScreen( gs, false );
} else { } else {
removeGameState( gs ); removeGameState( gs );
gs = NULL;
} }
} else { } else {
XP_LOGFF( "ERROR: no saved data for game %s", gameIDStr ); XP_LOGFF( "ERROR: no saved data for game %s", gameIDStr );
@ -1672,12 +1708,18 @@ updateScreen( GameState* gs, bool doSave )
game_saveToStream( &gs->game, NULL, &gs->gi, game_saveToStream( &gs->game, NULL, &gs->gi,
stream, ++gs->saveToken ); stream, ++gs->saveToken );
GameSummary summary;
game_summarize( &gs->game, &gs->gi, &summary );
char gameIDStr[32]; char gameIDStr[32];
formatGameID( gameIDStr, sizeof(gameIDStr), gs->gi.gameID ); formatGameID( gameIDStr, sizeof(gameIDStr), gs->gi.gameID );
const XP_UCHAR* keys[] = { KEY_GAMES, gameIDStr, KEY_GAME, NULL }; const XP_UCHAR* keys[] = { KEY_GAMES, gameIDStr, KEY_GAME, NULL };
dutil_storeStream( globals->dutil, NULL, keys, stream ); dutil_storeStream( globals->dutil, NULL, keys, stream );
stream_destroy( stream, NULL ); stream_destroy( stream, NULL );
game_saveSucceeded( &gs->game, NULL, gs->saveToken ); game_saveSucceeded( &gs->game, NULL, gs->saveToken );
keys[2] = KEY_SUMMARY;
dutil_storePtr( globals->dutil, NULL, keys, &summary, sizeof(summary) );
} }
} }

View file

@ -97,7 +97,7 @@
<table class='centered'> <table class='centered'>
<tr><td>MQTT Dev ID:</td><td id="mqtt_span"></td></tr> <tr><td>MQTT Dev ID:</td><td id="mqtt_span"></td></tr>
<tr><td>MQTT Status:</td><td id="mqtt_status">Unconnected</td></tr> <tr><td>MQTT Status:</td><td id="mqtt_status">Unconnected</td></tr>
<tr><td>MemPool used:</td><td id="mempool">0</td></tr> <tr hidden=1><td>MemPool used:</td><td id="mempool">0</td></tr>
</table> </table>
<hr/> <hr/>
<!-- Remove until I figure out how this works <!-- Remove until I figure out how this works

View file

@ -218,7 +218,7 @@ function newDlgWMsg(msg) {
return dlg; return dlg;
} }
function newButtonDiv(buttons, proc) { function newButtonDiv(buttons, proc, asDivs) {
let div = document.createElement('div'); let div = document.createElement('div');
div.classList.add('buttonRow'); div.classList.add('buttonRow');
for ( let ii = 0; ii < buttons.length; ++ii ) { for ( let ii = 0; ii < buttons.length; ++ii ) {
@ -226,7 +226,12 @@ function newButtonDiv(buttons, proc) {
let button = document.createElement('button'); let button = document.createElement('button');
button.textContent = buttonTxt; button.textContent = buttonTxt;
button.onclick = function() { proc(ii); }; button.onclick = function() { proc(ii); };
div.appendChild( button ); if ( asDivs ) {
let bdiv = document.createElement('div');
bdiv.appendChild(button);
button = bdiv;
}
div.appendChild(button);
} }
return div; return div;
@ -239,7 +244,7 @@ function nbDialog(msg, buttons, proc, closure) {
dlg.parentNode.removeChild(dlg); dlg.parentNode.removeChild(dlg);
ccallString(proc, closure, buttons[indx]); ccallString(proc, closure, buttons[indx]);
} }
dlg.appendChild( newButtonDiv( buttons, butProc ) ); dlg.appendChild( newButtonDiv( buttons, butProc, false ) );
addDepthNote(dlg); addDepthNote(dlg);
} }
@ -251,7 +256,7 @@ function nbBlankPick(title, buttons, proc, closure) {
ccallString(proc, closure, indx.toString()); ccallString(proc, closure, indx.toString());
} }
dlg.appendChild( newButtonDiv( buttons, butProc ) ); dlg.appendChild( newButtonDiv( buttons, butProc, false ) );
addDepthNote(dlg); addDepthNote(dlg);
} }
@ -281,8 +286,14 @@ function nbGamePick(title, gameMap, proc, closure) {
dlg.parentNode.removeChild(dlg); dlg.parentNode.removeChild(dlg);
ccallString(proc, closure, pairs[indx].id); ccallString(proc, closure, pairs[indx].id);
} }
dlg.appendChild( newButtonDiv( buttons, butProc, true ) );
cancelProc = function() {
dlg.parentNode.removeChild(dlg);
ccallString(proc, closure, null);
};
dlg.appendChild( newButtonDiv( ['Cancel'], cancelProc, false ) );
dlg.appendChild( newButtonDiv( buttons, butProc ) );
addDepthNote(dlg); addDepthNote(dlg);
} }
@ -296,7 +307,7 @@ function setDivButtons(divid, buttons, proc, closure) {
ccallString(proc, closure, buttons[indx]); ccallString(proc, closure, buttons[indx]);
} }
parent.appendChild( newButtonDiv( buttons, butProc ) ); parent.appendChild( newButtonDiv( buttons, butProc, false ) );
} }
function nbGetString(msg, dflt, proc, closure) { function nbGetString(msg, dflt, proc, closure) {
@ -320,7 +331,7 @@ function nbGetString(msg, dflt, proc, closure) {
dismissed(tarea.value); dismissed(tarea.value);
} }
} }
dlg.appendChild( newButtonDiv( buttons, butProc ) ); dlg.appendChild( newButtonDiv( buttons, butProc, false ) );
addDepthNote(dlg); addDepthNote(dlg);
} }
@ -383,7 +394,7 @@ function nbGetNewGame(closure, msg, langs) {
dlg.parentNode.removeChild(dlg); dlg.parentNode.removeChild(dlg);
} }
dlg.appendChild( newButtonDiv( ['Cancel', 'OK'], butProc ) ); dlg.appendChild( newButtonDiv( ['Cancel', 'OK'], butProc, false ) );
addDepthNote(dlg); addDepthNote(dlg);
} }