assign faces to blanks asynchronously

Next step in converting util_ methods that required blocking: blank tile
assignment. Now post a query and add a method that the client code can
call when the user's decided. Include enough state (col, row, and
playerNum) so that it's probably pretty safe.
This commit is contained in:
Eric House 2017-02-18 19:16:32 -08:00
parent ca6edcfc9a
commit 5f12d1a03b
16 changed files with 181 additions and 70 deletions

View file

@ -334,7 +334,24 @@ public class BoardDelegate extends DelegateBase
}
break;
case PICK_TILE_REQUESTBLANK_BLK:
case PICK_TILE_REQUESTBLANK: {
final int turn = (Integer)params[0];
final int col = (Integer)params[1];
final int row = (Integer)params[2];
String[] texts = (String[])params[3];
dialog = ab.setItems( texts, new OnClickListener() {
public void onClick( DialogInterface dialog,
int item ) {
handleViaThread( JNICmd.CMD_SET_BLANK, turn, col,
row, item );
}
})
.setNegativeButton( android.R.string.cancel, null )
.setTitle( R.string.title_tile_picker )
.create();
}
break;
case PICK_TILE_REQUESTTRAY_BLK: {
String[] texts = (String[])params[0];
checkBlocking();
@ -346,34 +363,30 @@ public class BoardDelegate extends DelegateBase
};
ab.setItems( texts, lstnr );
if ( DlgID.PICK_TILE_REQUESTBLANK_BLK == dlgID ) {
ab.setTitle( R.string.title_tile_picker );
} else {
String curTiles = (String)params[1];
boolean canUndoTiles = (Boolean)params[2];
String curTiles = (String)params[1];
boolean canUndoTiles = (Boolean)params[2];
ab.setTitle( getString( R.string.cur_tiles_fmt, curTiles ) );
if ( canUndoTiles ) {
OnClickListener undoClicked = new OnClickListener() {
public void onClick( DialogInterface dialog,
int whichButton ) {
m_resultCode = UtilCtxt.PICKER_BACKUP;
removeDialog( dlgID );
}
};
ab.setPositiveButton( R.string.tilepick_undo,
undoClicked );
}
OnClickListener doAllClicked = new OnClickListener() {
ab.setTitle( getString( R.string.cur_tiles_fmt, curTiles ) );
if ( canUndoTiles ) {
OnClickListener undoClicked = new OnClickListener() {
public void onClick( DialogInterface dialog,
int whichButton ) {
m_resultCode = UtilCtxt.PICKER_PICKALL;
m_resultCode = UtilCtxt.PICKER_BACKUP;
removeDialog( dlgID );
}
};
ab.setNegativeButton( R.string.tilepick_all, doAllClicked );
ab.setPositiveButton( R.string.tilepick_undo,
undoClicked );
}
OnClickListener doAllClicked = new OnClickListener() {
public void onClick( DialogInterface dialog,
int whichButton ) {
m_resultCode = UtilCtxt.PICKER_PICKALL;
removeDialog( dlgID );
}
};
ab.setNegativeButton( R.string.tilepick_all, doAllClicked );
dialog = ab.create();
alert.setOnDismissListener( m_blockingODL );
}
@ -1789,10 +1802,10 @@ public class BoardDelegate extends DelegateBase
// This is supposed to be called from the jni thread
@Override
public int userPickTileBlank( int playerNum, String[] texts )
public void notifyPickTileBlank( int playerNum, int col, int row, String[] texts )
{
waitBlockingDialog( DlgID.PICK_TILE_REQUESTBLANK_BLK, 0, (Object)texts );
return m_resultCode;
showDialogFragment( DlgID.PICK_TILE_REQUESTBLANK, playerNum, col,
row, texts );
}
@Override

View file

@ -60,7 +60,7 @@ public enum DlgID {
, DLG_BADWORDS_BLK
, QUERY_MOVE
, QUERY_TRADE
, PICK_TILE_REQUESTBLANK_BLK
, PICK_TILE_REQUESTBLANK
, ASK_PASSWORD
, DLG_RETRY
, DLG_SCORES

View file

@ -95,6 +95,7 @@ public class JNIThread extends Thread {
CMD_SENDCHAT,
CMD_NETSTATS,
CMD_PASS_PASSWD,
CMD_SET_BLANK,
// CMD_DRAW_CONNS_STATUS,
// CMD_DRAW_BT_STATUS,
// CMD_DRAW_SMS_STATUS,
@ -671,6 +672,14 @@ public class JNIThread extends Thread {
draw = XwJNI.board_passwordProvided( m_jniGamePtr, player, pwd );
break;
case CMD_SET_BLANK:
draw = XwJNI.board_setBlankValue( m_jniGamePtr,
((Integer)args[0]).intValue(),
((Integer)args[1]).intValue(),
((Integer)args[2]).intValue(),
((Integer)args[3]).intValue() );
break;
case CMD_TIMER_FIRED:
draw = XwJNI.timerFired( m_jniGamePtr,
((Integer)args[0]).intValue(),

View file

@ -37,7 +37,7 @@ public interface UtilCtxt {
public static final int PICKER_PICKALL = -1;
public static final int PICKER_BACKUP = -2;
int userPickTileBlank( int playerNum, String[] texts );
void notifyPickTileBlank( int playerNum, int col, int row, String[] texts );
int userPickTileTray( int playerNum, String[] tiles,
String[] curTiles, int nPicked );

View file

@ -48,10 +48,10 @@ public class UtilCtxtImpl implements UtilCtxt {
subclassOverride( "requestTime" );
}
public int userPickTileBlank( int playerNum, String[] texts )
public void notifyPickTileBlank( int playerNum, int col, int row,
String[] texts )
{
subclassOverride( "userPickTileBlank" );
return 0;
}
public int userPickTileTray( int playerNum, String[] texts,

View file

@ -314,6 +314,9 @@ public class XwJNI {
public static native boolean board_beginTrade( GamePtr gamePtr );
public static native boolean board_endTrade( GamePtr gamePtr );
public static native boolean board_setBlankValue( GamePtr gamePtr, int player,
int col, int row, int tile );
public static native String board_formatRemainingTiles( GamePtr gamePtr );
public static native void board_sendChat( GamePtr gamePtr, String msg );

View file

@ -147,21 +147,19 @@ and_util_notifyTrade( XW_UtilCtxt* uc, const XP_UCHAR** tiles, XP_U16 nTiles )
UTIL_CBK_TAIL();
}
static XP_S16
and_util_userPickTileBlank( XW_UtilCtxt* uc, XP_U16 playerNum,
const XP_UCHAR** tileFaces, XP_U16 nTiles )
static void
and_util_notifyPickTileBlank( XW_UtilCtxt* uc, XP_U16 playerNum,
XP_U16 col, XP_U16 row,
const XP_UCHAR** tileFaces, XP_U16 nTiles )
{
XP_S16 result = -1;
UTIL_CBK_HEADER("userPickTileBlank", "(I[Ljava/lang/String;)I" );
UTIL_CBK_HEADER("notifyPickTileBlank", "(III[Ljava/lang/String;)V" );
jobject jtexts = makeStringArray( env, nTiles, tileFaces );
result = (*env)->CallIntMethod( env, util->jutil, mid,
playerNum, jtexts );
(*env)->CallVoidMethod( env, util->jutil, mid, playerNum, col, row, jtexts );
deleteLocalRef( env, jtexts );
UTIL_CBK_TAIL();
return result;
}
static XP_S16
@ -710,7 +708,7 @@ makeUtil( MPFORMAL EnvThreadInfo* ti, jobject jutil, CurGameInfo* gi,
SET_PROC(userError);
SET_PROC(notifyMove);
SET_PROC(notifyTrade);
SET_PROC(userPickTileBlank);
SET_PROC(notifyPickTileBlank);
SET_PROC(userPickTileTray);
SET_PROC(informNeedPassword);
SET_PROC(trayHiddenChange);

View file

@ -1257,6 +1257,18 @@ Java_org_eehouse_android_xw4_jni_XwJNI_board_1endTrade
return result;
}
JNIEXPORT jboolean JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_board_1setBlankValue
( JNIEnv* env, jclass C, GamePtrType gamePtr, jint player,
jint col, jint row, jint tile )
{
jboolean result;
XWJNI_START();
result = board_setBlankValue( state->game.board, player, col, row, tile );
XWJNI_END();
return result;
}
JNIEXPORT jboolean JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_board_1toggle_1showValues
( JNIEnv* env, jclass C, GamePtrType gamePtr )

View file

@ -1211,6 +1211,19 @@ board_resetEngine( BoardCtxt* board )
server_resetEngine( board->server, board->selPlayer );
} /* board_resetEngine */
XP_Bool
board_setBlankValue( BoardCtxt* board, XP_U16 player, XP_U16 col, XP_U16 row,
XP_U16 tileIndex )
{
XP_Bool draw = model_setBlankValue( board->model, player, col, row,
tileIndex );
if ( draw ) {
invalCell( board, col, row );
}
return draw;
}
#ifdef XWFEATURE_MINIWIN
/* Find a rectangle either centered on the board or pinned to the point at
* which the mouse went down.

View file

@ -171,6 +171,8 @@ XP_Bool board_prefsChanged( BoardCtxt* board, const CommonPrefs* cp );
BoardObjectType board_getFocusOwner( BoardCtxt* board );
void board_hiliteCellAt( BoardCtxt* board, XP_U16 col, XP_U16 row );
XP_Bool board_setBlankValue( BoardCtxt* board, XP_U16 XP_UNUSED(player),
XP_U16 col, XP_U16 row, XP_U16 tileIndex );
void board_resetEngine( BoardCtxt* board );

View file

@ -26,6 +26,7 @@
#include "util.h"
#include "pool.h"
#include "game.h"
#include "dbgutil.h"
#include "memstream.h"
#include "strutils.h"
#include "LocalizedStrIncludes.h"
@ -749,7 +750,7 @@ model_foreachPendingCell( ModelCtxt* model, XP_S16 turn,
for ( pt = player->pendingTiles; count--; ++pt ) {
(*bl)( closure, turn, pt->col, pt->row, XP_FALSE );
}
} /* model_invalPendingCells */
} /* model_foreachPendingCell */
XP_U16
model_getCellOwner( ModelCtxt* model, XP_U16 col, XP_U16 row )
@ -1329,23 +1330,21 @@ model_packTilesUtil( ModelCtxt* model, PoolContext* pool,
} /* model_packTilesUtil */
/* setup async query for blank value, but while at it return a reasonable
default. */
static Tile
askBlankTile( ModelCtxt* model, XP_U16 turn )
askBlankTile( ModelCtxt* model, XP_U16 turn, XP_U16 col, XP_U16 row)
{
XP_U16 nUsed = MAX_UNIQUE_TILES;
XP_S16 chosen;
const XP_UCHAR* tfaces[MAX_UNIQUE_TILES];
Tile tiles[MAX_UNIQUE_TILES];
model_packTilesUtil( model, NULL, XP_FALSE,
&nUsed, tfaces, tiles );
chosen = util_userPickTileBlank( model->vol.util, turn, tfaces, nUsed );
if ( chosen < 0 ) {
chosen = 0;
}
return tiles[chosen];
util_notifyPickTileBlank( model->vol.util, turn, col, row,
tfaces, nUsed );
return tiles[0];
} /* askBlankTile */
void
@ -1362,7 +1361,7 @@ model_moveTrayToBoard( ModelCtxt* model, XP_S16 turn, XP_U16 col, XP_U16 row,
tile = blankFace;
} else {
XP_ASSERT( turn >= 0 );
tile = askBlankTile( model, (XP_U16)turn );
tile = TILE_BLANK_BIT | askBlankTile( model, (XP_U16)turn, col, row );
}
tile |= TILE_BLANK_BIT;
}
@ -1387,6 +1386,32 @@ model_moveTrayToBoard( ModelCtxt* model, XP_S16 turn, XP_U16 col, XP_U16 row,
notifyBoardListeners( model, turn, col, row, XP_TRUE );
} /* model_moveTrayToBoard */
XP_Bool
model_setBlankValue( ModelCtxt* model, XP_U16 turn,
XP_U16 col, XP_U16 row, XP_U16 newIndex )
{
XP_Bool found = XP_FALSE;
PlayerCtxt* player = &model->players[turn];
for ( int ii = 0; ii < player->nPending; ++ii ) {
PendingTile* pt = &player->pendingTiles[ii];
found = pt->col == col && pt->row == row;
if ( found ) {
XP_ASSERT( (pt->tile & TILE_BLANK_BIT) != 0 );
if ( (pt->tile & TILE_BLANK_BIT) != 0 ) {
XP_U16 nUsed = MAX_UNIQUE_TILES;
const XP_UCHAR* tfaces[MAX_UNIQUE_TILES];
Tile tiles[MAX_UNIQUE_TILES];
model_packTilesUtil( model, NULL, XP_FALSE,
&nUsed, tfaces, tiles );
pt->tile = tiles[newIndex] | TILE_BLANK_BIT;
}
break;
}
}
return found;
}
XP_Bool
model_redoPendingTiles( ModelCtxt* model, XP_S16 turn )
{
@ -1505,7 +1530,7 @@ model_moveTileOnBoard( ModelCtxt* model, XP_S16 turn, XP_U16 colCur,
pt->col = colNew;
pt->row = rowNew;
if ( isBlank ) {
pt->tile = TILE_BLANK_BIT | askBlankTile( model, turn );
(void)askBlankTile( model, turn, colNew, rowNew );
}
decrPendingTileCountAt( model, colCur, rowCur );

View file

@ -176,7 +176,8 @@ void model_moveTrayToBoard( ModelCtxt* model, XP_S16 turn, XP_U16 col,
XP_Bool model_moveTileOnBoard( ModelCtxt* model, XP_S16 turn, XP_U16 colCur,
XP_U16 rowCur, XP_U16 colNew, XP_U16 rowNew );
XP_Bool model_redoPendingTiles( ModelCtxt* model, XP_S16 turn );
XP_Bool model_setBlankValue( ModelCtxt* model, XP_U16 XP_UNUSED(player),
XP_U16 col, XP_U16 row, XP_U16 tileIndex );
XP_S16 model_trayContains( ModelCtxt* model, XP_S16 turn, Tile tile );

View file

@ -102,7 +102,8 @@ typedef struct UtilVtable {
void (*m_util_notifyTrade)( XW_UtilCtxt* uc, const XP_UCHAR** tiles,
XP_U16 nTiles );
/* return of < 0 means computer should pick */
XP_S16 (*m_util_userPickTileBlank)( XW_UtilCtxt* uc, XP_U16 playerNum,
void (*m_util_notifyPickTileBlank)( XW_UtilCtxt* uc, XP_U16 playerNum,
XP_U16 col, XP_U16 row,
const XP_UCHAR** tileFaces,
XP_U16 nTiles );
XP_S16 (*m_util_userPickTileTray)( XW_UtilCtxt* uc, const PickInfo* pi,
@ -234,8 +235,8 @@ struct XW_UtilCtxt {
#define util_notifyTrade(uc, tx, nt) \
(uc)->vtable->m_util_notifyTrade((uc), (tx), (nt))
#define util_userPickTileBlank( uc, n, tx, nt ) \
(uc)->vtable->m_util_userPickTileBlank( (uc), (n), (tx), (nt) )
#define util_notifyPickTileBlank( uc, c, r, n, tx, nt ) \
(uc)->vtable->m_util_notifyPickTileBlank( (uc), (c), (r), (n), (tx), (nt) )
#define util_userPickTileTray( uc, w, n, tx, nt ) \
(uc)->vtable->m_util_userPickTileTray( (uc), (w), (n), (tx), (nt) )
#define util_informNeedPassword( uc, pn, n ) \

View file

@ -226,21 +226,21 @@ cursesUserError( CursesAppGlobals* globals, const char* format, ... )
va_end(ap);
} /* cursesUserError */
static XP_S16
curses_util_userPickTileBlank( XW_UtilCtxt* uc, XP_U16 playerNum,
const XP_UCHAR** texts, XP_U16 nTiles )
static void
curses_util_notifyPickTileBlank( XW_UtilCtxt* uc, XP_U16 playerNum,
XP_U16 XP_UNUSED(col), XP_U16 XP_UNUSED(row),
const XP_UCHAR** texts, XP_U16 nTiles )
{
CursesAppGlobals* globals = (CursesAppGlobals*)uc->closure;
char query[128];
XP_S16 index;
char* playerName = globals->cGlobals.gi->players[playerNum].name;
snprintf( query, sizeof(query),
"Pick tile for %s! (Tab or type letter to select "
"then hit <cr>.)", playerName );
index = curses_askLetter( globals, query, texts, nTiles );
return index;
/*index = */curses_askLetter( globals, query, texts, nTiles );
// return index;
} /* util_userPickTile */
static XP_S16
@ -1456,7 +1456,7 @@ setupCursesUtilCallbacks( CursesAppGlobals* globals, XW_UtilCtxt* util )
util->vtable->m_util_notifyMove = curses_util_notifyMove;
util->vtable->m_util_notifyTrade = curses_util_notifyTrade;
util->vtable->m_util_userPickTileBlank = curses_util_userPickTileBlank;
util->vtable->m_util_notifyPickTileBlank = curses_util_notifyPickTileBlank;
util->vtable->m_util_userPickTileTray = curses_util_userPickTileTray;
util->vtable->m_util_trayHiddenChange = curses_util_trayHiddenChange;
util->vtable->m_util_informMove = curses_util_informMove;

View file

@ -1684,16 +1684,45 @@ gtk_util_getVTManager(XW_UtilCtxt* uc)
return globals->cGlobals.params->vtMgr;
} /* linux_util_getVTManager */
static XP_S16
gtk_util_userPickTileBlank( XW_UtilCtxt* uc, XP_U16 playerNum,
const XP_UCHAR** texts, XP_U16 nTiles )
{
XP_S16 chosen;
GtkGameGlobals* globals = (GtkGameGlobals*)uc->closure;
XP_UCHAR* name = globals->cGlobals.gi->players[playerNum].name;
chosen = gtkletterask( NULL, XP_FALSE, name, nTiles, texts );
return chosen;
static gint
ask_blank( gpointer data )
{
GtkGameGlobals* globals = (GtkGameGlobals*)data;
CommonGlobals* cGlobals = &globals->cGlobals;
XP_UCHAR* name = globals->cGlobals.gi->players[cGlobals->selPlayer].name;
XP_S16 result = gtkletterask( NULL, XP_FALSE, name,
cGlobals->nTiles, cGlobals->tiles );
for ( int ii = 0; ii < cGlobals->nTiles; ++ii ) {
g_free( (gpointer)cGlobals->tiles[ii] );
}
if ( result >= 0
&& board_setBlankValue( cGlobals->game.board, cGlobals->selPlayer,
cGlobals->blankCol, cGlobals->blankRow,
result ) ) {
board_draw( cGlobals->game.board );
}
return 0;
}
static void
gtk_util_notifyPickTileBlank( XW_UtilCtxt* uc, XP_U16 playerNum, XP_U16 col,
XP_U16 row, const XP_UCHAR** texts, XP_U16 nTiles )
{
GtkGameGlobals* globals = (GtkGameGlobals*)uc->closure;
CommonGlobals* cGlobals = &globals->cGlobals;
cGlobals->selPlayer = playerNum;
cGlobals->blankCol = col;
cGlobals->blankRow = row;
cGlobals->nTiles = nTiles;
for ( int ii = 0; ii < nTiles; ++ii ) {
cGlobals->tiles[ii] = g_strdup( texts[ii] );
}
(void)g_idle_add( ask_blank, globals );
}
static XP_S16
@ -2437,7 +2466,7 @@ setupGtkUtilCallbacks( GtkGameGlobals* globals, XW_UtilCtxt* util )
util->vtable->m_util_notifyMove = gtk_util_notifyMove;
util->vtable->m_util_notifyTrade = gtk_util_notifyTrade;
util->vtable->m_util_getVTManager = gtk_util_getVTManager;
util->vtable->m_util_userPickTileBlank = gtk_util_userPickTileBlank;
util->vtable->m_util_notifyPickTileBlank = gtk_util_notifyPickTileBlank;
util->vtable->m_util_userPickTileTray = gtk_util_userPickTileTray;
util->vtable->m_util_informNeedPassword = gtk_util_informNeedPassword;
util->vtable->m_util_trayHiddenChange = gtk_util_trayHiddenChange;

View file

@ -219,6 +219,11 @@ struct CommonGlobals {
XP_U16 selPlayer;
char question[256*4];
const XP_UCHAR* askPassName;
XP_U16 nTiles;
XP_U16 blankCol;
XP_U16 blankRow;
const XP_UCHAR* tiles[MAX_UNIQUE_TILES];
#ifdef XWFEATURE_RELAY
int relaySocket; /* tcp connection to relay */