mirror of
git://xwords.git.sourceforge.net/gitroot/xwords/xwords
synced 2025-01-04 23:02:02 +01:00
d799b94169
I didn't understand MQTT at all. Per the docs anyway it only keeps a message around if its "persist" flag is set, and then it only keeps the most recent per topic. I expected that when a device connected, messages would be waiting for it, but that's apparently not true (some evidence to the contrary.) But having all games on a device share the same topic means only one message can be waiting. So switch to including gameID in the topic, subscribing to a wildcard topic and sending to a different one per game. For now, for legacy purposes, we'll keep sending to the old per-device topic.
1188 lines
37 KiB
C
1188 lines
37 KiB
C
/* -*- compile-command: "find-and-gradle.sh inXw4dDeb"; -*- */
|
|
/*
|
|
* Copyright 2001 - 2022 by Eric House (xwords@eehouse.org). All rights
|
|
* reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*/
|
|
#include <sys/time.h>
|
|
|
|
#include <jni.h>
|
|
|
|
#include "comtypes.h"
|
|
#include "utilwrapper.h"
|
|
#include "anddict.h"
|
|
#include "andutils.h"
|
|
#include "paths.h"
|
|
#include "LocalizedStrIncludes.h"
|
|
#include "dbgutil.h"
|
|
#include "nli.h"
|
|
#include "strutils.h"
|
|
|
|
#define MAX_QUANTITY_STRS 4
|
|
|
|
typedef struct _AndDUtil {
|
|
XW_DUtilCtxt dutil;
|
|
JNIUtilCtxt* jniutil;
|
|
jobject jdutil; /* global ref to object implementing XW_DUtilCtxt */
|
|
DictMgrCtxt* dictMgr;
|
|
XP_UCHAR* userStrings[N_AND_USER_STRINGS];
|
|
XP_U32 userStringsBits;
|
|
#ifdef MAP_THREAD_TO_ENV
|
|
EnvThreadInfo* ti;
|
|
#endif
|
|
#ifdef XWFEATURE_DEVID
|
|
XP_UCHAR* devIDStorage;
|
|
#endif
|
|
} AndDUtil;
|
|
|
|
typedef struct _TimerStorage {
|
|
XWTimerProc proc;
|
|
void* closure;
|
|
} TimerStorage;
|
|
|
|
typedef struct _AndUtil {
|
|
XW_UtilCtxt util;
|
|
#ifdef MAP_THREAD_TO_ENV
|
|
EnvThreadInfo* ti;
|
|
#endif
|
|
jobject jutil; /* global ref to object implementing XW_UtilCtxt */
|
|
TimerStorage timerStorage[NUM_TIMERS_PLUS_ONE];
|
|
} AndUtil;
|
|
|
|
#ifndef XWFEATURE_STANDALONE_ONLY
|
|
static XWStreamCtxt*
|
|
and_util_makeStreamFromAddr( XW_UtilCtxt* uc, XWEnv XP_UNUSED(xwe),
|
|
XP_PlayerAddr channelNo )
|
|
{
|
|
#ifdef DEBUG
|
|
AndUtil* util = (AndUtil*)uc;
|
|
#endif
|
|
AndGameGlobals* globals = (AndGameGlobals*)uc->closure;
|
|
XWStreamCtxt* stream = and_empty_stream( MPPARM(util->util.mpool)
|
|
globals );
|
|
stream_setAddress( stream, channelNo );
|
|
stream_setOnCloseProc( stream, and_send_on_close );
|
|
return stream;
|
|
}
|
|
#endif
|
|
|
|
#define UTIL_CBK_HEADER(nam,sig) \
|
|
AndUtil* util = (AndUtil*)uc; \
|
|
JNIEnv* env = xwe; \
|
|
ASSERT_ENV(util->ti, env); \
|
|
if ( NULL != util->jutil ) { \
|
|
jmethodID mid = getMethodID( env, util->jutil, nam, sig )
|
|
|
|
#define UTIL_CBK_TAIL() \
|
|
} else { \
|
|
XP_LOGFF( "skipping call into java because jutil==NULL" ); \
|
|
}
|
|
|
|
#define DUTIL_CBK_HEADER(nam,sig) \
|
|
AndDUtil* dutil = (AndDUtil*)duc; \
|
|
JNIEnv* env = xwe; \
|
|
ASSERT_ENV(dutil->ti, env); \
|
|
if ( NULL != dutil->jdutil ) { \
|
|
jmethodID mid = getMethodID( env, dutil->jdutil, nam, sig )
|
|
|
|
#define DUTIL_CBK_TAIL() UTIL_CBK_TAIL()
|
|
|
|
static void
|
|
and_util_userError( XW_UtilCtxt* uc, XWEnv xwe, UtilErrID id )
|
|
{
|
|
UTIL_CBK_HEADER( "userError", "(I)V" );
|
|
(*env)->CallVoidMethod( env, util->jutil, mid, id );
|
|
if ((*env)->ExceptionOccurred(env)) {
|
|
(*env)->ExceptionDescribe(env);
|
|
(*env)->ExceptionClear(env);
|
|
XP_LOGFF( "exception found" );
|
|
}
|
|
UTIL_CBK_TAIL();
|
|
}
|
|
|
|
static void
|
|
and_util_notifyMove( XW_UtilCtxt* uc, XWEnv xwe, XWStreamCtxt* stream )
|
|
{
|
|
UTIL_CBK_HEADER("notifyMove", "(Ljava/lang/String;)V" );
|
|
|
|
jstring jstr = NULL;
|
|
if ( NULL != stream ) {
|
|
jstr = streamToJString( env, stream );
|
|
}
|
|
(*env)->CallVoidMethod( env, util->jutil, mid, jstr );
|
|
deleteLocalRef( env, jstr );
|
|
UTIL_CBK_TAIL();
|
|
}
|
|
|
|
static void
|
|
and_util_notifyTrade( XW_UtilCtxt* uc, XWEnv xwe, const XP_UCHAR** tiles, XP_U16 nTiles )
|
|
{
|
|
UTIL_CBK_HEADER("notifyTrade", "([Ljava/lang/String;)V" );
|
|
jobjectArray jtiles = makeStringArray( env, nTiles, tiles );
|
|
(*env)->CallVoidMethod( env, util->jutil, mid, jtiles );
|
|
deleteLocalRef( env, jtiles );
|
|
UTIL_CBK_TAIL();
|
|
}
|
|
|
|
static void
|
|
and_util_notifyPickTileBlank( XW_UtilCtxt* uc, XWEnv xwe, XP_U16 playerNum,
|
|
XP_U16 col, XP_U16 row,
|
|
const XP_UCHAR** tileFaces, XP_U16 nTiles )
|
|
{
|
|
UTIL_CBK_HEADER("notifyPickTileBlank", "(III[Ljava/lang/String;)V" );
|
|
|
|
jobject jtexts = makeStringArray( env, nTiles, tileFaces );
|
|
|
|
(*env)->CallVoidMethod( env, util->jutil, mid, playerNum, col, row, jtexts );
|
|
|
|
deleteLocalRef( env, jtexts );
|
|
UTIL_CBK_TAIL();
|
|
}
|
|
|
|
static void
|
|
and_util_informNeedPickTiles( XW_UtilCtxt* uc, XWEnv xwe, XP_Bool isInitial,
|
|
XP_U16 player, XP_U16 nToPick,
|
|
XP_U16 nFaces, const XP_UCHAR** faces,
|
|
const XP_U16* counts )
|
|
{
|
|
UTIL_CBK_HEADER("informNeedPickTiles",
|
|
"(ZII[Ljava/lang/String;[I)V" );
|
|
jobject jtexts = makeStringArray( env, nFaces, faces );
|
|
jobject jcounts = makeIntArray( env, nFaces, counts, sizeof(counts[0]) );
|
|
|
|
(*env)->CallVoidMethod( env, util->jutil, mid, isInitial, player,
|
|
nToPick, jtexts, jcounts );
|
|
|
|
deleteLocalRefs( env, jtexts, jcounts, DELETE_NO_REF );
|
|
UTIL_CBK_TAIL();
|
|
} /* and_util_informNeedPickTiles */
|
|
|
|
static void
|
|
and_util_informNeedPassword( XW_UtilCtxt* uc, XWEnv xwe, XP_U16 player,
|
|
const XP_UCHAR* name )
|
|
{
|
|
UTIL_CBK_HEADER("informNeedPassword", "(ILjava/lang/String;)V" );
|
|
|
|
jstring jname = (*env)->NewStringUTF( env, name );
|
|
(*env)->CallVoidMethod( env, util->jutil, mid, player, jname );
|
|
deleteLocalRef( env, jname );
|
|
UTIL_CBK_TAIL();
|
|
}
|
|
|
|
static void
|
|
and_util_trayHiddenChange(XW_UtilCtxt* uc, XWEnv xwe,
|
|
XW_TrayVisState newState,
|
|
XP_U16 nVisibleRows )
|
|
{
|
|
}
|
|
|
|
static void
|
|
and_util_yOffsetChange(XW_UtilCtxt* uc, XWEnv xwe, XP_U16 maxOffset,
|
|
XP_U16 oldOffset, XP_U16 newOffset )
|
|
{
|
|
#if 0
|
|
AndUtil* util = (AndUtil*)uc;
|
|
JNIEnv* env = *util->env;
|
|
const char* sig = "(III)V";
|
|
jmethodID mid = getMethodID( env, util->jutil, "yOffsetChange", sig );
|
|
|
|
(*env)->CallVoidMethod( env, util->jutil, mid,
|
|
maxOffset, oldOffset, newOffset );
|
|
#endif
|
|
}
|
|
|
|
#ifdef XWFEATURE_TURNCHANGENOTIFY
|
|
static void
|
|
and_util_turnChanged( XW_UtilCtxt* uc, XWEnv xwe, XP_S16 turn )
|
|
{
|
|
UTIL_CBK_HEADER( "turnChanged", "(I)V" );
|
|
(*env)->CallVoidMethod( env, util->jutil, mid, turn );
|
|
UTIL_CBK_TAIL();
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
and_util_informMove( XW_UtilCtxt* uc, XWEnv xwe, XP_S16 turn, XWStreamCtxt* expl,
|
|
XWStreamCtxt* words )
|
|
{
|
|
UTIL_CBK_HEADER( "informMove", "(ILjava/lang/String;Ljava/lang/String;)V" );
|
|
jstring jexpl = streamToJString( env, expl );
|
|
jstring jwords = !!words ? streamToJString( env, words ) : NULL;
|
|
(*env)->CallVoidMethod( env, util->jutil, mid, turn, jexpl, jwords );
|
|
deleteLocalRefs( env, jexpl, jwords, DELETE_NO_REF );
|
|
UTIL_CBK_TAIL();
|
|
}
|
|
|
|
static void
|
|
and_util_notifyDupStatus( XW_UtilCtxt* uc, XWEnv xwe, XP_Bool amHost, const XP_UCHAR* msg )
|
|
{
|
|
UTIL_CBK_HEADER( "notifyDupStatus", "(ZLjava/lang/String;)V" );
|
|
jstring jmsg = (*env)->NewStringUTF( env, msg );
|
|
(*env)->CallVoidMethod( env, util->jutil, mid, amHost, jmsg );
|
|
deleteLocalRefs( env, jmsg, DELETE_NO_REF );
|
|
UTIL_CBK_TAIL();
|
|
}
|
|
|
|
static void
|
|
and_util_informUndo( XW_UtilCtxt* uc, XWEnv xwe )
|
|
{
|
|
UTIL_CBK_HEADER( "informUndo", "()V" );
|
|
(*env)->CallVoidMethod( env, util->jutil, mid );
|
|
UTIL_CBK_TAIL();
|
|
}
|
|
|
|
static void
|
|
and_util_informNetDict( XW_UtilCtxt* uc, XWEnv xwe, const XP_UCHAR* isoCodeStr,
|
|
const XP_UCHAR* oldName, const XP_UCHAR* newName,
|
|
const XP_UCHAR* newSum, XWPhoniesChoice phoniesAction )
|
|
{
|
|
LOG_FUNC();
|
|
UTIL_CBK_HEADER( "informNetDict",
|
|
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;"
|
|
"Ljava/lang/String;L"
|
|
PKG_PATH("jni/CurGameInfo$XWPhoniesChoice") ";)V" );
|
|
jstring jIsoCode = (*env)->NewStringUTF( env, isoCodeStr );
|
|
jstring jnew = (*env)->NewStringUTF( env, newName );
|
|
jstring jsum = (*env)->NewStringUTF( env, newSum );
|
|
jstring jold = (*env)->NewStringUTF( env, oldName );
|
|
jobject jphon = intToJEnum( env, phoniesAction,
|
|
PKG_PATH("jni/CurGameInfo$XWPhoniesChoice") );
|
|
|
|
(*env)->CallVoidMethod( env, util->jutil, mid, jIsoCode, jold, jnew, jsum,
|
|
jphon );
|
|
deleteLocalRefs( env, jnew, jold, jsum, jphon, jIsoCode, DELETE_NO_REF );
|
|
|
|
UTIL_CBK_TAIL();
|
|
}
|
|
|
|
const DictionaryCtxt*
|
|
and_util_getDict( XW_UtilCtxt* uc, XWEnv xwe,
|
|
const XP_UCHAR* XP_UNUSED_DBG(isoCode),
|
|
const XP_UCHAR* dictName )
|
|
{
|
|
XP_LOGFF( "(isoCode: %s, name: %s)", isoCode, dictName );
|
|
JNIEnv* env = xwe;
|
|
XW_DUtilCtxt* duc = util_getDevUtilCtxt( uc, xwe );
|
|
|
|
DictMgrCtxt* dictMgr = ((AndDUtil*)duc)->dictMgr;
|
|
DictionaryCtxt* dict = (DictionaryCtxt*)
|
|
dmgr_get( dictMgr, xwe, dictName );
|
|
if ( !dict ) {
|
|
jstring jname = (*env)->NewStringUTF( env, dictName );
|
|
|
|
jobjectArray jstrs = makeStringArray( env, 1, NULL );
|
|
jobjectArray jbytes = makeByteArrayArray( env, 1 );
|
|
|
|
DUTIL_CBK_HEADER( "getDictPath",
|
|
"(Ljava/lang/String;[Ljava/lang/String;[[B)V" );
|
|
(*env)->CallVoidMethod( env, dutil->jdutil, mid, jname, jstrs, jbytes );
|
|
DUTIL_CBK_TAIL();
|
|
|
|
jstring jpath = (*env)->GetObjectArrayElement( env, jstrs, 0 );
|
|
jbyteArray jdata = (*env)->GetObjectArrayElement( env, jbytes, 0 );
|
|
|
|
dict = makeDict( MPPARM(duc->mpool) xwe,
|
|
TI_IF(&globalState->ti)
|
|
dictMgr, ((AndDUtil*)duc)->jniutil,
|
|
jname, jdata, jpath, NULL, false );
|
|
deleteLocalRefs( env, jname, jstrs, jbytes, jdata, jpath, DELETE_NO_REF );
|
|
}
|
|
return dict;
|
|
}
|
|
|
|
static void
|
|
and_util_notifyGameOver( XW_UtilCtxt* uc, XWEnv xwe, XP_S16 XP_UNUSED(quitter) )
|
|
{
|
|
UTIL_CBK_HEADER( "notifyGameOver", "()V" );
|
|
(*env)->CallVoidMethod( env, util->jutil, mid );
|
|
UTIL_CBK_TAIL();
|
|
}
|
|
|
|
#ifdef XWFEATURE_HILITECELL
|
|
static XP_Bool
|
|
and_util_hiliteCell( XW_UtilCtxt* uc, XP_U16 col, XP_U16 row )
|
|
{
|
|
/* don't log; this is getting called a lot */
|
|
return XP_TRUE; /* means keep going */
|
|
}
|
|
#endif
|
|
|
|
static XP_Bool
|
|
and_util_engineProgressCallback( XW_UtilCtxt* uc, XWEnv xwe )
|
|
{
|
|
XP_Bool result = XP_FALSE;
|
|
UTIL_CBK_HEADER("engineProgressCallback","()Z" );
|
|
result = (*env)->CallBooleanMethod( env, util->jutil, mid );
|
|
UTIL_CBK_TAIL();
|
|
return result;
|
|
}
|
|
|
|
/* This is added for java, not part of the util api */
|
|
bool
|
|
utilTimerFired( XW_UtilCtxt* uc, XWEnv xwe, XWTimerReason why, int handle )
|
|
{
|
|
bool handled = false;
|
|
AndUtil* util = (AndUtil*)uc;
|
|
TimerStorage* timerStorage = &util->timerStorage[why];
|
|
if ( handle == (int)timerStorage ) {
|
|
XWTimerProc proc = timerStorage->proc;
|
|
if ( !!proc ) {
|
|
handled = (*proc)( timerStorage->closure, xwe, why );
|
|
} else {
|
|
XP_LOGFF( "(why=%d): ERROR: no proc set", why );
|
|
}
|
|
} else {
|
|
XP_LOGFF( "mismatch: handle=%d; timerStorage=%d",
|
|
handle, (int)timerStorage );
|
|
}
|
|
return handled;
|
|
}
|
|
|
|
static void
|
|
and_util_setTimer( XW_UtilCtxt* uc, XWEnv xwe, XWTimerReason why, XP_U16 when,
|
|
XWTimerProc proc, void* closure )
|
|
{
|
|
UTIL_CBK_HEADER("setTimer", "(III)V" );
|
|
|
|
XP_ASSERT( why < VSIZE(util->timerStorage) );
|
|
TimerStorage* storage = &util->timerStorage[why];
|
|
storage->proc = proc;
|
|
storage->closure = closure;
|
|
(*env)->CallVoidMethod( env, util->jutil, mid,
|
|
why, when, (int)storage );
|
|
UTIL_CBK_TAIL();
|
|
}
|
|
|
|
static void
|
|
and_util_clearTimer( XW_UtilCtxt* uc, XWEnv xwe, XWTimerReason why )
|
|
{
|
|
UTIL_CBK_HEADER("clearTimer", "(I)V" );
|
|
(*env)->CallVoidMethod( env, util->jutil, mid, why );
|
|
UTIL_CBK_TAIL();
|
|
}
|
|
|
|
|
|
static void
|
|
and_util_requestTime( XW_UtilCtxt* uc, XWEnv xwe )
|
|
{
|
|
UTIL_CBK_HEADER("requestTime", "()V" );
|
|
(*env)->CallVoidMethod( env, util->jutil, mid );
|
|
UTIL_CBK_TAIL();
|
|
}
|
|
|
|
static XP_Bool
|
|
and_util_altKeyDown( XW_UtilCtxt* uc, XWEnv xwe )
|
|
{
|
|
LOG_FUNC();
|
|
return XP_FALSE;
|
|
}
|
|
|
|
XP_U32
|
|
and_dutil_getCurSeconds( XW_DUtilCtxt* XP_UNUSED(duc), XWEnv xwe )
|
|
{
|
|
XP_U32 curSeconds = getCurSeconds( xwe );
|
|
return curSeconds;
|
|
}
|
|
|
|
static DictionaryCtxt*
|
|
and_util_makeEmptyDict( XW_UtilCtxt* uc, XWEnv xwe )
|
|
{
|
|
#ifdef STUBBED_DICT
|
|
XP_ASSERT(0);
|
|
#else
|
|
AndGameGlobals* globals = (AndGameGlobals*)uc->closure;
|
|
DictionaryCtxt* result =
|
|
and_dictionary_make_empty( MPPARM( ((AndUtil*)uc)->util.mpool )
|
|
globals->jniutil );
|
|
return (DictionaryCtxt*)dict_ref( result, xwe );
|
|
#endif
|
|
}
|
|
|
|
static const XP_UCHAR*
|
|
and_dutil_getUserString( XW_DUtilCtxt* duc, XWEnv xwe, XP_U16 stringCode )
|
|
{
|
|
XP_UCHAR* result = "";
|
|
DUTIL_CBK_HEADER("getUserString", "(I)Ljava/lang/String;" );
|
|
int index = stringCode - 1; /* see LocalizedStrIncludes.h */
|
|
XP_ASSERT( index < VSIZE( dutil->userStrings ) );
|
|
|
|
XP_ASSERT( 0 == (dutil->userStringsBits & (1 << index)) );
|
|
|
|
if ( ! dutil->userStrings[index] ) {
|
|
jstring jresult = (*env)->CallObjectMethod( env, dutil->jdutil, mid,
|
|
stringCode );
|
|
jsize len = (*env)->GetStringUTFLength( env, jresult );
|
|
XP_UCHAR* buf = XP_MALLOC( dutil->dutil.mpool, len + 1 );
|
|
|
|
const char* jchars = (*env)->GetStringUTFChars( env, jresult, NULL );
|
|
XP_MEMCPY( buf, jchars, len );
|
|
buf[len] = '\0';
|
|
(*env)->ReleaseStringUTFChars( env, jresult, jchars );
|
|
deleteLocalRef( env, jresult );
|
|
dutil->userStrings[index] = buf;
|
|
}
|
|
|
|
result = dutil->userStrings[index];
|
|
DUTIL_CBK_TAIL();
|
|
return result;
|
|
}
|
|
|
|
static const XP_UCHAR*
|
|
and_dutil_getUserQuantityString( XW_DUtilCtxt* duc, XWEnv xwe,
|
|
XP_U16 stringCode, XP_U16 quantity )
|
|
{
|
|
XP_UCHAR* result = "";
|
|
DUTIL_CBK_HEADER("getUserQuantityString", "(II)Ljava/lang/String;" );
|
|
int index = stringCode - 1; /* see LocalizedStrIncludes.h */
|
|
XP_ASSERT( index < VSIZE( dutil->userStrings ) );
|
|
XP_UCHAR** ptrs;
|
|
|
|
dutil->userStringsBits |= 1 << index;
|
|
ptrs = (XP_UCHAR**)dutil->userStrings[index];
|
|
if ( !ptrs ) {
|
|
ptrs = (XP_UCHAR**)XP_CALLOC( dutil->dutil.mpool, MAX_QUANTITY_STRS * sizeof(*ptrs) );
|
|
dutil->userStrings[index] = (XP_UCHAR*)ptrs;
|
|
}
|
|
|
|
jstring jresult = (*env)->CallObjectMethod( env, dutil->jdutil, mid,
|
|
stringCode, quantity );
|
|
const char* jchars = (*env)->GetStringUTFChars( env, jresult, NULL );
|
|
int indx = 0;
|
|
for ( ; indx < MAX_QUANTITY_STRS; ++indx ) {
|
|
if ( !ptrs[indx] ) {
|
|
XP_LOGFF( "found empty slot %d for %s", indx, jchars );
|
|
break;
|
|
} else if ( 0 == XP_STRCMP( jchars, ptrs[indx] ) ) {
|
|
XP_LOGFF( "found %s at slot %d", jchars, indx );
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( !ptrs[indx] ) {
|
|
XP_ASSERT( indx < MAX_QUANTITY_STRS );
|
|
jsize len = (*env)->GetStringUTFLength( env, jresult );
|
|
XP_UCHAR* buf = XP_MALLOC( dutil->dutil.mpool, len + 1 );
|
|
XP_MEMCPY( buf, jchars, len );
|
|
buf[len] = '\0';
|
|
ptrs[indx] = buf;
|
|
}
|
|
|
|
(*env)->ReleaseStringUTFChars( env, jresult, jchars );
|
|
deleteLocalRef( env, jresult );
|
|
|
|
result = ptrs[indx];
|
|
DUTIL_CBK_TAIL();
|
|
return result;
|
|
}
|
|
|
|
static void
|
|
and_dutil_storePtr( XW_DUtilCtxt* duc, XWEnv xwe, const XP_UCHAR* keys[],
|
|
const void* data, XP_U32 len )
|
|
{
|
|
DUTIL_CBK_HEADER( "store", "(Ljava/lang/String;[B)V" );
|
|
XP_ASSERT( NULL == keys[1] );
|
|
|
|
jbyteArray jdata = makeByteArray( env, len, data );
|
|
jstring jkey = (*env)->NewStringUTF( env, keys[0] );
|
|
|
|
(*env)->CallVoidMethod( env, dutil->jdutil, mid, jkey, jdata );
|
|
|
|
deleteLocalRefs( env, jdata, jkey, DELETE_NO_REF );
|
|
|
|
DUTIL_CBK_TAIL();
|
|
}
|
|
|
|
static jbyteArray
|
|
loadToByteArray( XW_DUtilCtxt* duc, XWEnv xwe, const XP_UCHAR* key )
|
|
{
|
|
jbyteArray result = NULL;
|
|
DUTIL_CBK_HEADER( "load", "(Ljava/lang/String;)[B");
|
|
|
|
jstring jkey = (*env)->NewStringUTF( env, key );
|
|
result = (*env)->CallObjectMethod( env, dutil->jdutil, mid, jkey );
|
|
deleteLocalRef( env, jkey );
|
|
DUTIL_CBK_TAIL();
|
|
return result;
|
|
}
|
|
|
|
static void
|
|
and_dutil_loadPtr( XW_DUtilCtxt* duc, XWEnv xwe, const XP_UCHAR* keys[],
|
|
void* data, XP_U32* lenp )
|
|
{
|
|
XP_ASSERT( NULL == keys[1] );
|
|
JNIEnv* env = xwe;
|
|
jbyteArray jvalue = loadToByteArray( duc, env, keys[0] );
|
|
jsize len = 0;
|
|
if ( jvalue != NULL ) {
|
|
len = (*env)->GetArrayLength( env, jvalue );
|
|
if ( len <= *lenp ) {
|
|
jbyte* jelems = (*env)->GetByteArrayElements( env, jvalue, NULL );
|
|
XP_MEMCPY( data, jelems, len );
|
|
(*env)->ReleaseByteArrayElements( env, jvalue, jelems, 0 );
|
|
}
|
|
deleteLocalRef( env, jvalue );
|
|
}
|
|
*lenp = len;
|
|
}
|
|
|
|
#ifdef XWFEATURE_DEVICE
|
|
static void
|
|
and_dutil_forEach( XW_DUtilCtxt* duc, XWEnv xwe, const XP_UCHAR* keys[],
|
|
OnOneProc proc, void* closure )
|
|
{
|
|
XP_ASSERT(0);
|
|
}
|
|
|
|
static void
|
|
and_dutil_remove( XW_DUtilCtxt* duc, const XP_UCHAR* keys[] )
|
|
{
|
|
XP_ASSERT(0);
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
and_util_notifyIllegalWords( XW_UtilCtxt* uc, XWEnv xwe, BadWordInfo* bwi,
|
|
XP_U16 turn, XP_Bool turnLost )
|
|
{
|
|
UTIL_CBK_HEADER("notifyIllegalWords",
|
|
"(Ljava/lang/String;[Ljava/lang/String;IZ)V" );
|
|
XP_ASSERT( bwi->nWords > 0 );
|
|
if ( bwi->nWords > 0 ) {
|
|
jobjectArray jwords = makeStringArray( env, bwi->nWords,
|
|
(const XP_UCHAR**)bwi->words );
|
|
XP_ASSERT( !!bwi->dictName );
|
|
jstring jname = (*env)->NewStringUTF( env, bwi->dictName );
|
|
(*env)->CallVoidMethod( env, util->jutil, mid,
|
|
jname, jwords, turn, turnLost );
|
|
deleteLocalRefs( env, jwords, jname, DELETE_NO_REF );
|
|
}
|
|
UTIL_CBK_TAIL();
|
|
}
|
|
|
|
#ifdef XWFEATURE_CHAT
|
|
static void
|
|
and_util_showChat( XW_UtilCtxt* uc, XWEnv xwe, const XP_UCHAR* msg, XP_S16 from, XP_U32 timestamp )
|
|
{
|
|
UTIL_CBK_HEADER( "showChat", "(Ljava/lang/String;ILjava/lang/String;I)V" );
|
|
jstring jname = NULL;
|
|
if ( 0 <= from ) {
|
|
LocalPlayer* lp = &uc->gameInfo->players[from];
|
|
XP_ASSERT( !lp->isLocal );
|
|
jname = (*env)->NewStringUTF( env, lp->name );
|
|
}
|
|
|
|
jstring jmsg = (*env)->NewStringUTF( env, msg );
|
|
(*env)->CallVoidMethod( env, util->jutil, mid, jmsg, from, jname, timestamp );
|
|
deleteLocalRefs( env, jmsg, jname, DELETE_NO_REF );
|
|
UTIL_CBK_TAIL();
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
and_util_remSelected( XW_UtilCtxt* uc, XWEnv xwe )
|
|
{
|
|
UTIL_CBK_HEADER("remSelected", "()V" );
|
|
(*env)->CallVoidMethod( env, util->jutil, mid );
|
|
UTIL_CBK_TAIL();
|
|
}
|
|
|
|
static void
|
|
and_util_getMQTTIDsFor( XW_UtilCtxt* uc, XWEnv xwe, XP_U16 nRelayIDs,
|
|
const XP_UCHAR* relayIDs[] )
|
|
{
|
|
UTIL_CBK_HEADER("getMQTTIDsFor", "([Ljava/lang/String;)V" );
|
|
|
|
jobjectArray jrids = makeStringArray( env, nRelayIDs, relayIDs );
|
|
(*env)->CallVoidMethod( env, util->jutil, mid, jrids );
|
|
deleteLocalRef( env, jrids );
|
|
UTIL_CBK_TAIL();
|
|
}
|
|
|
|
static void
|
|
and_util_timerSelected( XW_UtilCtxt* uc, XWEnv xwe, XP_Bool inDuplicateMode, XP_Bool canPause )
|
|
{
|
|
UTIL_CBK_HEADER("timerSelected", "(ZZ)V" );
|
|
(*env)->CallVoidMethod( env, util->jutil, mid, inDuplicateMode, canPause );
|
|
UTIL_CBK_TAIL();
|
|
}
|
|
|
|
static void
|
|
and_util_formatPauseHistory( XW_UtilCtxt* uc, XWEnv xwe, XWStreamCtxt* stream,
|
|
DupPauseType typ, XP_S16 turn,
|
|
XP_U32 secsPrev, XP_U32 secsCur,
|
|
const XP_UCHAR* msg )
|
|
{
|
|
UTIL_CBK_HEADER( "formatPauseHistory",
|
|
"(IIIILjava/lang/String;)Ljava/lang/String;" );
|
|
jstring jmsg = !! msg ? (*env)->NewStringUTF( env, msg ) : NULL;
|
|
|
|
jstring jresult = (*env)->CallObjectMethod( env, util->jutil, mid, typ,
|
|
turn, secsPrev, secsCur, jmsg );
|
|
|
|
const char* jchars = (*env)->GetStringUTFChars( env, jresult, NULL );
|
|
stream_catString( stream, jchars );
|
|
(*env)->ReleaseStringUTFChars( env, jresult, jchars );
|
|
deleteLocalRefs( env, jresult, jmsg, DELETE_NO_REF );
|
|
UTIL_CBK_TAIL();
|
|
}
|
|
|
|
#ifndef XWFEATURE_MINIWIN
|
|
static void
|
|
and_util_bonusSquareHeld( XW_UtilCtxt* uc, XWEnv xwe, XWBonusType bonus )
|
|
{
|
|
UTIL_CBK_HEADER( "bonusSquareHeld", "(I)V" );
|
|
(*env)->CallVoidMethod( env, util->jutil, mid, bonus );
|
|
UTIL_CBK_TAIL();
|
|
}
|
|
|
|
static void
|
|
and_util_playerScoreHeld( XW_UtilCtxt* uc, XWEnv xwe, XP_U16 player )
|
|
{
|
|
UTIL_CBK_HEADER( "playerScoreHeld", "(I)V" );
|
|
(*env)->CallVoidMethod( env, util->jutil, mid, player );
|
|
UTIL_CBK_TAIL();
|
|
}
|
|
#endif
|
|
|
|
#ifdef XWFEATURE_SMS
|
|
static XP_Bool
|
|
and_dutil_phoneNumbersSame( XW_DUtilCtxt* duc, XWEnv xwe, const XP_UCHAR* p1,
|
|
const XP_UCHAR* p2 )
|
|
{
|
|
XP_Bool same = 0 == strcmp( p1, p2 );
|
|
if ( !same ) {
|
|
DUTIL_CBK_HEADER( "phoneNumbersSame",
|
|
"(Ljava/lang/String;Ljava/lang/String;)Z" );
|
|
jstring js1 = (*env)->NewStringUTF( env, p1 );
|
|
jstring js2 = (*env)->NewStringUTF( env, p2 );
|
|
same = (*env)->CallBooleanMethod( env, dutil->jdutil, mid, js1, js2 );
|
|
deleteLocalRefs( env, js1, js2, DELETE_NO_REF );
|
|
DUTIL_CBK_TAIL();
|
|
}
|
|
return same;
|
|
}
|
|
#endif
|
|
|
|
#ifdef XWFEATURE_BOARDWORDS
|
|
static void
|
|
and_util_cellSquareHeld( XW_UtilCtxt* uc, XWEnv xwe, XWStreamCtxt* words )
|
|
{
|
|
if ( NULL != words ) {
|
|
UTIL_CBK_HEADER( "cellSquareHeld", "(Ljava/lang/String;)V" );
|
|
jstring jwords = streamToJString( env, words );
|
|
(*env)->CallVoidMethod( env, util->jutil, mid, jwords );
|
|
deleteLocalRef( env, jwords );
|
|
UTIL_CBK_TAIL();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifndef XWFEATURE_STANDALONE_ONLY
|
|
|
|
static void
|
|
and_util_informMissing( XW_UtilCtxt* uc, XWEnv xwe, XP_Bool isServer,
|
|
const CommsAddrRec* hostAddr,
|
|
const CommsAddrRec* selfAddr, XP_U16 nDevs,
|
|
XP_U16 nMissing, XP_U16 nInvited )
|
|
{
|
|
UTIL_CBK_HEADER( "informMissing",
|
|
"(ZL" PKG_PATH("jni/CommsAddrRec") ";"
|
|
"L" PKG_PATH("jni/CommsAddrRec$CommsConnTypeSet") ";III)V" );
|
|
jobject jHostAddr = NULL;
|
|
if ( !!hostAddr ) {
|
|
jHostAddr = makeJAddr( env, hostAddr );
|
|
}
|
|
|
|
jobject jtypset = NULL;
|
|
if ( !!selfAddr ) {
|
|
jtypset = addrTypesToJ( env, selfAddr );
|
|
}
|
|
(*env)->CallVoidMethod( env, util->jutil, mid, isServer, jHostAddr,
|
|
jtypset, nDevs, nMissing, nInvited );
|
|
deleteLocalRefs( env, jHostAddr, jtypset, DELETE_NO_REF );
|
|
UTIL_CBK_TAIL();
|
|
}
|
|
|
|
static void
|
|
and_util_addrChange( XW_UtilCtxt* uc, XWEnv xwe,
|
|
const CommsAddrRec* oldAddr,
|
|
const CommsAddrRec* newAddr )
|
|
{
|
|
// LOG_FUNC();
|
|
}
|
|
|
|
static void
|
|
and_util_informWordsBlocked( XW_UtilCtxt* uc, XWEnv xwe, XP_U16 nBadWords,
|
|
XWStreamCtxt* words, const XP_UCHAR* dict )
|
|
{
|
|
UTIL_CBK_HEADER( "informWordsBlocked", "(ILjava/lang/String;Ljava/lang/String;)V" );
|
|
jstring jwords = streamToJString( env, words );
|
|
jstring jdict = (*env)->NewStringUTF( env, dict );
|
|
(*env)->CallVoidMethod( env, util->jutil, mid, nBadWords, jwords, jdict );
|
|
deleteLocalRefs( env, jwords, jdict, DELETE_NO_REF );
|
|
UTIL_CBK_TAIL();
|
|
}
|
|
|
|
static void
|
|
and_util_getInviteeName( XW_UtilCtxt* uc, XWEnv xwe, XP_U16 plyrNum,
|
|
XP_UCHAR* buf, XP_U16* bufLen )
|
|
{
|
|
UTIL_CBK_HEADER( "getInviteeName", "(I)Ljava/lang/String;" );
|
|
jstring jresult = (*env)->CallObjectMethod( env, util->jutil, mid, plyrNum );
|
|
if ( NULL != jresult ) {
|
|
jsize len = (*env)->GetStringUTFLength( env, jresult );
|
|
if ( len < *bufLen ) {
|
|
const char* jchars = (*env)->GetStringUTFChars( env, jresult, NULL );
|
|
XP_STRCAT( buf, jchars );
|
|
(*env)->ReleaseStringUTFChars( env, jresult, jchars );
|
|
*bufLen = len;
|
|
} else {
|
|
*bufLen = 0;
|
|
}
|
|
deleteLocalRef( env, jresult );
|
|
}
|
|
UTIL_CBK_TAIL();
|
|
}
|
|
|
|
#if defined XWFEATURE_DEVID && defined XWFEATURE_RELAY
|
|
static const XP_UCHAR*
|
|
and_dutil_getDevID( XW_DUtilCtxt* duc, XWEnv xwe, DevIDType* typ )
|
|
{
|
|
const XP_UCHAR* result = NULL;
|
|
*typ = ID_TYPE_NONE;
|
|
DUTIL_CBK_HEADER( "getDevID", "([B)Ljava/lang/String;" );
|
|
jbyteArray jbarr = makeByteArray( env, 1, NULL );
|
|
jstring jresult = (*env)->CallObjectMethod( env, dutil->jdutil, mid, jbarr );
|
|
if ( NULL != jresult ) {
|
|
const char* jchars = (*env)->GetStringUTFChars( env, jresult, NULL );
|
|
jsize len = (*env)->GetStringUTFLength( env, jresult );
|
|
if ( NULL != dutil->devIDStorage
|
|
&& 0 == XP_MEMCMP( dutil->devIDStorage, jchars, len ) ) {
|
|
XP_LOGFF( "already have matching devID" );
|
|
} else {
|
|
XP_LOGFF( "allocating storage for devID" );
|
|
XP_FREEP( dutil->dutil.mpool, &dutil->devIDStorage );
|
|
dutil->devIDStorage = XP_MALLOC( dutil->dutil.mpool, len + 1 );
|
|
XP_MEMCPY( dutil->devIDStorage, jchars, len );
|
|
dutil->devIDStorage[len] = '\0';
|
|
}
|
|
(*env)->ReleaseStringUTFChars( env, jresult, jchars );
|
|
result = (const XP_UCHAR*)dutil->devIDStorage;
|
|
|
|
jbyte* elems = (*env)->GetByteArrayElements( env, jbarr, NULL );
|
|
*typ = (DevIDType)elems[0];
|
|
(*env)->ReleaseByteArrayElements( env, jbarr, elems, 0 );
|
|
}
|
|
deleteLocalRefs( env, jbarr, jresult, DELETE_NO_REF );
|
|
DUTIL_CBK_TAIL();
|
|
return result;
|
|
}
|
|
|
|
static void
|
|
and_dutil_deviceRegistered( XW_DUtilCtxt* duc, XWEnv xwe, DevIDType typ,
|
|
const XP_UCHAR* idRelay )
|
|
{
|
|
DUTIL_CBK_HEADER( "deviceRegistered",
|
|
"(L" PKG_PATH("jni/DUtilCtxt$DevIDType") ";Ljava/lang/String;)V" );
|
|
jstring jstr = (*env)->NewStringUTF( env, idRelay );
|
|
jobject jtyp = intToJEnum( env, typ,
|
|
PKG_PATH("jni/DUtilCtxt$DevIDType") );
|
|
(*env)->CallVoidMethod( env, dutil->jdutil, mid, jtyp, jstr );
|
|
deleteLocalRefs( env, jstr, jtyp, DELETE_NO_REF );
|
|
DUTIL_CBK_TAIL();
|
|
}
|
|
#endif /* XWFEATURE_DEVID && XWFEATURE_RELAY */
|
|
|
|
#endif
|
|
|
|
#ifdef XWFEATURE_SEARCHLIMIT
|
|
static XP_Bool
|
|
and_util_getTraySearchLimits(XW_UtilCtxt* uc, XP_U16* min, XP_U16* max )
|
|
{
|
|
LOG_FUNC();
|
|
foobar; /* this should not be compiling */
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef SHOW_PROGRESS
|
|
static void
|
|
and_util_engineStarting( XW_UtilCtxt* uc, XP_U16 nBlanks )
|
|
{
|
|
UTIL_CBK_HEADER("engineStarting", "(I)V" );
|
|
(*env)->CallVoidMethod( env, util->jutil, mid, nBlanks );
|
|
UTIL_CBK_TAIL();
|
|
}
|
|
|
|
static void
|
|
and_util_engineStopping( XW_UtilCtxt* uc )
|
|
{
|
|
UTIL_CBK_HEADER("engineStopping", "()V" );
|
|
(*env)->CallVoidMethod( env, util->jutil, mid );
|
|
UTIL_CBK_TAIL();
|
|
}
|
|
#endif
|
|
|
|
static XW_DUtilCtxt*
|
|
and_util_getDevUtilCtxt( XW_UtilCtxt* uc, XWEnv xwe )
|
|
{
|
|
AndGameGlobals* globals = (AndGameGlobals*)uc->closure;
|
|
XP_ASSERT( !!globals->dutil );
|
|
return globals->dutil;
|
|
}
|
|
|
|
#ifdef COMMS_CHECKSUM
|
|
static XP_UCHAR*
|
|
and_dutil_md5sum( XW_DUtilCtxt* duc, XWEnv xwe, const XP_U8* ptr, XP_U32 len )
|
|
{
|
|
/* will be signed in the java world, and negative is bad! */
|
|
XP_ASSERT( (0x80000000 & len) == 0 );
|
|
AndDUtil* dutil = (AndDUtil*)duc;
|
|
JNIEnv* env = xwe;
|
|
struct JNIUtilCtxt* jniutil = dutil->jniutil;
|
|
jstring jsum = and_util_getMD5SumForBytes( jniutil, env, ptr, len );
|
|
XP_UCHAR* result = getStringCopy( MPPARM(duc->mpool) env, jsum );
|
|
deleteLocalRef( env, jsum );
|
|
return result;
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
and_dutil_getUsername( XW_DUtilCtxt* duc, XWEnv xwe, XP_U16 num,
|
|
XP_Bool isLocal, XP_Bool isRobot,
|
|
XP_UCHAR* buf, XP_U16* lenp )
|
|
{
|
|
DUTIL_CBK_HEADER( "getUsername", "(IZZ)Ljava/lang/String;" );
|
|
|
|
jstring jresult = (*env)->CallObjectMethod( env, dutil->jdutil, mid,
|
|
num, isLocal, isRobot );
|
|
jsize len = (*env)->GetStringUTFLength( env, jresult );
|
|
if ( len < *lenp ) {
|
|
const char* jchars = (*env)->GetStringUTFChars( env, jresult, NULL );
|
|
*lenp = XP_SNPRINTF( buf, len+1, "%s", jchars );
|
|
(*env)->ReleaseStringUTFChars( env, jresult, jchars );
|
|
deleteLocalRef( env, jresult );
|
|
}
|
|
DUTIL_CBK_TAIL();
|
|
}
|
|
|
|
static void
|
|
and_dutil_notifyPause( XW_DUtilCtxt* duc, XWEnv xwe, XP_U32 gameID, DupPauseType pauseTyp,
|
|
XP_U16 pauser, const XP_UCHAR* name,
|
|
const XP_UCHAR* msg )
|
|
{
|
|
DUTIL_CBK_HEADER( "notifyPause", "(IIILjava/lang/String;Ljava/lang/String;)V" );
|
|
jstring jname = (*env)->NewStringUTF( env, name );
|
|
jstring jmsg = (*env)->NewStringUTF( env, msg );
|
|
(*env)->CallVoidMethod( env, dutil->jdutil, mid, gameID, pauseTyp, pauser,
|
|
jname, jmsg );
|
|
deleteLocalRefs( env, jname, jmsg, DELETE_NO_REF );
|
|
DUTIL_CBK_TAIL();
|
|
}
|
|
|
|
static XP_Bool
|
|
and_dutil_haveGame( XW_DUtilCtxt* duc, XWEnv xwe, XP_U32 gameID, XP_U8 channel )
|
|
{
|
|
XP_Bool result = XP_FALSE;
|
|
DUTIL_CBK_HEADER( "haveGame", "(II)Z" );
|
|
XP_ASSERT( !!dutil->jdutil ); /* returning false by default a problem */
|
|
result = (*env)->CallBooleanMethod( env, dutil->jdutil, mid, gameID, channel );
|
|
DUTIL_CBK_TAIL();
|
|
return result;
|
|
}
|
|
|
|
static void
|
|
and_dutil_onDupTimerChanged( XW_DUtilCtxt* duc, XWEnv xwe, XP_U32 gameID,
|
|
XP_U32 oldVal, XP_U32 newVal )
|
|
{
|
|
DUTIL_CBK_HEADER( "onDupTimerChanged", "(III)V" );
|
|
(*env)->CallVoidMethod( env, dutil->jdutil, mid, gameID, oldVal, newVal );
|
|
DUTIL_CBK_TAIL();
|
|
}
|
|
|
|
static void
|
|
and_dutil_onInviteReceived( XW_DUtilCtxt* duc, XWEnv xwe, const NetLaunchInfo* nli )
|
|
{
|
|
LOGNLI( nli );
|
|
DUTIL_CBK_HEADER( "onInviteReceived", "(L" PKG_PATH("NetLaunchInfo") ";)V" );
|
|
|
|
/* Allocate a new NetLaunchInfo */
|
|
jobject jnli = makeObjectEmptyConst( env, PKG_PATH("NetLaunchInfo") );
|
|
XP_ASSERT( !!jnli );
|
|
setNLI( env, jnli, nli );
|
|
|
|
(*env)->CallVoidMethod( env, dutil->jdutil, mid, jnli );
|
|
|
|
deleteLocalRef( env, jnli );
|
|
DUTIL_CBK_TAIL();
|
|
}
|
|
|
|
static void
|
|
and_dutil_onMessageReceived( XW_DUtilCtxt* duc, XWEnv xwe, XP_U32 gameID,
|
|
const CommsAddrRec* from, const XP_U8* data, XP_U16 len )
|
|
{
|
|
LOG_FUNC();
|
|
DUTIL_CBK_HEADER( "onMessageReceived",
|
|
"(IL" PKG_PATH("jni/CommsAddrRec") ";[B)V" );
|
|
|
|
jbyteArray jmsg = makeByteArray( env, len, (jbyte*)data );
|
|
|
|
jobject jaddr = makeJAddr( env, from );
|
|
|
|
(*env)->CallVoidMethod( env, dutil->jdutil, mid, gameID, jaddr, jmsg );
|
|
|
|
deleteLocalRefs( env, jmsg, jaddr, DELETE_NO_REF );
|
|
|
|
DUTIL_CBK_TAIL();
|
|
LOG_RETURN_VOID();
|
|
}
|
|
|
|
static void
|
|
and_dutil_onCtrlReceived( XW_DUtilCtxt* duc, XWEnv xwe, const XP_U8* buf, XP_U16 len )
|
|
{
|
|
DUTIL_CBK_HEADER( "onCtrlReceived", "([B)V" );
|
|
jbyteArray jmsg = makeByteArray( env, len, (jbyte*)buf );
|
|
(*env)->CallVoidMethod( env, dutil->jdutil, mid, jmsg );
|
|
deleteLocalRef( env, jmsg );
|
|
DUTIL_CBK_TAIL();
|
|
}
|
|
|
|
static void
|
|
and_dutil_onGameGoneReceived( XW_DUtilCtxt* duc, XWEnv xwe, XP_U32 gameID,
|
|
const CommsAddrRec* from )
|
|
{
|
|
DUTIL_CBK_HEADER( "onGameGoneReceived",
|
|
"(IL" PKG_PATH("jni/CommsAddrRec") ";)V" );
|
|
jobject jaddr = makeJAddr( env, from );
|
|
(*env)->CallVoidMethod( env, dutil->jdutil, mid, gameID, jaddr );
|
|
|
|
deleteLocalRefs( env, jaddr, DELETE_NO_REF );
|
|
DUTIL_CBK_TAIL();
|
|
}
|
|
|
|
static void
|
|
and_dutil_ackMQTTMsg( XW_DUtilCtxt* duc, XWEnv xwe, XP_U32 gameID,
|
|
const MQTTDevID* senderID, const XP_U8* msg, XP_U16 len )
|
|
{
|
|
DUTIL_CBK_HEADER( "ackMQTTMsg", "(ILjava/lang/String;[B)V" );
|
|
|
|
XP_UCHAR tmp[32];
|
|
formatMQTTDevID( senderID, tmp, VSIZE(tmp) );
|
|
jstring jdevid = (*env)->NewStringUTF( env, tmp );
|
|
jbyteArray jmsg = makeByteArray( env, len, (const jbyte*)msg );
|
|
|
|
(*env)->CallVoidMethod( env, dutil->jdutil, mid, gameID, jdevid, jmsg );
|
|
|
|
deleteLocalRefs( env, jdevid, jmsg, DELETE_NO_REF );
|
|
DUTIL_CBK_TAIL();
|
|
}
|
|
|
|
XW_UtilCtxt*
|
|
makeUtil( MPFORMAL JNIEnv* env,
|
|
#ifdef MAP_THREAD_TO_ENV
|
|
EnvThreadInfo* ti,
|
|
#endif
|
|
jobject jutil, CurGameInfo* gi,
|
|
AndGameGlobals* closure )
|
|
{
|
|
AndUtil* util = (AndUtil*)XP_CALLOC( mpool, sizeof(*util) );
|
|
#ifdef MAP_THREAD_TO_ENV
|
|
util->ti = ti;
|
|
#endif
|
|
UtilVtable* vtable = (UtilVtable*)XP_CALLOC( mpool, sizeof(*vtable) );
|
|
if ( NULL != jutil ) {
|
|
util->jutil = (*env)->NewGlobalRef( env, jutil );
|
|
}
|
|
util->util.vtable = vtable;
|
|
MPASSIGN( util->util.mpool, mpool );
|
|
util->util.closure = closure;
|
|
util->util.gameInfo = gi;
|
|
|
|
#define SET_PROC(nam) vtable->m_util_##nam = and_util_##nam
|
|
|
|
#ifndef XWFEATURE_STANDALONE_ONLY
|
|
SET_PROC(makeStreamFromAddr);
|
|
#endif
|
|
SET_PROC(userError);
|
|
SET_PROC(notifyMove);
|
|
SET_PROC(notifyTrade);
|
|
SET_PROC(notifyPickTileBlank);
|
|
SET_PROC(informNeedPickTiles);
|
|
SET_PROC(informNeedPassword);
|
|
SET_PROC(trayHiddenChange);
|
|
SET_PROC(yOffsetChange);
|
|
#ifdef XWFEATURE_TURNCHANGENOTIFY
|
|
SET_PROC(turnChanged);
|
|
#endif
|
|
SET_PROC(informMove);
|
|
SET_PROC(notifyDupStatus);
|
|
SET_PROC(informUndo);
|
|
SET_PROC(informNetDict);
|
|
SET_PROC(getDict);
|
|
SET_PROC(notifyGameOver);
|
|
#ifdef XWFEATURE_HILITECELL
|
|
SET_PROC(hiliteCell);
|
|
#endif
|
|
SET_PROC(engineProgressCallback);
|
|
SET_PROC(setTimer);
|
|
SET_PROC(clearTimer);
|
|
SET_PROC(requestTime);
|
|
SET_PROC(altKeyDown);
|
|
SET_PROC(makeEmptyDict);
|
|
SET_PROC(notifyIllegalWords);
|
|
#ifdef XWFEATURE_CHAT
|
|
SET_PROC(showChat);
|
|
#endif
|
|
SET_PROC(remSelected);
|
|
SET_PROC(getMQTTIDsFor);
|
|
SET_PROC(timerSelected);
|
|
SET_PROC(formatPauseHistory);
|
|
|
|
#ifndef XWFEATURE_MINIWIN
|
|
SET_PROC(bonusSquareHeld);
|
|
SET_PROC(playerScoreHeld);
|
|
#endif
|
|
|
|
#ifdef XWFEATURE_BOARDWORDS
|
|
SET_PROC(cellSquareHeld);
|
|
#endif
|
|
|
|
#ifndef XWFEATURE_STANDALONE_ONLY
|
|
SET_PROC(informMissing);
|
|
SET_PROC(addrChange);
|
|
#endif
|
|
#ifdef XWFEATURE_SEARCHLIMIT
|
|
SET_PROC(getTraySearchLimits);
|
|
#endif
|
|
#ifdef SHOW_PROGRESS
|
|
SET_PROC(engineStarting);
|
|
SET_PROC(engineStopping);
|
|
#endif
|
|
|
|
SET_PROC(getDevUtilCtxt);
|
|
SET_PROC(informWordsBlocked);
|
|
SET_PROC(getInviteeName);
|
|
|
|
#undef SET_PROC
|
|
assertTableFull( vtable, sizeof(*vtable), "util" );
|
|
return (XW_UtilCtxt*)util;
|
|
} /* makeUtil */
|
|
|
|
void
|
|
destroyUtil( XW_UtilCtxt** utilc, JNIEnv* env )
|
|
{
|
|
AndUtil* util = (AndUtil*)*utilc;
|
|
|
|
if ( NULL != util->jutil ) {
|
|
(*env)->DeleteGlobalRef( env, util->jutil );
|
|
}
|
|
XP_FREE( util->util.mpool, util->util.vtable );
|
|
XP_FREE( util->util.mpool, util );
|
|
*utilc = NULL;
|
|
}
|
|
|
|
XW_DUtilCtxt*
|
|
makeDUtil( MPFORMAL JNIEnv* env,
|
|
#ifdef MAP_THREAD_TO_ENV
|
|
EnvThreadInfo* ti,
|
|
#endif
|
|
jobject jdutil, VTableMgr* vtMgr,
|
|
DictMgrCtxt* dmgr, JNIUtilCtxt* jniutil,
|
|
void* closure )
|
|
{
|
|
AndDUtil* dutil = (AndDUtil*)XP_CALLOC( mpool, sizeof(*dutil) );
|
|
dutil_super_init( MPPARM(mpool) &dutil->dutil );
|
|
#ifdef MAP_THREAD_TO_ENV
|
|
dutil->ti = ti;
|
|
#endif
|
|
dutil->jniutil = jniutil;
|
|
dutil->dutil.closure = closure;
|
|
dutil->dutil.vtMgr = vtMgr;
|
|
dutil->dictMgr = dmgr;
|
|
|
|
if ( NULL != jdutil ) {
|
|
dutil->jdutil = (*env)->NewGlobalRef( env, jdutil );
|
|
}
|
|
|
|
DUtilVtable* vtable = &dutil->dutil.vtable;
|
|
#define SET_DPROC(nam) vtable->m_dutil_##nam = and_dutil_##nam
|
|
SET_DPROC(getCurSeconds);
|
|
SET_DPROC(getUserString);
|
|
SET_DPROC(getUserQuantityString);
|
|
SET_DPROC(storePtr);
|
|
SET_DPROC(loadPtr);
|
|
# ifdef XWFEATURE_DEVICE
|
|
SET_DPROC(forEach);
|
|
SET_DPROC(remove);
|
|
# endif
|
|
|
|
# if defined XWFEATURE_DEVID && defined XWFEATURE_RELAY
|
|
SET_DPROC(getDevID);
|
|
SET_DPROC(deviceRegistered);
|
|
# endif
|
|
#ifdef XWFEATURE_SMS
|
|
SET_DPROC(phoneNumbersSame);
|
|
#endif
|
|
#ifdef COMMS_CHECKSUM
|
|
SET_DPROC(md5sum);
|
|
#endif
|
|
SET_DPROC(getUsername);
|
|
SET_DPROC(notifyPause);
|
|
SET_DPROC(haveGame);
|
|
SET_DPROC(onDupTimerChanged);
|
|
|
|
SET_DPROC(onInviteReceived);
|
|
SET_DPROC(onMessageReceived);
|
|
SET_DPROC(onCtrlReceived);
|
|
|
|
SET_DPROC(onGameGoneReceived);
|
|
SET_DPROC(ackMQTTMsg);
|
|
|
|
#undef SET_DPROC
|
|
|
|
assertTableFull( vtable, sizeof(*vtable), "dutil" );
|
|
return &dutil->dutil;
|
|
}
|
|
|
|
void
|
|
destroyDUtil( XW_DUtilCtxt** dutilp, JNIEnv* env )
|
|
{
|
|
AndDUtil* dutil = (AndDUtil*)*dutilp;
|
|
if ( NULL != dutil->jdutil ) {
|
|
(*env)->DeleteGlobalRef( env, dutil->jdutil );
|
|
}
|
|
|
|
for ( int ii = 0; ii < VSIZE(dutil->userStrings); ++ii ) {
|
|
XP_UCHAR* ptr = dutil->userStrings[ii];
|
|
if ( NULL != ptr ) {
|
|
if ( 0 == (dutil->userStringsBits & (1 << ii)) ) {
|
|
XP_FREE( dutil->dutil.mpool, ptr );
|
|
} else {
|
|
XP_UCHAR** ptrs = (XP_UCHAR**)ptr;
|
|
for ( int jj = 0; jj < MAX_QUANTITY_STRS; ++jj ) {
|
|
ptr = ptrs[jj];
|
|
if ( !!ptr ) {
|
|
XP_FREE( dutil->dutil.mpool, ptr );
|
|
}
|
|
}
|
|
XP_FREE( dutil->dutil.mpool, ptrs );
|
|
}
|
|
}
|
|
}
|
|
#ifdef XWFEATURE_DEVID
|
|
XP_FREEP( dutil->dutil.mpool, &dutil->devIDStorage );
|
|
#endif
|
|
}
|