diff --git a/xwords4/wasm/main.c b/xwords4/wasm/main.c index 5a4c60b26..b85d680c1 100644 --- a/xwords4/wasm/main.c +++ b/xwords4/wasm/main.c @@ -110,8 +110,21 @@ static void saveName( GameState* gs ); static bool isVisible( GameState* gs ); static int countDicts( Globals* globals ); -EM_JS(void, call_get_dict, (void* closure), { - getDict(closure); +typedef void (*GotDictProc)(void* closure, const char* lc, const char* name, + uint8_t* data, int len); + +EM_JS(void, call_get_dict, (const char* lc, GotDictProc proc, + void* closure), { + let langs; + if (lc) { + langs = [UTF8ToString(lc)]; + } else { + langs = [navigator.language.split('-')[0]]; + if (langs[0] != 'en') { + langs.push('en'); + } + } + getDict(langs, proc, closure); }); EM_JS(void, show_name, (const char* name), { @@ -315,7 +328,6 @@ sendInviteTo(GameState* gs, const MQTTDevID* remoteDevID) nli_init( &nli, &gs->gi, &myAddr, 1, 1 ); nli_setGameName( &nli, gs->gameName ); - XWStreamCtxt* stream = mem_stream_make_raw( MPPARM(globals->mpool) globals->vtMgr ); dvc_makeMQTTInvite( globals->dutil, NULL, stream, &nli ); @@ -691,6 +703,15 @@ countDicts( Globals* globals ) return count; } +static bool +haveDictFor(Globals* globals, const char* lc) +{ + int count = 0; + const XP_UCHAR* keys[] = {KEY_DICTS, lc, NULL}; + dutil_forEach( globals->dutil, NULL, keys, upCounter, &count ); + return 0 < count; +} + static void updateDeviceButtons( Globals* globals ) { @@ -740,6 +761,31 @@ onMqttMsg(void* closure, const uint8_t* data, int len ) dvc_parseMQTTPacket( globals->dutil, NULL, data, len ); } +static void +storeAsDict(Globals* globals, const char* lc, const char* name, + uint8_t* data, int len ) +{ + char shortName[32]; + sprintf( shortName, "%s", name ); + char* dot = strstr(shortName, ".xwd"); + if ( !!dot ) { + *dot = '\0'; + } + XP_LOGFF("shortName: %s", shortName); + + const XP_UCHAR* keys[] = {KEY_DICTS, lc, shortName, NULL}; + dutil_storePtr( globals->dutil, NULL, keys, data, len ); +} + +static void +onGotDict( void* closure, const char* lc, const char* name, + uint8_t* data, int len) +{ + CAST_GLOB(Globals*, globals, closure); + storeAsDict( globals, lc, name, data, len ); + updateDeviceButtons( globals ); +} + static void initDeviceGlobals( Globals* globals ) { @@ -771,7 +817,7 @@ initDeviceGlobals( Globals* globals ) call_setup( globals, buf, GITREV, now, onConflict, onFocussed, onMqttMsg ); if ( 0 == countDicts( globals ) ) { - call_get_dict( globals ); + call_get_dict( NULL, onGotDict, globals ); } } @@ -840,6 +886,40 @@ newFromInvite( Globals* globals, const NetLaunchInfo* invite ) return gs; } +typedef struct _DictDownState { + Globals* globals; + NetLaunchInfo invite; + const char* lc; +} DictDownState; + + +static void +onDictForInvite( void* closure, const char* lc, const char* name, + uint8_t* data, int len ) +{ + DictDownState* dds = (DictDownState*)closure; + if ( !!data && 0 < len ) { + storeAsDict( dds->globals, lc, name, data, len ); + loadAndDraw( dds->globals, &dds->invite, NULL, NULL ); + } else { + char msg[128]; + sprintf( msg, "Unable to download %s worldlist for invitation", dds->lc ); + call_alert( msg ); + } + XP_FREE( dds->globals->mpool, dds ); +} + +static void +onDictConfirmed( void* closure, bool confirmed ) +{ + DictDownState* dds = (DictDownState*)closure; + if ( confirmed ) { + call_get_dict( dds->lc, onDictForInvite, dds ); + } else { + XP_FREE( dds->globals->mpool, dds ); + } + } + /* If you launch a URL that encodes an invitation, you'll get here. If it's * the first time (the game hasn't been created yet) you'll get a new game * that connects to the host. If you've already created the game, you'll be @@ -853,10 +933,19 @@ gameFromInvite( Globals* globals, const NetLaunchInfo* invite ) bool needsLoad = true; GameState* gs = getSavedGame( globals, invite->gameID ); if ( !gs ) { - if ( invite->lang == 1 ) { + const char* lc = lcToLocale( invite->lang ); + if ( haveDictFor(globals, lc) ) { gs = newFromInvite( globals, invite ); } else { - call_alert( "Invitations are only supported for play in English right now." ); + char msg[128]; + sprintf( msg, "Invitation requires a wordlist %s for " + "language %s; download now?", invite->dict, lc ); + + DictDownState* dds = XP_MALLOC( globals->mpool, sizeof(*dds) ); + dds->globals = globals; + dds->invite = *invite; + dds->lc = lc; + call_confirm(globals, msg, onDictConfirmed, dds); } } return gs; @@ -1674,16 +1763,11 @@ cbckString( StringProc proc, void* closure, const char* str ) } void -gotDictBinary( void* closure, const char* xwd, const char* lang, - const char* lc, int len, uint8_t* data ) +gotDictBinary( GotDictProc proc, void* closure, const char* xwd, + const char* lc, uint8_t* data, int len ) { - XP_LOGFF( "xwd: %s; lang: %s, lc: %s, len: %d", xwd, lang, lc, len ); - - CAST_GLOB(Globals*, globals, closure); - const XP_UCHAR* keys[] = {KEY_DICTS, lc, xwd, NULL}; - dutil_storePtr( globals->dutil, NULL, keys, data, len ); - - updateDeviceButtons( globals ); + XP_LOGFF( "lc: %s, xwd: %s; len: %d", lc, xwd, len ); + (*proc)(closure, lc, xwd, data, len); } void diff --git a/xwords4/wasm/main.h b/xwords4/wasm/main.h index 52f357ac8..af8c785cb 100644 --- a/xwords4/wasm/main.h +++ b/xwords4/wasm/main.h @@ -34,7 +34,7 @@ typedef struct _TimerState { } TimerState; typedef XP_Bool (*IdleProc)(void* closure); -typedef void (*BinProc)(void* closure, const uint8_t* data, int len ); +typedef void (*BinProc)(void* closure, const uint8_t* data, int len); typedef void (*BoolProc)(void* closure, bool result); typedef struct GameState { @@ -81,7 +81,7 @@ typedef struct Globals { #define CAST_GS(typ, var, ptr) XP_ASSERT(((typ)(ptr))->_GUARD == GUARD_GS); typ var = (typ)(ptr) #define KEY_DICTS "dicts" -#define ROOT_PATH "/persisted" +#define ROOT_PATH "/persisted0.3" void main_set_timer( GameState* gs, XWTimerReason why, XP_U16 when, XWTimerProc proc, void* closure ); diff --git a/xwords4/wasm/wasmdict.c b/xwords4/wasm/wasmdict.c index a071ea69d..1278ee60d 100644 --- a/xwords4/wasm/wasmdict.c +++ b/xwords4/wasm/wasmdict.c @@ -163,6 +163,16 @@ wasm_dictionary_destroy( DictionaryCtxt* dict, XWEnv xwe ) XP_FREE( dict->mpool, ctxt ); } +DictionaryCtxt* +wasm_dictionary_make_empty( Globals* globals ) +{ + WasmDictionaryCtxt* wdict = XP_CALLOC( globals->mpool, sizeof( *wdict ) ); + dict_super_init( MPPARM(globals->mpool) (DictionaryCtxt*)wdict ); + + LOG_RETURNF( "%p", wdict ); + return (DictionaryCtxt*)wdict; +} + DictionaryCtxt* wasm_dictionary_make( Globals* globals, XWEnv xwe, const char* name, uint8_t* base, size_t len ) diff --git a/xwords4/wasm/wasmdict.h b/xwords4/wasm/wasmdict.h index ab813e1f6..205bfc87f 100644 --- a/xwords4/wasm/wasmdict.h +++ b/xwords4/wasm/wasmdict.h @@ -26,6 +26,7 @@ DictionaryCtxt* wasm_dictionary_make( Globals* globals, XWEnv xwe, const char* name, uint8_t* base, size_t len ); +DictionaryCtxt* wasm_dictionary_make_empty( Globals* globals ); void formatDictIndx( char buf[], size_t len, const char* lang, const char* name ); #endif diff --git a/xwords4/wasm/wasmdutil.c b/xwords4/wasm/wasmdutil.c index d61f692b5..8c1935054 100644 --- a/xwords4/wasm/wasmdutil.c +++ b/xwords4/wasm/wasmdutil.c @@ -410,6 +410,28 @@ wasm_dutil_md5sum( XW_DUtilCtxt* duc, XWEnv xwe, const XP_U8* ptr, return NULL; } +typedef struct _ForLangState { + XW_DUtilCtxt* duc; + XWEnv xwe; + uint8_t* ptr; + XP_U32 len; +} ForLangState; + +static XP_Bool +gotForLang( void* closure, const XP_UCHAR* keys[] ) +{ + XP_LOGFF("name: %s", keys[2]); + ForLangState* fls = (ForLangState*)closure; + dutil_loadPtr( fls->duc, fls->xwe, keys, NULL, &fls->len ); + if ( 0 < fls->len ) { + fls->ptr = XP_MALLOC( fls->duc->mpool, fls->len ); + dutil_loadPtr( fls->duc, fls->xwe, keys, fls->ptr, &fls->len ); + } else { + XP_LOGFF( "nothing for %s/%s", keys[1], keys[2] ); + } + return NULL != fls->ptr; +} + static const DictionaryCtxt* wasm_dutil_getDict( XW_DUtilCtxt* duc, XWEnv xwe, XP_LangCode lang, const XP_UCHAR* dictName ) @@ -429,10 +451,22 @@ wasm_dutil_getDict( XW_DUtilCtxt* duc, XWEnv xwe, dutil_loadPtr( duc, xwe, keys, ptr, &len ); result = wasm_dictionary_make( globals, xwe, dictName, ptr, len ); dmgr_put( globals->dictMgr, xwe, dictName, result ); + } else { + /* Try another dict in same language */ + XP_LOGFF( "trying for another %s dict", lc ); + ForLangState fls = { .duc = duc, + .xwe = xwe, + }; + const char* langKeys[] = {KEY_DICTS, lc, KEY_WILDCARD, NULL}; + dutil_forEach( duc, xwe, langKeys, gotForLang, &fls ); + if ( !!fls.ptr ) { + result = wasm_dictionary_make( globals, xwe, dictName, fls.ptr, fls.len ); + dmgr_put( globals->dictMgr, xwe, dictName, result ); + } } } - LOG_RETURNF( "%p", result ); + XP_LOGFF("(%s, %s)=>%p", lc, dictName, result ); return result; } diff --git a/xwords4/wasm/wasmutil.c b/xwords4/wasm/wasmutil.c index 27570001f..7e6484f1e 100644 --- a/xwords4/wasm/wasmutil.c +++ b/xwords4/wasm/wasmutil.c @@ -410,11 +410,9 @@ wasm_util_altKeyDown( XW_UtilCtxt* uc, XWEnv xwe ) static DictionaryCtxt* wasm_util_makeEmptyDict( XW_UtilCtxt* uc, XWEnv xwe ) { - LOG_FUNC(); /* firing */ - XP_ASSERT(0); - /* return wasm_dictionary_make( MPPARM(uc->mpool) NULL, uc->closure, */ - /* NULL, false, NULL ); */ - return NULL; + WasmUtilCtx* wuctxt = (WasmUtilCtx*)uc; + GameState* gs = wuctxt->closure; + return wasm_dictionary_make_empty( gs->globals ); } static void diff --git a/xwords4/wasm/xwutils.js b/xwords4/wasm/xwutils.js index a3abc8265..c3df4aafa 100644 --- a/xwords4/wasm/xwutils.js +++ b/xwords4/wasm/xwutils.js @@ -45,18 +45,11 @@ function registerOnce(devid, gitrev, now) { } } -function getDict(closure) { +function getDict(langs, proc, closure) { // set these later let gots = {}; - let langs = 'en'; // navigator.language; - if (langs) { - langs = [langs.split('-')[0]]; - } console.log('langs: ' + langs + '; langs[0]: ' + langs[0]); - if (langs[0] != 'en' ) { - langs.push('en'); - } let args = '?lc=' + langs.join('|'); console.log('args: ' + args); fetch('/xw4/info.py/listDicts' + args, { @@ -97,8 +90,8 @@ function getDict(closure) { dataHeap.set( new Uint8Array(data) ); // console.log('made array?: ' + dataHeap); Module.ccall('gotDictBinary', null, - ['number', 'string', 'string', 'string', 'number', 'array'], - [closure, gots.xwd, gots.langName, gots.lc, len, dataHeap]); + ['number', 'number', 'string', 'string', 'array', 'number'], + [proc, closure, gots.xwd, gots.lc, dataHeap, len]); Module._free(dataPtr); }); console.log('getDict() done');