first of a set of changes to communicate to clients dictionary being

used by server.  Clients need to care if e.g. the server's disallowing
phonies based on its dict.  Can only be sent if client is of latest
version.  In that case, common code calls into new util function.  In
future changes, BoardActivity's implemention of the callback will need
to check if the server's choice of dict is available, and if not offer
to download it.  Once it's available, will want to install it.
This commit is contained in:
Eric House 2012-09-03 21:29:21 -07:00
parent d12ed144ea
commit b7a0f1489b
15 changed files with 206 additions and 27 deletions

View file

@ -268,6 +268,28 @@ and_util_informUndo( XW_UtilCtxt* uc )
UTIL_CBK_TAIL();
}
static void
and_util_informNetDict( XW_UtilCtxt* uc, const XP_UCHAR* oldName,
const XP_UCHAR* newName,
XWPhoniesChoice phoniesAction )
{
LOG_FUNC();
UTIL_CBK_HEADER( "informNetDict",
"(Ljava/lang/String;Ljava/lang/String;L"
PKG_PATH("jni/CurGameInfo$XWPhoniesChoice") ";)V" );
jstring jnew = (*env)->NewStringUTF( env, newName );
jstring jold = (*env)->NewStringUTF( env, oldName );
jobject jphon = intToJEnum( env, phoniesAction,
PKG_PATH("jni/CurGameInfo$XWPhoniesChoice") );
(*env)->CallVoidMethod( env, util->jutil, mid, jold, jnew, jphon );
(*env)->DeleteLocalRef( env, jnew );
(*env)->DeleteLocalRef( env, jold );
(*env)->DeleteLocalRef( env, jphon );
UTIL_CBK_TAIL();
}
static void
and_util_notifyGameOver( XW_UtilCtxt* uc )
{
@ -585,6 +607,7 @@ makeUtil( MPFORMAL JNIEnv** envp, jobject jutil, CurGameInfo* gi,
#endif
SET_PROC(informMove);
SET_PROC(informUndo);
SET_PROC(informNetDict);
SET_PROC(notifyGameOver);
SET_PROC(hiliteCell);
SET_PROC(engineProgressCallback);

View file

@ -1472,6 +1472,14 @@ public class BoardActivity extends XWActivity
nonBlockingDialog( DLG_OKONLY, getString( R.string.remote_undone ) );
}
@Override
public void informNetDict( String oldName, String newName,
CurGameInfo.XWPhoniesChoice phonies )
{
DbgUtils.logf( "informNetDict(%s, %s, %s)", oldName, newName,
phonies.toString() );
}
@Override
public void notifyGameOver()
{

View file

@ -116,6 +116,9 @@ public interface UtilCtxt {
void informMove( String expl, String words );
void informUndo();
void informNetDict( String oldName, String newName,
CurGameInfo.XWPhoniesChoice phonies );
void informMissing( boolean isServer, CommsAddrRec.CommsConnType connType,
int nMissingPlayers );

View file

@ -222,6 +222,12 @@ public class UtilCtxtImpl implements UtilCtxt {
subclassOverride( "informUndo" );
}
public void informNetDict( String oldName, String newName,
CurGameInfo.XWPhoniesChoice phonies )
{
subclassOverride( "informNetDict" );
}
public void informMissing( boolean isServer,
CommsAddrRec.CommsConnType connType,
int nMissingPlayers )

View file

@ -47,6 +47,7 @@
#endif
#define MAX_COLS MAX_ROWS
#define STREAM_VERS_DICTNAME 0x15
#ifdef HASH_STREAM
# define STREAM_VERS_HASHSTREAM 0x14
#endif
@ -81,13 +82,7 @@
#define STREAM_VERS_41B4 0x02
#define STREAM_VERS_405 0x01
#ifdef STREAM_VERS_HASHSTREAM
# define CUR_STREAM_VERS STREAM_VERS_HASHSTREAM
#elif MAX_COLS > 16
# define CUR_STREAM_VERS STREAM_VERS_BIGBOARD
#else
# define CUR_STREAM_VERS STREAM_VERS_BLUETOOTH2
#endif
#define CUR_STREAM_VERS STREAM_VERS_DICTNAME
typedef struct XP_Rect {
XP_S16 left;

View file

@ -1231,8 +1231,6 @@ client_readInitialMessage( ServerCtxt* server, XWStreamCtxt* stream )
gi_readFromStream( MPPARM(server->mpool) stream, &localGI );
localGI.serverRole = SERVER_ISCLIENT;
/* so it's not lost (HACK!). Without this, a client won't have a default
dict name when a new game is started. */
localGI.dictName = copyString( server->mpool, gi->dictName );
gi_copy( MPPARM(server->mpool) gi, &localGI );
@ -1241,6 +1239,17 @@ client_readInitialMessage( ServerCtxt* server, XWStreamCtxt* stream )
newDict = util_makeEmptyDict( server->vol.util );
dict_loadFromStream( newDict, stream );
#ifdef STREAM_VERS_BIGBOARD
if ( STREAM_VERS_DICTNAME <= streamVersion ) {
XP_UCHAR buf[128];
stringFromStreamHere( stream, buf, VSIZE(buf) );
if ( 0 != XP_STRCMP( buf, gi->dictName ) ) {
util_informNetDict( server->vol.util, gi->dictName, buf,
localGI.phoniesAction );
}
}
#endif
channelNo = stream_getAddress( stream );
XP_ASSERT( channelNo != 0 );
server->nv.addresses[0].channelNo = channelNo;
@ -1373,7 +1382,11 @@ server_sendInitialMessage( ServerCtxt* server )
gi_writeToStream( stream, &localGI );
dict_writeToStream( dict, stream );
#ifdef STREAM_VERS_BIGBOARD
if ( STREAM_VERS_DICTNAME <= addr->streamVersion ) {
stringToStream( stream, dict_getShortName(dict) );
}
#endif
/* send tiles currently in tray */
for ( ii = 0; ii < nPlayers; ++ii ) {
model_trayToStream( model, ii, stream );

View file

@ -129,6 +129,10 @@ typedef struct UtilVtable {
void (*m_util_informMove)( XW_UtilCtxt* uc, XWStreamCtxt* expl,
XWStreamCtxt* words );
void (*m_util_informUndo)( XW_UtilCtxt* uc );
void (*m_util_informNetDict)( XW_UtilCtxt* uc, const XP_UCHAR* oldName,
const XP_UCHAR* newName,
XWPhoniesChoice phoniesAction );
void (*m_util_notifyGameOver)( XW_UtilCtxt* uc );
XP_Bool (*m_util_hiliteCell)( XW_UtilCtxt* uc, XP_U16 col, XP_U16 row );
@ -244,6 +248,8 @@ struct XW_UtilCtxt {
(uc)->vtable->m_util_informMove( (uc),(e),(w))
#define util_informUndo(uc) \
(uc)->vtable->m_util_informUndo( (uc))
#define util_informNetDict(uc, on, nn, pa ) \
(uc)->vtable->m_util_informNetDict( (uc), (on), (nn), (pa) )
#define util_notifyGameOver( uc ) \
(uc)->vtable->m_util_notifyGameOver((uc))

View file

@ -394,6 +394,16 @@ curses_util_notifyGameOver( XW_UtilCtxt* uc )
}
} /* curses_util_notifyGameOver */
static void
curses_util_informNetDict( XW_UtilCtxt* uc, const XP_UCHAR* oldName,
const XP_UCHAR* newName,
XWPhoniesChoice phoniesAction )
{
XP_USE(uc);
XP_USE(phoniesAction);
XP_LOGF( "%s: %s => %s", __func__, oldName, newName );
}
static XP_Bool
curses_util_hiliteCell( XW_UtilCtxt* uc,
XP_U16 XP_UNUSED(col), XP_U16 XP_UNUSED(row) )
@ -1516,6 +1526,7 @@ setupCursesUtilCallbacks( CursesAppGlobals* globals, XW_UtilCtxt* util )
util->vtable->m_util_informMove = curses_util_informMove;
util->vtable->m_util_informUndo = curses_util_informUndo;
util->vtable->m_util_notifyGameOver = curses_util_notifyGameOver;
util->vtable->m_util_informNetDict = curses_util_informNetDict;
util->vtable->m_util_hiliteCell = curses_util_hiliteCell;
util->vtable->m_util_engineProgressCallback =
curses_util_engineProgressCallback;

View file

@ -1468,6 +1468,23 @@ gtk_util_notifyGameOver( XW_UtilCtxt* uc )
}
} /* gtk_util_notifyGameOver */
static void
gtk_util_informNetDict( XW_UtilCtxt* uc, const XP_UCHAR* oldName,
const XP_UCHAR* newName,
XWPhoniesChoice phoniesAction )
{
GtkAppGlobals* globals = (GtkAppGlobals*)uc->closure;
gchar buf[512];
int offset = snprintf( buf, VSIZE(buf), "dict changing from %s to %s.",
oldName, newName );
if ( PHONIES_DISALLOW == phoniesAction ) {
snprintf( &buf[offset], VSIZE(buf)-offset, "%s",
"\nPHONIES_DISALLOW is set so this may lead to some surprises." );
}
(void)gtkask( globals->window, buf, GTK_BUTTONS_OK );
}
/* define this to prevent user events during debugging from stopping the engine */
/* #define DONT_ABORT_ENGINE */
@ -1993,6 +2010,7 @@ setupGtkUtilCallbacks( GtkAppGlobals* globals, XW_UtilCtxt* util )
util->vtable->m_util_informMove = gtk_util_informMove;
util->vtable->m_util_informUndo = gtk_util_informUndo;
util->vtable->m_util_notifyGameOver = gtk_util_notifyGameOver;
util->vtable->m_util_informNetDict = gtk_util_informNetDict;
util->vtable->m_util_hiliteCell = gtk_util_hiliteCell;
util->vtable->m_util_altKeyDown = gtk_util_altKeyDown;
util->vtable->m_util_engineProgressCallback =

View file

@ -49,6 +49,7 @@ typedef struct LinuxDictionaryCtxt {
/************************ Prototypes ***********************/
static XP_Bool initFromDictFile( LinuxDictionaryCtxt* dctx,
const LaunchParams* params,
const char* fileName );
static void linux_dictionary_destroy( DictionaryCtxt* dict );
static const XP_UCHAR* linux_dict_getShortName( const DictionaryCtxt* dict );
@ -57,7 +58,8 @@ static const XP_UCHAR* linux_dict_getShortName( const DictionaryCtxt* dict );
*
****************************************************************************/
DictionaryCtxt*
linux_dictionary_make( MPFORMAL const char* dictFileName, XP_Bool useMMap )
linux_dictionary_make( MPFORMAL const LaunchParams* params,
const char* dictFileName, XP_Bool useMMap )
{
LinuxDictionaryCtxt* result =
(LinuxDictionaryCtxt*)XP_MALLOC(mpool, sizeof(*result));
@ -69,7 +71,7 @@ linux_dictionary_make( MPFORMAL const char* dictFileName, XP_Bool useMMap )
result->useMMap = useMMap;
if ( !!dictFileName ) {
XP_Bool success = initFromDictFile( result, dictFileName );
XP_Bool success = initFromDictFile( result, params, dictFileName );
if ( success ) {
result->super.destructor = linux_dictionary_destroy;
result->super.func_dict_getShortName = linux_dict_getShortName;
@ -205,7 +207,8 @@ dict_splitFaces( DictionaryCtxt* dict, const XP_U8* utf8,
} /* dict_splitFaces */
static XP_Bool
initFromDictFile( LinuxDictionaryCtxt* dctx, const char* fileName )
initFromDictFile( LinuxDictionaryCtxt* dctx, const LaunchParams* params,
const char* fileName )
{
XP_Bool formatOk = XP_TRUE;
long curPos, dictLength;
@ -217,15 +220,20 @@ initFromDictFile( LinuxDictionaryCtxt* dctx, const char* fileName )
XP_Bool isUTF8 = XP_FALSE;
XP_Bool hasHeader = XP_FALSE;
const XP_U8* ptr;
char path[256];
if ( !getDictPath( params, fileName, path, VSIZE(path) ) ) {
XP_LOGF( "%s: path=%s", __func__, path );
goto closeAndExit;
}
struct stat statbuf;
if ( 0 != stat( fileName, &statbuf ) || 0 == statbuf.st_size ) {
if ( 0 != stat( path, &statbuf ) || 0 == statbuf.st_size ) {
goto closeAndExit;
}
dctx->dictLength = statbuf.st_size;
{
FILE* dictF = fopen( fileName, "r" );
FILE* dictF = fopen( path, "r" );
XP_ASSERT( !!dictF );
if ( dctx->useMMap ) {
dctx->dictBase = mmap( NULL, dctx->dictLength, PROT_READ,

View file

@ -18,11 +18,13 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <getopt.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <locale.h>
#include <string.h>
#include <netdb.h> /* gethostbyname */
#include <errno.h>
@ -74,11 +76,12 @@
#define DEFAULT_LISTEN_PORT 4998
XP_Bool
file_exists( const char* fileName )
file_exists( const char* fileName )
{
struct stat statBuf;
int statResult = stat( fileName, &statBuf );
XP_LOGF( "%s(%s)=>%d", __func__, fileName, statResult == 0 );
return statResult == 0;
} /* file_exists */
@ -454,6 +457,7 @@ typedef enum {
,CMD_TESTPRFX
,CMD_TESTMINMAX
#endif
,CMD_DICTDIR
,CMD_PLAYERDICT
,CMD_SEED
,CMD_GAMESEED
@ -545,6 +549,7 @@ static CmdInfoRec CmdInfoRecs[] = {
,{ CMD_TESTPRFX, true, "test-prefix", "list first word starting with this" }
,{ CMD_TESTMINMAX, true, "test-minmax", "M:M -- include only words whose len in range" }
#endif
,{ CMD_DICTDIR, true, "dict-dir", "path to dir in which dicts will be sought" }
,{ CMD_PLAYERDICT, true, "player-dict", "dictionary name for player (in sequence)" }
,{ CMD_SEED, true, "seed", "random seed" }
,{ CMD_GAMESEED, true, "game-seed", "game seed (for relay play)" }
@ -1345,7 +1350,7 @@ walk_dict_test_all( const LaunchParams* params, GSList* testDicts,
for ( ii = 0; ii < count; ++ii ) {
gchar* name = (gchar*)g_slist_nth_data( testDicts, ii );
DictionaryCtxt* dict =
linux_dictionary_make( MPPARM(params->util->mpool) name,
linux_dictionary_make( MPPARM(params->util->mpool) params, name,
params->useMmap );
if ( NULL != dict ) {
XP_LOGF( "walk_dict_test(%s)", name );
@ -1356,6 +1361,55 @@ walk_dict_test_all( const LaunchParams* params, GSList* testDicts,
}
#endif
static void
trimDictPath( const char* input, char* buf, int bufsiz, char** path, char** dict )
{
struct stat statBuf;
int statResult = stat( input, &statBuf );
if ( 0 == statResult && S_ISLNK(statBuf.st_mode) ) {
ssize_t nWritten = readlink( input, buf, bufsiz );
buf[nWritten] = '\0';
} else {
snprintf( buf, bufsiz, "%s", input );
}
char* result = strrchr( buf, '/' );
if ( !!result ) { /* is is a full path */
*path = buf;
*result = '\0'; /* null-terminate it */
*dict = 1 + result;
} else {
*path = NULL;
*dict = buf;
}
char* dot = strrchr( *dict, '.' );
if ( !!dot && 0 == strcmp(dot, ".xwd") ) {
*dot = '\0';
}
XP_LOGF( "%s=> dict: %s; path: %s", __func__, *dict, *path );
}
XP_Bool
getDictPath( const LaunchParams *params, const char* name,
char* result, int resultLen )
{
XP_Bool success = XP_FALSE;
GSList* dictDirs;
result[0] = '\0';
for ( dictDirs = params->dictDirs; !!dictDirs; dictDirs = dictDirs->next ) {
const char* path = dictDirs->data;
char buf[256];
int len = snprintf( buf, VSIZE(buf), "%s/%s.xwd", path, name );
if ( len < VSIZE(buf) && file_exists( buf ) ) {
snprintf( result, resultLen, "%s", buf );
success = XP_TRUE;
break;
}
}
LOG_RETURNF( "%d", success );
return success;
}
int
main( int argc, char** argv )
{
@ -1376,6 +1430,9 @@ main( int argc, char** argv )
GSList* testPrefixes = NULL;
char* testMinMax = NULL;
#endif
char dictbuf[256];
char* dict;
char* path;
/* install a no-op signal handler. Later curses- or gtk-specific code
will install one that does the right thing in that context */
@ -1450,6 +1507,19 @@ main( int argc, char** argv )
mainParams.showRobotScores = XP_FALSE;
mainParams.useMmap = XP_TRUE;
char* envDictPath = getenv( "XW_DICTSPATH" );
if ( !!envDictPath ) {
char *saveptr;
for ( ; ; ) {
char* path = strtok_r( envDictPath, ":", &saveptr );
if ( !path ) {
break;
}
mainParams.dictDirs = g_slist_append( mainParams.dictDirs, path );
envDictPath = NULL;
}
}
/* serverName = mainParams.info.clientInfo.serverName = "localhost"; */
#if defined PLATFORM_GTK
@ -1489,8 +1559,12 @@ main( int argc, char** argv )
conType = COMMS_CONN_IP_DIRECT;
break;
case CMD_DICT:
mainParams.gi.dictName = copyString( mainParams.util->mpool,
(XP_UCHAR*)optarg );
trimDictPath( optarg, dictbuf, VSIZE(dictbuf), &path, &dict );
mainParams.gi.dictName = copyString( mainParams.util->mpool, dict );
if ( !path ) {
path = ".";
}
mainParams.dictDirs = g_slist_append( mainParams.dictDirs, path );
break;
#ifdef XWFEATURE_WALKDICT
case CMD_TESTDICT:
@ -1503,8 +1577,16 @@ main( int argc, char** argv )
testMinMax = optarg;
break;
#endif
case CMD_DICTDIR:
mainParams.dictDirs = g_slist_append( mainParams.dictDirs, optarg );
break;
case CMD_PLAYERDICT:
mainParams.playerDictNames[nPlayerDicts++] = optarg;
trimDictPath( optarg, dictbuf, VSIZE(dictbuf), &path, &dict );
mainParams.playerDictNames[nPlayerDicts++] = dict;
if ( !path ) {
path = ".";
}
mainParams.dictDirs = g_slist_append( mainParams.dictDirs, path );
break;
case CMD_SEED:
seed = atoi(optarg);
@ -1757,9 +1839,11 @@ main( int argc, char** argv )
}
if ( !!mainParams.gi.dictName ) {
/* char path[256]; */
/* getDictPath( &mainParams, mainParams.gi.dictName, path, VSIZE(path) ); */
mainParams.dict =
linux_dictionary_make( MPPARM(mainParams.util->mpool)
mainParams.gi.dictName,
linux_dictionary_make( MPPARM(mainParams.util->mpool) &mainParams,
mainParams.gi.dictName,
mainParams.useMmap );
XP_ASSERT( !!mainParams.dict );
mainParams.gi.dictLang = dict_getLangCode( mainParams.dict );
@ -1787,8 +1871,8 @@ main( int argc, char** argv )
const XP_UCHAR* name = mainParams.playerDictNames[ii];
if ( !!name ) {
mainParams.dicts.dicts[ii] =
linux_dictionary_make( MPPARM(mainParams.util->mpool) name,
mainParams.useMmap );
linux_dictionary_make( MPPARM(mainParams.util->mpool)
&mainParams, name, mainParams.useMmap );
}
}

View file

@ -66,6 +66,8 @@ XWStreamCtxt* streamFromFile( CommonGlobals* cGlobals, char* name,
void* closure );
XWStreamCtxt* streamFromDB( CommonGlobals* cGlobals, void* closure );
void writeToFile( XWStreamCtxt* stream, void* closure );
XP_Bool getDictPath( const LaunchParams *params, const char* name,
char* result, int resultLen );
int blocking_read( int fd, unsigned char* buf, int len );

View file

@ -89,7 +89,7 @@ static DictionaryCtxt*
linux_util_makeEmptyDict( XW_UtilCtxt* XP_UNUSED_DBG(uctx) )
{
XP_DEBUGF( "linux_util_makeEmptyDict called" );
return linux_dictionary_make( MPPARM(uctx->mpool) NULL, XP_FALSE );
return linux_dictionary_make( MPPARM(uctx->mpool) NULL, NULL, XP_FALSE );
} /* linux_util_makeEmptyDict */
#define EM BONUS_NONE
@ -350,7 +350,6 @@ linux_util_vt_init( MPFORMAL XW_UtilCtxt* util )
util->vtable->m_util_getSquareBonus = linux_util_getSquareBonus;
util->vtable->m_util_getCurSeconds = linux_util_getCurSeconds;
util->vtable->m_util_getUserString = linux_util_getUserString;
}
void

View file

@ -32,7 +32,9 @@ void linux_debugf(const char*, ...)
__attribute__ ((format (printf, 1, 2)));
#endif
DictionaryCtxt* linux_dictionary_make( MPFORMAL const char* dictFileName, XP_Bool useMmap );
DictionaryCtxt* linux_dictionary_make( MPFORMAL const LaunchParams* mainParams,
const char* dictFileName, XP_Bool useMMap );
void linux_util_vt_init( MPFORMAL XW_UtilCtxt* util );
void linux_util_vt_destroy( XW_UtilCtxt* util );

View file

@ -48,6 +48,7 @@ typedef struct LaunchParams {
DictionaryCtxt* dict;
CurGameInfo gi;
PlayerDicts dicts;
GSList* dictDirs;
char* fileName;
const XP_UCHAR* playerDictNames[MAX_NUM_PLAYERS];
#ifdef USE_SQLITE